瀏覽代碼

Merge branch 'development'

Simon Krajewski 8 年之前
父節點
當前提交
ebc057214a
共有 100 個文件被更改,包括 18206 次插入7175 次删除
  1. 1 0
      .gitignore
  2. 4 0
      .merlin
  3. 9 3
      .travis.yml
  4. 3 0
      .travis/setenv_lua.sh
  5. 1 0
      .travis/setup_lua.sh
  6. 1 1
      CONTRIBUTING.md
  7. 102 61
      Makefile
  8. 4 4
      Makefile.win
  9. 17 16
      README.md
  10. 10 7
      appveyor.yml
  11. 41 1
      extra/CHANGES.txt
  12. 1 1
      extra/ImportAll.hx
  13. 1 1
      extra/haxelib_src
  14. 1 1
      extra/release-checklist.txt
  15. 113 1
      haxe.hxproj
  16. 1 1
      libs
  17. 334 343
      src/context/common.ml
  18. 424 0
      src/context/meta.ml
  19. 872 368
      src/display/display.ml
  20. 801 0
      src/display/displayOutput.ml
  21. 59 0
      src/display/displayTypes.ml
  22. 132 682
      src/generators/codegen.ml
  23. 18 18
      src/generators/genas3.ml
  24. 686 857
      src/generators/gencommon.ml
  25. 365 133
      src/generators/gencpp.ml
  26. 172 221
      src/generators/gencs.ml
  27. 217 496
      src/generators/genhl.ml
  28. 108 152
      src/generators/genjava.ml
  29. 78 57
      src/generators/genjs.ml
  30. 372 217
      src/generators/genlua.ml
  31. 4 3
      src/generators/genneko.ml
  32. 11 10
      src/generators/genphp.ml
  33. 60 39
      src/generators/genpy.ml
  34. 26 25
      src/generators/genswf.ml
  35. 25 27
      src/generators/genswf9.ml
  36. 6 5
      src/generators/genxml.ml
  37. 1218 0
      src/generators/hl2c.ml
  38. 650 0
      src/generators/hlcode.ml
  39. 2810 0
      src/generators/hlinterp.ml
  40. 838 0
      src/generators/hlopt.ml
  41. 64 0
      src/globals.ml
  42. 554 0
      src/macro/hlmacro.ml
  43. 63 734
      src/macro/interp.ml
  44. 1869 0
      src/macro/macroApi.ml
  45. 765 0
      src/macro/macroContext.ml
  46. 187 767
      src/main.ml
  47. 45 28
      src/optimization/analyzer.ml
  48. 14 2
      src/optimization/analyzerConfig.ml
  49. 579 208
      src/optimization/analyzerTexpr.ml
  50. 125 72
      src/optimization/analyzerTexprTransformer.ml
  51. 4 2
      src/optimization/analyzerTypes.ml
  52. 97 47
      src/optimization/dce.ml
  53. 69 14
      src/optimization/filters.ml
  54. 33 256
      src/optimization/optimizer.ml
  55. 306 0
      src/optimization/optimizerTexpr.ml
  56. 148 0
      src/path.ml
  57. 634 0
      src/server.ml
  58. 124 233
      src/syntax/ast.ml
  59. 17 3
      src/syntax/lexer.mll
  60. 206 112
      src/syntax/parser.ml
  61. 67 0
      src/typing/abstract.ml
  62. 94 0
      src/typing/error.ml
  63. 60 47
      src/typing/matcher.ml
  64. 261 0
      src/typing/overloads.ml
  65. 293 113
      src/typing/type.ml
  66. 335 119
      src/typing/typecore.ml
  67. 217 227
      src/typing/typeload.ml
  68. 166 349
      src/typing/typer.ml
  69. 37 0
      std/Any.hx
  70. 5 5
      std/Array.hx
  71. 1 1
      std/Class.hx
  72. 3 3
      std/Date.hx
  73. 14 0
      std/DateTools.hx
  74. 1 1
      std/EReg.hx
  75. 1 1
      std/Enum.hx
  76. 1 1
      std/EnumValue.hx
  77. 1 1
      std/IntIterator.hx
  78. 1 1
      std/Lambda.hx
  79. 1 1
      std/List.hx
  80. 1 1
      std/Map.hx
  81. 6 1
      std/Math.hx
  82. 1 1
      std/Reflect.hx
  83. 17 16
      std/StdTypes.hx
  84. 1 1
      std/String.hx
  85. 4 4
      std/StringTools.hx
  86. 2 2
      std/Type.hx
  87. 2 2
      std/UInt.hx
  88. 2 2
      std/Xml.hx
  89. 2 3
      std/cpp/AutoCast.hx
  90. 4 1
      std/cpp/Callable.hx
  91. 7 0
      std/cpp/ConstPointer.hx
  92. 28 0
      std/cpp/FILE.hx
  93. 20 17
      std/cpp/Function.hx
  94. 5 1
      std/cpp/Lib.hx
  95. 12 0
      std/cpp/NativeArc.hx
  96. 19 1
      std/cpp/NativeArray.hx
  97. 2 13
      std/cpp/Object.hx
  98. 9 10
      std/cpp/Pointer.hx
  99. 2 0
      std/cpp/RawConstPointer.hx
  100. 2 0
      std/cpp/RawPointer.hx

+ 1 - 0
.gitignore

@@ -4,6 +4,7 @@
 *.cmi
 *.cmxa
 *.cmo
+*.cmt
 *.a
 *.exe
 .*.swp

+ 4 - 0
.merlin

@@ -0,0 +1,4 @@
+S src/**
+B src/**
+S libs/**
+B libs/**

+ 9 - 3
.travis.yml

@@ -11,8 +11,6 @@ env:
     # - secure: "ETbwZaeRq8wIVZVyUk1IsNctYVuQa/U2biRkF9pQkz3MEXpaneynclVzNjm8rnm8JqfKcjUDUvQJBP1KYrJYq3tAJFhl31YUnS0FsF3sgLIcnHkhbRA24xJdIlCwHP6QUPoiyPbkec43NRwrF0071KOMD51vgUToXRtAe3o/15g="
     # - secure: "Fcrrge2f4jFYDOopig2rwkQvgJw6Ra8UK6OwTVk08wecytzVaOJK1TcB22PSvZ+h0ZLJs34T+pXHFjlNuSWm4+CwGSvnltRD1/svjS8zOqK7RzuUdzHz87yruz9PFqV63HTas6qtmgLqp8n/Q6AhtDLF39BTZPyDzEbi9qkwRuI="
     # - secure: "VBJDQNJ9uvdt0aszo7oU3txuRvjkuLmuHZGOkrd4wE/5B4sX5jzx/+dnrKcNTXJCmQ/rVLuMu9GyxqVjNHlzce678voxdQNOtNkNgpkr1qN9/A9rRnCp77hH27ErdthpWxbmcnE62hAJ83TIKSvn//5lAkx4sMCKS1NXEWQ5qec="
-    # HAXECI_GH_TOKEN: haxe-ci Github personal access token
-    - secure: "TpEMYTLgNrVD7kR6hs6EwyWNXUxnfV6XO5MGvYQncKXB1N65PG18n4WQFhnKaH8C2QTFE7dq7688ooXGzwWeoT9WAOBey10jP1f7LXEAjMGAUA4vh2zS93qBZ92ZgzCDZnQN7ZOTQGocwU6Xolu+7/6hP2M8041HBixmFuNkXF4="
     # PPA configs
     - PPA="ppa:haxe/snapshots"
     - DEBFULLNAME="Haxe CI Bot"
@@ -23,8 +21,11 @@ env:
 
 sudo: required
 dist: trusty
+osx_image: xcode6.4
 addons: &addons
-  ssh_known_hosts: haxe.org
+  ssh_known_hosts:
+    - haxe.org
+    - api.haxe.org
 
 install_linux: &install_linux
   # Install neko and haxe dependencies
@@ -56,11 +57,13 @@ install_linux: &install_linux
   - make package_src -s
   - make -s
   - make package_bin -s
+  - ls -l out
   - export PATH="$PATH:$TRAVIS_BUILD_DIR"
   - export HAXE_STD_PATH="$TRAVIS_BUILD_DIR/std"
 
 install_osx: &install_osx
   # Install haxe dependencies
+  - brew uninstall --force brew-cask # https://github.com/caskroom/homebrew-cask/pull/15381
   - travis_retry brew update
   - travis_retry brew install ocaml camlp4;
   # Install neko
@@ -75,8 +78,11 @@ install_osx: &install_osx
   - make package_src -s
   - make -s
   - make package_bin -s
+  - ls -l out
   - export PATH="$PATH:$TRAVIS_BUILD_DIR"
   - export HAXE_STD_PATH="$TRAVIS_BUILD_DIR/std"
+  # Install pip (used in lua test)
+  - curl https://bootstrap.pypa.io/get-pip.py -o - | sudo python
 
 matrix:
   include:

+ 3 - 0
.travis/setenv_lua.sh

@@ -1,3 +1,6 @@
+set -o xtrace
+set -e
+
 export PATH=${PATH}:$HOME/.lua:$HOME/.local/bin:$TRAVIS_BUILD_DIR/install/luarocks/bin
 bash .travis/setup_lua.sh
 eval `$HOME/.lua/luarocks path`

+ 1 - 0
.travis/setup_lua.sh

@@ -6,6 +6,7 @@
 # luajit2.0 - master v2.0
 # luajit2.1 - master v2.1
 
+set -o xtrace
 set -eufo pipefail
 
 LUAJIT_VERSION="2.0.4"

+ 1 - 1
CONTRIBUTING.md

@@ -1,6 +1,6 @@
 ## Things to check before/while filing an issue:
 
-- Check if you actually suspect that there's an issue in the Haxe code. If you find yourself writing "How do I..." you may want to consider a different communication channel. Refer to http://haxe.org/community/community-support.html for more information.
+- Check if you actually suspect that there's an issue in the Haxe code. If you find yourself writing "How do I..." you may want to consider a different communication channel. Refer to https://haxe.org/community/community-support.html for more information.
 - Reduce your code to a minimal example (see http://sscce.org/). In particular avoid library dependencies: If you cannot reproduce your issue without using a specific library, it might not be a Haxe issue to begin with.
 - Check if your problems are already resolved in the Haxe development version (for builds see http://builds.haxe.org/).
 - Most targets produce readable code. If you suspect the generated code to be wrong, try checking the output. Note that you can add `-D dump=pretty` to your compilation parameters and find the code which is passed to the generators in a `dump` subdirectory.

+ 102 - 61
Makefile

@@ -17,14 +17,17 @@ INSTALL_STD_DIR=$(INSTALL_LIB_DIR)/std
 PACKAGE_OUT_DIR=out
 PACKAGE_SRC_EXTENSION=.tar.gz
 
+PLATFORM?=unix
+
 OUTPUT=haxe
 EXTENSION=
 OCAMLOPT?=ocamlopt
 OCAMLC?=ocamlc
 LFLAGS=
 
-CFLAGS= -g -w -3 -I libs/extlib -I libs/extc -I libs/neko -I libs/javalib -I libs/ziplib -I libs/swflib -I libs/xml-light -I libs/ttflib -I libs/ilib -I libs/objsize \
-	-I src -I src/generators -I src/macro -I src/optimization -I src/syntax -I src/typing -I src/display
+CFLAGS= -bin-annot
+ALL_CFLAGS= $(CFLAGS) -g -w -3 -I libs/extlib -I libs/extc -I libs/neko -I libs/javalib -I libs/ziplib -I libs/swflib -I libs/xml-light -I libs/ttflib -I libs/ilib -I libs/objsize \
+	-I src -I src/context -I src/generators -I src/macro -I src/optimization -I src/syntax -I src/typing -I src/display
 
 LIBS=unix str libs/extlib/extLib libs/xml-light/xml-light libs/swflib/swflib \
 	libs/extc/extc libs/neko/neko libs/javalib/java libs/ziplib/zip \
@@ -45,21 +48,22 @@ else
 	MODULE_EXT = cmx
 endif
 
-CC_CMD = $(COMPILER) $(CFLAGS) -c $<
+CC_CMD = $(COMPILER) $(ALL_CFLAGS) -c $<
 
-CC_PARSER_CMD = $(COMPILER) -pp camlp4o $(CFLAGS) -c src/syntax/parser.ml
+CC_PARSER_CMD = $(COMPILER) -pp camlp4o $(ALL_CFLAGS) -c src/syntax/parser.ml
 
 RELDIR=../../..
 
-MODULES=json syntax/ast typing/type syntax/lexer typing/common generators/genxml syntax/parser typing/typecore \
-	display/display \
-	optimization/optimizer typing/typeload generators/codegen generators/gencommon generators/genas3 \
+MODULES=json version globals path context/meta syntax/ast display/displayTypes typing/type typing/error \
+	syntax/lexer context/common generators/genxml \
+	syntax/parser typing/abstract typing/typecore display/display optimization/optimizerTexpr \
+	optimization/optimizer typing/overloads typing/typeload generators/codegen generators/gencommon generators/genas3 \
 	generators/gencpp generators/genjs generators/genneko generators/genphp generators/genswf9 \
-	generators/genswf generators/genjava generators/gencs generators/genpy macro/interp generators/genhl \
+	generators/genswf generators/genjava generators/gencs generators/genpy macro/macroApi macro/interp generators/hlcode generators/hlopt generators/hlinterp generators/hl2c \
 	generators/genlua \
 	optimization/dce optimization/analyzerConfig optimization/analyzerTypes optimization/analyzerTexpr \
-	optimization/analyzerTexprTransformer optimization/analyzer \
-	optimization/filters typing/typer typing/matcher version main
+	optimization/analyzerTexprTransformer optimization/analyzer generators/genhl \
+	optimization/filters macro/hlmacro macro/macroContext typing/typer typing/matcher display/displayOutput server main
 
 ADD_REVISION?=0
 
@@ -104,17 +108,13 @@ haxelib:
 
 tools: haxelib
 
-install:
-	rm -rf $(INSTALL_LIB_DIR)
+install: uninstall
 	mkdir -p $(INSTALL_BIN_DIR)
 	mkdir -p $(INSTALL_LIB_DIR)/lib
-	rm -rf $(INSTALL_STD_DIR)
 	cp -rf std $(INSTALL_STD_DIR)
 	cp -rf extra $(INSTALL_LIB_DIR)
-	rm -f $(INSTALL_BIN_DIR)/haxe
 	cp haxe $(INSTALL_LIB_DIR)
 	ln -s $(INSTALL_LIB_DIR)/haxe $(INSTALL_BIN_DIR)/haxe
-	rm -f $(INSTALL_BIN_DIR)/haxelib
 	cp haxelib $(INSTALL_LIB_DIR)
 	ln -s $(INSTALL_LIB_DIR)/haxelib $(INSTALL_BIN_DIR)/haxelib
 	chmod -R a+rx $(INSTALL_LIB_DIR)
@@ -127,99 +127,139 @@ install_tools: tools
 	chmod a+rx $(INSTALL_BIN_DIR)/haxelib
 
 uninstall:
-	rm -rf $(INSTALL_BIN_DIR)/haxe $(INSTALL_BIN_DIR)/haxelib $(INSTALL_LIB_DIR)
+	rm -rf $(INSTALL_BIN_DIR)/haxe $(INSTALL_BIN_DIR)/haxelib
+	if [ -d "$(INSTALL_LIB_DIR)/lib" ] && find "$(INSTALL_LIB_DIR)/lib" -mindepth 1 -print -quit | grep -q .; then \
+		echo "The local haxelib repo at $(INSTALL_LIB_DIR)/lib will not be removed. Remove it manually if you want."; \
+		find $(INSTALL_LIB_DIR)/ ! -name 'lib' -mindepth 1 -maxdepth 1 -exec rm -rf {} +; \
+	else \
+		rm -rf $(INSTALL_LIB_DIR); \
+	fi
+	
 
 # Modules
 
+# context
+
+src/context/common.$(MODULE_EXT): src/globals.$(MODULE_EXT) src/typing/type.$(MODULE_EXT) src/syntax/ast.$(MODULE_EXT) src/display/displayTypes.$(MODULE_EXT) libs/ilib/il.$(LIB_EXT)
+
+src/context/meta.$(MODULE_EXT): src/globals.$(MODULE_EXT)
+
 # display
 
-src/display/display.$(MODULE_EXT): src/syntax/ast.$(MODULE_EXT) src/typing/common.$(MODULE_EXT) src/typing/type.$(MODULE_EXT) src/typing/typecore.$(MODULE_EXT) src/syntax/parser.$(MODULE_EXT)
+src/display/display.$(MODULE_EXT): src/globals.$(MODULE_EXT) src/context/meta.$(MODULE_EXT) src/path.$(MODULE_EXT) src/syntax/ast.$(MODULE_EXT) src/context/common.$(MODULE_EXT) src/typing/type.$(MODULE_EXT) src/typing/typecore.$(MODULE_EXT) src/syntax/parser.$(MODULE_EXT) src/display/displayTypes.$(MODULE_EXT)
+
+src/display/displayTypes.$(MODULE_EXT) : src/globals.$(MODULE_EXT) src/syntax/ast.$(MODULE_EXT)
+
+src/display/displayOutput.$(MODULE_EXT): src/globals.$(MODULE_EXT) src/typing/type.$(MODULE_EXT) src/typing/typer.$(MODULE_EXT) src/context/common.$(MODULE_EXT) src/display/display.$(MODULE_EXT)
 
 # generators
 
-src/generators/codegen.$(MODULE_EXT): src/typing/typecore.$(MODULE_EXT) src/typing/type.$(MODULE_EXT) src/generators/genxml.$(MODULE_EXT) src/typing/common.$(MODULE_EXT) src/syntax/ast.$(MODULE_EXT) src/display/display.$(MODULE_EXT)
+src/generators/codegen.$(MODULE_EXT): src/globals.$(MODULE_EXT) src/typing/type.$(MODULE_EXT) src/generators/genxml.$(MODULE_EXT) src/context/common.$(MODULE_EXT) src/syntax/ast.$(MODULE_EXT)
+
+src/generators/genas3.$(MODULE_EXT): src/typing/abstract.$(MODULE_EXT) src/globals.$(MODULE_EXT) src/context/meta.$(MODULE_EXT) src/typing/type.$(MODULE_EXT) src/context/common.$(MODULE_EXT) src/generators/codegen.$(MODULE_EXT) src/syntax/ast.$(MODULE_EXT)
+
+src/generators/gencommon.$(MODULE_EXT): src/typing/abstract.$(MODULE_EXT) src/context/meta.$(MODULE_EXT) src/globals.$(MODULE_EXT) src/typing/error.$(MODULE_EXT) src/typing/overloads.$(MODULE_EXT) src/path.$(MODULE_EXT) src/typing/type.$(MODULE_EXT) src/context/common.$(MODULE_EXT) src/generators/codegen.$(MODULE_EXT) src/syntax/ast.$(MODULE_EXT) libs/ilib/il.$(LIB_EXT)
 
-src/generators/genas3.$(MODULE_EXT): src/typing/type.$(MODULE_EXT) src/typing/common.$(MODULE_EXT) src/generators/codegen.$(MODULE_EXT) src/syntax/ast.$(MODULE_EXT)
+src/generators/gencpp.$(MODULE_EXT): src/typing/abstract.$(MODULE_EXT) src/globals.$(MODULE_EXT) src/context/meta.$(MODULE_EXT) src/path.$(MODULE_EXT) src/typing/type.$(MODULE_EXT) src/syntax/lexer.$(MODULE_EXT) src/context/common.$(MODULE_EXT) src/generators/codegen.$(MODULE_EXT) src/syntax/ast.$(MODULE_EXT)
 
-src/generators/gencommon.$(MODULE_EXT): src/typing/type.$(MODULE_EXT) src/typing/common.$(MODULE_EXT) src/generators/codegen.$(MODULE_EXT) src/syntax/ast.$(MODULE_EXT) src/typing/typeload.$(MODULE_EXT) libs/ilib/il.$(LIB_EXT)
+src/generators/gencs.$(MODULE_EXT): src/typing/abstract.$(MODULE_EXT) src/globals.$(MODULE_EXT) src/context/meta.$(MODULE_EXT) src/path.$(MODULE_EXT) src/typing/type.$(MODULE_EXT) src/syntax/lexer.$(MODULE_EXT) src/generators/gencommon.$(MODULE_EXT) src/context/common.$(MODULE_EXT) src/generators/codegen.$(MODULE_EXT) src/syntax/ast.$(MODULE_EXT) libs/ilib/il.$(LIB_EXT)
 
-src/generators/gencpp.$(MODULE_EXT): src/typing/type.$(MODULE_EXT) src/syntax/lexer.$(MODULE_EXT) src/typing/common.$(MODULE_EXT) src/generators/codegen.$(MODULE_EXT) src/syntax/ast.$(MODULE_EXT) src/generators/gencommon.$(MODULE_EXT)
+src/generators/genjava.$(MODULE_EXT): src/typing/abstract.$(MODULE_EXT) src/globals.$(MODULE_EXT) src/context/meta.$(MODULE_EXT) src/path.$(MODULE_EXT) src/typing/type.$(MODULE_EXT) src/generators/gencommon.$(MODULE_EXT) src/context/common.$(MODULE_EXT) src/generators/codegen.$(MODULE_EXT) src/syntax/ast.$(MODULE_EXT)
 
-src/generators/gencs.$(MODULE_EXT): src/typing/type.$(MODULE_EXT) src/syntax/lexer.$(MODULE_EXT) src/generators/gencommon.$(MODULE_EXT) src/typing/common.$(MODULE_EXT) src/generators/codegen.$(MODULE_EXT) src/syntax/ast.$(MODULE_EXT) libs/ilib/il.$(LIB_EXT)
+src/generators/genjs.$(MODULE_EXT): src/typing/abstract.$(MODULE_EXT) src/globals.$(MODULE_EXT) src/context/meta.$(MODULE_EXT) src/path.$(MODULE_EXT) src/typing/type.$(MODULE_EXT) src/syntax/lexer.$(MODULE_EXT) src/context/common.$(MODULE_EXT) src/generators/codegen.$(MODULE_EXT) src/syntax/ast.$(MODULE_EXT)
 
-src/generators/genjava.$(MODULE_EXT): src/typing/type.$(MODULE_EXT) src/generators/gencommon.$(MODULE_EXT) src/typing/common.$(MODULE_EXT) src/generators/codegen.$(MODULE_EXT) src/syntax/ast.$(MODULE_EXT)
+src/generators/genneko.$(MODULE_EXT): src/globals.$(MODULE_EXT) src/typing/type.$(MODULE_EXT) src/syntax/lexer.$(MODULE_EXT) src/context/common.$(MODULE_EXT) src/generators/codegen.$(MODULE_EXT) src/syntax/ast.$(MODULE_EXT)
 
-src/generators/genjs.$(MODULE_EXT): src/typing/type.$(MODULE_EXT) src/syntax/lexer.$(MODULE_EXT) src/typing/common.$(MODULE_EXT) src/generators/codegen.$(MODULE_EXT) src/syntax/ast.$(MODULE_EXT)
+src/generators/genlua.$(MODULE_EXT): src/typing/abstract.$(MODULE_EXT) src/globals.$(MODULE_EXT) src/context/meta.$(MODULE_EXT) src/typing/type.$(MODULE_EXT) src/syntax/lexer.$(MODULE_EXT) src/context/common.$(MODULE_EXT) src/generators/codegen.$(MODULE_EXT) src/syntax/ast.$(MODULE_EXT)
 
-src/generators/genneko.$(MODULE_EXT): src/typing/type.$(MODULE_EXT) src/syntax/lexer.$(MODULE_EXT) src/typing/common.$(MODULE_EXT) src/generators/codegen.$(MODULE_EXT) src/syntax/ast.$(MODULE_EXT)
+src/generators/genphp.$(MODULE_EXT): src/typing/abstract.$(MODULE_EXT) src/globals.$(MODULE_EXT) src/context/meta.$(MODULE_EXT) src/typing/type.$(MODULE_EXT) src/syntax/lexer.$(MODULE_EXT) src/context/common.$(MODULE_EXT) src/generators/codegen.$(MODULE_EXT) src/syntax/ast.$(MODULE_EXT)
 
-src/generators/genlua.$(MODULE_EXT): src/typing/type.$(MODULE_EXT) src/syntax/lexer.$(MODULE_EXT) src/typing/common.$(MODULE_EXT) src/generators/codegen.$(MODULE_EXT) src/syntax/ast.$(MODULE_EXT)
+src/generators/genpy.$(MODULE_EXT): src/typing/abstract.$(MODULE_EXT) src/globals.$(MODULE_EXT) src/context/meta.$(MODULE_EXT) src/typing/type.$(MODULE_EXT) src/syntax/lexer.$(MODULE_EXT) src/context/common.$(MODULE_EXT) src/generators/codegen.$(MODULE_EXT) src/syntax/ast.$(MODULE_EXT)
 
-src/generators/genphp.$(MODULE_EXT): src/typing/type.$(MODULE_EXT) src/syntax/lexer.$(MODULE_EXT) src/typing/common.$(MODULE_EXT) src/generators/codegen.$(MODULE_EXT) src/syntax/ast.$(MODULE_EXT)
+src/generators/genswf.$(MODULE_EXT): src/globals.$(MODULE_EXT) src/typing/type.$(MODULE_EXT) src/generators/genswf9.$(MODULE_EXT) src/context/common.$(MODULE_EXT) src/syntax/ast.$(MODULE_EXT)
 
-src/generators/genpy.$(MODULE_EXT): src/typing/type.$(MODULE_EXT) src/syntax/lexer.$(MODULE_EXT) src/typing/common.$(MODULE_EXT) src/generators/codegen.$(MODULE_EXT) src/syntax/ast.$(MODULE_EXT)
+src/generators/hlinterp.$(MODULE_EXT): src/context/common.$(MODULE_EXT) src/generators/hlcode.$(MODULE_EXT) src/macro/interp.$(MODULE_EXT) src/generators/hlopt.$(MODULE_EXT) src/macro/macroApi.$(MODULE_EXT)
 
-src/generators/genswf.$(MODULE_EXT): src/typing/type.$(MODULE_EXT) src/generators/genswf9.$(MODULE_EXT) src/typing/common.$(MODULE_EXT) src/syntax/ast.$(MODULE_EXT)
+src/generators/hl2c.$(MODULE_EXT): src/generators/hlcode.$(MODULE_EXT)
 
-src/generators/genhl.$(MODULE_EXT): src/typing/type.$(MODULE_EXT) src/syntax/lexer.$(MODULE_EXT) src/typing/common.$(MODULE_EXT) src/generators/codegen.$(MODULE_EXT) src/syntax/ast.$(MODULE_EXT) src/macro/interp.$(MODULE_EXT)
+src/generators/hlopt.$(MODULE_EXT): src/generators/hlcode.$(MODULE_EXT)
 
-src/generators/genswf9.$(MODULE_EXT): src/typing/type.$(MODULE_EXT) src/syntax/lexer.$(MODULE_EXT) src/typing/common.$(MODULE_EXT) src/generators/codegen.$(MODULE_EXT) src/syntax/ast.$(MODULE_EXT)
+src/generators/genhl.$(MODULE_EXT): src/globals.$(MODULE_EXT) src/context/meta.$(MODULE_EXT) src/typing/type.$(MODULE_EXT) src/syntax/lexer.$(MODULE_EXT) src/context/common.$(MODULE_EXT) src/generators/codegen.$(MODULE_EXT) src/syntax/ast.$(MODULE_EXT) src/generators/hlcode.$(MODULE_EXT) src/generators/hlinterp.$(MODULE_EXT) src/generators/hl2c.$(MODULE_EXT) src/generators/hlopt.$(MODULE_EXT)
 
-src/generators/genxml.$(MODULE_EXT): src/typing/type.$(MODULE_EXT) src/syntax/lexer.$(MODULE_EXT) src/typing/common.$(MODULE_EXT) src/syntax/ast.$(MODULE_EXT)
+src/generators/genswf9.$(MODULE_EXT): src/typing/abstract.$(MODULE_EXT) src/globals.$(MODULE_EXT) src/context/meta.$(MODULE_EXT) src/path.$(MODULE_EXT) src/typing/type.$(MODULE_EXT) src/syntax/lexer.$(MODULE_EXT) src/context/common.$(MODULE_EXT) src/generators/codegen.$(MODULE_EXT) src/syntax/ast.$(MODULE_EXT)
+
+src/generators/genxml.$(MODULE_EXT): src/globals.$(MODULE_EXT) src/context/meta.$(MODULE_EXT) src/typing/type.$(MODULE_EXT) src/syntax/lexer.$(MODULE_EXT) src/context/common.$(MODULE_EXT) src/syntax/ast.$(MODULE_EXT)
 
 # macro
 
-src/macro/interp.$(MODULE_EXT): src/typing/typecore.$(MODULE_EXT) src/typing/type.$(MODULE_EXT) src/syntax/lexer.$(MODULE_EXT) src/generators/genneko.$(MODULE_EXT) src/typing/common.$(MODULE_EXT) src/generators/codegen.$(MODULE_EXT) src/syntax/ast.$(MODULE_EXT) src/generators/genswf.$(MODULE_EXT) src/generators/genjava.$(MODULE_EXT) src/generators/gencs.$(MODULE_EXT) src/syntax/parser.$(MODULE_EXT) libs/ilib/il.$(LIB_EXT)
+src/macro/interp.$(MODULE_EXT): src/typing/abstract.$(MODULE_EXT) src/context/meta.$(MODULE_EXT) src/globals.$(MODULE_EXT) src/typing/error.$(MODULE_EXT) src/globals.$(MODULE_EXT) src/path.$(MODULE_EXT) src/typing/type.$(MODULE_EXT) src/syntax/lexer.$(MODULE_EXT) src/generators/genneko.$(MODULE_EXT) src/context/common.$(MODULE_EXT) src/generators/codegen.$(MODULE_EXT) src/syntax/ast.$(MODULE_EXT) src/generators/genswf.$(MODULE_EXT) src/generators/genjava.$(MODULE_EXT) src/generators/gencs.$(MODULE_EXT) src/syntax/parser.$(MODULE_EXT) libs/ilib/il.$(LIB_EXT) src/macro/macroApi.$(MODULE_EXT)
+
+src/macro/macroContext.$(MODULE_EXT): src/typing/abstract.$(MODULE_EXT) src/context/meta.$(MODULE_EXT) src/globals.$(MODULE_EXT) src/typing/error.$(MODULE_EXT) src/optimization/optimizerTexpr.$(MODULE_EXT) src/typing/overloads.$(MODULE_EXT) src/path.$(MODULE_EXT) src/typing/typeload.$(MODULE_EXT) src/typing/typecore.$(MODULE_EXT) src/typing/type.$(MODULE_EXT) src/syntax/parser.$(MODULE_EXT) src/optimization/optimizer.$(MODULE_EXT) src/syntax/lexer.$(MODULE_EXT) src/macro/interp.$(MODULE_EXT) src/context/common.$(MODULE_EXT) src/generators/codegen.$(MODULE_EXT) src/syntax/ast.$(MODULE_EXT) src/optimization/filters.$(MODULE_EXT) src/generators/genjs.$(MODULE_EXT) src/display/display.$(MODULE_EXT) src/macro/hlmacro.$(MODULE_EXT) src/macro/macroApi.$(MODULE_EXT)
+
+src/macro/hlmacro.$(MODULE_EXT): src/generators/hlinterp.$(MODULE_EXT) src/generators/genhl.$(MODULE_EXT) src/macro/macroApi.$(MODULE_EXT)
+
+src/macro/macroApi.$(MODULE_EXT): src/generators/genjs.$(MODULE_EXT) src/generators/gencs.$(MODULE_EXT) src/generators/genjava.$(MODULE_EXT) src/syntax/parser.$(MODULE_EXT)
 
 # optimization
 
-src/optimization/analyzer.$(MODULE_EXT): src/syntax/ast.$(MODULE_EXT) src/typing/type.$(MODULE_EXT) src/typing/common.$(MODULE_EXT) src/optimization/analyzerConfig.$(MODULE_EXT) src/optimization/analyzerTypes.$(MODULE_EXT) src/optimization/analyzerTexpr.$(MODULE_EXT) src/optimization/analyzerTexprTransformer.$(MODULE_EXT) src/generators/codegen.$(MODULE_EXT)
+src/optimization/analyzer.$(MODULE_EXT): src/context/meta.$(MODULE_EXT) src/globals.$(MODULE_EXT) src/optimization/optimizer.$(MODULE_EXT) src/syntax/ast.$(MODULE_EXT) src/typing/type.$(MODULE_EXT) src/context/common.$(MODULE_EXT) src/optimization/analyzerConfig.$(MODULE_EXT) src/optimization/analyzerTypes.$(MODULE_EXT) src/optimization/analyzerTexpr.$(MODULE_EXT) src/optimization/analyzerTexprTransformer.$(MODULE_EXT) src/generators/codegen.$(MODULE_EXT)
+
+src/optimization/analyzerConfig.$(MODULE_EXT): src/context/meta.$(MODULE_EXT) src/globals.$(MODULE_EXT) src/syntax/ast.$(MODULE_EXT) src/typing/type.$(MODULE_EXT) src/context/common.$(MODULE_EXT)
 
-src/optimization/analyzerConfig.$(MODULE_EXT): src/syntax/ast.$(MODULE_EXT) src/typing/type.$(MODULE_EXT) src/typing/common.$(MODULE_EXT)
+src/optimization/analyzerTexpr.$(MODULE_EXT): src/typing/abstract.$(MODULE_EXT) src/context/meta.$(MODULE_EXT) src/globals.$(MODULE_EXT) src/typing/error.$(MODULE_EXT) src/syntax/ast.$(MODULE_EXT) src/typing/type.$(MODULE_EXT) src/context/common.$(MODULE_EXT) src/generators/codegen.$(MODULE_EXT) src/optimization/analyzerConfig.$(MODULE_EXT) src/optimization/optimizerTexpr.$(MODULE_EXT)
 
-src/optimization/analyzerTexpr.$(MODULE_EXT): src/syntax/ast.$(MODULE_EXT) src/typing/type.$(MODULE_EXT) src/typing/common.$(MODULE_EXT) src/generators/codegen.$(MODULE_EXT) src/optimization/analyzerConfig.$(MODULE_EXT) src/optimization/optimizer.$(MODULE_EXT)
+src/optimization/analyzerTexprTransformer.$(MODULE_EXT): src/context/meta.$(MODULE_EXT) src/globals.$(MODULE_EXT) src/typing/error.$(MODULE_EXT) src/syntax/ast.$(MODULE_EXT) src/typing/type.$(MODULE_EXT) src/context/common.$(MODULE_EXT) src/generators/codegen.$(MODULE_EXT) src/optimization/analyzerConfig.$(MODULE_EXT) src/optimization/analyzerTypes.$(MODULE_EXT) src/optimization/analyzerTexpr.$(MODULE_EXT)
 
-src/optimization/analyzerTexprTransformer.$(MODULE_EXT): src/syntax/ast.$(MODULE_EXT) src/typing/type.$(MODULE_EXT) src/typing/common.$(MODULE_EXT) src/generators/codegen.$(MODULE_EXT) src/optimization/analyzerConfig.$(MODULE_EXT) src/optimization/analyzerTypes.$(MODULE_EXT) src/optimization/analyzerTexpr.$(MODULE_EXT)
+src/optimization/analyzerTypes.$(MODULE_EXT): src/globals.$(MODULE_EXT) src/syntax/ast.$(MODULE_EXT) src/typing/type.$(MODULE_EXT) src/context/common.$(MODULE_EXT) src/optimization/analyzerConfig.$(MODULE_EXT)
 
-src/optimization/analyzerTypes.$(MODULE_EXT): src/syntax/ast.$(MODULE_EXT) src/typing/type.$(MODULE_EXT) src/typing/common.$(MODULE_EXT) src/optimization/analyzerConfig.$(MODULE_EXT)
+src/optimization/dce.$(MODULE_EXT): src/typing/abstract.$(MODULE_EXT) src/context/meta.$(MODULE_EXT) src/globals.$(MODULE_EXT) src/typing/typecore.$(MODULE_EXT) src/typing/error.$(MODULE_EXT) src/path.$(MODULE_EXT) src/syntax/ast.$(MODULE_EXT) src/context/common.$(MODULE_EXT) src/typing/type.$(MODULE_EXT)
 
-src/optimization/dce.$(MODULE_EXT): src/syntax/ast.$(MODULE_EXT) src/typing/common.$(MODULE_EXT) src/generators/codegen.$(MODULE_EXT) src/typing/type.$(MODULE_EXT)
+src/optimization/filters.$(MODULE_EXT): src/context/meta.$(MODULE_EXT) src/globals.$(MODULE_EXT) src/typing/error.$(MODULE_EXT) src/syntax/ast.$(MODULE_EXT) src/optimization/analyzer.$(MODULE_EXT) src/context/common.$(MODULE_EXT) src/typing/type.$(MODULE_EXT) src/optimization/dce.$(MODULE_EXT) src/generators/codegen.$(MODULE_EXT) src/typing/typecore.$(MODULE_EXT)
 
-src/optimization/filters.$(MODULE_EXT): src/syntax/ast.$(MODULE_EXT) src/optimization/analyzer.$(MODULE_EXT) src/typing/common.$(MODULE_EXT) src/typing/type.$(MODULE_EXT) src/optimization/dce.$(MODULE_EXT) src/generators/codegen.$(MODULE_EXT) src/typing/typecore.$(MODULE_EXT)
+src/optimization/optimizer.$(MODULE_EXT): src/context/meta.$(MODULE_EXT) src/globals.$(MODULE_EXT) src/optimization/optimizerTexpr.$(MODULE_EXT) src/display/display.$(MODULE_EXT) src/typing/typecore.$(MODULE_EXT) src/typing/type.$(MODULE_EXT) src/syntax/parser.$(MODULE_EXT) src/context/common.$(MODULE_EXT) src/syntax/ast.$(MODULE_EXT)
 
-src/optimization/optimizer.$(MODULE_EXT): src/typing/typecore.$(MODULE_EXT) src/typing/type.$(MODULE_EXT) src/syntax/parser.$(MODULE_EXT) src/typing/common.$(MODULE_EXT) src/syntax/ast.$(MODULE_EXT)
+src/optimization/optimizerTexpr.$(MODULE_EXT): src/globals.$(MODULE_EXT) src/context/meta.$(MODULE_EXT) src/context/common.$(MODULE_EXT) src/typing/type.$(MODULE_EXT) src/syntax/ast.$(MODULE_EXT)
 
 # syntax
 
-src/syntax/ast.$(MODULE_EXT):
+src/syntax/ast.$(MODULE_EXT): src/context/meta.$(MODULE_EXT) src/globals.$(MODULE_EXT)
 
-src/syntax/lexer.$(MODULE_EXT): src/syntax/lexer.ml src/syntax/ast.$(MODULE_EXT)
+src/syntax/lexer.$(MODULE_EXT): src/globals.$(MODULE_EXT) src/syntax/lexer.ml src/syntax/ast.$(MODULE_EXT)
 
-src/syntax/parser.$(MODULE_EXT): src/syntax/parser.ml src/syntax/lexer.$(MODULE_EXT) src/typing/common.$(MODULE_EXT) src/syntax/ast.$(MODULE_EXT)
+src/syntax/parser.$(MODULE_EXT): src/globals.$(MODULE_EXT) src/context/meta.$(MODULE_EXT) src/path.$(MODULE_EXT) src/syntax/parser.ml src/syntax/lexer.$(MODULE_EXT) src/context/common.$(MODULE_EXT) src/syntax/ast.$(MODULE_EXT)
 	$(CC_PARSER_CMD)
 
 # typing
 
-src/typing/common.$(MODULE_EXT): src/typing/type.$(MODULE_EXT) src/syntax/ast.$(MODULE_EXT) libs/ilib/il.$(LIB_EXT)
+src/typing/abstract.$(MODULE_EXT): src/context/meta.$(MODULE_EXT) src/typing/type.$(MODULE_EXT) src/typing/error.$(MODULE_EXT)
 
-src/typing/matcher.$(MODULE_EXT): src/optimization/optimizer.$(MODULE_EXT) src/generators/codegen.$(MODULE_EXT) src/typing/typecore.$(MODULE_EXT) src/typing/type.$(MODULE_EXT) src/typing/typer.$(MODULE_EXT) src/typing/common.$(MODULE_EXT) src/syntax/ast.$(MODULE_EXT)
+src/typing/error.$(MODULE_EXT): src/globals.$(MODULE_EXT) src/typing/type.$(MODULE_EXT)
 
-src/typing/type.$(MODULE_EXT): src/syntax/ast.$(MODULE_EXT) src/json.$(MODULE_EXT)
+src/typing/matcher.$(MODULE_EXT): src/typing/abstract.$(MODULE_EXT) src/context/meta.$(MODULE_EXT) src/globals.$(MODULE_EXT) src/typing/error.$(MODULE_EXT) src/optimization/optimizer.$(MODULE_EXT) src/generators/codegen.$(MODULE_EXT) src/typing/typecore.$(MODULE_EXT) src/typing/type.$(MODULE_EXT) src/typing/typer.$(MODULE_EXT) src/context/common.$(MODULE_EXT) src/syntax/ast.$(MODULE_EXT)
 
-src/typing/typecore.$(MODULE_EXT): src/typing/type.$(MODULE_EXT) src/typing/common.$(MODULE_EXT) src/syntax/ast.$(MODULE_EXT)
+src/typing/overloads.$(MODULE_EXT): src/typing/abstract.$(MODULE_EXT) src/context/meta.$(MODULE_EXT) src/typing/type.$(MODULE_EXT)
 
-src/typing/typeload.$(MODULE_EXT): src/typing/typecore.$(MODULE_EXT) src/typing/type.$(MODULE_EXT) src/syntax/parser.$(MODULE_EXT) src/optimization/optimizer.$(MODULE_EXT) src/syntax/lexer.$(MODULE_EXT) src/typing/common.$(MODULE_EXT) src/syntax/ast.$(MODULE_EXT) src/json.$(MODULE_EXT) src/display/display.$(MODULE_EXT)
+src/typing/type.$(MODULE_EXT): src/globals.$(MODULE_EXT) src/context/meta.$(MODULE_EXT) src/syntax/ast.$(MODULE_EXT) src/json.$(MODULE_EXT)
 
-src/typing/typer.$(MODULE_EXT): src/typing/typeload.$(MODULE_EXT) src/typing/typecore.$(MODULE_EXT) src/typing/type.$(MODULE_EXT) src/syntax/parser.$(MODULE_EXT) src/optimization/optimizer.$(MODULE_EXT) src/syntax/lexer.$(MODULE_EXT) src/macro/interp.$(MODULE_EXT) src/typing/common.$(MODULE_EXT) src/generators/codegen.$(MODULE_EXT) src/syntax/ast.$(MODULE_EXT) src/optimization/filters.$(MODULE_EXT) src/generators/gencommon.$(MODULE_EXT) src/generators/genjs.$(MODULE_EXT) src/generators/genlua.$(MODULE_EXT) src/display/display.$(MODULE_EXT)
+src/typing/typecore.$(MODULE_EXT): src/typing/abstract.$(MODULE_EXT) src/context/meta.$(MODULE_EXT) src/globals.$(MODULE_EXT) src/path.$(MODULE_EXT) src/typing/type.$(MODULE_EXT) src/context/common.$(MODULE_EXT) src/syntax/ast.$(MODULE_EXT)
 
+src/typing/typeload.$(MODULE_EXT): src/globals.$(MODULE_EXT) src/context/meta.$(MODULE_EXT) src/globals.$(MODULE_EXT) src/optimization/optimizerTexpr.$(MODULE_EXT) src/typing/overloads.$(MODULE_EXT) src/path.$(MODULE_EXT) src/typing/typecore.$(MODULE_EXT) src/typing/type.$(MODULE_EXT) src/syntax/parser.$(MODULE_EXT) src/optimization/optimizer.$(MODULE_EXT) src/syntax/lexer.$(MODULE_EXT) src/context/common.$(MODULE_EXT) src/syntax/ast.$(MODULE_EXT) src/json.$(MODULE_EXT) src/display/display.$(MODULE_EXT)
+
+src/typing/typer.$(MODULE_EXT): src/typing/abstract.$(MODULE_EXT) src/context/meta.$(MODULE_EXT) src/globals.$(MODULE_EXT) src/typing/error.$(MODULE_EXT) src/optimization/optimizerTexpr.$(MODULE_EXT) src/typing/overloads.$(MODULE_EXT) src/path.$(MODULE_EXT) src/typing/typeload.$(MODULE_EXT) src/typing/typecore.$(MODULE_EXT) src/typing/type.$(MODULE_EXT) src/syntax/parser.$(MODULE_EXT) src/optimization/optimizer.$(MODULE_EXT) src/syntax/lexer.$(MODULE_EXT) src/macro/interp.$(MODULE_EXT) src/context/common.$(MODULE_EXT) src/generators/codegen.$(MODULE_EXT) src/syntax/ast.$(MODULE_EXT) src/optimization/filters.$(MODULE_EXT) src/generators/genjs.$(MODULE_EXT) src/display/display.$(MODULE_EXT) src/macro/macroContext.$(MODULE_EXT)
 
 # main
 
-src/main.$(MODULE_EXT): src/optimization/filters.$(MODULE_EXT) src/typing/matcher.$(MODULE_EXT) src/typing/typer.$(MODULE_EXT) src/typing/typeload.$(MODULE_EXT) src/typing/typecore.$(MODULE_EXT) src/typing/type.$(MODULE_EXT) src/syntax/parser.$(MODULE_EXT) src/optimization/optimizer.$(MODULE_EXT) src/syntax/lexer.$(MODULE_EXT) src/macro/interp.$(MODULE_EXT) src/generators/genxml.$(MODULE_EXT) src/generators/genswf.$(MODULE_EXT) src/generators/genphp.$(MODULE_EXT) src/generators/genneko.$(MODULE_EXT) src/generators/genjs.$(MODULE_EXT) src/generators/genlua.$(MODULE_EXT) src/generators/gencpp.$(MODULE_EXT) src/generators/genas3.$(MODULE_EXT) src/typing/common.$(MODULE_EXT) src/generators/codegen.$(MODULE_EXT) src/syntax/ast.$(MODULE_EXT) src/generators/gencommon.$(MODULE_EXT) src/generators/genjava.$(MODULE_EXT) src/generators/gencs.$(MODULE_EXT) src/generators/genpy.$(MODULE_EXT) src/generators/genhl.$(MODULE_EXT) src/version.$(MODULE_EXT) src/display/display.$(MODULE_EXT) libs/ilib/il.$(LIB_EXT)
+src/main.$(MODULE_EXT): src/context/meta.$(MODULE_EXT) src/globals.$(MODULE_EXT) src/typing/error.$(MODULE_EXT) src/globals.$(MODULE_EXT) src/path.$(MODULE_EXT) src/optimization/filters.$(MODULE_EXT) src/typing/matcher.$(MODULE_EXT) src/typing/typer.$(MODULE_EXT) src/typing/typeload.$(MODULE_EXT) src/typing/typecore.$(MODULE_EXT) src/typing/type.$(MODULE_EXT) src/syntax/parser.$(MODULE_EXT) src/optimization/optimizer.$(MODULE_EXT) src/syntax/lexer.$(MODULE_EXT) src/macro/interp.$(MODULE_EXT) src/generators/genxml.$(MODULE_EXT) src/generators/genswf.$(MODULE_EXT) src/generators/genphp.$(MODULE_EXT) src/generators/genneko.$(MODULE_EXT) src/generators/genjs.$(MODULE_EXT) src/generators/genlua.$(MODULE_EXT) src/generators/gencpp.$(MODULE_EXT) src/generators/genas3.$(MODULE_EXT) src/context/common.$(MODULE_EXT) src/generators/codegen.$(MODULE_EXT) src/generators/genjava.$(MODULE_EXT) src/generators/gencs.$(MODULE_EXT) src/generators/genpy.$(MODULE_EXT) src/generators/genhl.$(MODULE_EXT) src/display/display.$(MODULE_EXT) src/server.$(MODULE_EXT) src/display/displayOutput.$(MODULE_EXT) libs/ilib/il.$(LIB_EXT)
+
+src/globals.$(MODULE_EXT): src/version.$(MODULE_EXT)
+
+src/path.$(MODULE_EXT): src/globals.$(MODULE_EXT)
+
+src/server.$(MODULE_EXT): src/context/meta.$(MODULE_EXT) src/globals.$(MODULE_EXT) src/path.$(MODULE_EXT) src/typing/typer.$(MODULE_EXT) src/typing/typeload.$(MODULE_EXT) src/typing/typecore.$(MODULE_EXT) src/typing/type.$(MODULE_EXT) src/syntax/parser.$(MODULE_EXT) src/typing/typecore.$(MODULE_EXT) src/typing/type.$(MODULE_EXT) src/context/common.$(MODULE_EXT) src/context/common.$(MODULE_EXT) src/syntax/parser.$(MODULE_EXT) src/syntax/ast.$(MODULE_EXT) src/display/displayOutput.$(MODULE_EXT)
 
 src/version.$(MODULE_EXT):
 	$(MAKE) -f Makefile.version_extra -s --no-print-directory ADD_REVISION=$(ADD_REVISION) BRANCH=$(BRANCH) COMMIT_SHA=$(COMMIT_SHA) COMMIT_DATE=$(COMMIT_DATE) > src/version.ml
-	$(COMPILER) $(CFLAGS) -c src/version.ml
+	$(COMPILER) $(ALL_CFLAGS) -c src/version.ml
 
 # Package
 
@@ -227,10 +267,10 @@ package_src:
 	mkdir -p $(PACKAGE_OUT_DIR)
 	# use git-archive-all since we have submodules
 	# https://github.com/Kentzo/git-archive-all
-	curl -s https://raw.githubusercontent.com/Kentzo/git-archive-all/1.12/git-archive-all -o extra/git-archive-all
-	python extra/git-archive-all $(PACKAGE_OUT_DIR)/$(PACKAGE_FILE_NAME)_src$(PACKAGE_SRC_EXTENSION)
+	curl -s https://raw.githubusercontent.com/Kentzo/git-archive-all/1.15/git_archive_all.py -o extra/git_archive_all.py
+	python extra/git_archive_all.py $(PACKAGE_OUT_DIR)/$(PACKAGE_FILE_NAME)_src$(PACKAGE_SRC_EXTENSION)
 
-package_bin:
+package_unix:
 	mkdir -p $(PACKAGE_OUT_DIR)
 	rm -rf $(PACKAGE_FILE_NAME) $(PACKAGE_FILE_NAME).tar.gz
 	# Copy the package contents to $(PACKAGE_FILE_NAME)
@@ -240,25 +280,26 @@ package_bin:
 	tar -zcf $(PACKAGE_OUT_DIR)/$(PACKAGE_FILE_NAME)_bin.tar.gz $(PACKAGE_FILE_NAME)
 	rm -r $(PACKAGE_FILE_NAME)
 
+package_bin: package_$(PLATFORM)
 
 install_dox:
-	haxelib git hxparse https://github.com/Simn/hxparse development src
+	haxelib git hxparse https://github.com/Simn/hxparse master src
 	haxelib git hxtemplo https://github.com/Simn/hxtemplo
 	haxelib git hxargs https://github.com/Simn/hxargs
 	haxelib git markdown https://github.com/dpeek/haxe-markdown master src
 	haxelib git hxcpp https://github.com/HaxeFoundation/hxcpp
 	haxelib git hxjava https://github.com/HaxeFoundation/hxjava
 	haxelib git hxcs https://github.com/HaxeFoundation/hxcs
-	haxelib git dox https://github.com/dpeek/dox
+	haxelib git dox https://github.com/HaxeFoundation/dox
 
 package_doc:
 	mkdir -p $(PACKAGE_OUT_DIR)
 	cd $$(haxelib path dox | head -n 1) && haxe run.hxml && haxe gen.hxml
-	haxelib run dox --title "Haxe API" -o $(PACKAGE_OUT_DIR)/$(PACKAGE_FILE_NAME)_doc.zip -D version "$$(haxe -version 2>&1)" -i $$(haxelib path dox | head -n 1)bin/xml -ex microsoft -ex javax -ex cs.internal -D source-path https://github.com/HaxeFoundation/haxe/blob/$(BRANCH)/std/
+	haxelib run dox -theme haxe_api -D website "http://haxe.org/" --title "Haxe API" -o $(PACKAGE_OUT_DIR)/$(PACKAGE_FILE_NAME)_doc.zip -D version "$$(haxe -version 2>&1)" -i $$(haxelib path dox | head -n 1)bin/xml -ex microsoft -ex javax -ex cs.internal -D source-path https://github.com/HaxeFoundation/haxe/blob/$(BRANCH)/std/
 
 deploy_doc:
-	scp $(PACKAGE_OUT_DIR)/$(PACKAGE_FILE_NAME)_doc.zip [email protected]:/data/haxeapi/www/v/dev/api-latest.zip
-	ssh [email protected] "cd /data/haxeapi/www/v/dev && find . ! -name 'api-latest.zip' -maxdepth 1 -mindepth 1 -exec rm -rf {} + && unzip -q -o api-latest.zip"
+	scp $(PACKAGE_OUT_DIR)/$(PACKAGE_FILE_NAME)_doc.zip www-haxe@api.haxe.org:/data/haxeapi/www/v/dev/api-latest.zip
+	ssh www-haxe@api.haxe.org "cd /data/haxeapi/www/v/dev && find . ! -name 'api-latest.zip' -maxdepth 1 -mindepth 1 -exec rm -rf {} + && unzip -q -o api-latest.zip"
 
 # Clean
 
@@ -277,7 +318,7 @@ clean_libs:
 	make -C libs/objsize clean
 
 clean_haxe:
-	rm -f -r  $(MODULES:%=src/%.obj) $(MODULES:%=src/%.o) $(MODULES:%=src/%.cmx) $(MODULES:%=src/%.cmi) $(MODULES:%=src/%.cmo) src/syntax/lexer.ml src/version.ml $(OUTPUT)
+	rm -f -r  $(MODULES:%=src/%.obj) $(MODULES:%=src/%.o) $(MODULES:%=src/%.cmx) $(MODULES:%=src/%.cmi) $(MODULES:%=src/%.cmo) $(MODULES:%=src/%.cmt) src/syntax/lexer.ml src/version.ml $(OUTPUT)
 
 clean_tools:
 	rm -f $(OUTPUT) haxelib

+ 4 - 4
Makefile.win

@@ -1,5 +1,5 @@
+PLATFORM=win
 include Makefile
-
 OUTPUT=haxe.exe
 EXTENSION=.exe
 PACKAGE_SRC_EXTENSION=.zip
@@ -32,11 +32,11 @@ FILTER=sed '/File/{ N; s/File "\([^"]\+\)", line \([0-9]\+\), characters \([0-9-
 endif
 
 ifdef FILTER
-CC_CMD=($(OCAMLOPT) $(CFLAGS) -c $< 2>tmp.cmi && $(FILTER)) || ($(FILTER) && exit 1)
-CC_PARSER_CMD=($(OCAMLOPT) -pp camlp4o $(CFLAGS) -c src/syntax/parser.ml 2>tmp.cmi && $(FILTER)) || ($(FILTER) && exit 1)
+CC_CMD=($(OCAMLOPT) $(ALL_CFLAGS) -c $< 2>tmp.cmi && $(FILTER)) || ($(FILTER) && exit 1)
+CC_PARSER_CMD=($(OCAMLOPT) -pp camlp4o $(ALL_CFLAGS) -c src/syntax/parser.ml 2>tmp.cmi && $(FILTER)) || ($(FILTER) && exit 1)
 endif
 
-package_bin:
+package_win:
 	mkdir -p out
 	rm -rf $(PACKAGE_FILE_NAME) $(PACKAGE_FILE_NAME).zip temp.zip
 	# Copy the package contents to $(PACKAGE_FILE_NAME)

+ 17 - 16
README.md

@@ -1,5 +1,5 @@
 
-# [<img src="http://haxe.org/img/haxe-logo-horizontal.svg" alt="Haxe logo" width="140">](http://haxe.org) - [The Cross-Platform Toolkit](http://haxe.org)
+# [<img src="http://haxe.org/img/haxe-logo-horizontal.svg" alt="Haxe logo" width="140">](https://haxe.org) - [The Cross-Platform Toolkit](https://haxe.org)
 [![TravisCI Build Status](https://travis-ci.org/HaxeFoundation/haxe.svg?branch=development)](https://travis-ci.org/HaxeFoundation/haxe)
 [![AppVeyor Build Status](https://ci.appveyor.com/api/projects/status/github/HaxeFoundation/haxe?branch=development&svg=true)](https://ci.appveyor.com/project/HaxeFoundation/haxe)
 [![SauceLabs Test Status](https://saucelabs.com/buildstatus/haxe)](https://saucelabs.com/u/haxe)
@@ -22,10 +22,11 @@ Haxe allows you to compile for the following targets:
  * NekoVM
  * PHP
  * Python
+ * Lua
 
 You can try Haxe directly from your browser at [try.haxe.org](http://try.haxe.org)!
 
-For more information about Haxe, head to the [offical Haxe website](http://haxe.org).
+For more information about Haxe, head to the [offical Haxe website](https://haxe.org).
 
 ## License
 
@@ -35,19 +36,19 @@ The Haxe project has several licenses, covering different parts of the projects.
  * The Haxe standard library is released under the MIT license.
  * The Neko virtual machine is released under the MIT license. Its bundled runtime libraries (ndll) and tools are released under open source licenses as described in https://github.com/HaxeFoundation/neko/blob/master/LICENSE
 
-For the complete Haxe licenses, please see http://haxe.org/foundation/open-source.html or [extra/LICENSE.txt](extra/LICENSE.txt).
+For the complete Haxe licenses, please see https://haxe.org/foundation/open-source.html or [extra/LICENSE.txt](extra/LICENSE.txt).
 
 ## Installing Haxe
 
-The latest stable release is [Haxe 3.3.0-rc.1](http://haxe.org/download/version/3.3.0-rc.1/). Pre-built binaries are available for your platform:
+The latest stable release is [Haxe 3.4.0-rc.1](https://haxe.org/download/version/3.4.0-rc.1/). Pre-built binaries are available for your platform:
 
- * **[Windows installer](http://haxe.org/download/file/3.3.0-rc.1/haxe-3.3.0-rc.1-win.exe)**
- * **[Windows binaries](http://haxe.org/download/file/3.3.0-rc.1/haxe-3.3.0-rc.1-win.zip)**
- * **[OSX installer](http://haxe.org/download/file/3.3.0-rc.1/haxe-3.3.0-rc.1-osx-installer.pkg)**
- * **[OSX binaries](http://haxe.org/download/file/3.3.0-rc.1/haxe-3.3.0-rc.1-osx.tar.gz)**
- * **[Linux Software Packages](http://haxe.org/download/linux)**
- * **[Linux 32-bit binaries](http://haxe.org/download/file/3.3.0-rc.1/haxe-3.3.0-rc.1-linux32.tar.gz)**
- * **[Linux 64-bit binaries](http://haxe.org/download/file/3.3.0-rc.1/haxe-3.3.0-rc.1-linux64.tar.gz)**
+ * **[Windows installer](https://haxe.org/download/file/3.4.0-rc.1/haxe-3.4.0-rc.1-win.exe)**
+ * **[Windows binaries](https://haxe.org/download/file/3.4.0-rc.1/haxe-3.4.0-rc.1-win.zip)**
+ * **[OSX installer](https://haxe.org/download/file/3.4.0-rc.1/haxe-3.4.0-rc.1-osx-installer.pkg)**
+ * **[OSX binaries](https://haxe.org/download/file/3.4.0-rc.1/haxe-3.4.0-rc.1-osx.tar.gz)**
+ * **[Linux Software Packages](https://haxe.org/download/linux)**
+ * **[Linux 32-bit binaries](https://haxe.org/download/file/3.4.0-rc.1/haxe-3.4.0-rc.1-linux32.tar.gz)**
+ * **[Linux 64-bit binaries](https://haxe.org/download/file/3.4.0-rc.1/haxe-3.4.0-rc.1-linux64.tar.gz)**
 
 Automated development builds are available from [build.haxe.org](http://build.haxe.org).
 
@@ -58,17 +59,17 @@ Automated development builds are available from [build.haxe.org](http://build.ha
         git clone --recursive git://github.com/HaxeFoundation/haxe.git
         cd haxe
 
- 2. Follow the [documentation on building Haxe for your platform](http://haxe.org/documentation/introduction/building-haxe.html).
+ 2. Follow the [documentation on building Haxe for your platform](https://haxe.org/documentation/introduction/building-haxe.html).
 
 ## Using Haxe
 
-For information on on using Haxe, consult the [Haxe documentation](http://haxe.org/documentation):
+For information on on using Haxe, consult the [Haxe documentation](https://haxe.org/documentation):
 
- * [Haxe Introduction](http://haxe.org/documentation/introduction), an introduction to the Haxe toolkit
- * [The Haxe Manual](http://haxe.org/manual), the reference manual for the Haxe language
+ * [Haxe Introduction](https://haxe.org/documentation/introduction), an introduction to the Haxe toolkit
+ * [The Haxe Manual](https://haxe.org/manual), the reference manual for the Haxe language
  * [Haxe Code Cookbook](http://code.haxe.org), code snippets / learning resource
  * [Haxe API](http://api.haxe.org), documentation for the Haxe standard and native APIs
- * [Haxelib](http://lib.haxe.org), a repository of Haxe libraries for a variety of needs
+ * [Haxelib](https://lib.haxe.org), a repository of Haxe libraries for a variety of needs
 
 ## Community
 

+ 10 - 7
appveyor.yml

@@ -19,17 +19,21 @@ services:
 skip_tags: true
 
 cache:
-    - ocaml-installer.exe -> appveyor.yml
+    - opam32.tar.xz -> appveyor.yml
 
 install:
     - 'git submodule update --init --recursive'
     # Install ocaml
-    - if not exist "ocaml-installer.exe" (
-        curl -fsS -o ocaml-installer.exe --retry 3 http://gallium.inria.fr/~protzenk/caml-installer/ocaml-4.02.3-i686-mingw64-installer4-opam.exe
+    - curl -fsS -o cygwin-setup.exe --retry 3 https://cygwin.com/setup-x86.exe
+    - 'cygwin-setup.exe -g -q -R "%CYG_ROOT%" -P make -P git -P mingw64-i686-zlib -P rsync -P patch -P diffutils -P curl -P unzip -P m4 -P perl -P mingw64-i686-gcc-core'
+    - if not exist "opam32.tar.xz" (
+        curl -fsS -o opam32.tar.xz --retry 3 https://dl.dropboxusercontent.com/s/eo4igttab8ipyle/opam32.tar.xz
       )
-    - ocaml-installer.exe /S
-    - 'C:\Users\Public\Desktop\cygwin-setup.exe -g -q -R "%CYG_ROOT%" -P make -P git -P mingw64-i686-zlib'
-    - 'set PATH=%PATH%;%CYG_ROOT%/usr/i686-w64-mingw32/sys-root/mingw/bin'
+    - 7z x "opam32.tar.xz" -so | 7z x -aoa -si -ttar
+    - '%CYG_ROOT%/bin/bash -lc "echo initialize"'
+    - '%CYG_ROOT%/bin/bash -lc "cd \"$OLDPWD\" && bash opam32/install.sh"'
+    - '%CYG_ROOT%/bin/bash -lc "opam init mingw \"https://github.com/fdopen/opam-repository-mingw.git\" --comp 4.02.3+mingw32c --switch 4.02.3+mingw32c --auto-setup --yes"'
+    - '%CYG_ROOT%/bin/bash -lc "opam install camlp4 --yes"'
     # Install neko
     - choco install neko --prerelease --ignore-dependencies -s 'https://ci.appveyor.com/nuget/neko' -y
     - RefreshEnv
@@ -43,7 +47,6 @@ install:
 
 build_script:
     - 'cd %APPVEYOR_BUILD_FOLDER%'
-    - '%CYG_ROOT%/bin/bash -lc "echo initialize"'
     - '%CYG_ROOT%/bin/bash -lc "cd \"$OLDPWD\" && make -s -f Makefile.win package_src"'
     - '%CYG_ROOT%/bin/bash -lc "cd \"$OLDPWD\" && make -s -f Makefile.win"'
     - 'set PATH=%PATH%;%APPVEYOR_BUILD_FOLDER%'

+ 41 - 1
extra/CHANGES.txt

@@ -1,3 +1,43 @@
+2016-11-29: 3.4.0-RC1
+
+	New features:
+
+	all : support metadata completion
+	all : support type-hint completion
+	all : added @signature display mode (#4758)
+	all : finalized HashLink (HL) target
+
+	General improvements and optimizations:
+
+	all : greatly improved support for display mode in general
+	all : made --times output look much nicer
+	all : remove calls to functions that have no side-effect
+	all : hid private property accessors from completion (#5678)
+	js : updated jQuery extern (js.jquery.*) for jQuery 1.12.4 / 3.1.0 support.
+	js : jQuery extern (js.jquery.*) now includes deprecated fields marked with @:deprecated.
+	js : no longer rewrite `o["s"]` to `o.s` (#5724)
+	cpp : greatly improved ObjC output and integration options
+	lua : greatly improved code output quality
+
+	Bugfixes:
+
+	all : fixed various optimization issues
+	all : fixed various issues with side-effect detection
+	all : fixed performance drain in the DCE implementation (#5716)
+	all : fixed issue with assignments when inlining constructors (#5340)
+	all : fixed major inlining issue when using compilation server (#5320)
+	all : fixed pattern matching evaluation order issue (#5274)
+	cpp : fixed various minor code generation issues
+	js : fixed issue regarding iterating over abstracts (#5385)
+	python : fixed evaluation order issue for array writing (#5366)
+
+	Standard Library:
+
+	all : added Any type (#5500)
+	all : added haxe.extern.AsVar
+	all : added haxe.macro.CompilationServer (experimental)
+	all : fixed haxe.Template.resolve (#5301)
+
 2016-05-27: 3.3.0-RC1
 
 	New features:
@@ -853,7 +893,7 @@
 	resolve environment variable in -cmd commands
 	added flash.Vector.indexOf and lastIndexOf
 	fixed bug in interfaces that define the method toString (Haxe/PHP)
-	fixed bug in haxe.io.BytesInput.readBytes in Flash9 (was throwing Eof if full buffer can't be readed)
+	fixed bug in haxe.io.BytesInput.readBytes in Flash9 (was throwing Eof if full buffer can't be read)
 	fixed implements/extends special classes when they are imported
 	StringBuf now uses an array for JS implementation (around same on FF, faster on IE)
 	fixed assignment of field length in anonym objects (Haxe/PHP)

+ 1 - 1
extra/ImportAll.hx

@@ -71,7 +71,7 @@ class ImportAll {
 				if( file == ".svn" || file == "_std" )
 					continue;
 				var full = (pack == "") ? file : pack + "." + file;
-				if( StringTools.endsWith(file, ".hx") ) {
+				if( StringTools.endsWith(file, ".hx") && file.indexOf(".") < 0 ) {
 					var cl = full.substr(0, full.length - 3);
 					switch( cl ) {
 					case "ImportAll", "neko.db.MacroManager": continue;

+ 1 - 1
extra/haxelib_src

@@ -1 +1 @@
-Subproject commit f8bb48ba5a59c0dbab7a3373cdc908e9c1678644
+Subproject commit 9d79a55ddb054d79ed8d136f1c0583baa407fd67

+ 1 - 1
extra/release-checklist.txt

@@ -7,7 +7,7 @@
 # Building the binaries and installers
 
 - Make sure CHANGES.txt has a proper date set!
-- Make sure `version` in main.ml has the correct value.
+- Make sure `version` in globals.ml has the correct value.
 - Update README.md:
   - Installing Haxe: update "Latest stable version"
   - Version compatibility: add/update the Haxe/Neko version

+ 113 - 1
haxe.hxproj

@@ -15,7 +15,7 @@
   </output>
   <!-- Other classes to be compiled into your SWF -->
   <classpaths>
-    <!-- example: <class path="..." /> -->
+    <class path="std" />
   </classpaths>
   <!-- Build options -->
   <build>
@@ -304,6 +304,118 @@
     <hidden path="src\typing\typeload.o" />
     <hidden path="src\typing\typer.o" />
     <hidden path=".travis" />
+    <hidden path="src\json.obj" />
+    <hidden path="src\display\display.obj" />
+    <hidden path="src\generators\genlua.obj" />
+    <hidden path="src\display\display.cmt" />
+    <hidden path="src\display\displayTypes.cmt" />
+    <hidden path="src\display\displayTypes.cmi" />
+    <hidden path="src\display\displayTypes.cmx" />
+    <hidden path="src\display\displayTypes.o" />
+    <hidden path="src\generators\codegen.cmt" />
+    <hidden path="src\generators\genas3.cmt" />
+    <hidden path="src\generators\gencommon.cmt" />
+    <hidden path="src\generators\gencpp.cmt" />
+    <hidden path="src\generators\gencs.cmt" />
+    <hidden path="src\generators\genhl.cmt" />
+    <hidden path="src\generators\genjava.cmt" />
+    <hidden path="src\generators\genjs.cmt" />
+    <hidden path="src\generators\genlua.cmt" />
+    <hidden path="src\generators\genneko.cmt" />
+    <hidden path="src\generators\genphp.cmt" />
+    <hidden path="src\generators\genpy.cmt" />
+    <hidden path="src\generators\genswf.cmt" />
+    <hidden path="src\generators\genswf9.cmt" />
+    <hidden path="src\generators\genxml.cmt" />
+    <hidden path="src\macro\interp.cmt" />
+    <hidden path="src\optimization\analyzer.cmt" />
+    <hidden path="src\optimization\analyzerConfig.cmt" />
+    <hidden path="src\optimization\analyzerTexpr.cmt" />
+    <hidden path="src\optimization\analyzerTexprTransformer.cmt" />
+    <hidden path="src\optimization\analyzerTypes.cmt" />
+    <hidden path="src\optimization\dce.cmt" />
+    <hidden path="src\optimization\filters.cmt" />
+    <hidden path="src\optimization\optimizer.cmt" />
+    <hidden path="src\syntax\ast.cmt" />
+    <hidden path="src\syntax\lexer.cmt" />
+    <hidden path="src\syntax\parser.cmt" />
+    <hidden path="src\typing\common.cmt" />
+    <hidden path="src\typing\matcher.cmt" />
+    <hidden path="src\typing\type.cmt" />
+    <hidden path="src\typing\typecore.cmt" />
+    <hidden path="src\typing\typeload.cmt" />
+    <hidden path="src\typing\typer.cmt" />
+    <hidden path="src\globals.cmi" />
+    <hidden path="src\globals.cmt" />
+    <hidden path="src\globals.cmx" />
+    <hidden path="src\globals.o" />
+    <hidden path="src\main.cmt" />
+    <hidden path="src\path.cmi" />
+    <hidden path="src\path.cmt" />
+    <hidden path="src\path.cmx" />
+    <hidden path="src\path.o" />
+    <hidden path="src\server.cmi" />
+    <hidden path="src\server.cmt" />
+    <hidden path="src\server.cmx" />
+    <hidden path="src\server.o" />
+    <hidden path="src\typing\overloads.cmi" />
+    <hidden path="src\typing\overloads.cmt" />
+    <hidden path="src\typing\overloads.cmx" />
+    <hidden path="src\typing\overloads.o" />
+    <hidden path="src\display\displayOutput.cmi" />
+    <hidden path="src\display\displayOutput.cmt" />
+    <hidden path="src\display\displayOutput.cmx" />
+    <hidden path="src\display\displayOutput.o" />
+    <hidden path="src\generators\hlcode.cmt" />
+    <hidden path="src\generators\hlinterp.cmt" />
+    <hidden path="src\generators\hlcode.o" />
+    <hidden path="src\generators\hlcode.cmi" />
+    <hidden path="src\generators\hlcode.cmx" />
+    <hidden path="src\typing\error.cmi" />
+    <hidden path="src\typing\error.cmt" />
+    <hidden path="src\typing\error.cmx" />
+    <hidden path="src\typing\error.o" />
+    <hidden path="src\generators\hl2c.cmt" />
+    <hidden path="src\generators\hlinterp.cmi" />
+    <hidden path="src\generators\hlinterp.cmx" />
+    <hidden path="src\generators\hlinterp.o" />
+    <hidden path="src\generators\hl2c.cmi" />
+    <hidden path="src\generators\hl2c.cmx" />
+    <hidden path="src\generators\hl2c.o" />
+    <hidden path="src\optimization\optimizerTexpr.cmi" />
+    <hidden path="src\optimization\optimizerTexpr.cmt" />
+    <hidden path="src\optimization\optimizerTexpr.cmx" />
+    <hidden path="src\optimization\optimizerTexpr.o" />
+    <hidden path="src\generators\hlopt.cmi" />
+    <hidden path="src\generators\hlopt.cmt" />
+    <hidden path="src\generators\hlopt.cmx" />
+    <hidden path="src\generators\hlopt.o" />
+    <hidden path="src\json.cmt" />
+    <hidden path="src\version.cmt" />
+    <hidden path="src\context\common.cmi" />
+    <hidden path="src\context\common.cmt" />
+    <hidden path="src\context\common.cmx" />
+    <hidden path="src\context\common.o" />
+    <hidden path="src\context\meta.cmi" />
+    <hidden path="src\context\meta.cmt" />
+    <hidden path="src\context\meta.cmx" />
+    <hidden path="src\context\meta.o" />
+    <hidden path="src\typing\abstract.cmi" />
+    <hidden path="src\typing\abstract.cmt" />
+    <hidden path="src\typing\abstract.cmx" />
+    <hidden path="src\typing\abstract.o" />
+    <hidden path="src\macro\macroContext.cmt" />
+    <hidden path="src\macro\macroContext.cmi" />
+    <hidden path="src\macro\macroContext.cmx" />
+    <hidden path="src\macro\macroContext.o" />
+    <hidden path="src\macro\hlmacro.cmi" />
+    <hidden path="src\macro\hlmacro.cmt" />
+    <hidden path="src\macro\hlmacro.cmx" />
+    <hidden path="src\macro\hlmacro.o" />
+    <hidden path="src\macro\macroApi.cmi" />
+    <hidden path="src\macro\macroApi.cmt" />
+    <hidden path="src\macro\macroApi.cmx" />
+    <hidden path="src\macro\macroApi.o" />
   </hiddenPaths>
   <!-- Executed before build -->
   <preBuildCommand>make -j4 FD_OUTPUT=1 -f Makefile.win kill haxe</preBuildCommand>

+ 1 - 1
libs

@@ -1 +1 @@
-Subproject commit 39f1f79b52fbc02d3b89cda6cad4f08dd88b849a
+Subproject commit 26d15367c229b93ef2346bf7876e681b58c18a65

+ 334 - 343
src/typing/common.ml → src/context/common.ml

@@ -19,13 +19,13 @@
 
 open Ast
 open Type
+open Globals
 
 type package_rule =
 	| Forbidden
-	| Directory of string
 	| Remap of string
 
-type pos = Ast.pos
+type pos = Globals.pos
 
 type basic_types = {
 	mutable tvoid : t;
@@ -44,19 +44,6 @@ type stats = {
 	s_macros_called : int ref;
 }
 
-type platform =
-	| Cross
-	| Js
-	| Lua
-	| Neko
-	| Flash
-	| Php
-	| Cpp
-	| Cs
-	| Java
-	| Python
-	| Hl
-
 (**
 	The capture policy tells which handling we make of captured locals
 	(the locals which are referenced in local functions)
@@ -90,16 +77,119 @@ type platform_config = {
 	pf_reserved_type_paths : path list;
 }
 
-type display_mode =
-	| DMNone
-	| DMDefault
-	| DMUsage
-	| DMPosition
-	| DMToplevel
-	| DMResolve of string
-	| DMType
-	| DMModuleSymbols
-	| DMDiagnostics
+module DisplayMode = struct
+	type t =
+		| DMNone
+		| DMField
+		| DMUsage of bool (* true = also report definition *)
+		| DMPosition
+		| DMToplevel
+		| DMResolve of string
+		| DMPackage
+		| DMType
+		| DMModuleSymbols of string option
+		| DMDiagnostics of bool (* true = global, false = only in display file *)
+		| DMStatistics
+		| DMSignature
+
+	type error_policy =
+		| EPIgnore
+		| EPCollect
+		| EPShow
+
+	type display_file_policy =
+		| DFPOnly
+		| DFPAlso
+		| DFPNo
+
+	type settings = {
+		dms_kind : t;
+		dms_display : bool;
+		dms_full_typing : bool;
+		dms_force_macro_typing : bool;
+		dms_error_policy : error_policy;
+		dms_collect_data : bool;
+		dms_check_core_api : bool;
+		dms_inline : bool;
+		dms_display_file_policy : display_file_policy;
+		dms_exit_during_typing : bool;
+	}
+
+	let default_display_settings = {
+		dms_kind = DMField;
+		dms_display = true;
+		dms_full_typing = false;
+		dms_force_macro_typing = false;
+		dms_error_policy = EPIgnore;
+		dms_collect_data = false;
+		dms_check_core_api = false;
+		dms_inline = false;
+		dms_display_file_policy = DFPOnly;
+		dms_exit_during_typing = true;
+	}
+
+	let default_compilation_settings = {
+		dms_kind = DMNone;
+		dms_display = false;
+		dms_full_typing = true;
+		dms_force_macro_typing = true;
+		dms_error_policy = EPShow;
+		dms_collect_data = false;
+		dms_check_core_api = true;
+		dms_inline = true;
+		dms_display_file_policy = DFPNo;
+		dms_exit_during_typing = false;
+	}
+
+	let create dm =
+		let settings = { default_display_settings with dms_kind = dm } in
+		match dm with
+		| DMNone -> default_compilation_settings
+		| DMField | DMPosition | DMResolve _ | DMPackage | DMType | DMSignature -> settings
+		| DMUsage _ -> { settings with
+				dms_full_typing = true;
+				dms_collect_data = true;
+				dms_display_file_policy = DFPAlso;
+				dms_exit_during_typing = false
+			}
+		| DMToplevel -> { settings with dms_full_typing = true; }
+		| DMModuleSymbols filter -> { settings with
+				dms_display_file_policy = if filter = None then DFPOnly else DFPNo;
+				dms_exit_during_typing = false;
+				dms_force_macro_typing = false;
+			}
+		| DMDiagnostics global -> { settings with
+				dms_full_typing = true;
+				dms_error_policy = EPCollect;
+				dms_collect_data = true;
+				dms_inline = true;
+				dms_display_file_policy = if global then DFPNo else DFPAlso;
+				dms_exit_during_typing = false;
+			}
+		| DMStatistics -> { settings with
+				dms_full_typing = true;
+				dms_collect_data = true;
+				dms_inline = false;
+				dms_display_file_policy = DFPAlso;
+				dms_exit_during_typing = false
+			}
+
+	let to_string = function
+		| DMNone -> "none"
+		| DMField -> "field"
+		| DMPosition -> "position"
+		| DMResolve s -> "resolve " ^ s
+		| DMPackage -> "package"
+		| DMType -> "type"
+		| DMUsage true -> "rename"
+		| DMUsage false -> "references"
+		| DMToplevel -> "toplevel"
+		| DMModuleSymbols None -> "module-symbols"
+		| DMModuleSymbols (Some s) -> "workspace-symbols " ^ s
+		| DMDiagnostics b -> (if b then "global " else "") ^ "diagnostics"
+		| DMStatistics -> "statistics"
+		| DMSignature -> "signature"
+end
 
 type compiler_callback = {
 	mutable after_typing : (module_type list -> unit) list;
@@ -113,40 +203,35 @@ module IdentifierType = struct
 		| ITMember of tclass * tclass_field
 		| ITStatic of tclass * tclass_field
 		| ITEnum of tenum * tenum_field
+		| ITEnumAbstract of tabstract * tclass_field
 		| ITGlobal of module_type * string * Type.t
 		| ITType of module_type
 		| ITPackage of string
+		| ITLiteral of string
+		| ITTimer of string
 
 	let get_name = function
 		| ITLocal v -> v.v_name
-		| ITMember(_,cf) | ITStatic(_,cf) -> cf.cf_name
+		| ITMember(_,cf) | ITStatic(_,cf) | ITEnumAbstract(_,cf) -> cf.cf_name
 		| ITEnum(_,ef) -> ef.ef_name
 		| ITGlobal(_,s,_) -> s
 		| ITType mt -> snd (t_infos mt).mt_path
 		| ITPackage s -> s
-end
-
-module DiagnosticsSeverity = struct
-	type t =
-		| Error
-		| Warning
-		| Information
-		| Hint
-
-	let to_int = function
-		| Error -> 1
-		| Warning -> 2
-		| Information -> 3
-		| Hint -> 4
+		| ITLiteral s -> s
+		| ITTimer s -> s
 end
 
 type shared_display_information = {
-	mutable import_positions : (pos,bool ref) PMap.t;
-	mutable diagnostics_messages : (string * pos * DiagnosticsSeverity.t) list;
+	mutable import_positions : (pos,bool ref * placed_name list) PMap.t;
+	mutable diagnostics_messages : (string * pos * DisplayTypes.DiagnosticsSeverity.t) list;
+	mutable type_hints : (pos,Type.t) Hashtbl.t;
+	mutable document_symbols : (string * DisplayTypes.SymbolInformation.t DynArray.t) list;
+	mutable removable_code : (string * pos * pos) list;
 }
 
 type display_information = {
 	mutable unresolved_identifiers : (string * pos * (string * IdentifierType.t) list) list;
+	mutable interface_field_implementations : (tclass * tclass_field * tclass * tclass_field option) list;
 }
 
 (* This information is shared between normal and macro context. *)
@@ -161,7 +246,7 @@ type context = {
 	shared : shared_context;
 	display_information : display_information;
 	mutable sys_args : string list;
-	mutable display : display_mode;
+	mutable display : DisplayMode.settings;
 	mutable debug : bool;
 	mutable verbose : bool;
 	mutable foptimize : bool;
@@ -209,17 +294,148 @@ type context = {
 	memory_marker : float array;
 }
 
-exception Abort of string * Ast.pos
+exception Abort of string * pos
 
-let display_default = ref DMNone
+let display_default = ref DisplayMode.DMNone
 
-type cache = {
-	mutable c_haxelib : (string list, string list) Hashtbl.t;
-	mutable c_files : (string, float * Ast.package) Hashtbl.t;
-	mutable c_modules : (path * string, module_def) Hashtbl.t;
-}
+let get_signature com =
+	match com.defines_signature with
+	| Some s -> s
+	| None ->
+		let defines = PMap.foldi (fun k v acc ->
+			(* don't make much difference between these special compilation flags *)
+			match String.concat "_" (ExtString.String.nsplit k "-") with
+			(* If we add something here that might be used in conditional compilation it should be added to
+			   Parser.parse_macro_ident as well (issue #5682). *)
+			| "display" | "use_rtti_doc" | "macro_times" | "display_details" | "no_copt" | "display_stdin" -> acc
+			| _ -> (k ^ "=" ^ v) :: acc
+		) com.defines [] in
+		let str = String.concat "@" (List.sort compare defines) in
+		let s = Digest.string str in
+		com.defines_signature <- Some s;
+		s
+
+module CompilationServer = struct
+	type cache = {
+		c_haxelib : (string list, string list) Hashtbl.t;
+		c_files : ((string * string), float * Ast.package) Hashtbl.t;
+		c_modules : (path * string, module_def) Hashtbl.t;
+		c_directories : (string, (string * float ref) list) Hashtbl.t;
+	}
+
+	type t = {
+		cache : cache;
+		mutable signs : (string * string) list;
+	}
+
+	type context_options =
+		| NormalContext
+		| MacroContext
+		| NormalAndMacroContext
+
+	let instance : t option ref = ref None
+
+	let create_cache () = {
+		c_haxelib = Hashtbl.create 0;
+		c_files = Hashtbl.create 0;
+		c_modules = Hashtbl.create 0;
+		c_directories = Hashtbl.create 0;
+	}
+
+	let create () =
+		let cs = {
+			cache = create_cache();
+			signs = [];
+		} in
+		instance := Some cs;
+		cs
+
+	let get () =
+		!instance
+
+	let runs () =
+		!instance <> None
+
+	let get_context_files cs signs =
+		Hashtbl.fold (fun (file,sign) (_,data) acc ->
+			if (List.mem sign signs) then (file,data) :: acc
+			else acc
+		) cs.cache.c_files []
+
+	(* signatures *)
+
+	let get_sign cs sign =
+		List.assoc sign cs.signs
+
+	let add_sign cs sign =
+		let i = string_of_int (List.length cs.signs) in
+		cs.signs <- (sign,i) :: cs.signs;
+		i
+
+	(* modules *)
+
+	let find_module cs key =
+		Hashtbl.find cs.cache.c_modules key
+
+	let cache_module cs key value =
+		Hashtbl.replace cs.cache.c_modules key value
+
+	let taint_modules cs file =
+		Hashtbl.iter (fun _ m -> if m.m_extra.m_file = file then m.m_extra.m_dirty <- Some m) cs.cache.c_modules
+
+	(* files *)
+
+	let find_file cs key =
+		Hashtbl.find cs.cache.c_files key
+
+	let cache_file cs key value =
+		Hashtbl.replace cs.cache.c_files key value
+
+	let remove_file cs key =
+		Hashtbl.remove cs.cache.c_files key
+
+	let remove_files cs file =
+		List.iter (fun (sign,_) -> remove_file cs (sign,file)) cs.signs
+
+	(* haxelibs *)
 
-let global_cache : cache option ref = ref None
+	let find_haxelib cs key =
+		Hashtbl.find cs.cache.c_haxelib key
+
+	let cache_haxelib cs key value =
+		Hashtbl.replace cs.cache.c_haxelib key value
+
+	(* directories *)
+
+	let find_directories cs key =
+		Hashtbl.find cs.cache.c_directories key
+
+	let add_directories cs key value =
+		Hashtbl.replace cs.cache.c_directories key value
+
+	let remove_directory cs key value =
+		try
+			let current = find_directories cs key in
+			Hashtbl.replace cs.cache.c_directories key (List.filter (fun (s,_) -> s <> value) current);
+		with Not_found ->
+			()
+
+	let has_directory cs key value =
+		try
+			List.mem_assoc value (find_directories cs key)
+		with Not_found ->
+			false
+
+	let add_directory cs key value =
+		try
+			let current = find_directories cs key in
+			add_directories cs key (value :: current)
+		with Not_found ->
+			add_directories cs key [value]
+
+	let clear_directories cs key =
+		Hashtbl.remove cs.cache.c_directories key
+end
 
 module Define = struct
 
@@ -258,6 +474,7 @@ module Define = struct
 		| HaxeBoot
 		| HaxeVer
 		| HxcppApiLevel
+		| HxcppGcGenerational
 		| IncludePrefix
 		| Interp
 		| JavaVer
@@ -265,11 +482,13 @@ module Define = struct
 		| JsClassic
 		| JsEs
 		| JsUnflatten
+		| JsSourceMap
 		| KeepOldOutput
 		| LoopUnrollMaxCost
 		| LuaVer
 		| LuaJit
 		| Macro
+		| MacroDebug
 		| MacroTimes
 		| NekoSource
 		| NekoV1
@@ -350,6 +569,7 @@ module Define = struct
 		| HaxeBoot -> ("haxe_boot","Given the name 'haxe' to the flash boot class instead of a generated name")
 		| HaxeVer -> ("haxe_ver","The current Haxe version value")
 		| HxcppApiLevel -> ("hxcpp_api_level","Provided to allow compatibility between hxcpp versions")
+		| HxcppGcGenerational -> ("HXCPP_GC_GENERATIONAL","Experimental Garbage Collector")
 		| IncludePrefix -> ("include_prefix","prepend path to generated include files")
 		| Interp -> ("interp","The code is compiled to be run with --interp")
 		| JavaVer -> ("java_ver", "<version:5-7> Sets the Java version to be targeted")
@@ -357,11 +577,13 @@ module Define = struct
 		| JsClassic -> ("js_classic","Don't use a function wrapper and strict mode in JS output")
 		| JsEs -> ("js_es","Generate JS compilant with given ES standard version (default 5)")
 		| JsUnflatten -> ("js_unflatten","Generate nested objects for packages and types")
+		| JsSourceMap -> ("js_source_map","Generate JavaScript source map even in non-debug mode")
 		| KeepOldOutput -> ("keep_old_output","Keep old source files in the output directory (for C#/Java)")
 		| LoopUnrollMaxCost -> ("loop_unroll_max_cost","Maximum cost (number of expressions * iterations) before loop unrolling is canceled (default 250)")
 		| LuaJit -> ("lua_jit","Enable the jit compiler for lua (version 5.2 only")
 		| LuaVer -> ("lua_ver","The lua version to target")
 		| Macro -> ("macro","Defined when code is compiled in the macro context")
+		| MacroDebug -> ("macro_debug","Show warnings for potential macro problems (e.g. macro-in-macro calls)")
 		| MacroTimes -> ("macro_times","Display per-macro timing when used with --times")
 		| NetVer -> ("net_ver", "<version:20-45> Sets the .NET version to be targeted")
 		| NetTarget -> ("net_target", "<name> Sets the .NET target. Defaults to \"net\". xbox, micro (Micro Framework), compact (Compact Framework) are some valid values")
@@ -407,214 +629,18 @@ module Define = struct
 		| Last -> assert false
 end
 
-module MetaInfo = struct
-	open Meta
-	type meta_usage =
-		| TClass
-		| TClassField
-		| TAbstract
-		| TAbstractField
-		| TEnum
-		| TTypedef
-		| TAnyField
-		| TExpr
-		| TTypeParameter
-
-	type meta_parameter =
-		| HasParam of string
-		| Platform of platform
-		| Platforms of platform list
-		| UsedOn of meta_usage
-		| UsedOnEither of meta_usage list
-		| Internal
-
-	let to_string = function
-		| Abi -> ":abi",("Function ABI/calling convention",[Platforms [Cpp]])
-		| Abstract -> ":abstract",("Sets the underlying class implementation as 'abstract'",[Platforms [Java;Cs]])
-		| Access -> ":access",("Forces private access to package, type or field",[HasParam "Target path";UsedOnEither [TClass;TClassField]])
-		| Accessor -> ":accessor",("Used internally by DCE to mark property accessors",[UsedOn TClassField;Internal])
-		| Allow -> ":allow",("Allows private access from package, type or field",[HasParam "Target path";UsedOnEither [TClass;TClassField]])
-		| Analyzer -> ":analyzer",("Used to configure the static analyzer",[])
-		| Annotation -> ":annotation",("Annotation (@interface) definitions on -java-lib imports will be annotated with this metadata. Has no effect on types compiled by Haxe",[Platform Java; UsedOn TClass])
-		| ArrayAccess -> ":arrayAccess",("Allows [] access on an abstract",[UsedOnEither [TAbstract;TAbstractField]])
-		| Ast -> ":ast",("Internally used to pass the AST source into the typed AST",[Internal])
-		| AstSource -> ":astSource",("Filled by the compiler with the parsed expression of the field",[UsedOn TClassField])
-		| AutoBuild -> ":autoBuild",("Extends @:build metadata to all extending and implementing classes",[HasParam "Build macro call";UsedOn TClass])
-		| Bind -> ":bind",("Override Swf class declaration",[Platform Flash;UsedOn TClass])
-		| Bitmap -> ":bitmap",("Embeds given bitmap data into the class (must extend flash.display.BitmapData)",[HasParam "Bitmap file path";UsedOn TClass;Platform Flash])
-		| BridgeProperties -> ":bridgeProperties",("Creates native property bridges for all Haxe properties in this class",[UsedOn TClass;Platform Cs])
-		| Build -> ":build",("Builds a class or enum from a macro",[HasParam "Build macro call";UsedOnEither [TClass;TEnum]])
-		| BuildXml -> ":buildXml",("Specify xml data to be injected into Build.xml",[Platform Cpp])
-		| Callable -> ":callable",("Abstract forwards call to its underlying type",[UsedOn TAbstract])
-		| Class -> ":class",("Used internally to annotate an enum that will be generated as a class",[Platforms [Java;Cs]; UsedOn TEnum; Internal])
-		| ClassCode -> ":classCode",("Used to inject platform-native code into a class",[Platforms [Java;Cs]; UsedOn TClass])
-		| Commutative -> ":commutative",("Declares an abstract operator as commutative",[UsedOn TAbstractField])
-		| CompilerGenerated -> ":compilerGenerated",("Marks a field as generated by the compiler. Shouldn't be used by the end user",[Platforms [Java;Cs]])
-		| Const -> ":const",("Allows a type parameter to accept expression values",[UsedOn TTypeParameter])
-		| CoreApi -> ":coreApi",("Identifies this class as a core api class (forces Api check)",[UsedOnEither [TClass;TEnum;TTypedef;TAbstract]])
-		| CoreType -> ":coreType",("Identifies an abstract as core type so that it requires no implementation",[UsedOn TAbstract])
-		| CppFileCode -> ":cppFileCode",("Code to be injected into generated cpp file",[Platform Cpp])
-		| CppInclude -> ":cppInclude",("File to be included in generated cpp file",[Platform Cpp])
-		| CppNamespaceCode -> ":cppNamespaceCode",("",[Platform Cpp])
-		| CsNative -> ":csNative",("Automatically added by -net-lib on classes generated from .NET DLL files",[Platform Cs; UsedOnEither[TClass;TEnum]; Internal])
-		| Dce -> ":dce",("Forces dead code elimination even when -dce full is not specified",[UsedOnEither [TClass;TEnum]])
-		| Debug -> ":debug",("Forces debug information to be generated into the Swf even without -debug",[UsedOnEither [TClass;TClassField]; Platform Flash])
-		| Decl -> ":decl",("",[Platform Cpp])
-		| DefParam -> ":defParam",("?",[])
-		| Delegate -> ":delegate",("Automatically added by -net-lib on delegates",[Platform Cs; UsedOn TAbstract])
-		| Depend -> ":depend",("",[Platform Cpp])
-		| Deprecated -> ":deprecated",("Mark a type or field as deprecated",[])
-		| DirectlyUsed -> ":directlyUsed",("Marks types that are directly referenced by non-extern code",[Internal])
-		| DynamicObject -> ":dynamicObject",("Used internally to identify the Dynamic Object implementation",[Platforms [Java;Cs]; UsedOn TClass; Internal])
-		| Eager -> ":eager",("Forces typedefs to be followed early",[UsedOn TTypedef])
-		| Enum -> ":enum",("Defines finite value sets to abstract definitions",[UsedOn TAbstract])
-		| EnumConstructorParam -> ":enumConstructorParam",("Used internally to annotate GADT type parameters",[UsedOn TClass; Internal])
-		| Event -> ":event",("Automatically added by -net-lib on events. Has no effect on types compiled by Haxe",[Platform Cs; UsedOn TClassField])
-		| Exhaustive -> ":exhaustive",("",[Internal])
-		| Expose -> ":expose",("Makes the class available on the window object",[HasParam "?Name=Class path";UsedOn TClass;Platform Js])
-		| Extern -> ":extern",("Marks the field as extern so it is not generated",[UsedOn TClassField])
-		| FakeEnum -> ":fakeEnum",("Treat enum as collection of values of the specified type",[HasParam "Type name";UsedOn TEnum])
-		| File -> ":file",("Includes a given binary file into the target Swf and associates it with the class (must extend flash.utils.ByteArray)",[HasParam "File path";UsedOn TClass;Platform Flash])
-		| FileXml -> ":fileXml",("Include xml attribute snippet in Build.xml entry for file",[UsedOn TClass;Platform Cpp])
-		| Final -> ":final",("Prevents a class from being extended",[UsedOn TClass])
-		| Fixed -> ":fixed",("Delcares an anonymous object to have fixed fields",[ (*UsedOn TObjectDecl(_)*)])
-		| FlatEnum -> ":flatEnum",("Internally used to mark an enum as being flat, i.e. having no function constructors",[UsedOn TEnum; Internal])
-		| Font -> ":font",("Embeds the given TrueType font into the class (must extend flash.text.Font)",[HasParam "TTF path";HasParam "Range String";UsedOn TClass])
-		| Forward -> ":forward",("Forwards field access to underlying type",[HasParam "List of field names";UsedOn TAbstract])
-		| ForwardStatics -> ":forwardStatics",("Forwards static field access to underlying type",[HasParam "List of field names";UsedOn TAbstract])
-		| From -> ":from",("Specifies that the field of the abstract is a cast operation from the type identified in the function",[UsedOn TAbstractField])
-		| FunctionCode -> ":functionCode",("Used to inject platform-native code into a function",[Platforms [Cpp;Java;Cs]])
-		| FunctionTailCode -> ":functionTailCode",("",[Platform Cpp])
-		| Generic -> ":generic",("Marks a class or class field as generic so each type parameter combination generates its own type/field",[UsedOnEither [TClass;TClassField]])
-		| GenericBuild -> ":genericBuild",("Builds instances of a type using the specified macro",[UsedOn TClass])
-		| GenericInstance -> ":genericInstance",("Internally used to mark instances of @:generic methods",[UsedOn TClassField;Internal])
-		| Getter -> ":getter",("Generates a native getter function on the given field",[HasParam "Class field name";UsedOn TClassField;Platform Flash])
-		| Hack -> ":hack",("Allows extending classes marked as @:final",[UsedOn TClass])
-		| HasUntyped -> (":has_untyped",("Used by the typer to mark fields that have untyped expressions",[Internal]))
-		| HaxeGeneric -> ":haxeGeneric",("Used internally to annotate non-native generic classes",[Platform Cs; UsedOnEither[TClass;TEnum]; Internal])
-		| HeaderClassCode -> ":headerClassCode",("Code to be injected into the generated class, in the header",[Platform Cpp])
-		| HeaderCode -> ":headerCode",("Code to be injected into the generated header file",[Platform Cpp])
-		| HeaderInclude -> ":headerInclude",("File to be included in generated header file",[Platform Cpp])
-		| HeaderNamespaceCode -> ":headerNamespaceCode",("",[Platform Cpp])
-		| HxGen -> ":hxGen",("Annotates that an extern class was generated by Haxe",[Platforms [Java;Cs]; UsedOnEither [TClass;TEnum]])
-		| IfFeature -> ":ifFeature",("Causes a field to be kept by DCE if the given feature is part of the compilation",[HasParam "Feature name";UsedOn TClassField])
-		| Impl -> ":impl",("Used internally to mark abstract implementation fields",[UsedOn TAbstractField; Internal])
-		| PythonImport -> ":pythonImport",("Generates python import statement for extern classes",[Platforms [Python]; UsedOn TClass])
-		| ImplicitCast -> ":implicitCast",("Generated automatically on the AST when an implicit abstract cast happens",[Internal; UsedOn TExpr])
-		| Include -> ":include",("",[Platform Cpp])
-		| InitPackage -> ":initPackage",("?",[])
-		| Meta.Internal -> ":internal",("Generates the annotated field/class with 'internal' access",[Platforms [Java;Cs]; UsedOnEither[TClass;TEnum;TClassField]])
-		| IsVar -> ":isVar",("Forces a physical field to be generated for properties that otherwise would not require one",[UsedOn TClassField])
-		| JavaCanonical -> ":javaCanonical",("Used by the Java target to annotate the canonical path of the type",[HasParam "Output type package";HasParam "Output type name";UsedOnEither [TClass;TEnum]; Platform Java])
-		| JavaNative -> ":javaNative",("Automatically added by -java-lib on classes generated from JAR/class files",[Platform Java; UsedOnEither[TClass;TEnum]; Internal])
-		| JsRequire -> ":jsRequire",("Generate javascript module require expression for given extern",[Platform Js; UsedOn TClass])
-		| LuaRequire -> ":luaRequire",("Generate lua module require expression for given extern",[Platform Lua; UsedOn TClass])
-		| Keep -> ":keep",("Causes a field or type to be kept by DCE",[])
-		| KeepInit -> ":keepInit",("Causes a class to be kept by DCE even if all its field are removed",[UsedOn TClass])
-		| KeepSub -> ":keepSub",("Extends @:keep metadata to all implementing and extending classes",[UsedOn TClass])
-		| LibType -> ":libType",("Used by -net-lib and -java-lib to mark a class that shouldn't be checked (overrides, interfaces, etc) by the type loader",[Internal; UsedOn TClass; Platforms [Java;Cs]])
-		| Meta -> ":meta",("Internally used to mark a class field as being the metadata field",[])
-		| Macro -> ":macro",("(deprecated)",[])
-		| MaybeUsed -> ":maybeUsed",("Internally used by DCE to mark fields that might be kept",[Internal])
-		| MergeBlock -> ":mergeBlock",("Merge the annotated block into the current scope",[UsedOn TExpr])
-		| MultiType -> ":multiType",("Specifies that an abstract chooses its this-type from its @:to functions",[UsedOn TAbstract; HasParam "Relevant type parameters"])
-		| Native -> ":native",("Rewrites the path of a class or enum during generation",[HasParam "Output type path";UsedOnEither [TClass;TEnum]])
-		| NativeChildren -> ":nativeChildren",("Annotates that all children from a type should be treated as if it were an extern definition - platform native",[Platforms [Java;Cs]; UsedOn TClass])
-		| NativeGen -> ":nativeGen",("Annotates that a type should be treated as if it were an extern definition - platform native",[Platforms [Java;Cs;Python]; UsedOnEither[TClass;TEnum]])
-		| NativeGeneric -> ":nativeGeneric",("Used internally to annotate native generic classes",[Platform Cs; UsedOnEither[TClass;TEnum]; Internal])
-		| NativeProperty -> ":nativeProperty",("Use native properties which will execute even with dynamic usage",[Platform Cpp])
-		| NativeStaticExtension -> ":nativeStaticExtension",("Converts static function syntax into member call",[Platform Cpp])
-		| NoCompletion -> ":noCompletion",("Prevents the compiler from suggesting completion on this field",[UsedOn TClassField])
-		| NoDebug -> ":noDebug",("Does not generate debug information into the Swf even if -debug is set",[UsedOnEither [TClass;TClassField];Platform Flash])
-		| NoDoc -> ":noDoc",("Prevents a type from being included in documentation generation",[])
-		| NoExpr -> ":noExpr",("Internally used to mark abstract fields which have no expression by design",[Internal])
-		| NoImportGlobal -> ":noImportGlobal",("Prevents a static field from being imported with import Class.*",[UsedOn TAnyField])
-		| NonVirtual -> ":nonVirtual",("Declares function to be non-virtual in cpp",[Platform Cpp])
-		| NoPackageRestrict -> ":noPackageRestrict",("Allows a module to be accessed across all targets if found on its first type",[Internal])
-		| NoPrivateAccess -> ":noPrivateAccess",("Disallow private access to anything for the annotated expression",[UsedOn TExpr])
-		| NoStack -> ":noStack",("",[Platform Cpp])
-		| NotNull -> ":notNull",("Declares an abstract type as not accepting null values",[UsedOn TAbstract])
-		| NoUsing -> ":noUsing",("Prevents a field from being used with 'using'",[UsedOn TClassField])
-		| Ns -> ":ns",("Internally used by the Swf generator to handle namespaces",[Platform Flash])
-		| Objc -> ":objc",("Declares a class or interface that is used to interoperate with Objective-C code",[Platform Cpp;UsedOn TClass])
-		| Op -> ":op",("Declares an abstract field as being an operator overload",[HasParam "The operation";UsedOn TAbstractField])
-		| Optional -> ":optional",("Marks the field of a structure as optional",[UsedOn TClassField])
-		| Overload -> ":overload",("Allows the field to be called with different argument types",[HasParam "Function specification (no expression)";UsedOn TClassField])
-		| PhpConstants -> ":phpConstants",("Marks the static fields of a class as PHP constants, without $",[Platform Php;UsedOn TClass])
-		| PhpGlobal -> ":phpGlobal",("Puts the static fields of a class in the global PHP namespace",[Platform Php;UsedOn TClass])
-		| Public -> ":public",("Marks a class field as being public",[UsedOn TClassField])
-		| PublicFields -> ":publicFields",("Forces all class fields of inheriting classes to be public",[UsedOn TClass])
-		| QuotedField -> ":quotedField",("Used internally to mark structure fields which are quoted in syntax",[Internal])
-		| PrivateAccess -> ":privateAccess",("Allow private access to anything for the annotated expression",[UsedOn TExpr])
-		| Protected -> ":protected",("Marks a class field as being protected",[UsedOn TClassField])
-		| Property -> ":property",("Marks a property field to be compiled as a native C# property",[UsedOn TClassField;Platform Cs])
-		| Pure -> ":pure",("Marks a class field, class or expression as pure (side-effect free)",[UsedOnEither [TClass;TClassField;TExpr]])
-		| ReadOnly -> ":readOnly",("Generates a field with the 'readonly' native keyword",[Platform Cs; UsedOn TClassField])
-		| RealPath -> ":realPath",("Internally used on @:native types to retain original path information",[Internal])
-		| Remove -> ":remove",("Causes an interface to be removed from all implementing classes before generation",[UsedOn TClass])
-		| Require -> ":require",("Allows access to a field only if the specified compiler flag is set",[HasParam "Compiler flag to check";UsedOn TClassField])
-		| RequiresAssign -> ":requiresAssign",("Used internally to mark certain abstract operator overloads",[Internal])
-		| Resolve -> ":resolve",("Abstract fields marked with this metadata can be used to resolve unknown fields",[UsedOn TClassField])
-		| ReplaceReflection -> ":replaceReflection",("Used internally to specify a function that should replace its internal __hx_functionName counterpart",[Platforms [Java;Cs]; UsedOnEither[TClass;TEnum]; Internal])
-		| Rtti -> ":rtti",("Adds runtime type informations",[UsedOn TClass])
-		| Runtime -> ":runtime",("?",[])
-		| RuntimeValue -> ":runtimeValue",("Marks an abstract as being a runtime value",[UsedOn TAbstract])
-		| Scalar -> ":scalar",("Used by hxcpp to mark a custom coreType abstract",[UsedOn TAbstract; Platform Cpp])
-		| SelfCall -> ":selfCall",("Translates method calls into calling object directly",[UsedOn TClassField; Platform Js])
-		| Setter -> ":setter",("Generates a native setter function on the given field",[HasParam "Class field name";UsedOn TClassField;Platform Flash])
-		| StackOnly -> ":stackOnly",("Instances of this type can only appear on the stack",[Platform Cpp])
-		| StoredTypedExpr -> ":storedTypedExpr",("Used internally to reference a typed expression returned from a macro",[Internal])
-		| SkipCtor -> ":skipCtor",("Used internally to generate a constructor as if it were a native type (no __hx_ctor)",[Platforms [Java;Cs]; Internal])
-		| SkipReflection -> ":skipReflection",("Used internally to annotate a field that shouldn't have its reflection data generated",[Platforms [Java;Cs]; UsedOn TClassField; Internal])
-		| Sound -> ":sound",( "Includes a given .wav or .mp3 file into the target Swf and associates it with the class (must extend flash.media.Sound)",[HasParam "File path";UsedOn TClass;Platform Flash])
-		| SourceFile -> ":sourceFile",("Source code filename for external class",[Platform Cpp])
-		| Strict -> ":strict",("Used to declare a native C# attribute or a native Java metadata. Is type checked",[Platforms [Java;Cs]])
-		| Struct -> ":struct",("Marks a class definition as a struct",[Platform Cs; UsedOn TClass])
-		| StructAccess -> ":structAccess",("Marks an extern class as using struct access('.') not pointer('->')",[Platform Cpp; UsedOn TClass])
-		| StructInit -> ":structInit",("Allows to initialize the class with a structure that matches constructor parameters",[UsedOn TClass])
-		| SuppressWarnings -> ":suppressWarnings",("Adds a SuppressWarnings annotation for the generated Java class",[Platform Java; UsedOn TClass])
-		| SwitchVariable -> ":switchVariable",("Used internally to mark switch subject variables",[Internal])
-		| TemplatedCall -> ":templatedCall",("Indicates that the first parameter of static call should be treated as a template arguement",[Platform Cpp; UsedOn TClassField])
-		| Throws -> ":throws",("Adds a 'throws' declaration to the generated function",[HasParam "Type as String"; Platform Java; UsedOn TClassField])
-		| This -> ":this",("Internally used to pass a 'this' expression to macros",[Internal; UsedOn TExpr])
-		| To -> ":to",("Specifies that the field of the abstract is a cast operation to the type identified in the function",[UsedOn TAbstractField])
-		| ToString -> ":toString",("Internally used",[Internal])
-		| Transient -> ":transient",("Adds the 'transient' flag to the class field",[Platform Java; UsedOn TClassField])
-		| ValueUsed -> ":valueUsed",("Internally used by DCE to mark an abstract value as used",[Internal])
-		| Volatile -> ":volatile",("",[Platforms [Java;Cs]])
-		| Unbound -> ":unbound", ("Compiler internal to denote unbounded global variable",[])
-		| UnifyMinDynamic -> ":unifyMinDynamic",("Allows a collection of types to unify to Dynamic",[UsedOn TClassField])
-		| Unreflective -> ":unreflective",("",[Platform Cpp])
-		| Unsafe -> ":unsafe",("Declares a class, or a method with the C#'s 'unsafe' flag",[Platform Cs; UsedOnEither [TClass;TClassField]])
-		| Usage -> ":usage",("?",[])
-		| Used -> ":used",("Internally used by DCE to mark a class or field as used",[Internal])
-		| Value -> ":value",("Used to store default values for fields and function arguments",[UsedOn TClassField])
-		| Void -> ":void",("Use Cpp native 'void' return type",[Platform Cpp])
-		| Last -> assert false
-		(* do not put any custom metadata after Last *)
-		| Dollar s -> "$" ^ s,("",[])
-		| Custom 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 (to_string m)) m;
-				loop (i + 1);
-			end;
-		in
-		loop 0;
-		h
-
-	let parse s = try Hashtbl.find hmeta (":" ^ s) with Not_found -> Custom (":" ^ s)
-
-	let from_string s =
-		if s = "" then Custom "" else match s.[0] with
-		| ':' -> (try Hashtbl.find hmeta s with Not_found -> Custom s)
-		| '$' -> Dollar (String.sub s 1 (String.length s - 1))
-		| _ -> Custom s
-end
+let short_platform_name = function
+	| Cross -> "x"
+	| Js -> "js"
+	| Lua -> "lua"
+	| Neko -> "n"
+	| Flash -> "swf"
+	| Php -> "php"
+	| Cpp -> "cpp"
+	| Cs -> "cs"
+	| Java -> "jav"
+	| Python -> "py"
+	| Hl -> "hl"
 
 let stats =
 	{
@@ -732,7 +758,7 @@ let create version s_version args =
 	let defines =
 		PMap.add "true" "1" (
 		PMap.add "source-header" ("Generated by Haxe " ^ s_version) (
-		if !display_default <> DMNone then PMap.add "display" "1" PMap.empty else PMap.empty))
+		if !display_default <> DisplayMode.DMNone then PMap.add "display" "1" PMap.empty else PMap.empty))
 	in
 	{
 		version = version;
@@ -741,14 +767,18 @@ let create version s_version args =
 			shared_display_information = {
 				import_positions = PMap.empty;
 				diagnostics_messages = [];
+				type_hints = Hashtbl.create 0;
+				document_symbols = [];
+				removable_code = [];
 			}
 		};
 		display_information = {
 			unresolved_identifiers = [];
+			interface_field_implementations = [];
 		};
 		sys_args = args;
 		debug = false;
-		display = !display_default;
+		display = DisplayMode.create !display_default;
 		verbose = false;
 		foptimize = true;
 		features = Hashtbl.create 0;
@@ -811,58 +841,21 @@ let clone com =
 		main_class = None;
 		features = Hashtbl.create 0;
 		file_lookup_cache = Hashtbl.create 0;
+		parser_cache = Hashtbl.create 0 ;
 		callbacks = create_callbacks();
+		display_information = {
+			unresolved_identifiers = [];
+			interface_field_implementations = [];
+		};
 	}
 
-let file_time file =
-	try (Unix.stat file).Unix.st_mtime with _ -> 0.
-
-let get_signature com =
-	match com.defines_signature with
-	| Some s -> s
-	| None ->
-		let defines = PMap.foldi (fun k v acc ->
-			(* don't make much difference between these special compilation flags *)
-			match String.concat "_" (ExtString.String.nsplit k "-") with
-			| "display" | "use_rtti_doc" | "macro_times" | "display_details" | "no_copt" -> acc
-			| _ -> (k ^ "=" ^ v) :: acc
-		) com.defines [] in
-		let str = String.concat "@" (List.sort compare defines) in
-		let s = Digest.string str in
-		com.defines_signature <- Some s;
-		s
+let file_time file = Extc.filetime file
 
 let file_extension file =
 	match List.rev (ExtString.String.nsplit file ".") with
 	| e :: _ -> String.lowercase e
 	| [] -> ""
 
-let platforms = [
-	Js;
-	Lua;
-	Neko;
-	Flash;
-	Php;
-	Cpp;
-	Cs;
-	Java;
-	Python;
-	Hl;
-]
-
-let platform_name = function
-	| Cross -> "cross"
-	| Js -> "js"
-	| Lua -> "lua"
-	| Neko -> "neko"
-	| Flash -> "flash"
-	| Php -> "php"
-	| Cpp -> "cpp"
-	| Cs -> "cs"
-	| Java -> "java"
-	| Python -> "python"
-	| Hl -> "hl"
-
 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
@@ -944,7 +937,7 @@ let has_dce com =
 	and we wouldn't need to generate unnecessary imports in dce=no, but that's good enough for now.
 *)
 let is_directly_used com meta =
-	not (has_dce com) || Ast.Meta.has Ast.Meta.DirectlyUsed meta
+	not (has_dce com) || Meta.has Meta.DirectlyUsed meta
 
 let rec has_feature com f =
 	try
@@ -957,9 +950,9 @@ let rec has_feature com f =
 		| meth :: cl :: pack ->
 			let r = (try
 				let path = List.rev pack, cl in
-				(match List.find (fun t -> t_path t = path && not (Ast.Meta.has Ast.Meta.RealPath (t_infos t).mt_meta)) com.types with
-				| t when meth = "*" -> (match t with TAbstractDecl a -> Ast.Meta.has Ast.Meta.ValueUsed a.a_meta | _ ->
-					Ast.Meta.has Ast.Meta.Used (t_infos t).mt_meta)
+				(match List.find (fun t -> t_path t = path && not (Meta.has Meta.RealPath (t_infos t).mt_meta)) com.types with
+				| t when meth = "*" -> (match t with TAbstractDecl a -> Meta.has Meta.ValueUsed a.a_meta | _ ->
+					Meta.has Meta.Used (t_infos t).mt_meta)
 				| TClassDecl ({cl_extern = true} as c) when com.platform <> Js || cl <> "Array" && cl <> "Math" ->
 					Meta.has Meta.Used (try PMap.find meth c.cl_statics with Not_found -> PMap.find meth c.cl_fields).cf_meta
 				| TClassDecl c ->
@@ -979,7 +972,7 @@ let allow_package ctx s =
 	with Not_found ->
 		()
 
-let error msg p = raise (Abort (msg,p))
+let abort msg p = raise (Abort (msg,p))
 
 let platform ctx p = ctx.platform = p
 
@@ -1005,9 +998,14 @@ let find_file ctx f =
 			| [] -> loop true [""]
 			| p :: l ->
 				let file = p ^ f in
-				if Sys.file_exists file then
-					file
-				else
+				if Sys.file_exists file then begin
+					(try
+						let ext = String.rindex file '.' in
+						let file_pf = String.sub file 0 (ext + 1) ^ platform_name ctx.platform ^ String.sub file ext (String.length file - ext) in
+						if not (defined ctx Define.CoreApi) && Sys.file_exists file_pf then file_pf else file
+					with Not_found ->
+						file)
+				end else
 					loop (had_empty || p = "") l
 		in
 		let r = (try Some (loop false ctx.class_path) with Not_found -> None) in
@@ -1016,27 +1014,6 @@ let find_file ctx f =
 		| None -> raise Not_found
 		| Some f -> f)
 
-let get_full_path f = try Extc.get_full_path f with _ -> f
-
-let unique_full_path = if Sys.os_type = "Win32" || Sys.os_type = "Cygwin" then (fun f -> String.lowercase (get_full_path f)) else get_full_path
-
-let get_path_parts f =
-	let f = String.concat "/" (ExtString.String.nsplit f "\\") in
-	let cl = ExtString.String.nsplit f "." in
-	let cl = (match List.rev cl with
-		| ["hx";path] -> ExtString.String.nsplit path "/"
-		| _ -> cl
-	) in
-	cl
-
-let add_trailing_slash p =
-	let l = String.length p in
-	if l = 0 then
-		"./"
-	else match p.[l-1] with
-		| '\\' | '/' -> p
-		| _ -> p ^ "/"
-
 let rec mkdir_recursive base dir_list =
 	match dir_list with
 	| [] -> ()
@@ -1065,22 +1042,25 @@ let mem_size v =
 (* ------------------------- TIMERS ----------------------------- *)
 
 type timer_infos = {
-	name : string;
+	id : string list;
 	mutable start : float list;
 	mutable total : float;
+	mutable calls : int;
 }
 
 let get_time = Extc.time
 let htimers = Hashtbl.create 0
 
-let new_timer name =
+let new_timer id =
+	let key = String.concat "." id in
 	try
-		let t = Hashtbl.find htimers name in
+		let t = Hashtbl.find htimers key in
 		t.start <- get_time() :: t.start;
+		t.calls <- t.calls + 1;
 		t
 	with Not_found ->
-		let t = { name = name; start = [get_time()]; total = 0.; } in
-		Hashtbl.add htimers name t;
+		let t = { id = id; start = [get_time()]; total = 0.; calls = 1; } in
+		Hashtbl.add htimers key t;
 		t
 
 let curtime = ref []
@@ -1095,15 +1075,15 @@ let close t =
 	t.total <- t.total +. dt;
 	let rec loop() =
 		match !curtime with
-		| [] -> failwith ("Timer " ^ t.name ^ " closed while not active")
+		| [] -> failwith ("Timer " ^ (String.concat "." t.id) ^ " closed while not active")
 		| tt :: l -> curtime := l; if t != tt then loop()
 	in
 	loop();
 	(* because of rounding errors while adding small times, we need to make sure that we don't have start > now *)
 	List.iter (fun ct -> ct.start <- List.map (fun t -> let s = t +. dt in if s > now then now else s) ct.start) !curtime
 
-let timer name =
-	let t = new_timer name in
+let timer id =
+	let t = new_timer id in
 	curtime := t :: !curtime;
 	(function() -> close t)
 
@@ -1113,7 +1093,6 @@ let rec close_times() =
 	| t :: _ -> close t; close_times()
 
 ;;
-Ast.Meta.to_string_ref := fun m -> fst (MetaInfo.to_string m)
 
 (*  Taken from OCaml source typing/oprint.ml
 
@@ -1143,7 +1122,19 @@ let float_repres f =
 			Printf.sprintf "%.18g" f
 		in valid_float_lexeme float_val
 
-
 let add_diagnostics_message com s p sev =
 	let di = com.shared.shared_display_information in
 	di.diagnostics_messages <- (s,p,sev) :: di.diagnostics_messages
+
+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;
+	"defines_signature",s_opt (fun s -> Digest.to_hex s) com.defines_signature;
+]

+ 424 - 0
src/context/meta.ml

@@ -0,0 +1,424 @@
+open Globals
+
+type strict_meta =
+	| Abi
+	| Abstract
+	| Access
+	| Accessor
+	| Allow
+	| Analyzer
+	| Annotation
+	| ArrayAccess
+	| Ast
+	| AstSource
+	| AutoBuild
+	| Bind
+	| Bitmap
+	| BridgeProperties
+	| Build
+	| BuildXml
+	| Callable
+	| Class
+	| ClassCode
+	| Commutative
+	| CompilerGenerated
+	| Const
+	| CoreApi
+	| CoreType
+	| CppFileCode
+	| CppInclude
+	| CppNamespaceCode
+	| CsNative
+	| Dce
+	| Debug
+	| Decl
+	| DefParam
+	| Delegate
+	| Depend
+	| Deprecated
+	| DirectlyUsed
+	| DynamicObject
+	| Eager
+	| Enum
+	| EnumConstructorParam
+	| Event
+	| Exhaustive
+	| Expose
+	| Extern
+	| FakeEnum
+	| File
+	| FileXml
+	| Final
+	| Fixed
+	| FlatEnum
+	| Font
+	| Forward
+	| ForwardStatics
+	| From
+	| FunctionCode
+	| FunctionTailCode
+	| Generic
+	| GenericBuild
+	| GenericInstance
+	| Getter
+	| Hack
+	| HasUntyped
+	| HaxeGeneric
+	| HeaderClassCode
+	| HeaderCode
+	| HeaderInclude
+	| HeaderNamespaceCode
+	| HxGen
+	| IfFeature
+	| Impl
+	| PythonImport
+	| ImplicitCast
+	| Include
+	| InitPackage
+	| InlineConstructorVariable
+	| Internal
+	| IsVar
+	| JavaCanonical
+	| JavaNative
+	| JsRequire
+	| Keep
+	| KeepInit
+	| KeepSub
+	| LibType
+	| LuaRequire
+	| Meta
+	| Macro
+	| MaybeUsed
+	| MergeBlock
+	| MultiReturn
+	| MultiType
+	| Native
+	| NativeChildren
+	| NativeGen
+	| NativeGeneric
+	| NativeProperty
+	| NativeStaticExtension
+	| NoCompletion
+	| NoDebug
+	| NoDoc
+	| NoExpr
+	| NoImportGlobal
+	| NonVirtual
+	| NoPackageRestrict
+	| NoPrivateAccess
+	| NoStack
+	| NotNull
+	| NoUsing
+	| Ns
+	| Objc
+	| ObjcProtocol
+	| Op
+	| Optional
+	| Overload
+	| PhpConstants
+	| PhpGlobal
+	| PrivateAccess
+	| Property
+	| Protected
+	| Public
+	| PublicFields
+	| Pure
+	| QuotedField
+	| ReadOnly
+	| RealPath
+	| Remove
+	| Require
+	| RequiresAssign
+	| Resolve
+	| Rtti
+	| Runtime
+	| RuntimeValue
+	| Scalar
+	| SelfCall
+	| Setter
+	| SkipCtor
+	| SkipReflection
+	| Sound
+	| SourceFile
+	| StackOnly
+	| StoredTypedExpr
+	| Strict
+	| Struct
+	| StructAccess
+	| StructInit
+	| SuppressWarnings
+	| This
+	| Throws
+	| To
+	| ToString
+	| Transient
+	| TemplatedCall
+	| ValueUsed
+	| Volatile
+	| Unbound
+	| UnifyMinDynamic
+	| Unreflective
+	| Unsafe
+	| Usage
+	| Used
+	| UserVariable
+	| Value
+	| Void
+	| Last
+	(* do not put any custom metadata after Last *)
+	| Dollar of string
+	| Custom of string
+
+let has m ml = List.exists (fun (m2,_,_) -> m = m2) ml
+let get m ml = List.find (fun (m2,_,_) -> m = m2) ml
+
+type meta_usage =
+	| TClass
+	| TClassField
+	| TAbstract
+	| TAbstractField
+	| TEnum
+	| TTypedef
+	| TAnyField
+	| TExpr
+	| TTypeParameter
+
+type meta_parameter =
+	| HasParam of string
+	| Platform of platform
+	| Platforms of platform list
+	| UsedOn of meta_usage
+	| UsedOnEither of meta_usage list
+	| UsedInternally
+
+let get_info = function
+	| Abi -> ":abi",("Function ABI/calling convention",[Platforms [Cpp]])
+	| Abstract -> ":abstract",("Sets the underlying class implementation as 'abstract'",[Platforms [Java;Cs]])
+	| Access -> ":access",("Forces private access to package, type or field",[HasParam "Target path";UsedOnEither [TClass;TClassField]])
+	| Accessor -> ":accessor",("Used internally by DCE to mark property accessors",[UsedOn TClassField;UsedInternally])
+	| Allow -> ":allow",("Allows private access from package, type or field",[HasParam "Target path";UsedOnEither [TClass;TClassField]])
+	| Analyzer -> ":analyzer",("Used to configure the static analyzer",[])
+	| Annotation -> ":annotation",("Annotation (@interface) definitions on -java-lib imports will be annotated with this metadata. Has no effect on types compiled by Haxe",[Platform Java; UsedOn TClass])
+	| ArrayAccess -> ":arrayAccess",("Allows [] access on an abstract",[UsedOnEither [TAbstract;TAbstractField]])
+	| Ast -> ":ast",("Internally used to pass the AST source into the typed AST",[UsedInternally])
+	| AstSource -> ":astSource",("Filled by the compiler with the parsed expression of the field",[UsedOn TClassField])
+	| AutoBuild -> ":autoBuild",("Extends @:build metadata to all extending and implementing classes",[HasParam "Build macro call";UsedOn TClass])
+	| Bind -> ":bind",("Override Swf class declaration",[Platform Flash;UsedOn TClass])
+	| Bitmap -> ":bitmap",("Embeds given bitmap data into the class (must extend flash.display.BitmapData)",[HasParam "Bitmap file path";UsedOn TClass;Platform Flash])
+	| BridgeProperties -> ":bridgeProperties",("Creates native property bridges for all Haxe properties in this class",[UsedOn TClass;Platform Cs])
+	| Build -> ":build",("Builds a class or enum from a macro",[HasParam "Build macro call";UsedOnEither [TClass;TEnum]])
+	| BuildXml -> ":buildXml",("Specify xml data to be injected into Build.xml",[Platform Cpp])
+	| Callable -> ":callable",("Abstract forwards call to its underlying type",[UsedOn TAbstract])
+	| Class -> ":class",("Used internally to annotate an enum that will be generated as a class",[Platforms [Java;Cs]; UsedOn TEnum; UsedInternally])
+	| ClassCode -> ":classCode",("Used to inject platform-native code into a class",[Platforms [Java;Cs]; UsedOn TClass])
+	| Commutative -> ":commutative",("Declares an abstract operator as commutative",[UsedOn TAbstractField])
+	| CompilerGenerated -> ":compilerGenerated",("Marks a field as generated by the compiler. Shouldn't be used by the end user",[Platforms [Java;Cs]])
+	| Const -> ":const",("Allows a type parameter to accept expression values",[UsedOn TTypeParameter])
+	| CoreApi -> ":coreApi",("Identifies this class as a core api class (forces Api check)",[UsedOnEither [TClass;TEnum;TTypedef;TAbstract]])
+	| CoreType -> ":coreType",("Identifies an abstract as core type so that it requires no implementation",[UsedOn TAbstract])
+	| CppFileCode -> ":cppFileCode",("Code to be injected into generated cpp file",[Platform Cpp])
+	| CppInclude -> ":cppInclude",("File to be included in generated cpp file",[Platform Cpp])
+	| CppNamespaceCode -> ":cppNamespaceCode",("",[Platform Cpp])
+	| CsNative -> ":csNative",("Automatically added by -net-lib on classes generated from .NET DLL files",[Platform Cs; UsedOnEither[TClass;TEnum]; UsedInternally])
+	| Dce -> ":dce",("Forces dead code elimination even when -dce full is not specified",[UsedOnEither [TClass;TEnum]])
+	| Debug -> ":debug",("Forces debug information to be generated into the Swf even without -debug",[UsedOnEither [TClass;TClassField]; Platform Flash])
+	| Decl -> ":decl",("",[Platform Cpp])
+	| DefParam -> ":defParam",("Default function argument value loaded from the SWF and used for documentation in Genxml",[Platform Flash;UsedInternally])
+	| Delegate -> ":delegate",("Automatically added by -net-lib on delegates",[Platform Cs; UsedOn TAbstract])
+	| Depend -> ":depend",("",[Platform Cpp])
+	| Deprecated -> ":deprecated",("Mark a type or field as deprecated",[])
+	| DirectlyUsed -> ":directlyUsed",("Marks types that are directly referenced by non-extern code",[UsedInternally])
+	| DynamicObject -> ":dynamicObject",("Used internally to identify the Dynamic Object implementation",[Platforms [Java;Cs]; UsedOn TClass; UsedInternally])
+	| Eager -> ":eager",("Forces typedefs to be followed early",[UsedOn TTypedef])
+	| Enum -> ":enum",("Defines finite value sets to abstract definitions",[UsedOn TAbstract])
+	| EnumConstructorParam -> ":enumConstructorParam",("Used internally to annotate GADT type parameters",[UsedOn TClass; UsedInternally])
+	| Event -> ":event",("Automatically added by -net-lib on events. Has no effect on types compiled by Haxe",[Platform Cs; UsedOn TClassField])
+	| Exhaustive -> ":exhaustive",("",[UsedInternally])
+	| Expose -> ":expose",("Makes the class available on the window object",[HasParam "?Name=Class path";UsedOn TClass;Platform Js])
+	| Extern -> ":extern",("Marks the field as extern so it is not generated",[UsedOn TClassField])
+	| FakeEnum -> ":fakeEnum",("Treat enum as collection of values of the specified type",[HasParam "Type name";UsedOn TEnum])
+	| File -> ":file",("Includes a given binary file into the target Swf and associates it with the class (must extend flash.utils.ByteArray)",[HasParam "File path";UsedOn TClass;Platform Flash])
+	| FileXml -> ":fileXml",("Include xml attribute snippet in Build.xml entry for file",[UsedOn TClass;Platform Cpp])
+	| Final -> ":final",("Prevents a class from being extended",[UsedOn TClass])
+	| Fixed -> ":fixed",("Delcares an anonymous object to have fixed fields",[ (*UsedOn TObjectDecl(_)*)])
+	| FlatEnum -> ":flatEnum",("Internally used to mark an enum as being flat, i.e. having no function constructors",[UsedOn TEnum; UsedInternally])
+	| Font -> ":font",("Embeds the given TrueType font into the class (must extend flash.text.Font)",[HasParam "TTF path";HasParam "Range String";UsedOn TClass])
+	| Forward -> ":forward",("Forwards field access to underlying type",[HasParam "List of field names";UsedOn TAbstract])
+	| ForwardStatics -> ":forwardStatics",("Forwards static field access to underlying type",[HasParam "List of field names";UsedOn TAbstract])
+	| From -> ":from",("Specifies that the field of the abstract is a cast operation from the type identified in the function",[UsedOn TAbstractField])
+	| FunctionCode -> ":functionCode",("Used to inject platform-native code into a function",[Platforms [Cpp;Java;Cs]])
+	| FunctionTailCode -> ":functionTailCode",("",[Platform Cpp])
+	| Generic -> ":generic",("Marks a class or class field as generic so each type parameter combination generates its own type/field",[UsedOnEither [TClass;TClassField]])
+	| GenericBuild -> ":genericBuild",("Builds instances of a type using the specified macro",[UsedOn TClass])
+	| GenericInstance -> ":genericInstance",("Internally used to mark instances of @:generic methods",[UsedOn TClassField;UsedInternally])
+	| Getter -> ":getter",("Generates a native getter function on the given field",[HasParam "Class field name";UsedOn TClassField;Platform Flash])
+	| Hack -> ":hack",("Allows extending classes marked as @:final",[UsedOn TClass])
+	| HasUntyped -> (":has_untyped",("Used by the typer to mark fields that have untyped expressions",[UsedInternally]))
+	| HaxeGeneric -> ":haxeGeneric",("Used internally to annotate non-native generic classes",[Platform Cs; UsedOnEither[TClass;TEnum]; UsedInternally])
+	| HeaderClassCode -> ":headerClassCode",("Code to be injected into the generated class, in the header",[Platform Cpp])
+	| HeaderCode -> ":headerCode",("Code to be injected into the generated header file",[Platform Cpp])
+	| HeaderInclude -> ":headerInclude",("File to be included in generated header file",[Platform Cpp])
+	| HeaderNamespaceCode -> ":headerNamespaceCode",("",[Platform Cpp])
+	| HxGen -> ":hxGen",("Annotates that an extern class was generated by Haxe",[Platforms [Java;Cs]; UsedOnEither [TClass;TEnum]])
+	| IfFeature -> ":ifFeature",("Causes a field to be kept by DCE if the given feature is part of the compilation",[HasParam "Feature name";UsedOn TClassField])
+	| Impl -> ":impl",("Used internally to mark abstract implementation fields",[UsedOn TAbstractField; UsedInternally])
+	| PythonImport -> ":pythonImport",("Generates python import statement for extern classes",[Platforms [Python]; UsedOn TClass])
+	| ImplicitCast -> ":implicitCast",("Generated automatically on the AST when an implicit abstract cast happens",[UsedInternally; UsedOn TExpr])
+	| Include -> ":include",("",[Platform Cpp])
+	| InitPackage -> ":initPackage",("Some weird thing for Genjs we want to remove someday",[UsedInternally; Platform Js])
+	| InlineConstructorVariable -> ":inlineConstructorVariable",("Internally used to mark variables that come from inlined constructors",[UsedInternally])
+	| Internal -> ":internal",("Generates the annotated field/class with 'internal' access",[Platforms [Java;Cs]; UsedOnEither[TClass;TEnum;TClassField]])
+	| IsVar -> ":isVar",("Forces a physical field to be generated for properties that otherwise would not require one",[UsedOn TClassField])
+	| JavaCanonical -> ":javaCanonical",("Used by the Java target to annotate the canonical path of the type",[HasParam "Output type package";HasParam "Output type name";UsedOnEither [TClass;TEnum]; Platform Java])
+	| JavaNative -> ":javaNative",("Automatically added by -java-lib on classes generated from JAR/class files",[Platform Java; UsedOnEither[TClass;TEnum]; UsedInternally])
+	| JsRequire -> ":jsRequire",("Generate javascript module require expression for given extern",[Platform Js; UsedOn TClass])
+	| LuaRequire -> ":luaRequire",("Generate lua module require expression for given extern",[Platform Lua; UsedOn TClass])
+	| Keep -> ":keep",("Causes a field or type to be kept by DCE",[])
+	| KeepInit -> ":keepInit",("Causes a class to be kept by DCE even if all its field are removed",[UsedOn TClass])
+	| KeepSub -> ":keepSub",("Extends @:keep metadata to all implementing and extending classes",[UsedOn TClass])
+	| LibType -> ":libType",("Used by -net-lib and -java-lib to mark a class that shouldn't be checked (overrides, interfaces, etc) by the type loader",[UsedInternally; UsedOn TClass; Platforms [Java;Cs]])
+	| Meta -> ":meta",("Internally used to mark a class field as being the metadata field",[])
+	| Macro -> ":macro",("(deprecated)",[])
+	| MaybeUsed -> ":maybeUsed",("Internally used by DCE to mark fields that might be kept",[UsedInternally])
+	| MergeBlock -> ":mergeBlock",("Merge the annotated block into the current scope",[UsedOn TExpr])
+	| MultiReturn -> ":multiReturn",("Annotates an extern class as the result of multi-return function",[UsedOn TClass; Platform Lua])
+	| MultiType -> ":multiType",("Specifies that an abstract chooses its this-type from its @:to functions",[UsedOn TAbstract; HasParam "Relevant type parameters"])
+	| Native -> ":native",("Rewrites the path of a class or enum during generation",[HasParam "Output type path";UsedOnEither [TClass;TEnum]])
+	| NativeChildren -> ":nativeChildren",("Annotates that all children from a type should be treated as if it were an extern definition - platform native",[Platforms [Java;Cs]; UsedOn TClass])
+	| NativeGen -> ":nativeGen",("Annotates that a type should be treated as if it were an extern definition - platform native",[Platforms [Java;Cs;Python]; UsedOnEither[TClass;TEnum]])
+	| NativeGeneric -> ":nativeGeneric",("Used internally to annotate native generic classes",[Platform Cs; UsedOnEither[TClass;TEnum]; UsedInternally])
+	| NativeProperty -> ":nativeProperty",("Use native properties which will execute even with dynamic usage",[Platform Cpp])
+	| NativeStaticExtension -> ":nativeStaticExtension",("Converts static function syntax into member call",[Platform Cpp])
+	| NoCompletion -> ":noCompletion",("Prevents the compiler from suggesting completion on this field",[UsedOn TClassField])
+	| NoDebug -> ":noDebug",("Does not generate debug information into the Swf even if -debug is set",[UsedOnEither [TClass;TClassField];Platform Flash])
+	| NoDoc -> ":noDoc",("Prevents a type from being included in documentation generation",[])
+	| NoExpr -> ":noExpr",("Internally used to mark abstract fields which have no expression by design",[UsedInternally])
+	| NoImportGlobal -> ":noImportGlobal",("Prevents a static field from being imported with import Class.*",[UsedOn TAnyField])
+	| NonVirtual -> ":nonVirtual",("Declares function to be non-virtual in cpp",[Platform Cpp])
+	| NoPackageRestrict -> ":noPackageRestrict",("Allows a module to be accessed across all targets if found on its first type",[UsedInternally])
+	| NoPrivateAccess -> ":noPrivateAccess",("Disallow private access to anything for the annotated expression",[UsedOn TExpr])
+	| NoStack -> ":noStack",("",[Platform Cpp])
+	| NotNull -> ":notNull",("Declares an abstract type as not accepting null values",[UsedOn TAbstract])
+	| NoUsing -> ":noUsing",("Prevents a field from being used with 'using'",[UsedOn TClassField])
+	| Ns -> ":ns",("Internally used by the Swf generator to handle namespaces",[Platform Flash])
+	| Objc -> ":objc",("Declares a class or interface that is used to interoperate with Objective-C code",[Platform Cpp;UsedOn TClass])
+	| ObjcProtocol -> ":objcProtocol",("Associates an interface with, or describes a function in, a native Objective-C protocol.",[Platform Cpp;UsedOnEither [TClass;TClassField] ])
+	| Op -> ":op",("Declares an abstract field as being an operator overload",[HasParam "The operation";UsedOn TAbstractField])
+	| Optional -> ":optional",("Marks the field of a structure as optional",[UsedOn TClassField])
+	| Overload -> ":overload",("Allows the field to be called with different argument types",[HasParam "Function specification (no expression)";UsedOn TClassField])
+	| PhpConstants -> ":phpConstants",("Marks the static fields of a class as PHP constants, without $",[Platform Php;UsedOn TClass])
+	| PhpGlobal -> ":phpGlobal",("Puts the static fields of a class in the global PHP namespace",[Platform Php;UsedOn TClass])
+	| Public -> ":public",("Marks a class field as being public",[UsedOn TClassField;UsedInternally])
+	| PublicFields -> ":publicFields",("Forces all class fields of inheriting classes to be public",[UsedOn TClass])
+	| QuotedField -> ":quotedField",("Used internally to mark structure fields which are quoted in syntax",[UsedInternally])
+	| PrivateAccess -> ":privateAccess",("Allow private access to anything for the annotated expression",[UsedOn TExpr])
+	| Protected -> ":protected",("Marks a class field as being protected",[UsedOn TClassField;Platforms [Cs;Java;Flash]])
+	| Property -> ":property",("Marks a property field to be compiled as a native C# property",[UsedOn TClassField;Platform Cs])
+	| Pure -> ":pure",("Marks a class field, class or expression as pure (side-effect free)",[UsedOnEither [TClass;TClassField;TExpr]])
+	| ReadOnly -> ":readOnly",("Generates a field with the 'readonly' native keyword",[Platform Cs; UsedOn TClassField])
+	| RealPath -> ":realPath",("Internally used on @:native types to retain original path information",[UsedInternally])
+	| Remove -> ":remove",("Causes an interface to be removed from all implementing classes before generation",[UsedOn TClass])
+	| Require -> ":require",("Allows access to a field only if the specified compiler flag is set",[HasParam "Compiler flag to check";UsedOn TClassField])
+	| RequiresAssign -> ":requiresAssign",("Used internally to mark certain abstract operator overloads",[UsedInternally])
+	| Resolve -> ":resolve",("Abstract fields marked with this metadata can be used to resolve unknown fields",[UsedOn TClassField])
+	| Rtti -> ":rtti",("Adds runtime type informations",[UsedOn TClass])
+	| Runtime -> ":runtime",("?",[])
+	| RuntimeValue -> ":runtimeValue",("Marks an abstract as being a runtime value",[UsedOn TAbstract])
+	| Scalar -> ":scalar",("Used by hxcpp to mark a custom coreType abstract",[UsedOn TAbstract; Platform Cpp])
+	| SelfCall -> ":selfCall",("Translates method calls into calling object directly",[UsedOn TClassField; Platform Js])
+	| Setter -> ":setter",("Generates a native setter function on the given field",[HasParam "Class field name";UsedOn TClassField;Platform Flash])
+	| StackOnly -> ":stackOnly",("Instances of this type can only appear on the stack",[Platform Cpp])
+	| StoredTypedExpr -> ":storedTypedExpr",("Used internally to reference a typed expression returned from a macro",[UsedInternally])
+	| SkipCtor -> ":skipCtor",("Used internally to generate a constructor as if it were a native type (no __hx_ctor)",[Platforms [Java;Cs]; UsedInternally])
+	| SkipReflection -> ":skipReflection",("Used internally to annotate a field that shouldn't have its reflection data generated",[Platforms [Java;Cs]; UsedOn TClassField; UsedInternally])
+	| Sound -> ":sound",( "Includes a given .wav or .mp3 file into the target Swf and associates it with the class (must extend flash.media.Sound)",[HasParam "File path";UsedOn TClass;Platform Flash])
+	| SourceFile -> ":sourceFile",("Source code filename for external class",[Platform Cpp])
+	| Strict -> ":strict",("Used to declare a native C# attribute or a native Java metadata. Is type checked",[Platforms [Java;Cs]])
+	| Struct -> ":struct",("Marks a class definition as a struct",[Platform Cs; UsedOn TClass])
+	| StructAccess -> ":structAccess",("Marks an extern class as using struct access('.') not pointer('->')",[Platform Cpp; UsedOn TClass])
+	| StructInit -> ":structInit",("Allows to initialize the class with a structure that matches constructor parameters",[UsedOn TClass])
+	| SuppressWarnings -> ":suppressWarnings",("Adds a SuppressWarnings annotation for the generated Java class",[Platform Java; UsedOn TClass])
+	| TemplatedCall -> ":templatedCall",("Indicates that the first parameter of static call should be treated as a template arguement",[Platform Cpp; UsedOn TClassField])
+	| Throws -> ":throws",("Adds a 'throws' declaration to the generated function",[HasParam "Type as String"; Platform Java; UsedOn TClassField])
+	| This -> ":this",("Internally used to pass a 'this' expression to macros",[UsedInternally; UsedOn TExpr])
+	| To -> ":to",("Specifies that the field of the abstract is a cast operation to the type identified in the function",[UsedOn TAbstractField])
+	| ToString -> ":toString",("Internally used",[UsedInternally])
+	| Transient -> ":transient",("Adds the 'transient' flag to the class field",[Platform Java; UsedOn TClassField])
+	| ValueUsed -> ":valueUsed",("Internally used by DCE to mark an abstract value as used",[UsedInternally])
+	| Volatile -> ":volatile",("",[Platforms [Java;Cs]])
+	| Unbound -> ":unbound", ("Compiler internal to denote unbounded global variable",[UsedInternally])
+	| UnifyMinDynamic -> ":unifyMinDynamic",("Allows a collection of types to unify to Dynamic",[UsedOn TClassField])
+	| Unreflective -> ":unreflective",("",[Platform Cpp])
+	| Unsafe -> ":unsafe",("Declares a class, or a method with the C#'s 'unsafe' flag",[Platform Cs; UsedOnEither [TClass;TClassField]])
+	| Usage -> ":usage",("Internal metadata used to mark a symbol for which usage request was invoked",[UsedInternally])
+	| Used -> ":used",("Internally used by DCE to mark a class or field as used",[UsedInternally])
+	| UserVariable -> ":userVariable",("Internally used to mark variables that come from user code",[UsedInternally])
+	| Value -> ":value",("Used to store default values for fields and function arguments",[UsedOn TClassField])
+	| Void -> ":void",("Use Cpp native 'void' return type",[Platform Cpp])
+	| Last -> assert false
+	(* do not put any custom metadata after Last *)
+	| Dollar s -> "$" ^ s,("",[])
+	| Custom s -> s,("",[])
+
+let to_string m = fst (get_info m)
+
+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;
+			loop (i + 1);
+		end;
+	in
+	loop 0;
+	h
+
+let parse s = try Hashtbl.find hmeta (":" ^ s) with Not_found -> Custom (":" ^ s)
+
+let from_string s =
+	if s = "" then Custom "" else match s.[0] with
+	| ':' -> (try Hashtbl.find hmeta s with Not_found -> Custom s)
+	| '$' -> Dollar (String.sub s 1 (String.length s - 1))
+	| _ -> Custom s
+
+let get_documentation d =
+	let t, (doc,flags) = get_info d in
+	if not (List.mem UsedInternally flags) then begin
+		let params = ref [] and used = ref [] and pfs = ref [] in
+		List.iter (function
+			| HasParam s -> params := s :: !params
+			| Platform f -> pfs := f :: !pfs
+			| Platforms fl -> pfs := fl @ !pfs
+			| UsedOn u -> used := u :: !used
+			| UsedOnEither ul -> used := ul @ !used
+			| UsedInternally -> assert false
+		) flags;
+		let params = (match List.rev !params with
+			| [] -> ""
+			| l -> "(" ^ String.concat "," l ^ ")"
+		) in
+		let pfs = (match List.rev !pfs with
+			| [] -> ""
+			| [p] -> " (" ^ platform_name p ^ " only)"
+			| pl -> " (for " ^ String.concat "," (List.map platform_name pl) ^ ")"
+		) in
+		let str = "@" ^ t in
+		Some (str,params ^ doc ^ pfs)
+	end else
+		None
+
+let get_documentation_list () =
+	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
+			| None -> loop (i + 1)
+			| Some (str,desc) ->
+				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

+ 872 - 368
src/display/display.ml

@@ -1,26 +1,41 @@
 open Ast
 open Common
+open Common.DisplayMode
 open Type
 open Typecore
+open Globals
 
-(* order of these variants affects output sorting *)
 type display_field_kind =
-	| FKVar
-	| FKMethod
-	| FKType
+	| FKVar of t
+	| FKMethod of t
+	| FKType of t
+	| FKModule
 	| FKPackage
+	| FKMetadata
+	| FKTimer of string
+
+let display_field_kind_index = function
+	| FKVar _ -> 0
+	| FKMethod _ -> 1
+	| FKType _ -> 2
+	| FKModule -> 3
+	| FKPackage -> 4
+	| FKMetadata -> 5
+	| FKTimer _ -> 6
 
 exception Diagnostics of string
+exception Statistics of string
 exception ModuleSymbols of string
-exception DisplaySignatures of (t * documentation) list
-exception DisplayType of t * pos
-exception DisplayPosition of Ast.pos list
-exception DisplaySubExpression of Ast.expr
-exception DisplayFields of (string * t * display_field_kind option * documentation) list
+exception Metadata of string
+exception DisplaySignatures of (t * documentation) list * int
+exception DisplayType of t * pos * string option
+exception DisplayPosition of pos list
+exception DisplayFields of (string * display_field_kind * documentation) list
 exception DisplayToplevel of IdentifierType.t list
+exception DisplayPackage of string list
 
 let is_display_file file =
-	file <> "?" && Common.unique_full_path file = (!Parser.resume_display).pfile
+	file <> "?" && Path.unique_full_path file = (!Parser.resume_display).pfile
 
 let encloses_position p_target p =
 	p.pmin <= p_target.pmin && p.pmax >= p_target.pmax
@@ -28,316 +43,339 @@ let encloses_position p_target p =
 let is_display_position p =
 	encloses_position !Parser.resume_display p
 
-let find_enclosing com e =
-	let display_pos = ref (!Parser.resume_display) in
-	let mk_null p = (EDisplay(((EConst(Ident "null")),p),false),p) in
-	let encloses_display_pos p =
-		if encloses_position !display_pos p then begin
-			let p = !display_pos in
-			display_pos := { pfile = ""; pmin = -2; pmax = -2 };
-			Some p
-		end else
-			None
-	in
-	let rec loop e = match fst e with
-		| EBlock el ->
-			let p = pos e in
-			(* We want to find the innermost block which contains the display position. *)
-			let el = List.map loop el in
-			let el = match encloses_display_pos p with
-				| None ->
-					el
-				| Some p2 ->
-					let b,el = List.fold_left (fun (b,el) e ->
-						let p = pos e in
-						if b || p.pmax <= p2.pmin then begin
-							(b,e :: el)
-						end else begin
-							let e_d = (EDisplay(mk_null p,false)),p in
-							(true,e :: e_d :: el)
-						end
-					) (false,[]) el in
-					let el = if b then
+module ExprPreprocessing = struct
+	let find_enclosing com e =
+		let display_pos = ref (!Parser.resume_display) in
+		let mk_null p = (EDisplay(((EConst(Ident "null")),p),false),p) in
+		let encloses_display_pos p =
+			if encloses_position !display_pos p then begin
+				let p = !display_pos in
+				display_pos := { pfile = ""; pmin = -2; pmax = -2 };
+				Some p
+			end else
+				None
+		in
+		let rec loop e = match fst e with
+			| EBlock el ->
+				let p = pos e in
+				(* We want to find the innermost block which contains the display position. *)
+				let el = List.map loop el in
+				let el = match encloses_display_pos p with
+					| None ->
 						el
-					else begin
-						mk_null p :: el
-					end in
-					List.rev el
-			in
-			(EBlock el),(pos e)
-		| _ ->
-			Ast.map_expr loop e
-	in
-	loop e
-
-let find_before_pos com e =
-	let display_pos = ref (!Parser.resume_display) in
-	let is_annotated p =
-		if p.pmin <= !display_pos.pmin && p.pmax >= !display_pos.pmax then begin
-			display_pos := { pfile = ""; pmin = -2; pmax = -2 };
-			true
-		end else
-			false
-	in
-	let rec loop e =
-		if is_annotated (pos e) then
-			(EDisplay(e,false),(pos e))
-		else
-			e
-	in
-	let rec map e =
-		loop (Ast.map_expr map e)
-	in
-	map e
-
-let display_type dm t p =
-	try
-		let mt = module_type_of_type t in
-		begin match dm with
-			| DMPosition -> raise (DisplayPosition [(t_infos mt).mt_pos]);
-			| DMUsage ->
-				let ti = t_infos mt in
-				ti.mt_meta <- (Meta.Usage,[],ti.mt_pos) :: ti.mt_meta
-			| DMType -> raise (DisplayType (t,p))
-			| _ -> ()
-		end
-	with Exit ->
-		()
-
-let display_module_type dm mt =
-	display_type dm (type_of_module_type mt)
-
-let display_variable dm v p = match dm with
-	| DMPosition -> raise (DisplayPosition [v.v_pos])
-	| DMUsage -> v.v_meta <- (Meta.Usage,[],v.v_pos) :: v.v_meta;
-	| DMType -> raise (DisplayType (v.v_type,p))
-	| _ -> ()
-
-let display_field dm cf p = match dm with
-	| DMPosition -> raise (DisplayPosition [cf.cf_pos]);
-	| DMUsage -> cf.cf_meta <- (Meta.Usage,[],cf.cf_pos) :: cf.cf_meta;
-	| DMType -> raise (DisplayType (cf.cf_type,p))
-	| _ -> ()
-
-let display_enum_field dm ef p = match dm with
-	| DMPosition -> raise (DisplayPosition [p]);
-	| DMUsage -> ef.ef_meta <- (Meta.Usage,[],p) :: ef.ef_meta;
-	| DMType -> raise (DisplayType (ef.ef_type,p))
-	| _ -> ()
-
-module SymbolKind = struct
-	type t =
-		| Class
-		| Interface
-		| Enum
-		| Typedef
-		| Abstract
-		| Field
-		| Property
-		| Method
-		| Constructor
-		| Function
-		| Variable
-
-	let to_int = function
-		| Class -> 1
-		| Interface -> 2
-		| Enum -> 3
-		| Typedef -> 4
-		| Abstract -> 5
-		| Field -> 6
-		| Property -> 7
-		| Method -> 8
-		| Constructor -> 9
-		| Function -> 10
-		| Variable -> 11
-end
+					| Some p2 ->
+						let b,el = List.fold_left (fun (b,el) e ->
+							let p = pos e in
+							if b || p.pmax <= p2.pmin then begin
+								(b,e :: el)
+							end else begin
+								let e_d = (EDisplay(mk_null p,false)),p in
+								(true,e :: e_d :: el)
+							end
+						) (false,[]) el in
+						let el = if b then
+							el
+						else begin
+							mk_null p :: el
+						end in
+						List.rev el
+				in
+				(EBlock el),(pos e)
+			| _ ->
+				Ast.map_expr loop e
+		in
+		loop e
+
+	let find_before_pos com e =
+		let display_pos = ref (!Parser.resume_display) in
+		let is_annotated p =
+			if p.pmin <= !display_pos.pmin && p.pmax >= !display_pos.pmax then begin
+				display_pos := { pfile = ""; pmin = -2; pmax = -2 };
+				true
+			end else
+				false
+		in
+		let loop e =
+			if is_annotated (pos e) then
+				(EDisplay(e,false),(pos e))
+			else
+				e
+		in
+		let rec map e =
+			loop (Ast.map_expr map e)
+		in
+		map e
+
+	let find_display_call e =
+		let found = ref false in
+		let loop e = if !found then e else match fst e with
+			| ECall _ | ENew _ when is_display_position (pos e) ->
+				found := true;
+				(EDisplay(e,true),(pos e))
+			| _ ->
+				e
+		in
+		let rec map e = match fst e with
+			| EDisplay(_,true) ->
+				found := true;
+				e
+			| EDisplay(e1,false) -> map e1
+			| _ -> loop (Ast.map_expr map e)
+		in
+		map e
+
 
-module SymbolInformation = struct
-	type t = {
-		name : string;
-		kind : SymbolKind.t;
-		pos : pos;
-		container_name : string option;
-	}
-
-	let make name kind pos container_name = {
-		name = name;
-		kind = kind;
-		pos = pos;
-		container_name = container_name;
-	}
+	let process_expr com e = match com.display.dms_kind with
+		| DMToplevel -> find_enclosing com e
+		| DMPosition | DMUsage _ | DMType -> find_before_pos com e
+		| DMSignature -> find_display_call e
+		| _ -> e
 end
 
-open SymbolKind
-open SymbolInformation
-open Json
-
-let pos_to_json_range p =
-	if p.pmin = -1 then
-		JNull
-	else
-		let l1, p1, l2, p2 = Lexer.get_pos_coords p in
-		let to_json l c = JObject [("line", JInt (l - 1)); ("character", JInt c)] in
-		JObject [
-			("start", to_json l1 p1);
-			("end", to_json l2 p2);
-		]
-
-let print_module_symbols (pack,decls) =
-	let l = DynArray.create() in
-	let add name kind location parent =
-		let si = SymbolInformation.make name kind location (match parent with None -> None | Some si -> Some si.name) in
-		DynArray.add l si;
-		si
-	in
-(* 		let si_pack = match pack with
-		| [] -> None
-		| _ -> Some (add (String.concat "." pack) Package null_pos None) (* TODO: we don't have the position *)
-	in *)
-	let si_pack = None in (* TODO: no position, no point *)
-	let rec expr si (e,p) =
-		let add name kind location = add name kind location si in
-		let add_ignore name kind location = ignore(add name kind location) in
-		begin match e with
-			(* TODO: disabled for now because it's too spammy *)
-(* 			| EConst ct ->
-			begin match ct with
-				| Int i | Float i -> add_ignore i Number p
-				| Ast.String s -> add_ignore s String p
-				| Ident ("true" | "false" as s) -> add_ignore s Boolean p
-				| Ident _ -> (* Hmm... *) ()
+module DisplayEmitter = struct
+	let display_module_type dm mt p = match dm.dms_kind with
+		| DMPosition -> raise (DisplayPosition [(t_infos mt).mt_pos]);
+		| DMUsage _ ->
+			let ti = t_infos mt in
+			ti.mt_meta <- (Meta.Usage,[],ti.mt_pos) :: ti.mt_meta
+		| DMType -> raise (DisplayType (type_of_module_type mt,p,None))
+		| _ -> ()
+
+	let rec display_type dm t p = match dm.dms_kind with
+		| DMType -> raise (DisplayType (t,p,None))
+		| _ ->
+			try display_module_type dm (module_type_of_type t) p
+			with Exit -> match follow t,follow !t_dynamic_def with
+				| _,TDynamic _ -> () (* sanity check in case it's still t_dynamic *)
+				| TDynamic _,_ -> display_type dm !t_dynamic_def p
 				| _ -> ()
-			end *)
-		| EVars vl ->
-			List.iter (fun ((s,p),_,eo) ->
-				add_ignore s Variable p;
-				expr_opt si eo
-			) vl
-		| ETry(e1,catches) ->
-			expr si e1;
-			List.iter (fun ((s,p),_,e) ->
-				add_ignore s Variable p;
-				expr si e
-			) catches;
-		| EFunction(Some s,f) ->
-			let si_function = add s Function p in
-			func si_function f
-		| EIn((EConst(Ident s),p),e2) ->
-			add_ignore s Variable p;
-			expr si e2;
+
+	let check_display_type ctx t p =
+		let add_type_hint () =
+			Hashtbl.replace ctx.com.shared.shared_display_information.type_hints p t;
+		in
+		let maybe_display_type () =
+			if ctx.is_display_file && is_display_position p then
+				display_type ctx.com.display t p
+		in
+		match ctx.com.display.dms_kind with
+		| DMStatistics -> add_type_hint()
+		| DMUsage _ -> add_type_hint(); maybe_display_type()
+		| _ -> maybe_display_type()
+
+	let display_variable dm v p = match dm.dms_kind with
+		| DMPosition -> raise (DisplayPosition [v.v_pos])
+		| DMUsage _ -> v.v_meta <- (Meta.Usage,[],v.v_pos) :: v.v_meta;
+		| DMType -> raise (DisplayType (v.v_type,p,None))
+		| _ -> ()
+
+	let display_field dm cf p = match dm.dms_kind with
+		| DMPosition -> raise (DisplayPosition [cf.cf_pos]);
+		| DMUsage _ -> cf.cf_meta <- (Meta.Usage,[],cf.cf_pos) :: cf.cf_meta;
+		| DMType -> raise (DisplayType (cf.cf_type,p,cf.cf_doc))
+		| _ -> ()
+
+	let maybe_display_field ctx p cf =
+		if is_display_position p then display_field ctx.com.display cf p
+
+	let display_enum_field dm ef p = match dm.dms_kind with
+		| DMPosition -> raise (DisplayPosition [p]);
+		| DMUsage _ -> ef.ef_meta <- (Meta.Usage,[],p) :: ef.ef_meta;
+		| DMType -> raise (DisplayType (ef.ef_type,p,ef.ef_doc))
+		| _ -> ()
+
+	let display_meta dm meta = match dm.dms_kind with
+		| DMType ->
+			begin match meta with
+			| Meta.Custom _ | Meta.Dollar _ -> ()
+			| _ -> match Meta.get_documentation meta with
+				| None -> ()
+				| Some (_,s) ->
+					(* TODO: hack until we support proper output for hover display mode *)
+					raise (Metadata ("<metadata>" ^ s ^ "</metadata>"));
+			end
+		| DMField ->
+			let all,_ = Meta.get_documentation_list() in
+			let all = List.map (fun (s,doc) -> (s,FKMetadata,Some doc)) all in
+			raise (DisplayFields all)
 		| _ ->
-			iter_expr (expr si) (e,p)
-		end
-	and expr_opt si eo = match eo with
-		| None -> ()
-		| Some e -> expr si e
-	and func si f =
-		List.iter (fun ((s,p),_,_,_,eo) ->
-			let si_arg = add s Variable p (Some si) in
-			expr_opt (Some si_arg) eo
-		) f.f_args;
-		expr_opt (Some si) f.f_expr
-	in
-	let field si_type cff = match cff.cff_kind with
-		| FVar(_,eo) ->
-			let si_field = add (fst cff.cff_name) Field cff.cff_pos (Some si_type) in
-			expr_opt (Some si_field) eo
-		| FFun f ->
-			let si_method = add (fst cff.cff_name) (if fst cff.cff_name = "new" then Constructor else Method) cff.cff_pos (Some si_type) in
-			func si_method f
-		| FProp(_,_,_,eo) ->
-			let si_property = add (fst cff.cff_name) Property cff.cff_pos (Some si_type) in
-			expr_opt (Some si_property) eo
-	in
-	List.iter (fun (td,p) -> match td with
-		| EImport _ | EUsing _ ->
-			() (* TODO: Can we do anything with these? *)
-		| EClass d ->
-			let si_type = add (fst d.d_name) (if List.mem HInterface d.d_flags then Interface else Class) p si_pack in
-			List.iter (field si_type) d.d_data
-		| EEnum d ->
-			let si_type = add (fst d.d_name) Enum p si_pack in
-			List.iter (fun ef ->
-				ignore (add (fst ef.ec_name) Method ef.ec_pos (Some si_type))
-			) d.d_data
-		| ETypedef d ->
-			let si_type = add (fst d.d_name) Typedef p si_pack in
-			(match d.d_data with
-			| CTAnonymous fields,_ ->
-				List.iter (field si_type) fields
-			| _ -> ())
-		| EAbstract d ->
-			let si_type = add (fst d.d_name) Abstract p si_pack in
-			List.iter (field si_type) d.d_data
-	) decls;
-	let jl = List.map (fun si ->
-		let l =
-			("name",JString si.name) ::
-			("kind",JInt (to_int si.kind)) ::
-			("range", pos_to_json_range si.pos) ::
-			(match si.container_name with None -> [] | Some s -> ["containerName",JString s])
+			()
+
+	let check_display_metadata ctx meta =
+		List.iter (fun (meta,args,p) ->
+			if is_display_position p then display_meta ctx.com.display meta;
+			List.iter (fun e ->
+				if is_display_position (pos e) then begin
+					let e = ExprPreprocessing.process_expr ctx.com e in
+					delay ctx PTypeField (fun _ -> ignore(type_expr ctx e Value));
+				end
+			) args
+		) meta
+end
+
+module DocumentSymbols = struct
+	open DisplayTypes.SymbolKind
+
+	let collect_module_symbols (pack,decls) =
+		let l = DynArray.create() in
+		let add name kind location parent =
+			let si = DisplayTypes.SymbolInformation.make name kind location (if parent = "" then None else Some parent) in
+			DynArray.add l si;
 		in
-		JObject l
-	) (DynArray.to_list l) in
-	let js = JArray jl in
-	let b = Buffer.create 0 in
-	write_json (Buffer.add_string b) js;
-	Buffer.contents b
-
-type import_display_kind =
-	| IDKPackage of string list
-	| IDKModule of string list * string
-	| IDKSubType of string list * string * string
-	| IDKModuleField of string list * string * string
-	| IDKSubTypeField of string list * string * string * string
-	| IDK
-
-type import_display = import_display_kind * pos
-
-let convert_import_to_something_usable path =
-	let rec loop pack m t = function
-		| (s,p) :: l ->
-			let is_lower = is_lower_ident s in
-			let is_display_pos = encloses_position !Parser.resume_display p in
-			begin match is_lower,m,t with
-				| _,None,Some _ | false,Some _,Some _ ->
-					assert false (* impossible, I think *)
-				| true,Some m,None ->
-					if is_display_pos then (IDKModuleField(List.rev pack,m,s),p)
-					else (IDK,p) (* assume that we're done *)
-				| true,Some m,Some t ->
-					if is_display_pos then (IDKSubTypeField(List.rev pack,m,t,s),p)
-					else (IDK,p)
-				| true,None,None ->
-					if is_display_pos then (IDKPackage (List.rev (s :: pack)),p)
-					else loop (s :: pack) m t l
-				| false,Some sm,None ->
-					if is_display_pos then (IDKSubType (List.rev pack,sm,s),p)
-					else loop pack m (Some s) l
-				| false,None,None ->
-					if is_display_pos then (IDKModule (List.rev pack,s),p)
-					else loop pack (Some s) None l
+		let rec expr parent (e,p) =
+			let add name kind location = add name kind location parent in
+			begin match e with
+			| EVars vl ->
+				List.iter (fun ((s,p),_,eo) ->
+					add s Variable p;
+					expr_opt parent eo
+				) vl
+			| ETry(e1,catches) ->
+				expr parent e1;
+				List.iter (fun ((s,p),_,e,_) ->
+					add s Variable p;
+					expr parent e
+				) catches;
+			| EFunction(Some s,f) ->
+				add s Function p;
+				func parent f
+			| EIn((EConst(Ident s),p),e2) ->
+				add s Variable p;
+				expr parent e2;
+			| _ ->
+				iter_expr (expr parent) (e,p)
 			end
-		| [] ->
-			(IDK,null_pos)
-	in
-	loop [] None None path
+		and expr_opt parent eo = match eo with
+			| None -> ()
+			| Some e -> expr parent e
+		and func parent f =
+			List.iter (fun ((s,p),_,_,_,eo) ->
+				add s Variable p parent;
+				expr_opt parent eo
+			) f.f_args;
+			expr_opt parent f.f_expr
+		in
+		let field parent cff =
+			let field_parent = parent ^ "." ^ (fst cff.cff_name) in
+			match cff.cff_kind with
+			| FVar(_,eo) ->
+				add (fst cff.cff_name) Field cff.cff_pos parent;
+				expr_opt field_parent eo
+			| FFun f ->
+				add (fst cff.cff_name) (if fst cff.cff_name = "new" then Constructor else Method) cff.cff_pos parent;
+				func field_parent f
+			| FProp(_,_,_,eo) ->
+				add (fst cff.cff_name) Property cff.cff_pos parent;
+				expr_opt field_parent eo
+		in
+		List.iter (fun (td,p) -> match td with
+			| EImport _ | EUsing _ ->
+				() (* TODO: Can we do anything with these? *)
+			| EClass d ->
+				add (fst d.d_name) (if List.mem HInterface d.d_flags then Interface else Class) p "";
+				List.iter (field (fst d.d_name)) d.d_data
+			| EEnum d ->
+				add (fst d.d_name) Enum p "";
+				List.iter (fun ef ->
+					add (fst ef.ec_name) Method ef.ec_pos (fst d.d_name)
+				) d.d_data
+			| ETypedef d ->
+				add (fst d.d_name) Typedef p "";
+				(match d.d_data with
+				| CTAnonymous fields,_ ->
+					List.iter (field (fst d.d_name)) fields
+				| _ -> ())
+			| EAbstract d ->
+				add (fst d.d_name) Abstract p "";
+				List.iter (field (fst d.d_name)) d.d_data
+		) decls;
+		l
+end
+
+module DeprecationCheck = struct
+
+	let curclass = ref null_class
 
-let process_expr com e = match com.display with
-	| DMToplevel -> find_enclosing com e
-	| DMPosition | DMUsage | DMType -> find_before_pos com e
-	| _ -> e
+	let warned_positions = Hashtbl.create 0
 
-let add_import_position com p =
-	com.shared.shared_display_information.import_positions <- PMap.add p (ref false) com.shared.shared_display_information.import_positions
+	let print_deprecation_message com meta s p_usage =
+		let s = match meta with
+			| _,[EConst(String s),_],_ -> s
+			| _ -> Printf.sprintf "Usage of this %s is deprecated" s
+		in
+		if not (Hashtbl.mem warned_positions p_usage) then begin
+			Hashtbl.replace warned_positions p_usage true;
+			com.warning s p_usage;
+		end
 
-let mark_import_position com p =
-	try
-		let r = PMap.find p com.shared.shared_display_information.import_positions in
-		r := true
-	with Not_found ->
-		()
+	let check_meta com meta s p_usage =
+		try
+			print_deprecation_message com (Meta.get Meta.Deprecated meta) s p_usage;
+		with Not_found ->
+			()
+
+	let check_cf com cf p = check_meta com cf.cf_meta "field" p
+
+	let check_class com c p = if c != !curclass then check_meta com c.cl_meta "class" p
+
+	let check_enum com en p = check_meta com en.e_meta "enum" p
+
+	let check_ef com ef p = check_meta com ef.ef_meta "enum field" p
+
+	let check_typedef com t p = check_meta com t.t_meta "typedef" p
+
+	let check_module_type com mt p = match mt with
+		| TClassDecl c -> check_class com c p
+		| TEnumDecl en -> check_enum com en p
+		| _ -> ()
+
+	let run_on_expr com e =
+		let rec expr e = match e.eexpr with
+			| TField(e1,fa) ->
+				expr e1;
+				begin match fa with
+					| FStatic(c,cf) | FInstance(c,_,cf) ->
+						check_class com c e.epos;
+						check_cf com cf e.epos
+					| FAnon cf ->
+						check_cf com cf e.epos
+					| FClosure(co,cf) ->
+						(match co with None -> () | Some (c,_) -> check_class com c e.epos);
+						check_cf com cf e.epos
+					| FEnum(en,ef) ->
+						check_enum com en e.epos;
+						check_ef com ef e.epos;
+					| _ ->
+						()
+				end
+			| TNew(c,_,el) ->
+				List.iter expr el;
+				check_class com c e.epos;
+				(match c.cl_constructor with None -> () | Some cf -> check_cf com cf e.epos)
+			| TTypeExpr(mt) | TCast(_,Some mt) ->
+				check_module_type com mt e.epos
+			| TMeta((Meta.Deprecated,_,_) as meta,e1) ->
+				print_deprecation_message com meta "field" e1.epos;
+				expr e1;
+			| _ ->
+				Type.iter expr 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 com =
+		List.iter (fun t -> match t with
+			| TClassDecl c ->
+				curclass := c;
+				(match c.cl_constructor with None -> () | Some cf -> run_on_field com cf);
+				(match c.cl_init with None -> () | Some e -> run_on_expr com e);
+				List.iter (run_on_field com) c.cl_ordered_statics;
+				List.iter (run_on_field com) c.cl_ordered_fields;
+			| _ ->
+				()
+		) com.types
+end
 
 module Diagnostics = struct
 	module DiagnosticsKind = struct
@@ -345,78 +383,544 @@ module Diagnostics = struct
 			| DKUnusedImport
 			| DKUnresolvedIdentifier
 			| DKCompilerError
+			| DKRemovableCode
 
 		let to_int = function
 			| DKUnusedImport -> 0
 			| DKUnresolvedIdentifier -> 1
 			| DKCompilerError -> 2
+			| DKRemovableCode -> 3
 	end
 
-	type t = DiagnosticsKind.t * pos
+	open DiagnosticsKind
+	open DisplayTypes
+
+	let add_removable_code com s p prange =
+		let di = com.shared.shared_display_information in
+		di.removable_code <- (s,p,prange) :: di.removable_code
+
+	let find_unused_variables com e =
+		let vars = Hashtbl.create 0 in
+		let pmin_map = Hashtbl.create 0 in
+		let rec loop e = match e.eexpr with
+			| TVar(v,eo) when Meta.has Meta.UserVariable v.v_meta ->
+				Hashtbl.add pmin_map e.epos.pmin v;
+				let p = match eo with
+					| None -> e.epos
+					| Some e1 ->
+						loop e1;
+						{ e.epos with pmax = e1.epos.pmin }
+				in
+				Hashtbl.replace vars v.v_id (v,p);
+			| TLocal v when Meta.has Meta.UserVariable v.v_meta ->
+				Hashtbl.remove vars v.v_id;
+			| _ ->
+				Type.iter loop e
+		in
+		loop e;
+		Hashtbl.iter (fun _ (v,p) ->
+			let p = match (Hashtbl.find_all pmin_map p.pmin) with [_] -> p | _ -> null_pos in
+			add_removable_code com "Unused variable" v.v_pos p
+		) vars
+
+	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 DiagnosticsSeverity.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 DiagnosticsSeverity.Warning;
+		in
+		let rec compound s el p =
+			let old = !had_effect in
+			had_effect := false;
+			List.iter (loop true) el;
+			if not !had_effect then no_effect p else pointless_compound s p;
+			had_effect := old;
+		and loop in_value e = match e.eexpr with
+			| TBlock el ->
+				let rec loop2 el = match el with
+					| [] -> ()
+					| [e] -> loop in_value e
+					| e :: el -> loop false e; loop2 el
+				in
+				loop2 el
+			| TMeta((Meta.Extern,_,_),_) ->
+				(* This is so something like `[inlineFunc()]` is not reported. *)
+				had_effect := true;
+			| TLocal v when not (Meta.has Meta.UserVariable v.v_meta) ->
+				()
+			| TConst _ | TLocal _ | TTypeExpr _ | TFunction _ when not in_value ->
+				no_effect e.epos;
+			| TConst _ | TLocal _ | TTypeExpr _ | TEnumParameter _ | TVar _ ->
+				()
+			| TFunction tf ->
+				loop false tf.tf_expr
+			| TNew _ | TCall _ | TBinop ((Ast.OpAssignOp _ | Ast.OpAssign),_,_) | TUnop ((Ast.Increment | Ast.Decrement),_,_)
+			| TReturn _ | TBreak | TContinue | TThrow _ | TCast (_,Some _)
+			| TIf _ | TTry _ | TSwitch _ | TWhile _ | TFor _ ->
+				had_effect := true;
+				Type.iter (loop true) e
+			| TParenthesis e1 | TMeta(_,e1) ->
+				loop in_value e1
+			| TArray _ | TCast (_,None) | TBinop _ | TUnop _
+			| TField _ | TArrayDecl _ | TObjectDecl _ when in_value ->
+				Type.iter (loop true) e;
+			| TArray(e1,e2) -> compound "array access" [e1;e2] e.epos
+			| TCast(e1,None) -> compound "cast" [e1] e.epos
+			| TBinop(op,e1,e2) -> compound (Printf.sprintf "'%s' operator" (s_binop op)) [e1;e2] e.epos
+			| TUnop(op,_,e1) -> compound (Printf.sprintf "'%s' operator" (s_unop op)) [e1] e.epos
+			| TField(e1,_) -> compound "field access" [e1] e.epos
+			| TArrayDecl el -> compound "array declaration" el e.epos
+			| TObjectDecl fl -> compound "object declaration" (List.map snd fl) e.epos
+		in
+		loop true e
 
-	module UnresolvedIdentifierSuggestion = struct
-		type t =
-			| UISImport
-			| UISTypo
+	let prepare_field com cf = match cf.cf_expr with
+		| None -> ()
+		| Some e ->
+			find_unused_variables com e;
+			check_other_things com e;
+			DeprecationCheck.run_on_expr com e
+
+	let prepare com global =
+		List.iter (function
+			| TClassDecl c when global || is_display_file c.cl_pos.pfile ->
+				List.iter (prepare_field com) c.cl_ordered_fields;
+				List.iter (prepare_field com) c.cl_ordered_statics;
+				(match c.cl_constructor with None -> () | Some cf -> prepare_field com cf);
+			| _ ->
+				()
+		) com.types
 
-		let to_int = function
-			| UISImport -> 0
-			| UISTypo -> 1
-	end
+	let is_diagnostics_run ctx = match ctx.com.display.dms_kind with
+		| DMDiagnostics true -> true
+		| DMDiagnostics false -> ctx.is_display_file
+		| _ -> false
 
-	open UnresolvedIdentifierSuggestion
-	open DiagnosticsKind
+	let secure_generated_code ctx e =
+		if is_diagnostics_run ctx then mk (TMeta((Meta.Extern,[],e.epos),e)) e.etype e.epos else e
+end
 
-	let print_diagnostics com =
-		let diag = DynArray.create() in
-		let add dk p sev args =
-			DynArray.add diag (dk,p,sev,args)
+module ImportHandling = struct
+	type import_display_kind =
+		| IDKPackage of string list
+		| IDKModule of string list * string
+		| IDKSubType of string list * string * string
+		| IDKModuleField of string list * string * string
+		| IDKSubTypeField of string list * string * string * string
+		| IDK
+
+	type import_display = import_display_kind * pos
+
+	let convert_import_to_something_usable pt path =
+		let rec loop pack m t = function
+			| (s,p) :: l ->
+				let is_lower = is_lower_ident s in
+				let is_display_pos = encloses_position pt p in
+				begin match is_lower,m,t with
+					| _,None,Some _ ->
+						assert false (* impossible, I think *)
+					| true,Some m,None ->
+						if is_display_pos then (IDKModuleField(List.rev pack,m,s),p)
+						else (IDK,p) (* assume that we're done *)
+					| _,Some m,Some t ->
+						if is_display_pos then (IDKSubTypeField(List.rev pack,m,t,s),p)
+						else (IDK,p)
+					| true,None,None ->
+						if is_display_pos then (IDKPackage (List.rev (s :: pack)),p)
+						else loop (s :: pack) m t l
+					| false,Some sm,None ->
+						if is_display_pos then (IDKSubType (List.rev pack,sm,s),p)
+						else loop pack m (Some s) l
+					| false,None,None ->
+						if is_display_pos then (IDKModule (List.rev pack,s),p)
+						else loop pack (Some s) None l
+				end
+			| [] ->
+				(IDK,null_pos)
+		in
+		loop [] None None path
+
+	let add_import_position com p path =
+		com.shared.shared_display_information.import_positions <- PMap.add p (ref false,path) com.shared.shared_display_information.import_positions
+
+	let mark_import_position com p =
+		try
+			let r = fst (PMap.find p com.shared.shared_display_information.import_positions) in
+			r := true
+		with Not_found ->
+			()
+
+	let maybe_mark_import_position ctx p =
+		if Diagnostics.is_diagnostics_run ctx then mark_import_position ctx.com p
+end
+
+module Statistics = struct
+	open ImportHandling
+
+	type relation =
+		| Implemented
+		| Extended
+		| Overridden
+		| Referenced
+
+	type symbol =
+		| SKClass of tclass
+		| SKInterface of tclass
+		| SKEnum of tenum
+		| SKField of tclass_field
+		| SKEnumField of tenum_field
+		| SKVariable of tvar
+
+	let is_usage_symbol symbol =
+		let meta = match symbol with
+			| SKClass c | SKInterface c -> c.cl_meta
+			| SKEnum en -> en.e_meta
+			| SKField cf -> cf.cf_meta
+			| SKEnumField ef -> ef.ef_meta
+			| SKVariable v -> v.v_meta
+		in
+		Meta.has Meta.Usage meta
+
+	let collect_statistics ctx =
+		let relations = Hashtbl.create 0 in
+		let symbols = Hashtbl.create 0 in
+		let add_relation pos r =
+			if is_display_file pos.pfile then try
+				Hashtbl.replace relations pos (r :: Hashtbl.find relations pos)
+			with Not_found ->
+				Hashtbl.add relations pos [r]
+		in
+		let declare kind p =
+			if is_display_file p.pfile then begin
+				if not (Hashtbl.mem relations p) then Hashtbl.add relations p [];
+				Hashtbl.replace symbols p kind;
+			end
+		in
+		let collect_overrides c =
+			List.iter (fun cf ->
+				let rec loop c = match c.cl_super with
+					| Some (c,_) ->
+						begin try
+							let cf' = PMap.find cf.cf_name c.cl_fields in
+							add_relation cf'.cf_name_pos (Overridden,cf.cf_pos)
+						with Not_found ->
+							loop c
+						end
+					| _ ->
+						()
+				in
+				loop c
+			) c.cl_overrides
 		in
-		begin match !(Common.global_cache) with
-			| None ->
+		let rec find_real_constructor c = match c.cl_constructor,c.cl_super with
+			(* The pos comparison might be a bit weak, not sure... *)
+			| Some cf,_ when not (Meta.has Meta.CompilerGenerated cf.cf_meta) && c.cl_pos <> cf.cf_pos -> cf
+			| _,Some(c,_) -> find_real_constructor c
+			| _,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 patch_string_pos_front p s  = { p with pmax = p.pmin + String.length s } in
+		let field_reference cf p =
+			add_relation cf.cf_name_pos (Referenced,patch_string_pos p cf.cf_name)
+		in
+		let collect_references c e =
+			let rec loop e = match e.eexpr with
+				| TField(e1,fa) ->
+					(* Check if the sub-expression is actually shorter than the whole one. This should
+					   detect cases where it was automatically generated. *)
+					if e1.epos.pmin = e.epos.pmin && e1.epos.pmax <> e.epos.pmax then
+						loop e1;
+					begin match fa with
+						| FStatic(_,cf) | FInstance(_,_,cf) | FClosure(_,cf) ->
+							field_reference cf e.epos
+						| FAnon cf ->
+							declare  (SKField cf) cf.cf_name_pos;
+							field_reference cf e.epos
+						| FEnum(_,ef) ->
+							add_relation ef.ef_name_pos (Referenced,patch_string_pos e.epos ef.ef_name)
+						| FDynamic _ ->
+							()
+					end
+				| TTypeExpr mt ->
+					let tinfos = t_infos mt in
+					add_relation tinfos.mt_name_pos (Referenced,patch_string_pos e.epos (snd tinfos.mt_path))
+				| TNew(c,_,el) ->
+					List.iter loop el;
+					(try add_relation (find_real_constructor c).cf_name_pos (Referenced,e.epos) with Not_found -> ());
+				| TCall({eexpr = TConst TSuper},el) ->
+					List.iter loop el;
+					begin match c.cl_super with
+						| Some(c,_) -> (try add_relation (find_real_constructor c).cf_name_pos (Referenced,e.epos) with Not_found -> ())
+						| None -> ()
+					end
+				| TVar(v,eo) ->
+					Option.may loop eo;
+					var_decl v;
+				| TFor(v,e1,e2) ->
+					var_decl v;
+					loop e1;
+					loop e2;
+				| TFunction tf ->
+					List.iter (fun (v,_) -> var_decl v) tf.tf_args;
+					loop tf.tf_expr;
+				| TLocal v when e.epos.pmax - e.epos.pmin = String.length v.v_name ->
+					add_relation v.v_pos (Referenced,e.epos)
+				| _ ->
+					Type.iter loop e
+			in
+			loop e
+		in
+		List.iter (function
+			| TClassDecl c ->
+				declare (if c.cl_interface then (SKInterface c) else (SKClass c)) c.cl_name_pos;
+				List.iter (fun (c',_) -> add_relation c'.cl_name_pos ((if c.cl_interface then Extended else Implemented),c.cl_name_pos)) c.cl_implements;
+				begin match c.cl_super with
+					| None -> ()
+					| Some (c',_) -> add_relation c'.cl_name_pos (Extended,c.cl_name_pos);
+				end;
+				collect_overrides c;
+				let field cf =
+					if cf.cf_pos.pmin > c.cl_name_pos.pmin then declare (SKField cf) cf.cf_name_pos;
+					let _ = follow cf.cf_type in
+					match cf.cf_expr with None -> () | Some e -> collect_references c e
+				in
+				Option.may field c.cl_constructor;
+				List.iter field c.cl_ordered_fields;
+				List.iter field c.cl_ordered_statics;
+			| TEnumDecl en ->
+				declare (SKEnum en) en.e_name_pos;
+				PMap.iter (fun _ ef -> declare (SKEnumField ef) ef.ef_name_pos) en.e_constrs
+			| _ ->
 				()
-			| Some cache ->
-				let find_type i =
-					let types = ref [] in
-					Hashtbl.iter (fun _ m ->
-						List.iter (fun mt ->
-							let tinfos = t_infos mt in
-							if snd tinfos.mt_path = i then
-								types := JObject [
-									"kind",JInt (UnresolvedIdentifierSuggestion.to_int UISImport);
-									"name",JString (s_type_path m.m_path)
-								] :: !types
-						) m.m_types;
-					) cache.c_modules;
-					!types
+		) ctx.com.types;
+		let explore_type_hint p t = match follow t with
+			| TInst(c,_) -> add_relation c.cl_name_pos (Referenced,(patch_string_pos_front p (snd c.cl_path)))
+			| _ -> ()
+		in
+		Hashtbl.iter (fun p t ->
+			explore_type_hint p t
+		) ctx.com.shared.shared_display_information.type_hints;
+		let l = List.fold_left (fun acc (_,cfi,_,cfo) -> match cfo with
+			| Some cf -> if List.mem_assoc cf.cf_name_pos acc then acc else (cf.cf_name_pos,cfi.cf_name_pos) :: acc
+			| None -> acc
+		) [] ctx.com.display_information.interface_field_implementations in
+		List.iter (fun (p,p') -> add_relation p' (Implemented,p)) l;
+		let deal_with_imports paths =
+			let check_subtype m s p =
+				try
+					let mt = List.find (fun mt -> snd (t_infos mt).mt_path = s) m.m_types in
+					add_relation (t_infos mt).mt_name_pos (Referenced,p);
+					Some mt
+				with Not_found ->
+					None
+			in
+			let check_module path p =
+				let m = ctx.g.do_load_module ctx path p in
+				m
+			in
+			let check_field c s p =
+				let cf = PMap.find s c.cl_statics in
+				add_relation cf.cf_name_pos (Referenced,p)
+			in
+			let check_subtype_field m ssub psub sfield pfield = match check_subtype m ssub psub with
+				| Some (TClassDecl c) -> check_field c sfield pfield
+				| _ -> ()
+			in
+			PMap.iter (fun p (_,path) ->
+				match ImportHandling.convert_import_to_something_usable { p with pmin = p.pmax - 1; pmax = p.pmax - 1 } path,List.rev path with
+				| (IDKSubType(sl,s1,s2),_),(_,psubtype) :: (_,pmodule) :: _ ->
+					let m = check_module (sl,s1) pmodule in
+					(*ignore(check_subtype m s1 pmodule);*)
+					ignore(check_subtype m s2 psubtype)
+				| (IDKModuleField(sl,s1,s2),_),(_,pfield) :: (_,pmodule) :: _ ->
+					let m = check_module (sl,s1) pmodule in
+					check_subtype_field m s1 pmodule s2 pfield
+				| (IDKSubTypeField(sl,s1,s2,s3),_),(_,pfield) :: (_,psubtype) :: (_,pmodule) :: _ ->
+					let m = check_module (sl,s1) pmodule in
+					check_subtype_field m s2 psubtype s3 pfield
+				| (IDKModule(sl,s),_),(_,pmodule) :: _ ->
+					let m = check_module (sl,s) pmodule in
+					ignore(check_subtype m s pmodule);
+				| _ ->
+					()
+			) paths
+		in
+		if false then deal_with_imports ctx.com.shared.shared_display_information.import_positions;
+		symbols,relations
+end
+
+let explore_class_paths ctx class_paths recusive f_pack f_module f_type =
+	let rec loop dir pack =
+		try
+			let entries = Sys.readdir dir in
+			Array.iter (fun file ->
+				match file with
+					| "." | ".." ->
+						()
+					| _ when Sys.is_directory (dir ^ file) && file.[0] >= 'a' && file.[0] <= 'z' ->
+						f_pack file;
+						if recusive then loop (dir ^ file ^ "/") (file :: pack)
+					| _ ->
+						let l = String.length file in
+						if l > 3 && String.sub file (l - 3) 3 = ".hx" then begin
+							try
+								let name = String.sub file 0 (l - 3) in
+								let path = (List.rev pack,name) in
+								let md = ctx.g.do_load_module ctx path null_pos in
+								f_module md;
+								List.iter (fun mt -> f_type mt) md.m_types
+							with _ ->
+								()
+						end
+			) entries;
+		with Sys_error _ ->
+			()
+	in
+	List.iter (fun dir -> loop dir []) class_paths
+
+module ToplevelCollector = struct
+	open IdentifierType
+
+	let run ctx only_types =
+		let acc = DynArray.create () in
+
+		if not only_types then begin
+			(* locals *)
+			PMap.iter (fun _ v ->
+				if not (is_gen_local v) then
+					DynArray.add acc (ITLocal v)
+			) ctx.locals;
+
+			(* member vars *)
+			if ctx.curfun <> FunStatic then begin
+				let rec loop c =
+					List.iter (fun cf ->
+						DynArray.add acc (ITMember(ctx.curclass,cf))
+					) c.cl_ordered_fields;
+					match c.cl_super with
+						| None ->
+							()
+						| Some (csup,tl) ->
+							loop csup; (* TODO: type parameters *)
 				in
-			List.iter (fun (s,p,suggestions) ->
-				let suggestions = List.map (fun (s,_) ->
-					JObject [
-						"kind",JInt (UnresolvedIdentifierSuggestion.to_int UISTypo);
-						"name",JString s
-					]
-				) suggestions in
-				add DKUnresolvedIdentifier p DiagnosticsSeverity.Error (suggestions @ (find_type s));
-			) com.display_information.unresolved_identifiers;
+				loop ctx.curclass;
+				(* TODO: local using? *)
+			end;
+
+			(* statics *)
+			List.iter (fun cf ->
+				DynArray.add acc (ITStatic(ctx.curclass,cf))
+			) ctx.curclass.cl_ordered_statics;
+
+			(* enum constructors *)
+			let rec enum_ctors t =
+				match t with
+				| TAbstractDecl ({a_impl = Some c} as a) when Meta.has Meta.Enum a.a_meta ->
+					List.iter (fun cf ->
+						if (Meta.has Meta.Enum cf.cf_meta) then DynArray.add acc (ITEnumAbstract(a,cf));
+					) c.cl_ordered_statics
+				| TClassDecl _ | TAbstractDecl _ ->
+					()
+				| TTypeDecl t ->
+					begin match follow t.t_type with
+						| TEnum (e,_) -> enum_ctors (TEnumDecl e)
+						| _ -> ()
+					end
+				| TEnumDecl e ->
+					PMap.iter (fun _ ef ->
+						DynArray.add acc (ITEnum(e,ef))
+					) e.e_constrs;
+			in
+			List.iter enum_ctors ctx.m.curmod.m_types;
+			List.iter enum_ctors (List.map fst ctx.m.module_types);
+
+			(* imported globals *)
+			PMap.iter (fun _ (mt,s,_) ->
+				try
+					let t = match resolve_typedef mt with
+						| TClassDecl c -> (PMap.find s c.cl_statics).cf_type
+						| TEnumDecl en -> (PMap.find s en.e_constrs).ef_type
+						| TAbstractDecl {a_impl = Some c} -> (PMap.find s c.cl_statics).cf_type
+						| _ -> raise Not_found
+					in
+					DynArray.add acc (ITGlobal(mt,s,t))
+				with Not_found ->
+					()
+			) ctx.m.module_globals;
+
+			(* literals *)
+			DynArray.add acc (ITLiteral "null");
+			DynArray.add acc (ITLiteral "true");
+			DynArray.add acc (ITLiteral "false");
 		end;
-		PMap.iter (fun p r ->
-			if not !r then add DKUnusedImport p DiagnosticsSeverity.Warning []
-		) com.shared.shared_display_information.import_positions;
-		List.iter (fun (s,p,sev) ->
-			add DKCompilerError p sev [JString s]
-		) com.shared.shared_display_information.diagnostics_messages;
-		let jl = DynArray.fold_left (fun acc (dk,p,sev,args) ->
-			(JObject [
-				"kind",JInt (to_int dk);
-				"severity",JInt (DiagnosticsSeverity.to_int sev);
-				"range",pos_to_json_range p;
-				"args",JArray args
-			]) :: acc
-		) [] diag in
-		let js = JArray jl in
-		let b = Buffer.create 0 in
-		write_json (Buffer.add_string b) js;
-		Buffer.contents b
-end
 
+		let module_types = ref [] in
+
+		let add_type mt =
+			match mt with
+			| TClassDecl {cl_kind = KAbstractImpl _} -> ()
+			| _ ->
+				let path = (t_infos mt).mt_path in
+				if not (List.exists (fun mt2 -> (t_infos mt2).mt_path = path) !module_types) then begin
+					(match mt with
+					| TClassDecl c | TAbstractDecl { a_impl = Some c } when Meta.has Meta.CoreApi c.cl_meta ->
+						!merge_core_doc_ref ctx c
+					| _ -> ());
+					module_types := mt :: !module_types
+				end
+		in
+
+		(* module types *)
+		List.iter add_type ctx.m.curmod.m_types;
+
+		(* module imports *)
+		List.iter add_type (List.map fst ctx.m.module_types);
+
+		(* module using *)
+		List.iter (fun (c,_) ->
+			add_type (TClassDecl c)
+		) ctx.m.module_using;
+
+		(* TODO: wildcard packages. How? *)
+
+		(* packages and toplevel types *)
+		let class_paths = ctx.com.class_path in
+		let class_paths = List.filter (fun s -> s <> "") class_paths in
+
+		let packages = ref [] in
+		let add_package pack =
+			try
+				begin match PMap.find pack ctx.com.package_rules with
+					| Forbidden ->
+						()
+					| _ ->
+						raise Not_found
+				end
+			with Not_found ->
+				if not (List.mem pack !packages) then packages := pack :: !packages
+		in
+
+		explore_class_paths ctx class_paths false add_package (fun _ -> ()) add_type;
+
+		List.iter (fun pack ->
+			DynArray.add acc (ITPackage pack)
+		) !packages;
+
+		List.iter (fun mt ->
+			DynArray.add acc (ITType mt)
+		) !module_types;
+		DynArray.to_list acc
+
+	let handle_unresolved_identifier ctx i p only_types =
+		let l = run ctx only_types in
+		let cl = List.map (fun it ->
+			let s = IdentifierType.get_name it in
+			(s,it),StringError.levenshtein i s
+		) l in
+		let cl = List.sort (fun (_,c1) (_,c2) -> compare c1 c2) cl in
+		let cl = StringError.filter_similar (fun (s,_) r -> r > 0 && r <= (min (String.length s) (String.length i)) / 3) cl in
+		ctx.com.display_information.unresolved_identifiers <- (i,p,cl) :: ctx.com.display_information.unresolved_identifiers
+end

+ 801 - 0
src/display/displayOutput.ml

@@ -0,0 +1,801 @@
+open Globals
+open Common
+open Common.DisplayMode
+open Type
+open Display
+open Typecore
+
+(* Old XML stuff *)
+
+let htmlescape s =
+	let s = String.concat "&amp;" (ExtString.String.nsplit s "&") in
+	let s = String.concat "&lt;" (ExtString.String.nsplit s "<") in
+	let s = String.concat "&gt;" (ExtString.String.nsplit s ">") in
+	let s = String.concat "&quot;" (ExtString.String.nsplit s "\"") in
+	s
+
+let get_timer_fields start_time =
+	let tot = ref 0. in
+	Hashtbl.iter (fun _ t -> tot := !tot +. t.total) Common.htimers;
+	let fields = [("@TOTAL", Printf.sprintf "%.3fs" (get_time() -. start_time))] in
+	if !tot > 0. then
+		Hashtbl.fold (fun _ t acc ->
+			((String.concat "." t.id),(Printf.sprintf "%.3fs (%.0f%%)" t.total (t.total *. 100. /. !tot))) :: acc
+		) Common.htimers fields
+	else
+		fields
+
+let print_keywords () =
+	let b = Buffer.create 0 in
+	Buffer.add_string b "<list>\n";
+	Hashtbl.iter (fun k _ ->
+		Buffer.add_string b (Printf.sprintf "<i n=\"%s\"></i>\n" k)
+	) Lexer.keywords;
+	Buffer.add_string b "</list>\n";
+	Buffer.contents b
+
+let print_fields fields =
+	let b = Buffer.create 0 in
+	Buffer.add_string b "<list>\n";
+	List.iter (fun (n,k,d) ->
+		let s_kind, t = match k with
+			| FKVar t -> "var", s_type (print_context()) t
+			| FKMethod t -> "method", s_type (print_context()) t
+			| FKType t -> "type", s_type (print_context()) t
+			| FKPackage -> "package", ""
+			| FKModule -> "type", ""
+			| FKMetadata -> "metadata", ""
+			| FKTimer s -> "timer", s
+		in
+		Buffer.add_string b (Printf.sprintf "<i n=\"%s\" k=\"%s\"><t>%s</t><d>%s</d></i>\n" n s_kind (htmlescape t) (htmlescape d))
+	) (List.sort (fun (a,ak,_) (b,bk,_) -> compare (display_field_kind_index ak,a) (display_field_kind_index bk,b)) fields);
+	Buffer.add_string b "</list>\n";
+	Buffer.contents b
+
+let maybe_print_doc d =
+	Option.map_default (fun s -> Printf.sprintf " d=\"%s\"" (htmlescape s)) "" d
+
+let print_toplevel il =
+	let b = Buffer.create 0 in
+	Buffer.add_string b "<il>\n";
+	let s_type t = htmlescape (s_type (print_context()) t) in
+	let s_doc d = maybe_print_doc d in
+	List.iter (fun id -> match id with
+		| IdentifierType.ITLocal v ->
+			Buffer.add_string b (Printf.sprintf "<i k=\"local\" t=\"%s\">%s</i>\n" (s_type v.v_type) v.v_name);
+		| IdentifierType.ITMember(c,cf) ->
+			Buffer.add_string b (Printf.sprintf "<i k=\"member\" t=\"%s\"%s>%s</i>\n" (s_type cf.cf_type) (s_doc cf.cf_doc) cf.cf_name);
+		| IdentifierType.ITStatic(c,cf) ->
+			Buffer.add_string b (Printf.sprintf "<i k=\"static\" t=\"%s\"%s>%s</i>\n" (s_type cf.cf_type) (s_doc cf.cf_doc) cf.cf_name);
+		| IdentifierType.ITEnum(en,ef) ->
+			Buffer.add_string b (Printf.sprintf "<i k=\"enum\" t=\"%s\"%s>%s</i>\n" (s_type ef.ef_type) (s_doc ef.ef_doc) ef.ef_name);
+		| IdentifierType.ITEnumAbstract(a,cf) ->
+			Buffer.add_string b (Printf.sprintf "<i k=\"enumabstract\" t=\"%s\"%s>%s</i>\n" (s_type cf.cf_type) (s_doc cf.cf_doc) cf.cf_name);
+		| IdentifierType.ITGlobal(mt,s,t) ->
+			Buffer.add_string b (Printf.sprintf "<i k=\"global\" p=\"%s\" t=\"%s\">%s</i>\n" (s_type_path (t_infos mt).mt_path) (s_type t) s);
+		| IdentifierType.ITType(mt) ->
+			let infos = t_infos mt in
+			Buffer.add_string b (Printf.sprintf "<i k=\"type\" p=\"%s\"%s>%s</i>\n" (s_type_path infos.mt_path) (s_doc infos.mt_doc) (snd infos.mt_path));
+		| IdentifierType.ITPackage s ->
+			Buffer.add_string b (Printf.sprintf "<i k=\"package\">%s</i>\n" s)
+		| IdentifierType.ITLiteral s ->
+			Buffer.add_string b (Printf.sprintf "<i k=\"literal\">%s</i>\n" s)
+		| IdentifierType.ITTimer s ->
+			Buffer.add_string b (Printf.sprintf "<i k=\"timer\">%s</i>\n" s)
+	) il;
+	Buffer.add_string b "</il>";
+	Buffer.contents b
+
+let print_type t p doc =
+	let b = Buffer.create 0 in
+	if p = null_pos then
+		Buffer.add_string b "<type"
+	else begin
+		let error_printer file line = Printf.sprintf "%s:%d:" (Path.unique_full_path file) line in
+		let epos = Lexer.get_error_pos error_printer p in
+		Buffer.add_string b ("<type p=\"" ^ (htmlescape epos) ^ "\"")
+	end;
+	Buffer.add_string b (maybe_print_doc doc);
+	Buffer.add_string b ">\n";
+	Buffer.add_string b (htmlescape (s_type (print_context()) t));
+	Buffer.add_string b "\n</type>\n";
+	Buffer.contents b
+
+let print_signatures tl =
+	let b = Buffer.create 0 in
+	List.iter (fun (t,doc) ->
+		Buffer.add_string b "<type";
+		Option.may (fun s -> Buffer.add_string b (Printf.sprintf " d=\"%s\"" (htmlescape s))) doc;
+		Buffer.add_string b ">\n";
+		Buffer.add_string b (htmlescape (s_type (print_context()) (follow t)));
+		Buffer.add_string b "\n</type>\n";
+	) tl;
+	Buffer.contents b
+
+let print_positions pl =
+	let b = Buffer.create 0 in
+	let error_printer file line = Printf.sprintf "%s:%d:" (Path.get_real_path file) line in
+	Buffer.add_string b "<list>\n";
+	List.iter (fun p ->
+		let epos = Lexer.get_error_pos error_printer p in
+		Buffer.add_string b "<pos>";
+		Buffer.add_string b epos;
+		Buffer.add_string b "</pos>\n";
+	) pl;
+	Buffer.add_string b "</list>";
+	Buffer.contents b
+
+let display_memory com =
+	let verbose = com.verbose in
+	let print = print_endline in
+	let fmt_size sz =
+		if sz < 1024 then
+			string_of_int sz ^ " B"
+		else if sz < 1024*1024 then
+			string_of_int (sz asr 10) ^ " KB"
+		else
+			Printf.sprintf "%.1f MB" ((float_of_int sz) /. (1024.*.1024.))
+	in
+	let size v =
+		fmt_size (mem_size v)
+	in
+	Gc.full_major();
+	Gc.compact();
+	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 CompilationServer.get() with
+	| None ->
+		print "No cache found";
+	| Some {CompilationServer.cache = c} ->
+		print ("Total cache size " ^ size c);
+		print ("  haxelib " ^ size c.CompilationServer.c_haxelib);
+		print ("  parsed ast " ^ size c.CompilationServer.c_files ^ " (" ^ string_of_int (Hashtbl.length c.CompilationServer.c_files) ^ " files stored)");
+		print ("  typed modules " ^ size c.CompilationServer.c_modules ^ " (" ^ string_of_int (Hashtbl.length c.CompilationServer.c_modules) ^ " modules stored)");
+		let rec scan_module_deps m h =
+			if Hashtbl.mem h m.m_id then
+				()
+			else begin
+				Hashtbl.add h m.m_id m;
+				PMap.iter (fun _ m -> scan_module_deps m h) m.m_extra.m_deps
+			end
+		in
+		let all_modules = Hashtbl.fold (fun _ m acc -> PMap.add m.m_id m acc) c.CompilationServer.c_modules PMap.empty in
+		let modules = Hashtbl.fold (fun (path,key) m acc ->
+			let mdeps = Hashtbl.create 0 in
+			scan_module_deps m mdeps;
+			let deps = ref [] in
+			let out = ref all_modules in
+			Hashtbl.iter (fun _ md ->
+				out := PMap.remove md.m_id !out;
+				if m == md then () else begin
+				deps := Obj.repr md :: !deps;
+				List.iter (fun t ->
+					match t with
+					| TClassDecl c ->
+						deps := Obj.repr c :: !deps;
+						List.iter (fun f -> deps := Obj.repr f :: !deps) c.cl_ordered_statics;
+						List.iter (fun f -> deps := Obj.repr f :: !deps) c.cl_ordered_fields;
+					| TEnumDecl e ->
+						deps := Obj.repr e :: !deps;
+						List.iter (fun n -> deps := Obj.repr (PMap.find n e.e_constrs) :: !deps) e.e_names;
+					| TTypeDecl t -> deps := Obj.repr t :: !deps;
+					| TAbstractDecl a -> deps := Obj.repr a :: !deps;
+				) md.m_types;
+				end
+			) mdeps;
+			let chk = Obj.repr Common.memory_marker :: PMap.fold (fun m acc -> Obj.repr m :: acc) !out [] in
+			let inf = Objsize.objsize m !deps chk in
+			(m,Objsize.size_with_headers inf, (inf.Objsize.reached,!deps,!out)) :: acc
+		) c.CompilationServer.c_modules [] in
+		let cur_key = ref "" and tcount = ref 0 and mcount = ref 0 in
+		List.iter (fun (m,size,(reached,deps,out)) ->
+			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;
+			let sign md =
+				if md.m_extra.m_sign = key then "" else "(" ^ (try Digest.to_hex md.m_extra.m_sign with _ -> "???" ^ md.m_extra.m_sign) ^ ")"
+			in
+			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
+				if (Objsize.objsize m deps [Obj.repr Common.memory_marker]).Objsize.reached then leak "common";
+				PMap.iter (fun _ md ->
+					if (Objsize.objsize m deps [Obj.repr md]).Objsize.reached then leak (s_type_path md.m_path ^ sign md);
+				) out;
+			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) (sign 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")
+
+module TypePathHandler = struct
+	let unique l =
+		let rec _unique = function
+			| [] -> []
+			| x1 :: x2 :: l when x1 = x2 -> _unique (x2 :: l)
+			| x :: l -> x :: _unique l
+		in
+		_unique (List.sort compare l)
+
+	let rec read_type_path com p =
+		let classes = ref [] in
+		let packages = ref [] in
+		let p = (match p with
+			| x :: l ->
+				(try
+					match PMap.find x com.package_rules with
+					| Remap s -> s :: l
+					| _ -> p
+				with
+					Not_found -> p)
+			| _ -> p
+		) in
+		List.iter (fun path ->
+			let dir = path ^ String.concat "/" p in
+			let r = (try Sys.readdir dir with _ -> [||]) in
+			Array.iter (fun f ->
+				if (try (Unix.stat (dir ^ "/" ^ f)).Unix.st_kind = Unix.S_DIR with _ -> false) then begin
+					if f.[0] >= 'a' && f.[0] <= 'z' then begin
+						if p = ["."] then
+							match read_type_path com [f] with
+							| [] , [] -> ()
+							| _ ->
+								try
+									match PMap.find f com.package_rules with
+									| Forbidden -> ()
+									| Remap f -> packages := f :: !packages
+								with Not_found ->
+									packages := f :: !packages
+						else
+							packages := f :: !packages
+					end;
+				end else if file_extension f = "hx" then begin
+					let c = Filename.chop_extension f in
+					if String.length c < 2 || String.sub c (String.length c - 2) 2 <> "__" then classes := c :: !classes;
+				end;
+			) r;
+		) com.class_path;
+		List.iter (fun (_,_,extract) ->
+			Hashtbl.iter (fun (path,name) _ ->
+				if path = p then classes := name :: !classes else
+				let rec loop p1 p2 =
+					match p1, p2 with
+					| [], _ -> ()
+					| x :: _, [] -> packages := x :: !packages
+					| a :: p1, b :: p2 -> if a = b then loop p1 p2
+				in
+				loop path p
+			) (extract());
+		) com.swf_libs;
+		List.iter (fun (path,std,close,all_files,lookup) ->
+			List.iter (fun (path, name) ->
+				if path = p then classes := name :: !classes else
+				let rec loop p1 p2 =
+					match p1, p2 with
+					| [], _ -> ()
+					| x :: _, [] -> packages := x :: !packages
+					| a :: p1, b :: p2 -> if a = b then loop p1 p2
+				in
+				loop path p
+			) (all_files())
+		) com.java_libs;
+		List.iter (fun (path,std,all_files,lookup) ->
+			List.iter (fun (path, name) ->
+				if path = p then classes := name :: !classes else
+				let rec loop p1 p2 =
+					match p1, p2 with
+					| [], _ -> ()
+					| x :: _, [] -> packages := x :: !packages
+					| a :: p1, b :: p2 -> if a = b then loop p1 p2
+				in
+			loop path p
+			) (all_files())
+		) com.net_libs;
+		unique !packages, unique !classes
+
+	(** raise field completion listing packages and modules in a given package *)
+	let complete_type_path com p =
+		let packs, modules = read_type_path com p in
+		if packs = [] && modules = [] then
+			(abort ("No classes found in " ^ String.concat "." p) null_pos)
+		else
+			let packs = List.map (fun n -> n,Display.FKPackage,"") packs in
+			let modules = List.map (fun n -> n,Display.FKModule,"") modules in
+			Some (packs @ modules)
+
+	(** raise field completion listing module sub-types and static fields *)
+	let complete_type_path_inner com p c cur_package is_import =
+		try
+			let sl_pack,s_module = match List.rev p with
+				| s :: sl when s.[0] >= 'A' && s.[0] <= 'Z' -> List.rev sl,s
+				| _ -> p,c
+			in
+			let ctx = Typer.create com in
+			let rec lookup p =
+				try
+					Typeload.load_module ctx (p,s_module) null_pos
+				with e ->
+					if cur_package then
+						match List.rev p with
+						| [] -> raise e
+						| _ :: p -> lookup (List.rev p)
+					else
+						raise e
+			in
+			let m = lookup sl_pack in
+			let statics = ref None in
+			let public_types = List.filter (fun t ->
+				let tinfos = t_infos t in
+				let is_module_type = snd tinfos.mt_path = c in
+				if is_import && is_module_type then begin match t with
+					| TClassDecl c ->
+						ignore(c.cl_build());
+						statics := Some c.cl_ordered_statics
+					| _ -> ()
+				end;
+				not tinfos.mt_private
+			) m.m_types in
+			let types =
+				if c <> s_module then
+					[]
+				else
+					List.map (fun t ->
+						let infos = t_infos t in
+						(snd infos.mt_path), Display.FKModule, (Option.default "" infos.mt_doc)
+					) public_types
+			in
+			let make_field_doc cf =
+				cf.cf_name,
+				(match cf.cf_kind with Method _ -> Display.FKMethod cf.cf_type | Var _ -> Display.FKVar cf.cf_type),
+				(match cf.cf_doc with Some s -> s | None -> "")
+			in
+			let fields = match !statics with
+				| None -> types
+				| Some cfl -> types @ (List.map make_field_doc (List.filter (fun cf -> cf.cf_public) cfl))
+			in
+			Some fields
+		with _ ->
+			abort ("Could not load module " ^ (s_type_path (p,c))) null_pos
+end
+
+(* New JSON stuff *)
+
+open Json
+
+(** return a range JSON structure for given position
+    positions are 0-based and the result object looks like this:
+    {
+        start: {line: 0, character: 0},
+        end: {line: 3, character: 42},
+    }
+*)
+let pos_to_json_range p =
+	if p.pmin = -1 then
+		JNull
+	else
+		let l1, p1, l2, p2 = Lexer.get_pos_coords p in
+		let to_json l c = JObject [("line", JInt (l - 1)); ("character", JInt c)] in
+		JObject [
+			("start", to_json l1 p1);
+			("end", to_json l2 p2);
+		]
+
+let print_signature tl display_arg =
+	let st = s_type (print_context()) in
+	let s_arg (n,o,t) = Printf.sprintf "%s%s:%s" (if o then "?" else "") n (st t) in
+	let s_fun args ret = Printf.sprintf "(%s):%s" (String.concat ", " (List.map s_arg args)) (st ret) in
+	let siginf = List.map (fun (t,doc) ->
+		let label = match follow t with TFun(args,ret) -> s_fun args ret | _ -> st t in
+		let parameters = match follow t with
+			| TFun(args,_) ->
+				List.map (fun arg ->
+					let label = s_arg arg in
+					JObject [
+						"label",JString label
+					]
+				) args
+			| _ -> []
+		in
+		let js = [
+			"label",JString label;
+			"parameters",JArray parameters;
+		] in
+		JObject (match doc with None -> js | Some s -> ("documentation",JString s) :: js)
+	) tl in
+	let jo = JObject [
+		"signatures",JArray siginf;
+		"activeParameter",JInt display_arg;
+		"activeSignature",JInt 0;
+	] in
+	let b = Buffer.create 0 in
+	write_json (Buffer.add_string b) jo;
+	Buffer.contents b
+
+module StatisticsPrinter = struct
+	open Statistics
+
+	let relation_to_string = function
+		| Implemented -> "implementers"
+		| Extended -> "subclasses"
+		| Overridden -> "overrides"
+		| Referenced -> "references"
+
+	let symbol_to_string = function
+		| SKClass _ -> "class type"
+		| SKInterface _ -> "interface type"
+		| SKEnum _ -> "enum type"
+		| SKField _ -> "class field"
+		| SKEnumField _ -> "enum field"
+		| SKVariable _ -> "variable"
+
+	let print_statistics (kinds,relations) =
+		let files = Hashtbl.create 0 in
+		Hashtbl.iter (fun p rl ->
+			let file = Path.get_real_path p.pfile in
+			try
+				Hashtbl.replace files file ((p,rl) :: Hashtbl.find files file)
+			with Not_found ->
+				Hashtbl.add files file [p,rl]
+		) relations;
+		let ja = Hashtbl.fold (fun file relations acc ->
+			let l = List.map (fun (p,rl) ->
+				let h = Hashtbl.create 0 in
+				List.iter (fun (r,p) ->
+					let s = relation_to_string r in
+					let jo = JObject [
+						"range",pos_to_json_range p;
+						"file",JString (Path.get_real_path p.pfile);
+					] in
+					try Hashtbl.replace h s (jo :: Hashtbl.find h s)
+					with Not_found -> Hashtbl.add h s [jo]
+				) rl;
+				let l = Hashtbl.fold (fun s js acc -> (s,JArray js) :: acc) h [] in
+				let l = ("range",pos_to_json_range p) :: l in
+				let l = try ("kind",JString (symbol_to_string (Hashtbl.find kinds p))) :: l with Not_found -> l in
+				JObject l
+			) relations in
+			(JObject [
+				"file",JString file;
+				"statistics",JArray l
+			]) :: acc
+		) files [] in
+		let b = Buffer.create 0 in
+		write_json (Buffer.add_string b) (JArray ja);
+		Buffer.contents b
+end
+
+module DiagnosticsPrinter = struct
+	open Diagnostics
+	open Diagnostics.DiagnosticsKind
+	open DisplayTypes
+
+	type t = DiagnosticsKind.t * pos
+
+	module UnresolvedIdentifierSuggestion = struct
+		type t =
+			| UISImport
+			| UISTypo
+
+		let to_int = function
+			| UISImport -> 0
+			| UISTypo -> 1
+	end
+
+	let print_diagnostics ctx global =
+		let com = ctx.com in
+		let diag = Hashtbl.create 0 in
+		let add dk p sev args =
+			let file = Path.get_real_path p.pfile in
+			let diag = try
+				Hashtbl.find diag file
+			with Not_found ->
+				let d = DynArray.create() in
+				Hashtbl.add diag file d;
+				d
+			in
+			DynArray.add diag (dk,p,sev,args)
+		in
+		let add dk p sev args =
+			if global || is_display_file p.pfile then add dk p sev args
+		in
+		let find_type i =
+			let types = ref [] in
+			Hashtbl.iter (fun _ m ->
+				List.iter (fun mt ->
+					let s_full_type_path (p,s) n = s_type_path (p,s) ^ if (s <> n) then "." ^ n else "" in
+					let tinfos = t_infos mt in
+					if snd tinfos.mt_path = i then
+						types := JObject [
+							"kind",JInt (UnresolvedIdentifierSuggestion.to_int UnresolvedIdentifierSuggestion.UISImport);
+							"name",JString (s_full_type_path m.m_path i)
+						] :: !types
+				) m.m_types;
+			) ctx.g.modules;
+			!types
+		in
+		List.iter (fun (s,p,suggestions) ->
+			let suggestions = List.map (fun (s,_) ->
+				JObject [
+					"kind",JInt (UnresolvedIdentifierSuggestion.to_int UnresolvedIdentifierSuggestion.UISTypo);
+					"name",JString s
+				]
+			) suggestions in
+			add DKUnresolvedIdentifier p DiagnosticsSeverity.Error (JArray (suggestions @ (find_type s)));
+		) com.display_information.unresolved_identifiers;
+		PMap.iter (fun p (r,_) ->
+			if not !r then add DKUnusedImport p DiagnosticsSeverity.Warning (JArray [])
+		) com.shared.shared_display_information.import_positions;
+		List.iter (fun (s,p,sev) ->
+			add DKCompilerError p sev (JString s)
+		) com.shared.shared_display_information.diagnostics_messages;
+		List.iter (fun (s,p,prange) ->
+			add DKRemovableCode p DiagnosticsSeverity.Warning (JObject ["description",JString s;"range",if prange = null_pos then JNull else pos_to_json_range prange])
+		) com.shared.shared_display_information.removable_code;
+		let jl = Hashtbl.fold (fun file diag acc ->
+			let jl = DynArray.fold_left (fun acc (dk,p,sev,jargs) ->
+				(JObject [
+					"kind",JInt (to_int dk);
+					"severity",JInt (DiagnosticsSeverity.to_int sev);
+					"range",pos_to_json_range p;
+					"args",jargs
+				]) :: acc
+			) [] diag in
+			(JObject [
+				"file",JString file;
+				"diagnostics",JArray jl
+			]) :: acc
+		) diag [] in
+		let js = JArray jl in
+		let b = Buffer.create 0 in
+		write_json (Buffer.add_string b) js;
+		Buffer.contents b
+end
+
+module ModuleSymbolsPrinter = struct
+	open DisplayTypes.SymbolKind
+	open DisplayTypes.SymbolInformation
+
+	let print_module_symbols com symbols filter =
+		let regex = Option.map Str.regexp_case_fold filter in
+		let reported = Hashtbl.create 0 in
+		let add si =
+			if Hashtbl.mem reported si.pos then false
+			else begin
+				let b = match regex with
+					| None -> true
+					| Some regex -> (try ignore(Str.search_forward regex si.name 0); true with Not_found -> false)
+				in
+				Hashtbl.replace reported si.pos true;
+				b
+			end
+		in
+		let ja = List.fold_left (fun acc (file,l) ->
+			let jl = ExtList.List.filter_map (fun si ->
+				if not (add si) then
+					None
+				else begin
+					let l =
+						("name",JString si.name) ::
+						("kind",JInt (to_int si.kind)) ::
+						("range", pos_to_json_range si.pos) ::
+						(match si.container_name with None -> [] | Some s -> ["containerName",JString s])
+					in
+					Some (JObject l)
+				end
+			) (DynArray.to_list l) in
+			if jl = [] then
+				acc
+			else
+				(JObject [
+					"file",JString file;
+					"symbols",JArray jl
+				]) :: acc
+		) [] symbols in
+		let js = JArray ja in
+		let b = Buffer.create 0 in
+		write_json (Buffer.add_string b) js;
+		Buffer.contents b
+end
+
+(* Mode processing *)
+
+exception Completion of string
+
+let unquote v =
+	let len = String.length v in
+	if len > 0 && v.[0] = '"' && v.[len - 1] = '"' then String.sub v 1 (len - 2) 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))) :: !pre_compilation;
+	| "keywords" ->
+		raise (Completion (print_keywords ()))
+	| "memory" ->
+		did_something := true;
+		(try display_memory com with e -> prerr_endline (Printexc.get_backtrace ()));
+	| "diagnostics" ->
+		Common.define com Define.NoCOpt;
+		com.display <- DisplayMode.create (DMDiagnostics true);
+		Common.display_default := DMDiagnostics true;
+	| _ ->
+		let file, pos = try ExtString.String.split file_pos "@" with _ -> failwith ("Invalid format: " ^ file_pos) in
+		let file = unquote file in
+		let pos, smode = try ExtString.String.split pos "@" with _ -> pos,"" in
+		let mode = match smode with
+			| "position" ->
+				Common.define com Define.NoCOpt;
+				DMPosition
+			| "usage" ->
+				Common.define com Define.NoCOpt;
+				DMUsage false
+			(*| "rename" ->
+				Common.define com Define.NoCOpt;
+				DMUsage true*)
+			| "package" ->
+				DMPackage
+			| "type" ->
+				Common.define com Define.NoCOpt;
+				DMType
+			| "toplevel" ->
+				Common.define com Define.NoCOpt;
+				DMToplevel
+			| "module-symbols" ->
+				Common.define com Define.NoCOpt;
+				DMModuleSymbols None;
+			| "diagnostics" ->
+				Common.define com Define.NoCOpt;
+				DMDiagnostics false;
+			| "statistics" ->
+				Common.define com Define.NoCOpt;
+				DMStatistics
+			| "signature" ->
+				DMSignature
+			| "" ->
+				DMField
+			| _ ->
+				let smode,arg = try ExtString.String.split smode "@" with _ -> pos,"" in
+				match smode with
+					| "resolve" ->
+						DMResolve arg
+					| "workspace-symbols" ->
+						Common.define com Define.NoCOpt;
+						DMModuleSymbols (Some arg)
+					| _ ->
+						DMField
+		in
+		let pos = try int_of_string pos with _ -> failwith ("Invalid format: "  ^ pos) in
+		com.display <- DisplayMode.create mode;
+		Common.display_default := mode;
+		Common.define_value com Define.Display (if smode <> "" then smode else "1");
+		Parser.use_doc := true;
+		Parser.resume_display := {
+			pfile = Path.unique_full_path file;
+			pmin = pos;
+			pmax = pos;
+		}
+
+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_type_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 ->
+			()
+		| dfp ->
+			if dfp = DFPOnly then begin
+				classes := [];
+				com.main_class <- None;
+			end;
+			let real = Path.get_real_path (!Parser.resume_display).pfile in
+			(match get_module_path_from_file_path com real with
+			| Some path ->
+				if com.display.dms_kind = DMPackage then raise (DisplayPackage (fst path));
+				classes := path :: !classes
+			| 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.[1] <= 'z' -> failwith ("Display file '" ^ file ^ "' should not start with a lowercase letter")
+				| _ -> ());
+				failwith "Display file was not found in class path"
+			);
+			Common.log com ("Display file : " ^ real);
+			Common.log com ("Classes found : ["  ^ (String.concat "," (List.map s_type_path !classes)) ^ "]")
+
+let process_global_display_mode com tctx = match com.display.dms_kind with
+	| DMUsage with_definition ->
+		let symbols,relations = Statistics.collect_statistics tctx in
+		let rec loop acc relations = match relations with
+			| (Statistics.Referenced,p) :: relations -> loop (p :: acc) relations
+			| _ :: relations -> loop acc relations
+			| [] -> acc
+		in
+		let usages = Hashtbl.fold (fun p sym acc ->
+			if Statistics.is_usage_symbol sym then begin
+				let acc = if with_definition then p :: acc else acc in
+				(try loop acc (Hashtbl.find relations p)
+				with Not_found -> acc)
+			end else
+				acc
+		) symbols [] in
+		let usages = List.sort (fun p1 p2 ->
+			let c = compare p1.pfile p2.pfile in
+			if c <> 0 then c else compare p1.pmin p2.pmin
+		) usages in
+		raise (DisplayPosition usages)
+	| DMDiagnostics global ->
+		Diagnostics.prepare com global;
+		raise (Diagnostics (DiagnosticsPrinter.print_diagnostics tctx global))
+	| DMStatistics ->
+		let stats = Statistics.collect_statistics tctx in
+		raise (Statistics (StatisticsPrinter.print_statistics stats))
+	| DMModuleSymbols filter ->
+		let symbols = com.shared.shared_display_information.document_symbols in
+		let symbols = match CompilationServer.get() with
+			| None -> symbols
+			| Some cs ->
+				let l = CompilationServer.get_context_files cs ((get_signature com) :: (match com.get_macros() with None -> [] | Some com -> [get_signature com])) in
+				List.fold_left (fun acc (file,data) ->
+					print_endline (Printf.sprintf "%s %b" file (is_display_file file));
+					if (filter <> None || is_display_file file) then
+						(file,DocumentSymbols.collect_module_symbols data) :: acc
+					else
+						acc
+				) symbols l
+		in
+		raise (ModuleSymbols(ModuleSymbolsPrinter.print_module_symbols com symbols filter))
+	| _ -> ()
+
+let find_doc t =
+	let doc = match follow t with
+		| TAnon an ->
+			begin match !(an.a_status) with
+				| Statics c -> c.cl_doc
+				| EnumStatics en -> en.e_doc
+				| AbstractStatics a -> a.a_doc
+				| _ -> None
+			end
+		| _ ->
+			None
+	in
+	doc

+ 59 - 0
src/display/displayTypes.ml

@@ -0,0 +1,59 @@
+open Ast
+
+module SymbolKind = struct
+	type t =
+		| Class
+		| Interface
+		| Enum
+		| Typedef
+		| Abstract
+		| Field
+		| Property
+		| Method
+		| Constructor
+		| Function
+		| Variable
+
+	let to_int = function
+		| Class -> 1
+		| Interface -> 2
+		| Enum -> 3
+		| Typedef -> 4
+		| Abstract -> 5
+		| Field -> 6
+		| Property -> 7
+		| Method -> 8
+		| Constructor -> 9
+		| Function -> 10
+		| Variable -> 11
+end
+
+module SymbolInformation = struct
+	type t = {
+		name : string;
+		kind : SymbolKind.t;
+		pos : Globals.pos;
+		container_name : string option;
+	}
+
+	let make name kind pos container_name = {
+		name = name;
+		kind = kind;
+		pos = pos;
+		container_name = container_name;
+	}
+end
+
+module DiagnosticsSeverity = struct
+	type t =
+		| Error
+		| Warning
+		| Information
+		| Hint
+
+	let to_int = function
+		| Error -> 1
+		| Warning -> 2
+		| Information -> 3
+		| Hint -> 4
+end

+ 132 - 682
src/generators/codegen.ml

@@ -20,11 +20,49 @@
 open Ast
 open Type
 open Common
-open Typecore
+open Error
+open Globals
 
 (* -------------------------------------------------------------------------- *)
 (* TOOLS *)
 
+(* Collection of functions that return expressions *)
+module ExprBuilder = struct
+	let make_static_this c p =
+		let ta = TAnon { a_fields = c.cl_statics; a_status = ref (Statics c) } in
+		mk (TTypeExpr (TClassDecl c)) ta p
+
+	let make_static_field c cf p =
+		let e_this = make_static_this c p in
+		mk (TField(e_this,FStatic(c,cf))) cf.cf_type p
+
+	let make_int com i p =
+		mk (TConst (TInt (Int32.of_int i))) com.basic.tint p
+
+	let make_float com f p =
+		mk (TConst (TFloat f)) com.basic.tfloat p
+
+	let make_bool com b p =
+		mk (TConst(TBool b)) com.basic.tbool p
+
+	let make_string com s p =
+		mk (TConst (TString s)) com.basic.tstring p
+
+	let make_null t p =
+		mk (TConst TNull) t p
+
+	let make_local v p =
+		mk (TLocal v) v.v_type p
+
+	let make_const_texpr com ct p = match ct with
+		| TString s -> mk (TConst (TString s)) com.basic.tstring p
+		| TInt i -> mk (TConst (TInt i)) com.basic.tint p
+		| TFloat f -> mk (TConst (TFloat f)) com.basic.tfloat p
+		| TBool b -> mk (TConst (TBool b)) com.basic.tbool p
+		| TNull -> mk (TConst TNull) (com.basic.tnull (mk_mono())) p
+		| _ -> error "Unsupported constant" p
+end
+
 let field e name t p =
 	mk (TField (e,try quick_field e.etype name with Not_found -> assert false)) t p
 
@@ -35,9 +73,6 @@ let fcall e name el ret p =
 let mk_parent e =
 	mk (TParenthesis e) e.etype e.epos
 
-let string com str p =
-	mk (TConst (TString str)) com.basic.tstring p
-
 let binop op a b t p =
 	mk (TBinop (op,a,b)) t p
 
@@ -73,7 +108,7 @@ let rec type_constant_value com (e,p) =
 	| EParenthesis e ->
 		type_constant_value com e
 	| EObjectDecl el ->
-		mk (TObjectDecl (List.map (fun (n,e) -> n, type_constant_value com e) el)) (TAnon { a_fields = PMap.empty; a_status = ref Closed }) p
+		mk (TObjectDecl (List.map (fun ((n,_),e) -> n, type_constant_value com e) el)) (TAnon { a_fields = PMap.empty; a_status = ref Closed }) p
 	| EArrayDecl el ->
 		mk (TArrayDecl (List.map (type_constant_value com) el)) (com.basic.tarray t_dynamic) p
 	| _ ->
@@ -108,23 +143,16 @@ let add_property_field com c =
 	| [] -> ()
 	| _ ->
 		let fields,values = List.fold_left (fun (fields,values) (n,v) ->
-			let cf = mk_field n com.basic.tstring p in
-			PMap.add n cf fields,(n, string com v p) :: values
+			let cf = mk_field n com.basic.tstring p null_pos in
+			PMap.add n cf fields,(n, ExprBuilder.make_string com v p) :: values
 		) (PMap.empty,[]) props in
 		let t = mk_anon fields in
 		let e = mk (TObjectDecl values) t p in
-		let cf = mk_field "__properties__" t p in
+		let cf = mk_field "__properties__" t p null_pos in
 		cf.cf_expr <- Some e;
 		c.cl_statics <- PMap.add cf.cf_name cf c.cl_statics;
 		c.cl_ordered_statics <- cf :: c.cl_ordered_statics
 
-let is_removable_field ctx f =
-	Meta.has Meta.Extern f.cf_meta || Meta.has Meta.Generic f.cf_meta
-	|| (match f.cf_kind with
-		| Var {v_read = AccRequire (s,_)} -> true
-		| Method MethMacro -> not ctx.in_macro
-		| _ -> false)
-
 let escape_res_name name allow_dirs =
 	ExtString.String.replace_chars (fun chr ->
 		if (chr >= 'a' && chr <= 'z') || (chr >= 'A' && chr <= 'Z') || (chr >= '0' && chr <= '9') || chr = '_' || chr = '.' then
@@ -176,343 +204,6 @@ let build_metadata com t =
 		let meta_obj = (try ("obj", make_meta_field (List.assoc "" meta)) :: meta_obj with Not_found -> meta_obj) in
 		Some (mk (TObjectDecl meta_obj) t_dynamic p)
 
-(* -------------------------------------------------------------------------- *)
-(* ABSTRACT CASTS *)
-
-module AbstractCast = struct
-
-	let cast_stack = ref []
-
-	let make_static_call ctx c cf a pl args t p =
-		if cf.cf_kind = Method MethMacro then begin
-			match args with
-				| [e] ->
-					let e,f = push_this ctx e in
-					ctx.with_type_stack <- (WithType t) :: ctx.with_type_stack;
-					let e = match ctx.g.do_macro ctx MExpr c.cl_path cf.cf_name [e] p with
-						| Some e -> type_expr ctx e Value
-						| None ->  type_expr ctx (EConst (Ident "null"),p) Value
-					in
-					ctx.with_type_stack <- List.tl ctx.with_type_stack;
-					f();
-					e
-				| _ -> assert false
-		end else
-			make_static_call ctx c cf (apply_params a.a_params pl) args t p
-
-	let do_check_cast ctx tleft eright p =
-		let recurse cf f =
-			if cf == ctx.curfield || List.mem cf !cast_stack then error "Recursive implicit cast" p;
-			cast_stack := cf :: !cast_stack;
-			let r = f() in
-			cast_stack := List.tl !cast_stack;
-			r
-		in
-		let find a tl f =
-			let tcf,cf = f() in
-			if (Meta.has Meta.MultiType a.a_meta) then
-				mk_cast eright tleft p
-			else match a.a_impl with
-				| Some c -> recurse cf (fun () ->
-					let ret = make_static_call ctx c cf a tl [eright] tleft p in
-					{ ret with eexpr = TMeta( (Meta.ImplicitCast,[],ret.epos), ret) }
-				)
-				| None -> assert false
-		in
-		if type_iseq tleft eright.etype then
-			eright
-		else begin
-			let rec loop tleft tright = match follow tleft,follow tright with
-			| TAbstract(a1,tl1),TAbstract(a2,tl2) ->
-				begin try find a2 tl2 (fun () -> Abstract.find_to a2 tl2 tleft)
-				with Not_found -> try find a1 tl1 (fun () -> Abstract.find_from a1 tl1 eright.etype tleft)
-				with Not_found -> raise Not_found
-				end
-			| TAbstract(a,tl),_ ->
-				begin try find a tl (fun () -> Abstract.find_from a tl eright.etype tleft)
-				with Not_found ->
-					let rec loop2 tcl = match tcl with
-						| tc :: tcl ->
-							if not (type_iseq tc tleft) then loop (apply_params a.a_params tl tc) tright
-							else loop2 tcl
-						| [] -> raise Not_found
-					in
-					loop2 a.a_from
-				end
-			| _,TAbstract(a,tl) ->
-				begin try find a tl (fun () -> Abstract.find_to a tl tleft)
-				with Not_found ->
-					let rec loop2 tcl = match tcl with
-						| tc :: tcl ->
-							if not (type_iseq tc tright) then loop tleft (apply_params a.a_params tl tc)
-							else loop2 tcl
-						| [] -> raise Not_found
-					in
-					loop2 a.a_to
-				end
-			| _ ->
-				raise Not_found
-			in
-			loop tleft eright.etype
-		end
-
-	let cast_or_unify_raise ctx tleft eright p =
-		try
-			(* can't do that anymore because this might miss macro calls (#4315) *)
-			(* if ctx.com.display <> DMNone then raise Not_found; *)
-			do_check_cast ctx tleft eright p
-		with Not_found ->
-			unify_raise ctx eright.etype tleft p;
-			eright
-
-	let cast_or_unify ctx tleft eright p =
-		try
-			cast_or_unify_raise ctx tleft eright 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 rec loop cfl = match cfl with
-			| [] -> raise Not_found
-			| cf :: cfl ->
-				let monos = List.map (fun _ -> mk_mono()) 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
-				match follow (map cf.cf_type) with
-				| TFun([(_,_,tab);(_,_,ta1);(_,_,ta2)],r) as tf when is_set ->
-					begin try
-						Type.unify tab 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 _,_) ->
-						loop cfl
-					end
-				| TFun([(_,_,tab);(_,_,ta1)],r) as tf when not is_set ->
-					begin try
-						Type.unify tab ta;
-						let e1 = cast_or_unify_raise ctx ta1 e1 p in
-						check_constraints();
-						cf,tf,r,e1,None
-					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
-		with Not_found -> match e2o with
-			| None ->
-				error (Printf.sprintf "No @:arrayAccess function accepts argument of %s" (s_type (print_context()) e1.etype)) p
-			| Some e2 ->
-				error (Printf.sprintf "No @:arrayAccess function accepts arguments of %s and %s" (s_type (print_context()) e1.etype) (s_type (print_context()) e2.etype)) p
-
-	let find_multitype_specialization com a pl p =
-		let m = mk_mono() in
-		let tl = match Meta.get Meta.MultiType a.a_meta with
-			| _,[],_ -> pl
-			| _,el,_ ->
-				let relevant = Hashtbl.create 0 in
-				List.iter (fun e ->
-					let rec loop f e = match fst e with
-						| EConst(Ident s) ->
-							Hashtbl.replace relevant s f
-						| EMeta((Meta.Custom ":followWithAbstracts",_,_),e1) ->
-							loop Abstract.follow_with_abstracts e1;
-						| _ ->
-							error "Type parameter expected" (pos e)
-					in
-					loop (fun t -> t) e
-				) el;
-				let tl = List.map2 (fun (n,_) t ->
-					try
-						(Hashtbl.find relevant n) t
-					with Not_found ->
-						if not (has_mono t) then t
-						else t_dynamic
-				) a.a_params pl in
-				if com.platform = Js && a.a_path = ([],"Map") then begin match tl with
-					| t1 :: _ ->
-						let rec loop stack t =
-							if List.exists (fun t2 -> fast_eq t t2) stack then
-								t
-							else begin
-								let stack = t :: stack in
-								match follow t with
-								| TAbstract ({ a_path = [],"Class" },_) ->
-									error (Printf.sprintf "Cannot use %s as key type to Map because Class<T> is not comparable" (s_type (print_context()) t1)) p;
-								| TEnum(en,tl) ->
-									PMap.iter (fun _ ef -> ignore(loop stack ef.ef_type)) en.e_constrs;
-									Type.map (loop stack) t
-								| t ->
-									Type.map (loop stack) t
-							end
-						in
-						ignore(loop [] t1)
-					| _ -> assert false
-				end;
-				tl
-		in
-		let _,cf =
-			try
-				Abstract.find_to a tl m
-			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
-				else
-					error ("Abstract " ^ (s_type_path a.a_path) ^ " has no @:to function that accepts " ^ st) p;
-		in
-		cf, follow m
-
-	let handle_abstract_casts ctx e =
-		let rec loop ctx e = match e.eexpr with
-			| TNew({cl_kind = KAbstractImpl a} as c,pl,el) ->
-				if not (Meta.has Meta.MultiType a.a_meta) then begin
-					(* This must have been a @:generic expansion with a { new } constraint (issue #4364). In this case
-					   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
-				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
-					let e = make_static_call ctx c cf a pl ((mk (TConst TNull) (TAbstract(a,pl)) e.epos) :: el) m e.epos in
-					{e with etype = m}
-				end
-			| TCall({eexpr = TField(_,FStatic({cl_path=[],"Std"},{cf_name = "string"}))},[e1]) when (match follow e1.etype with TAbstract({a_impl = Some _},_) -> true | _ -> false) ->
-				begin match follow e1.etype with
-					| 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
-						with Not_found ->
-							e
-						end
-					| _ ->
-						assert false
-				end
-			| TCall(e1, el) ->
-				begin try
-					let rec find_abstract e = match follow e.etype,e.eexpr with
-						| TAbstract(a,pl),_ when Meta.has Meta.MultiType a.a_meta -> a,pl,e
-						| _,TCast(e1,None) -> find_abstract e1
-						| _ -> raise Not_found
-					in
-					let rec find_field e1 =
-						match e1.eexpr with
-						| TCast(e2,None) ->
-							{e1 with eexpr = TCast(find_field e2,None)}
-						| TField(e2,fa) ->
-							let a,pl,e2 = find_abstract e2 in
-							let m = Abstract.get_underlying_type a pl in
-							let fname = field_name fa in
-							let el = List.map (loop ctx) el in
-							begin try
-								let fa = quick_field m fname in
-								let get_fun_type t = match follow t with
-									| TFun(_,tr) as tf -> tf,tr
-									| _ -> raise Not_found
-								in
-								let tf,tr = match fa with
-									| FStatic(_,cf) -> get_fun_type cf.cf_type
-									| FInstance(c,tl,cf) -> get_fun_type (apply_params c.cl_params tl cf.cf_type)
-									| FAnon cf -> get_fun_type cf.cf_type
-									| _ -> raise Not_found
-								in
-								let ef = mk (TField({e2 with etype = m},fa)) tf e2.epos in
-								let ecall = make_call ctx ef el tr e.epos in
-								if not (type_iseq ecall.etype e.etype) then
-									mk (TCast(ecall,None)) e.etype e.epos
-								else
-									ecall
-							with Not_found ->
-								(* quick_field raises Not_found if m is an abstract, we have to replicate the 'using' call here *)
-								match follow m with
-								| TAbstract({a_impl = Some c} as a,pl) ->
-									let cf = PMap.find fname c.cl_statics in
-									make_static_call ctx c cf a pl (e2 :: el) e.etype e.epos
-								| _ -> raise Not_found
-							end
-						| _ ->
-							raise Not_found
-					in
-					find_field e1
-				with Not_found ->
-					Type.map_expr (loop ctx) e
-				end
-			| _ ->
-				Type.map_expr (loop ctx) e
-		in
-		loop ctx e
-end
-
-(* -------------------------------------------------------------------------- *)
-(* USAGE *)
-
-let detect_usage com =
-	let usage = ref [] in
-	List.iter (fun t -> match t with
-		| TClassDecl c ->
-			let check_constructor c p =
-				try
-					let _,cf = get_constructor (fun cf -> cf.cf_type) c in
-					if Meta.has Meta.Usage cf.cf_meta then
-						usage := p :: !usage;
-				with Not_found ->
-					()
-			in
-			let rec expr e = match e.eexpr with
-				| TField(_,FEnum(_,ef)) when Meta.has Meta.Usage ef.ef_meta ->
-					let p = {e.epos with pmin = e.epos.pmax - (String.length ef.ef_name)} in
-					usage := p :: !usage;
-					Type.iter expr e
-				| TField(_,(FAnon cf | FInstance (_,_,cf) | FStatic (_,cf) | FClosure (_,cf))) when Meta.has Meta.Usage cf.cf_meta ->
-					let p = {e.epos with pmin = e.epos.pmax - (String.length cf.cf_name)} in
-					usage := p :: !usage;
-					Type.iter expr e
-				| TLocal v when Meta.has Meta.Usage v.v_meta ->
-					usage := e.epos :: !usage
-				| TTypeExpr mt when (Meta.has Meta.Usage (t_infos mt).mt_meta) ->
-					usage := e.epos :: !usage
-				| TNew (c,_,_) ->
-					check_constructor c e.epos;
-					Type.iter expr e;
-				| TCall({eexpr = TConst TSuper},_) ->
-					begin match c.cl_super with
-						| Some (c,_) ->
-							check_constructor c e.epos
-						| _ ->
-							()
-					end
-				| _ -> Type.iter expr e
-			in
-			let field cf = ignore(follow cf.cf_type); match cf.cf_expr with None -> () | Some e -> expr e in
-			(match c.cl_constructor with None -> () | Some cf -> field cf);
-			(match c.cl_init with None -> () | Some e -> expr e);
-			List.iter field c.cl_ordered_statics;
-			List.iter field c.cl_ordered_fields;
-		| _ -> ()
-	) com.types;
-	let usage = List.sort (fun p1 p2 ->
-		let c = compare p1.pfile p2.pfile in
-		if c <> 0 then c else compare p1.pmin p2.pmin
-	) !usage in
-	raise (Display.DisplayPosition usage)
-
 let update_cache_dependencies t =
 	let rec check_t m t = match t with
 		| TInst(c,tl) ->
@@ -582,9 +273,9 @@ let stack_context_init com stack_var exc_var pos_var tmp_var use_add p =
 	let stack_push c m =
 		fcall stack_e "push" [
 			if use_add then
-				binop OpAdd (string com (s_type_path c.cl_path ^ "::") p) (string com m p) t.tstring p
+				binop OpAdd (ExprBuilder.make_string com (s_type_path c.cl_path ^ "::") p) (ExprBuilder.make_string com m p) t.tstring p
 			else
-				string com (s_type_path c.cl_path ^ "::" ^ m) p
+				ExprBuilder.make_string com (s_type_path c.cl_path ^ "::" ^ m) p
 		] t.tvoid p
 	in
 	let stack_return e =
@@ -858,7 +549,7 @@ module Dump = struct
 			close_out ch)
 
 	let create_dumpfile_from_path com path =
-		let buf,close = create_dumpfile [] ("dump" :: (Common.platform_name com.platform) :: fst path @ [snd path]) in
+		let buf,close = create_dumpfile [] ("dump" :: (platform_name com.platform) :: fst path @ [snd path]) in
 		buf,close
 
 	let dump_types com s_expr =
@@ -868,23 +559,63 @@ module Dump = struct
 			let path = Type.t_path mt in
 			let buf,close = create_dumpfile_from_path com path in
 			let print fmt = Printf.kprintf (fun s -> Buffer.add_string buf s) fmt in
+			let s_metas ml tabs =
+				let args el =
+					match el with
+					| [] -> ""
+					| el -> Printf.sprintf "(%s)" (String.concat ", " (List.map (fun e -> Ast.s_expr e) el)) in
+				match ml with
+				| [] -> ""
+				| ml -> String.concat " " (List.map (fun me -> match me with (m,el,_) -> "@" ^ Meta.to_string m ^ args el) ml) ^ "\n" ^ tabs in
 			(match mt with
 			| Type.TClassDecl c ->
+				let s_cf_expr f =
+					match f.cf_expr with
+					| None -> ""
+					| Some e -> Printf.sprintf "%s" (s_expr s_type e) in
+				let is_inline_var v : bool = v = Var { v_read = AccInline; v_write = AccNever } in
 				let rec print_field stat f =
-					print "\t%s%s%s%s" (if stat then "static " else "") (if f.cf_public then "public " else "") f.cf_name (params f.cf_params);
-					print "(%s) : %s" (s_kind f.cf_kind) (s_type f.cf_type);
-					(match f.cf_expr with
-					| None -> ()
-					| Some e -> print "\n\n\t = %s" (s_expr s_type e));
-					print "\n\n";
+					print "\n\t%s%s%s%s%s %s%s"
+						(s_metas f.cf_meta "\t")
+						(if (f.cf_public && not (c.cl_extern || c.cl_interface)) then "public " else "")
+						(if stat then "static " else "")
+						(match f.cf_kind with
+							| Var v when (is_inline_var f.cf_kind) -> "inline "
+							| Var v -> ""
+							| Method m ->
+								match m with
+								| MethNormal -> ""
+								| MethDynamic -> "dynamic "
+								| MethInline -> "inline "
+								| MethMacro -> "macro ")
+						(match f.cf_kind with Var v -> "var" | Method m -> "function")
+						(f.cf_name ^ match f.cf_kind with
+							| Var { v_read = AccNormal; v_write = AccNormal } -> ""
+							| Var v when (is_inline_var f.cf_kind) -> ""
+							| Var v -> "(" ^ s_access true v.v_read ^ "," ^ s_access false v.v_write ^ ")"
+							| _ -> "")
+						(params f.cf_params);
+					(match f.cf_kind with
+						| Var v -> print ":%s%s;" (s_type f.cf_type)
+							(match f.cf_expr with
+							| None -> ""
+							| Some e -> " = " ^ (s_cf_expr f));
+						| Method m -> if (c.cl_extern || c.cl_interface) then (
+							match f.cf_type with
+							| TFun(al,t) -> print "(%s):%s;" (String.concat ", " (
+								List.map (fun (n,o,t) -> n ^ ":" ^ (s_type t)) al))
+								(s_type t)
+							| _ -> ()
+						) else print "%s" (s_cf_expr f));
+					print "\n";
 					List.iter (fun f -> print_field stat f) f.cf_overloads
 				in
-				print "%s%s%s %s%s" (if c.cl_private then "private " else "") (if c.cl_extern then "extern " else "") (if c.cl_interface then "interface" else "class") (s_type_path path) (params c.cl_params);
+				print "%s%s%s%s %s%s" (s_metas c.cl_meta "") (if c.cl_private then "private " else "") (if c.cl_extern then "extern " else "") (if c.cl_interface then "interface" else "class") (s_type_path path) (params c.cl_params);
 				(match c.cl_super with None -> () | Some (c,pl) -> print " extends %s" (s_type (TInst (c,pl))));
 				List.iter (fun (c,pl) -> print " implements %s" (s_type (TInst (c,pl)))) c.cl_implements;
 				(match c.cl_dynamic with None -> () | Some t -> print " implements Dynamic<%s>" (s_type t));
 				(match c.cl_array_access with None -> () | Some t -> print " implements ArrayAccess<%s>" (s_type t));
-				print "{\n";
+				print " {\n";
 				(match c.cl_constructor with
 				| None -> ()
 				| Some f -> print_field false f);
@@ -893,21 +624,27 @@ module Dump = struct
 				(match c.cl_init with
 				| None -> ()
 				| Some e ->
-					print "\n\n\t__init__ = ";
+					print "\n\tstatic function __init__() ";
 					print "%s" (s_expr s_type e);
-					print "}\n");
+					print "\n");
 				print "}";
 			| Type.TEnumDecl e ->
-				print "%s%senum %s%s {\n" (if e.e_private then "private " else "") (if e.e_extern then "extern " else "") (s_type_path path) (params e.e_params);
+				print "%s%s%senum %s%s {\n" (s_metas e.e_meta "") (if e.e_private then "private " else "") (if e.e_extern then "extern " else "") (s_type_path path) (params e.e_params);
 				List.iter (fun n ->
 					let f = PMap.find n e.e_constrs in
-					print "\t%s : %s;\n" f.ef_name (s_type f.ef_type);
+					print "\t%s%s;\n" f.ef_name (
+						match f.ef_type with
+						| TFun (al,t) -> Printf.sprintf "(%s)" (String.concat ", "
+							(List.map (fun (n,o,t) -> (if o then "?" else "") ^ n ^ ":" ^ (s_type t)) al))
+						| _ -> "")
 				) e.e_names;
 				print "}"
 			| Type.TTypeDecl t ->
-				print "%stype %s%s = %s" (if t.t_private then "private " else "") (s_type_path path) (params t.t_params) (s_type t.t_type);
+				print "%s%stypedef %s%s = %s" (s_metas t.t_meta "") (if t.t_private then "private " else "") (s_type_path path) (params t.t_params) (s_type t.t_type);
 			| Type.TAbstractDecl a ->
-				print "%sabstract %s%s {}" (if a.a_private then "private " else "") (s_type_path path) (params a.a_params);
+				print "%s%sabstract %s%s%s%s {}" (s_metas a.a_meta "") (if a.a_private then "private " else "") (s_type_path path) (params a.a_params)
+				(String.concat " " (List.map (fun t -> " from " ^ s_type t) a.a_from))
+				(String.concat " " (List.map (fun t -> " to " ^ s_type t) a.a_to));
 			);
 			close();
 		) com.types
@@ -916,10 +653,10 @@ module Dump = struct
 		List.iter (fun mt ->
 			let buf,close = create_dumpfile_from_path com (t_path mt) in
 			let s = match mt with
-				| TClassDecl c -> Printer.s_tclass c
-				| TEnumDecl en -> Printer.s_tenum en
+				| TClassDecl c -> Printer.s_tclass "" c
+				| TEnumDecl en -> Printer.s_tenum "" en
 				| TTypeDecl t -> Printer.s_tdef "" t
-				| TAbstractDecl a -> Printer.s_tabstract a
+				| TAbstractDecl a -> Printer.s_tabstract "" a
 			in
 			Buffer.add_string buf s;
 			close();
@@ -927,13 +664,17 @@ module Dump = struct
 
 	let dump_types com =
 		match Common.defined_value_safe com Define.Dump with
-			| "pretty" -> dump_types com (Type.s_expr_pretty false "\t")
+			| "pretty" -> dump_types com (Type.s_expr_pretty false "\t" true)
 			| "legacy" -> dump_types com Type.s_expr
 			| "record" -> dump_record com
 			| _ -> dump_types com (Type.s_expr_ast (not (Common.defined com Define.DumpIgnoreVarIds)) "\t")
 
-	let dump_dependencies com =
-		let buf,close = create_dumpfile [] ["dump";Common.platform_name com.platform;".dependencies"] in
+	let dump_dependencies ?(target_override=None) com =
+		let target_name = match target_override with
+			| None -> platform_name com.platform
+			| Some s -> s
+		in
+		let buf,close = create_dumpfile [] ["dump";target_name;".dependencies"] in
 		let print fmt = Printf.kprintf (fun s -> Buffer.add_string buf s) fmt in
 		let dep = Hashtbl.create 0 in
 		List.iter (fun m ->
@@ -945,7 +686,7 @@ module Dump = struct
 			) m.m_extra.m_deps;
 		) com.Common.modules;
 		close();
-		let buf,close = create_dumpfile [] ["dump";Common.platform_name com.platform;".dependants"] in
+		let buf,close = create_dumpfile [] ["dump";target_name;".dependants"] in
 		let print fmt = Printf.kprintf (fun s -> Buffer.add_string buf s) fmt in
 		Hashtbl.iter (fun n ml ->
 			print "%s:\n" n;
@@ -986,201 +727,6 @@ let default_cast ?(vtmp="$t") com e texpr t p =
 	let check = mk (TIf (mk_parent is,mk (TCast (vexpr,None)) t p,Some exc)) t p in
 	mk (TBlock [var;check;vexpr]) t p
 
-(** Overload resolution **)
-module Overloads =
-struct
-	let rec simplify_t t = match t with
-		| TAbstract(a,_) when Meta.has Meta.CoreType a.a_meta ->
-			t
-		| TInst _ | TEnum _ ->
-			t
-		| TAbstract(a,tl) -> simplify_t (Abstract.get_underlying_type a tl)
-		| TType(({ t_path = [],"Null" } as t), [t2]) -> (match simplify_t t2 with
-			| (TAbstract(a,_) as t2) when Meta.has Meta.CoreType a.a_meta ->
-				TType(t, [simplify_t t2])
-			| (TEnum _ as t2) ->
-				TType(t, [simplify_t t2])
-			| t2 -> t2)
-		| TType(t, tl) ->
-			simplify_t (apply_params t.t_params tl t.t_type)
-		| TMono r -> (match !r with
-			| Some t -> simplify_t t
-			| None -> t_dynamic)
-		| TAnon _ -> t_dynamic
-		| TDynamic _ -> t
-		| TLazy f -> simplify_t (!f())
-		| TFun _ -> t
-
-	(* rate type parameters *)
-	let rate_tp tlfun tlarg =
-		let acc = ref 0 in
-		List.iter2 (fun f a -> if not (type_iseq f a) then incr acc) tlfun tlarg;
-		!acc
-
-	(**
-		The rate function returns an ( int * int ) type.
-		The smaller the int, the best rated the caller argument is in comparison with the callee.
-
-		The first int refers to how many "conversions" would be necessary to convert from the callee to the caller type, and
-		the second refers to the type parameters.
-	**)
-	let rec rate_conv cacc tfun targ =
-		match simplify_t tfun, simplify_t targ with
-		| TInst({ cl_interface = true } as cf, tlf), TInst(ca, tla) ->
-			(* breadth-first *)
-			let stack = ref [0,ca,tla] in
-			let cur = ref (0, ca,tla) in
-			let rec loop () =
-				match !stack with
-				| [] -> (let acc, ca, tla = !cur in match ca.cl_super with
-					| None -> raise Not_found
-					| Some (sup,tls) ->
-						cur := (acc+1,sup,List.map (apply_params ca.cl_params tla) tls);
-						stack := [!cur];
-						loop())
-				| (acc,ca,tla) :: _ when ca == cf ->
-					acc,tla
-				| (acc,ca,tla) :: s ->
-					stack := s @ List.map (fun (c,tl) -> (acc+1,c,List.map (apply_params ca.cl_params tla) tl)) ca.cl_implements;
-					loop()
-			in
-			let acc, tla = loop() in
-			(cacc + acc, rate_tp tlf tla)
-		| TInst(cf,tlf), TInst(ca,tla) ->
-			let rec loop acc ca tla =
-				if cf == ca then
-					acc, tla
-				else match ca.cl_super with
-				| None -> raise Not_found
-				| Some(sup,stl) ->
-					loop (acc+1) sup (List.map (apply_params ca.cl_params tla) stl)
-			in
-			let acc, tla = loop 0 ca tla in
-			(cacc + acc, rate_tp tlf tla)
-		| TEnum(ef,tlf), TEnum(ea, tla) ->
-			if ef != ea then raise Not_found;
-			(cacc, rate_tp tlf tla)
-		| TDynamic _, TDynamic _ ->
-			(cacc, 0)
-		| TDynamic _, _ ->
-			(max_int, 0) (* a function with dynamic will always be worst of all *)
-		| TAbstract(a, _), TDynamic _ when Meta.has Meta.CoreType a.a_meta ->
-			(cacc + 2, 0) (* a dynamic to a basic type will have an "unboxing" penalty *)
-		| _, TDynamic _ ->
-			(cacc + 1, 0)
-		| TAbstract(af,tlf), TAbstract(aa,tla) ->
-			(if af == aa then
-				(cacc, rate_tp tlf tla)
-			else
-				let ret = ref None in
-				if List.exists (fun t -> try
-					ret := Some (rate_conv (cacc+1) (apply_params af.a_params tlf t) targ);
-					true
-				with | Not_found ->
-					false
-				) af.a_from then
-					Option.get !ret
-			else
-				if List.exists (fun t -> try
-					ret := Some (rate_conv (cacc+1) tfun (apply_params aa.a_params tla t));
-					true
-				with | Not_found ->
-					false
-				) aa.a_to then
-					Option.get !ret
-			else
-				raise Not_found)
-		| TType({ t_path = [], "Null" }, [tf]), TType({ t_path = [], "Null" }, [ta]) ->
-			rate_conv (cacc+0) tf ta
-		| TType({ t_path = [], "Null" }, [tf]), ta ->
-			rate_conv (cacc+1) tf ta
-		| tf, TType({ t_path = [], "Null" }, [ta]) ->
-			rate_conv (cacc+1) tf ta
-		| TFun _, TFun _ -> (* unify will make sure they are compatible *)
-			cacc,0
-		| tfun,targ ->
-			raise Not_found
-
-	let is_best arg1 arg2 =
-		(List.for_all2 (fun v1 v2 ->
-			v1 <= v2)
-		arg1 arg2) && (List.exists2 (fun v1 v2 ->
-			v1 < v2)
-		arg1 arg2)
-
-	let rec rm_duplicates acc ret = match ret with
-		| [] -> acc
-		| ( el, t, _ ) :: ret when List.exists (fun (_,t2,_) -> type_iseq t t2) acc ->
-			rm_duplicates acc ret
-		| r :: ret ->
-			rm_duplicates (r :: acc) ret
-
-	let s_options rated =
-		String.concat ",\n" (List.map (fun ((elist,t,_),rate) ->
-			"( " ^ (String.concat "," (List.map (fun(e,_) -> s_expr (s_type (print_context())) e) elist)) ^ " ) => " ^
-			"( " ^ (String.concat "," (List.map (fun (i,i2) -> string_of_int i ^ ":" ^ string_of_int i2) rate)) ^ " ) => " ^ (s_type (print_context()) t)
-		) rated)
-
-	let count_optionals elist =
-		List.fold_left (fun acc (_,is_optional) -> if is_optional then acc + 1 else acc) 0 elist
-
-	let rec fewer_optionals acc compatible = match acc, compatible with
-		| _, [] -> acc
-		| [], c :: comp -> fewer_optionals [c] comp
-		| (elist_acc, _, _) :: _, ((elist, _, _) as cur) :: comp ->
-			let acc_opt = count_optionals elist_acc in
-			let comp_opt = count_optionals elist in
-			if acc_opt = comp_opt then
-				fewer_optionals (cur :: acc) comp
-			else if acc_opt < comp_opt then
-				fewer_optionals acc comp
-			else
-				fewer_optionals [cur] comp
-
-	let reduce_compatible compatible = match fewer_optionals [] (rm_duplicates [] compatible) with
-		| [] -> []
-		| [v] -> [v]
-		| compatible ->
-			(* convert compatible into ( rate * compatible_type ) list *)
-			let rec mk_rate acc elist args = match elist, args with
-				| [], [] -> acc
-				| (_,true) :: elist, _ :: args -> mk_rate acc elist args
-				| (e,false) :: elist, (n,o,t) :: args ->
-					(* if the argument is an implicit cast, we need to start with a penalty *)
-					(* The penalty should be higher than any other implicit cast - other than Dynamic *)
-					(* since Dynamic has a penalty of max_int, we'll impose max_int - 1 to it *)
-					(match e.eexpr with
-						| TMeta( (Meta.ImplicitCast,_,_), _) ->
-							mk_rate ((max_int - 1, 0) :: acc) elist args
-						| _ ->
-							mk_rate (rate_conv 0 t e.etype :: acc) elist args)
-				| _ -> assert false
-			in
-
-			let rated = ref [] in
-			List.iter (function
-				| (elist,TFun(args,ret),d) -> (try
-					rated := ( (elist,TFun(args,ret),d), mk_rate [] elist args ) :: !rated
-					with | Not_found -> ())
-				| _ -> assert false
-			) 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
-			in
-
-			let r = loop [] !rated in
-			List.map fst r
-end;;
-
 module UnificationCallback = struct
 	let tf_stack = ref []
 
@@ -1267,87 +813,6 @@ module UnificationCallback = struct
 				check (Type.map_expr (run ff) e)
 end;;
 
-module DeprecationCheck = struct
-
-	let curclass = ref null_class
-
-	let warned_positions = Hashtbl.create 0
-
-	let print_deprecation_message com meta s p_usage =
-		let s = match meta with
-			| _,[EConst(String s),_],_ -> s
-			| _ -> Printf.sprintf "Usage of this %s is deprecated" s
-		in
-		if not (Hashtbl.mem warned_positions p_usage) then begin
-			Hashtbl.replace warned_positions p_usage true;
-			com.warning s p_usage;
-		end
-
-	let check_meta com meta s p_usage =
-		try
-			print_deprecation_message com (Meta.get Meta.Deprecated meta) s p_usage;
-		with Not_found ->
-			()
-
-	let check_cf com cf p = check_meta com cf.cf_meta "field" p
-
-	let check_class com c p = if c != !curclass then check_meta com c.cl_meta "class" p
-
-	let check_enum com en p = check_meta com en.e_meta "enum" p
-
-	let check_ef com ef p = check_meta com ef.ef_meta "enum field" p
-
-	let check_typedef com t p = check_meta com t.t_meta "typedef" p
-
-	let check_module_type com mt p = match mt with
-		| TClassDecl c -> check_class com c p
-		| TEnumDecl en -> check_enum com en p
-		| _ -> ()
-
-	let run com =
-		let rec expr e = match e.eexpr with
-			| TField(e1,fa) ->
-				expr e1;
-				begin match fa with
-					| FStatic(c,cf) | FInstance(c,_,cf) ->
-						check_class com c e.epos;
-						check_cf com cf e.epos
-					| FAnon cf ->
-						check_cf com cf e.epos
-					| FClosure(co,cf) ->
-						(match co with None -> () | Some (c,_) -> check_class com c e.epos);
-						check_cf com cf e.epos
-					| FEnum(en,ef) ->
-						check_enum com en e.epos;
-						check_ef com ef e.epos;
-					| _ ->
-						()
-				end
-			| TNew(c,_,el) ->
-				List.iter expr el;
-				check_class com c e.epos;
-				(match c.cl_constructor with None -> () | Some cf -> check_cf com cf e.epos)
-			| TTypeExpr(mt) | TCast(_,Some mt) ->
-				check_module_type com mt e.epos
-			| TMeta((Meta.Deprecated,_,_) as meta,e1) ->
-				print_deprecation_message com meta "field" e1.epos;
-				expr e1;
-			| _ ->
-				Type.iter expr e
-		in
-		List.iter (fun t -> match t with
-			| TClassDecl c ->
-				curclass := c;
-				let field cf = match cf.cf_expr with None -> () | Some e -> expr e in
-				(match c.cl_constructor with None -> () | Some cf -> field cf);
-				(match c.cl_init with None -> () | Some e -> expr e);
-				List.iter field c.cl_ordered_statics;
-				List.iter field c.cl_ordered_fields;
-			| _ ->
-				()
-		) com.types
-end
-
 let interpolate_code com code tl f_string f_expr p =
 	let exprs = Array.of_list tl in
 	let i = ref 0 in
@@ -1391,36 +856,6 @@ let map_source_header com f =
 	| "" -> ()
 	| s -> f s
 
-(* Collection of functions that return expressions *)
-module ExprBuilder = struct
-	let make_static_this c p =
-		let ta = TAnon { a_fields = c.cl_statics; a_status = ref (Statics c) } in
-		mk (TTypeExpr (TClassDecl c)) ta p
-
-	let make_static_field c cf p =
-		let e_this = make_static_this c p in
-		mk (TField(e_this,FStatic(c,cf))) cf.cf_type p
-
-	let make_int com i p =
-		mk (TConst (TInt (Int32.of_int i))) com.basic.tint p
-
-	let make_float com f p =
-		mk (TConst (TFloat f)) com.basic.tfloat p
-
-	let make_null t p =
-		mk (TConst TNull) t p
-
-	let make_local v p =
-		mk (TLocal v) v.v_type p
-
-	let make_const_texpr com ct p = match ct with
-		| TString s -> mk (TConst (TString s)) com.basic.tstring p
-		| TInt i -> mk (TConst (TInt i)) com.basic.tint p
-		| TFloat f -> mk (TConst (TFloat f)) com.basic.tfloat p
-		| TBool b -> mk (TConst (TBool b)) com.basic.tbool p
-		| TNull -> mk (TConst TNull) (com.basic.tnull (mk_mono())) p
-		| _ -> error "Unsupported constant" p
-end
 
 (* Static extensions for classes *)
 module ExtClass = struct
@@ -1435,3 +870,18 @@ module ExtClass = struct
 		let e_assign = mk (TBinop(OpAssign,ef1,e)) e.etype p in
 		add_cl_init c e_assign
 end
+
+let for_remap com v e1 e2 p =
+	let v' = alloc_var 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',quick_field t1 "hasNext")) (tfun [] com.basic.tbool) e1.epos in
+	let ehasnext = mk (TCall(ehasnext,[])) com.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
+	let eassign = mk (TVar(v,Some enext)) com.basic.tvoid p in
+	let ebody = Type.concat eassign e2 in
+	mk (TBlock [
+		mk (TVar (v',Some e1)) com.basic.tvoid e1.epos;
+		mk (TWhile((mk (TParenthesis ehasnext) ehasnext.etype ehasnext.epos),ebody,NormalWhile)) com.basic.tvoid e1.epos;
+	]) com.basic.tvoid p

+ 18 - 18
src/generators/genas3.ml

@@ -109,7 +109,7 @@ let s_path ctx stat path p =
 		let name = protect name in
 		let packs = (try Hashtbl.find ctx.imports name with Not_found -> []) in
 		if not (List.mem pack packs) then Hashtbl.replace ctx.imports name (pack :: packs);
-		Ast.s_type_path (pack,name)
+		Globals.s_type_path (pack,name)
 
 let reserved =
 	let h = Hashtbl.create 0 in
@@ -190,7 +190,7 @@ let close ctx =
 	Hashtbl.iter (fun name paths ->
 		List.iter (fun pack ->
 			let path = pack, name in
-			if path <> ctx.path then output_string ctx.ch ("\timport " ^ Ast.s_type_path path ^ ";\n");
+			if path <> ctx.path then output_string ctx.ch ("\timport " ^ Globals.s_type_path path ^ ";\n");
 		) paths
 	) ctx.imports;
 	output_string ctx.ch (Buffer.contents ctx.buf);
@@ -203,7 +203,7 @@ let gen_local ctx l =
 let spr ctx s = Buffer.add_string ctx.buf s
 let print ctx = Printf.kprintf (fun s -> Buffer.add_string ctx.buf s)
 
-let unsupported p = error "This expression cannot be generated to AS3" p
+let unsupported p = abort "This expression cannot be generated to AS3" p
 
 let newline ctx =
 	let rec loop p =
@@ -247,7 +247,7 @@ let rec type_str ctx t p =
 	match t with
 	| TEnum _ | TInst _ when List.memq t ctx.local_types ->
 		"*"
-	| TAbstract (a,pl) when not (Ast.Meta.has Ast.Meta.CoreType a.a_meta) ->
+	| TAbstract (a,pl) when not (Meta.has Meta.CoreType a.a_meta) ->
 		type_str ctx (Abstract.get_underlying_type a pl) p
 	| TAbstract (a,_) ->
 		(match a.a_path with
@@ -264,7 +264,7 @@ let rec type_str ctx t p =
 			| _ ->
 				let rec loop = function
 					| [] -> "Object"
-					| (Ast.Meta.FakeEnum,[Ast.EConst (Ast.Ident n),_],_) :: _ ->
+					| (Meta.FakeEnum,[Ast.EConst (Ast.Ident n),_],_) :: _ ->
 						(match n with
 						| "Int" -> "int"
 						| "UInt" -> "uint"
@@ -281,7 +281,7 @@ let rec type_str ctx t p =
 	| TInst (c,_) ->
 		(match c.cl_kind with
 		| KNormal | KGeneric | KGenericInstance _ | KAbstractImpl _ -> s_path ctx false c.cl_path p
-		| KTypeParameter _ | KExtension _ | KExpr _ | KMacroType | KGenericBuild _ -> "*")
+		| KTypeParameter _ | KExpr _ | KMacroType | KGenericBuild _ -> "*")
 	| TFun _ ->
 		"Function"
 	| TMono r ->
@@ -397,8 +397,8 @@ let gen_function_header ctx name f params p =
 	print ctx "function%s(" (match name with None -> "" | Some (n,meta) ->
 		let rec loop = function
 			| [] -> n
-			| (Ast.Meta.Getter,[Ast.EConst (Ast.Ident i),_],_) :: _ -> "get " ^ i
-			| (Ast.Meta.Setter,[Ast.EConst (Ast.Ident i),_],_) :: _ -> "set " ^ i
+			| (Meta.Getter,[Ast.EConst (Ast.Ident i),_],_) :: _ -> "get " ^ i
+			| (Meta.Setter,[Ast.EConst (Ast.Ident i),_],_) :: _ -> "set " ^ i
 			| _ :: l -> loop l
 		in
 		" " ^ loop meta
@@ -964,7 +964,7 @@ and gen_value ctx e =
 		v()
 
 let final m =
-	if Ast.Meta.has Ast.Meta.Final m then " final " else ""
+	if Meta.has Meta.Final m then " final " else ""
 
 let generate_field ctx static f =
 	newline ctx;
@@ -972,12 +972,12 @@ let generate_field ctx static f =
 	ctx.gen_uid <- 0;
 	List.iter (fun(m,pl,_) ->
 		match m,pl with
-		| Ast.Meta.Meta, [Ast.ECall ((Ast.EConst (Ast.Ident n),_),args),_] ->
+		| Meta.Meta, [Ast.ECall ((Ast.EConst (Ast.Ident n),_),args),_] ->
 			let mk_arg (a,p) =
 				match a with
 				| Ast.EConst (Ast.String s) -> (None, s)
 				| Ast.EBinop (Ast.OpAssign,(Ast.EConst (Ast.Ident n),_),(Ast.EConst (Ast.String s),_)) -> (Some n, s)
-				| _ -> error "Invalid meta definition" p
+				| _ -> abort "Invalid meta definition" p
 			in
 			print ctx "[%s" n;
 			(match args with
@@ -994,7 +994,7 @@ let generate_field ctx static f =
 		| _ -> ()
 	) f.cf_meta;
 	let public = f.cf_public || Hashtbl.mem ctx.get_sets (f.cf_name,static) || (f.cf_name = "main" && static)
-		|| f.cf_name = "resolve" || Ast.Meta.has Ast.Meta.Public f.cf_meta
+		|| f.cf_name = "resolve" || Meta.has Meta.Public f.cf_meta
 		(* consider all abstract methods public to avoid issues with inlined private access *)
 	    || (match ctx.curclass.cl_kind with KAbstractImpl _ -> true | _ -> false)
 	in
@@ -1024,8 +1024,8 @@ let generate_field ctx static f =
 			| TFun (args,r) when (match f.cf_kind with Method MethDynamic | Var _ -> false | _ -> true) ->
 				let rec loop = function
 					| [] -> f.cf_name
-					| (Ast.Meta.Getter,[Ast.EConst (Ast.String name),_],_) :: _ -> "get " ^ name
-					| (Ast.Meta.Setter,[Ast.EConst (Ast.String name),_],_) :: _ -> "set " ^ name
+					| (Meta.Getter,[Ast.EConst (Ast.String name),_],_) :: _ -> "get " ^ name
+					| (Meta.Setter,[Ast.EConst (Ast.String name),_],_) :: _ -> "set " ^ name
 					| _ :: l -> loop l
 				in
 				print ctx "function %s(" (loop f.cf_meta);
@@ -1137,13 +1137,13 @@ let generate_class ctx c =
 		newline ctx;
 		spr ctx "namespace static_init";
 		newline ctx;
-		print ctx "%s.static_init::init()" (s_path ctx true ctx.curclass.cl_path Ast.null_pos);
+		print ctx "%s.static_init::init()" (s_path ctx true ctx.curclass.cl_path Globals.null_pos);
 	end;
 	newline ctx;
-	if c.cl_interface && Ast.Meta.has (Ast.Meta.Custom ":hasMetadata") c.cl_meta then begin
+	if c.cl_interface && Meta.has (Meta.Custom ":hasMetadata") c.cl_meta then begin
 		(* we have to reference the metadata class in order for it to be compiled *)
 		let path = fst c.cl_path,snd c.cl_path ^ "_HxMeta" in
-		spr ctx (Ast.s_type_path path);
+		spr ctx (Globals.s_type_path path);
 		newline ctx
 	end
 
@@ -1152,7 +1152,7 @@ let generate_main ctx inits =
 	let pack = open_block ctx in
 	print ctx "\timport flash.Lib";
 	newline ctx;
-	print ctx "public class __main__ extends %s {" (s_path ctx true (["flash"],"Boot") Ast.null_pos);
+	print ctx "public class __main__ extends %s {" (s_path ctx true (["flash"],"Boot") Globals.null_pos);
 	let cl = open_block ctx in
 	newline ctx;
 	spr ctx "public function __main__() {";

文件差異過大導致無法顯示
+ 686 - 857
src/generators/gencommon.ml


文件差異過大導致無法顯示
+ 365 - 133
src/generators/gencpp.ml


+ 172 - 221
src/generators/gencs.ml

@@ -17,12 +17,14 @@
 	Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  *)
 
+open Globals
 open Gencommon.ReflectionCFs
 open Ast
 open Common
 open Type
 open Gencommon
 open Gencommon.SourceWriter
+open Codegen
 open Printf
 open Option
 open ExtString
@@ -185,24 +187,25 @@ let rec change_md = function
 		TAbstractDecl a
 	| md -> md
 
+(* used in c#-specific filters to skip some of them for the special haxe.lang.Runtime class *)
+let in_runtime_class gen =
+	match gen.gcurrent_class with
+	| Some { cl_path = ["haxe";"lang"],"Runtime"} -> true
+	| _ -> false
+
 (* ******************************************* *)
 (* CSharpSpecificESynf *)
 (* ******************************************* *)
-
 (*
-
 	Some CSharp-specific syntax filters that must run before ExpressionUnwrap
 
 	dependencies:
 		It must run before ExprUnwrap, as it may not return valid Expr/Statement expressions
 		It must run before ClassInstance, as it will detect expressions that need unchanged TTypeExpr
-
 *)
 module CSharpSpecificESynf =
 struct
-
 	let name = "csharp_specific_e"
-
 	let priority = solve_deps name [DBefore ExpressionUnwrap.priority; DBefore ClassInstance.priority; DAfter TryCatchWrapper.priority]
 
 	let get_cl_from_t t =
@@ -215,15 +218,11 @@ struct
 			| TAbstract(ab,_) -> ab
 			| _ -> assert false
 
-	let traverse gen runtime_cl =
+	let configure gen runtime_cl =
 		let basic = gen.gcon.basic in
 		let uint = match get_type gen ([], "UInt") with | TTypeDecl t -> TType(t, []) | TAbstractDecl a -> TAbstract(a, []) | _ -> assert false in
 
 		let is_var = alloc_var "__is__" t_dynamic in
-		let name () = match gen.gcurrent_class with
-			| Some cl -> path_s cl.cl_path
-			| _ -> ""
-		in
 
 		let rec run e =
 			match e.eexpr with
@@ -270,7 +269,7 @@ struct
 
 					let obj = run obj in
 					(match follow_module follow md with
-						| TAbstractDecl{ a_path = ([], "Float") } when name() <> "haxe.lang.Runtime" ->
+						| TAbstractDecl{ a_path = ([], "Float") } when not (in_runtime_class gen) ->
 							(* on the special case of seeing if it is a Float, we need to test if both it is a float and if it is an Int *)
 							let mk_is local =
 								(* we check if it float or int or uint *)
@@ -281,7 +280,7 @@ struct
 							in
 							wrap_if_needed obj mk_is
 
-						| TAbstractDecl{ a_path = ([], "Int") } when name() <> "haxe.lang.Runtime" ->
+						| TAbstractDecl{ a_path = ([], "Int") } when not (in_runtime_class gen) ->
 							(* int can be stored in double variable because of anonymous functions, check that case *)
 							let mk_isint_call local =
 								{
@@ -300,7 +299,7 @@ struct
 							in
 							wrap_if_needed obj mk_is
 
-						| TAbstractDecl{ a_path = ([], "UInt") } when name() <> "haxe.lang.Runtime" ->
+						| TAbstractDecl{ a_path = ([], "UInt") } when not (in_runtime_class gen) ->
 							(* uint can be stored in double variable because of anonymous functions, check that case *)
 							let mk_isuint_call local =
 								{
@@ -360,12 +359,8 @@ struct
 
 				| _ -> Type.map_expr run e
 		in
-		run
-
-	let configure gen (mapping_func:texpr->texpr) =
-		let map e = Some(mapping_func e) in
+		let map e = Some(run e) in
 		gen.gsyntax_filters#add ~name:name ~priority:(PCustom priority) map
-
 end;;
 
 (* ******************************************* *)
@@ -383,9 +378,7 @@ end;;
 
 module CSharpSpecificSynf =
 struct
-
 	let name = "csharp_specific"
-
 	let priority = solve_deps name [ DAfter ExpressionUnwrap.priority; DAfter ObjectDeclMap.priority; DAfter ArrayDeclSynf.priority;	DAfter HardNullableSynf.priority ]
 
 	let get_cl_from_t t =
@@ -398,7 +391,7 @@ struct
 			| TInst( { cl_kind = KTypeParameter _ }, _ ) -> true
 			| _ -> false
 
-	let traverse gen runtime_cl =
+	let configure gen runtime_cl =
 		let basic = gen.gcon.basic in
 		let tchar = match ( get_type gen (["cs"], "Char16") ) with
 			| TTypeDecl t -> TType(t,[])
@@ -423,10 +416,6 @@ struct
 		in
 
 		let is_cl t = match gen.greal_type t with | TInst ( { cl_path = (["System"], "Type") }, [] ) -> true | _ -> false in
-		let name () = match gen.gcurrent_class with
-			| Some cl -> path_s cl.cl_path
-			| _ -> ""
-		in
 
 		let as_var = alloc_var "__as__" t_dynamic in
 		let fast_cast = Common.defined gen.gcon Define.FastCast in
@@ -451,7 +440,7 @@ struct
 					{ e with eexpr = TField(run ef, FDynamic "ToUpperInvariant") }
 
 				| TCall( { eexpr = TField(_, FStatic({ cl_path = [], "String" }, { cf_name = "fromCharCode" })) }, [cc] ) ->
-					{ e with eexpr = TNew(get_cl_from_t basic.tstring, [], [mk_cast tchar (run cc); mk_int gen 1 cc.epos]) }
+					{ e with eexpr = TNew(get_cl_from_t basic.tstring, [], [mk_cast tchar (run cc); ExprBuilder.make_int gen.gcon 1 cc.epos]) }
 				| TCall( { eexpr = TField(ef, FInstance({ cl_path = [], "String" }, _, { cf_name = ("charAt" as field) })) }, args )
 				| TCall( { eexpr = TField(ef, FInstance({ cl_path = [], "String" }, _, { cf_name = ("charCodeAt" as field) })) }, args )
 				| TCall( { eexpr = TField(ef, FInstance({ cl_path = [], "String" }, _, { cf_name = ("indexOf" as field) })) }, args )
@@ -492,7 +481,7 @@ struct
 						etype = basic.tbool;
 						epos = e.epos
 					}
-				| TCast(expr, _) when is_int_float gen e.etype && is_dynamic gen expr.etype && ( Common.defined gen.gcon Define.EraseGenerics || not (is_null e.etype) ) && name() <> "haxe.lang.Runtime" ->
+				| TCast(expr, _) when is_int_float gen e.etype && is_dynamic gen expr.etype && ( Common.defined gen.gcon Define.EraseGenerics || not (is_null e.etype) ) && not (in_runtime_class gen) ->
 					let needs_cast = match gen.gfollow#run_f e.etype with
 						| TInst _ -> false
 						| _ -> true
@@ -511,7 +500,7 @@ struct
 
 					if needs_cast then mk_cast e.etype ret else ret
 
-				| TCast(expr, _) when Common.defined gen.gcon Define.EraseGenerics && like_i64 e.etype && is_dynamic gen expr.etype && name() <> "haxe.lang.Runtime" ->
+				| TCast(expr, _) when Common.defined gen.gcon Define.EraseGenerics && like_i64 e.etype && is_dynamic gen expr.etype && not (in_runtime_class gen) ->
 					{
 						eexpr = TCall(
 							mk_static_field_access_infer runtime_cl "toLong" expr.epos [],
@@ -527,20 +516,20 @@ struct
 					else
 						{ e with eexpr = TCall(mk_local as_var e.epos, [run expr]) }
 
-				| TCast(expr, _) when (is_string e.etype) && (not (is_string expr.etype)) && name() <> "haxe.lang.Runtime" ->
+				| TCast(expr, _) when (is_string e.etype) && (not (is_string expr.etype)) && not (in_runtime_class gen) ->
 					{ e with eexpr = TCall( mk_static_field_access_infer runtime_cl "toString" expr.epos [], [run expr] ) }
 				| TBinop( (Ast.OpNotEq as op), e1, e2)
 				| TBinop( (Ast.OpEq as op), e1, e2) when is_string e1.etype || is_string e2.etype ->
 					let mk_ret e = match op with | Ast.OpNotEq -> { e with eexpr = TUnop(Ast.Not, Ast.Prefix, e) } | _ -> e in
 					mk_ret { e with
 						eexpr = TCall({
-							eexpr = TField(mk_classtype_access clstring e.epos, FDynamic "Equals");
+							eexpr = TField(ExprBuilder.make_static_this clstring e.epos, FDynamic "Equals");
 							etype = TFun(["obj1",false,basic.tstring; "obj2",false,basic.tstring], basic.tbool);
 							epos = e1.epos
 						}, [ run e1; run e2 ])
 					}
 
-				| TCast(expr, _) when is_tparam e.etype && name() <> "haxe.lang.Runtime" && not (Common.defined gen.gcon Define.EraseGenerics) ->
+				| TCast(expr, _) when is_tparam e.etype && not (in_runtime_class gen) && not (Common.defined gen.gcon Define.EraseGenerics) ->
 					let static = mk_static_field_access_infer (runtime_cl) "genericCast" e.epos [e.etype] in
 					{ e with eexpr = TCall(static, [mk_local (alloc_var "$type_param" e.etype) expr.epos; run expr]); }
 
@@ -556,7 +545,7 @@ struct
 					}
 
 				| TBinop ( (Ast.OpEq as op), e1, e2 )
-				| TBinop ( (Ast.OpNotEq as op), e1, e2 ) when is_cl e1.etype && name() <> "haxe.lang.Runtime" ->
+				| TBinop ( (Ast.OpNotEq as op), e1, e2 ) when is_cl e1.etype && not (in_runtime_class gen) ->
 					let static = mk_static_field_access_infer (runtime_cl) "typeEq" e.epos [] in
 					let ret = { e with eexpr = TCall(static, [run e1; run e2]); } in
 					if op = Ast.OpNotEq then
@@ -566,12 +555,8 @@ struct
 
 				| _ -> Type.map_expr run e
 		in
-		run
-
-	let configure gen (mapping_func:texpr->texpr) =
-		let map e = Some(mapping_func e) in
+		let map e = Some(run e) in
 		gen.gsyntax_filters#add ~name:name ~priority:(PCustom priority) map
-
 end;;
 
 let add_cast_handler gen =
@@ -620,7 +605,7 @@ let add_cast_handler gen =
 				epos = e.epos
 			};
 			{
-				eexpr = TVar(i, Some( mk_int gen (-1) e.epos ));
+				eexpr = TVar(i, Some( ExprBuilder.make_int gen.gcon (-1) e.epos ));
 				etype = basic.tvoid;
 				epos = e.epos
 			};
@@ -686,12 +671,6 @@ let add_cast_handler gen =
 	Hashtbl.add gen.gtparam_cast (["cs"], "NativeArray") gtparam_cast_native_array
 	(* end set gtparam_cast *)
 
-
-(* Type Parameters Handling *)
-let handle_type_params gen ifaces base_generic =
-	add_cast_handler gen;
-	TypeParams.RealTypeParams.default_config gen (fun e t -> gen.gcon.warning ("Cannot cast to " ^ (debug_type t)) e.epos; mk_cast t e) ifaces base_generic
-
 let connecting_string = "?" (* ? see list here http://www.fileformat.info/info/unicode/category/index.htm and here for C# http://msdn.microsoft.com/en-us/library/aa664670.aspx *)
 let default_package = "cs" (* I'm having this separated as I'm still not happy with having a cs package. Maybe dotnet would be better? *)
 let strict_mode = ref false (* strict mode is so we can check for unexpected information *)
@@ -731,7 +710,7 @@ let rec get_fun_modifiers meta access modifiers =
 		| (Meta.ReadOnly,[],_) :: meta -> get_fun_modifiers meta access ("readonly" :: modifiers)
 		| (Meta.Unsafe,[],_) :: meta -> get_fun_modifiers meta access ("unsafe" :: modifiers)
 		| (Meta.Volatile,[],_) :: meta -> get_fun_modifiers meta access ("volatile" :: modifiers)
-		| (Meta.Custom ("?prop_impl" | "?event_impl"),[],_) :: meta -> get_fun_modifiers meta "private" modifiers
+		| (Meta.Custom ("?prop_impl" | ":cs_event_impl"),[],_) :: meta -> get_fun_modifiers meta "private" modifiers
 		| _ :: meta -> get_fun_modifiers meta access modifiers
 
 (* this was the way I found to pass the generator context to be accessible across all functions here *)
@@ -817,7 +796,7 @@ let configure gen =
 	let rec field_is_hxgeneric e = match e.eexpr with
 		| TParenthesis e | TMeta(_,e) -> field_is_hxgeneric e
 		| TField(_, (FStatic(cl,_) | FInstance(cl,_,_)) ) ->
-			(* print_endline ("is_hxgeneric " ^ path_s cl.cl_path ^ " : " ^ string_of_bool (is_hxgeneric (TClassDecl cl))); *)
+			(* print_endline ("is_hxgeneric " ^ s_type_path cl.cl_path ^ " : " ^ string_of_bool (is_hxgeneric (TClassDecl cl))); *)
 			is_hxgeneric (TClassDecl cl)
 		| _ -> true
 	in
@@ -863,10 +842,10 @@ let configure gen =
 		let path = (t_infos md).mt_path in
 		match path with
 			| ([], "String") -> "string", params
-			| ([], "Null") -> path_s (change_ns md ["haxe"; "lang"], change_clname "Null"), params
+			| ([], "Null") -> s_type_path (change_ns md ["haxe"; "lang"], change_clname "Null"), params
 			| (ns,clname) ->
 				let ns, params = change_ns_params md params ns in
-				path_s (ns, change_clname clname), params
+				s_type_path (ns, change_clname clname), params
 	in
 
 	let module_s md =
@@ -1156,9 +1135,9 @@ let configure gen =
 		if skip_line_directives then
 			fun w p -> ()
 		else fun w p ->
-			if p.pfile <> Ast.null_pos.pfile then (* Compiler Error CS1560 https://msdn.microsoft.com/en-us/library/z3t5e5sw(v=vs.90).aspx *)
+			if p.pfile <> null_pos.pfile then (* Compiler Error CS1560 https://msdn.microsoft.com/en-us/library/z3t5e5sw(v=vs.90).aspx *)
 			let cur_line = Lexer.get_error_line p in
-			let file = Common.get_full_path p.pfile in
+			let file = Path.get_full_path p.pfile in
 			if cur_line <> ((!last_line)+1) then
 				let line = Ast.s_escape file in
 				if String.length line <= 256 then
@@ -1385,7 +1364,7 @@ let configure gen =
 				| TField (e, s) when is_pointer gen e.etype ->
 					(* take off the extra cast if possible *)
 					let e = match e.eexpr with
-						| TCast(e1,_) when Gencommon.CastDetect.type_iseq gen e.etype e1.etype ->
+						| TCast(e1,_) when CastDetect.type_iseq gen e.etype e1.etype ->
 							e1
 						| _ -> e
 					in
@@ -1601,12 +1580,20 @@ let configure gen =
 					) el;
 					end_block w
 				| TIf (econd, e1, Some(eelse)) when was_in_value ->
+					let base = t_s e.etype in
 					write w "( ";
 					expr_s w (mk_paren econd);
 					write w " ? ";
-					expr_s w (mk_paren e1);
+					if t_s e1.etype <> base then
+						expr_s w (mk_cast e.etype e1)
+					else
+						expr_s w (mk_paren e1);
+
 					write w " : ";
-					expr_s w (mk_paren eelse);
+					if t_s eelse.etype <> base then
+						expr_s w (mk_cast e.etype eelse)
+					else
+						expr_s w (mk_paren eelse);
 					write w " )";
 				| TIf (econd, e1, eelse) ->
 					write w "if ";
@@ -2044,7 +2031,7 @@ let configure gen =
 			| "new" -> snd cl.cl_path, true, false
 			| name when String.contains name '.' ->
 				let fn_name, path = parse_explicit_iface name in
-				(path_s path) ^ "." ^ fn_name, false, true
+				(s_type_path path) ^ "." ^ fn_name, false, true
 			| name -> try
 				let binop = PMap.find name binops_names in
 				"operator " ^ s_binop binop, false, false
@@ -2052,7 +2039,7 @@ let configure gen =
 				let unop = PMap.find name unops_names in
 				"operator " ^ s_unop unop, false, false
 			with | Not_found ->
-				if Meta.has (Meta.Custom "?prop_impl") cf.cf_meta || Meta.has (Meta.Custom "?event_impl") cf.cf_meta then
+				if Meta.has (Meta.Custom "?prop_impl") cf.cf_meta || Meta.has (Meta.Custom ":cs_event_impl") cf.cf_meta then
 					"_" ^ name, false, false
 				else
 					name, false, false
@@ -2064,7 +2051,7 @@ let configure gen =
 				| true, Some(cl,_) ->
 					 (try
 							let cf2 = PMap.find cf.cf_name cl.cl_statics in
-							Gencommon.CastDetect.type_eq gen EqStrict cf.cf_type cf2.cf_type;
+							CastDetect.type_eq gen EqStrict cf.cf_type cf2.cf_type;
 							["new"]
 						with
 							| Not_found | Unify_error _ ->
@@ -2144,7 +2131,7 @@ let configure gen =
 						match meta with
 							| [] ->
 								let expr = match cf.cf_expr with
-									| None -> mk (TBlock([])) t_dynamic Ast.null_pos
+									| None -> mk (TBlock([])) t_dynamic null_pos
 									| Some s ->
 										match s.eexpr with
 											| TFunction tf ->
@@ -2160,7 +2147,7 @@ let configure gen =
 									| TBlock _ ->
 										let unchecked = needs_unchecked e in
 										if unchecked then (begin_block w; write w "unchecked ");
-										let t = Common.timer "expression to string" in
+										let t = Common.timer ["expression to string"] in
 										expr_s w e;
 										t();
 										line_reset_directive w;
@@ -2187,7 +2174,7 @@ let configure gen =
 												| None -> ()
 												| Some sc ->
 													write w ": ";
-													let t = Common.timer "expression to string" in
+													let t = Common.timer ["expression to string"] in
 													expr_s w sc;
 													write w " ";
 													t()
@@ -2352,7 +2339,7 @@ let configure gen =
 				in
 				if prop v.v_read && prop v.v_write && (v.v_read = AccCall || v.v_write = AccCall) then begin
 					let this = if static then
-						mk_classtype_access cl f.cf_pos
+						ExprBuilder.make_static_this cl f.cf_pos
 					else
 						{ eexpr = TConst TThis; etype = TInst(cl,List.map snd cl.cl_params); epos = f.cf_pos }
 					in
@@ -2424,8 +2411,12 @@ let configure gen =
 					write w "public static void Main() ";
 					begin_block w;
 					(if Hashtbl.mem gen.gtypes (["cs"], "Boot") then write w "global::cs.Boot.init();"; newline w);
-					expr_s w { eexpr = TTypeExpr(TClassDecl cl); etype = t_dynamic; epos = Ast.null_pos };
-					write w ".main();";
+					(match gen.gcon.main with
+						| None ->
+							expr_s w { eexpr = TTypeExpr(TClassDecl cl); etype = t_dynamic; epos = null_pos };
+							write w ".main();"
+						| Some expr ->
+							expr_s w (mk_block expr));
 					end_block w;
 					end_block w;
 					newline w;
@@ -2465,7 +2456,11 @@ let configure gen =
 			write w "public static void Main()";
 			begin_block w;
 			(if Hashtbl.mem gen.gtypes (["cs"], "Boot") then write w "global::cs.Boot.init();"; newline w);
-			write w "main();";
+			(match gen.gcon.main with
+				| None ->
+					write w "main();";
+				| Some expr ->
+						expr_s w (mk_block expr));
 			end_block w
 		end;
 
@@ -2493,8 +2488,6 @@ let configure gen =
 				| Var { v_read = AccCall } | Var { v_write = AccCall } when Type.is_extern_field v && Meta.has Meta.Property v.cf_meta ->
 					props := (v.cf_name, ref (v, v.cf_type, None, None)) :: !props;
 				| Var { v_read = AccNormal; v_write = AccNormal } when Meta.has Meta.Event v.cf_meta ->
-					if v.cf_public then gen.gcon.error "@:event fields must be private" v.cf_pos;
-					v.cf_meta <- (Meta.SkipReflection, [], null_pos) :: v.cf_meta;
 					events := (v.cf_name, ref (v, v.cf_type, false, None, None)) :: !events;
 				| _ ->
 					nonprops := v :: !nonprops;
@@ -2543,7 +2536,6 @@ let configure gen =
 					let event = find_event (String.sub cf.cf_name 4 (String.length cf.cf_name - 4)) in
 					let v, t, _, add, remove = !event in
 					assert (add = None);
-					cf.cf_meta <- (Meta.Custom "?event_impl", [], null_pos) :: cf.cf_meta;
 					let custom = not (is_empty_function cf) in
 					event := (v, t, custom, Some cf, remove);
 					false
@@ -2552,7 +2544,6 @@ let configure gen =
 					let event = find_event (String.sub cf.cf_name 7 (String.length cf.cf_name - 7)) in
 					let v, t, _, add, remove = !event in
 					assert (remove = None);
-					cf.cf_meta <- (Meta.Custom "?event_impl", [], null_pos) :: cf.cf_meta;
 					let custom = not (is_empty_function cf) in
 					event := (v, t, custom, add, Some cf);
 					false
@@ -2563,25 +2554,11 @@ let configure gen =
 			let nonprops = ref nonprops in
 			List.iter (fun (n,r) ->
 				let ev, t, custom, add, remove = !r in
-				let tmeth = (tfun [t] basic.tvoid) in
 				match add, remove with
-				| None, _ ->
-					gen.gcon.error ("Missing event method add_" ^ n) ev.cf_pos;
-					failwith "Build failed"
-				| _, None ->
-					gen.gcon.error ("Missing event method remove_" ^ n) ev.cf_pos;
-					failwith "Build failed"
 				| Some add, Some remove ->
-					let check cf = try
-						type_eq EqStrict cf.cf_type tmeth
-					with Unify_error el ->
-						List.iter (fun e -> gen.gcon.error (Typecore.unify_error_msg (print_context()) e) cf.cf_pos) el;
-						failwith "Build failed";
-					in
-					check add;
-					check remove;
 					if custom && not cl.cl_interface then
 						nonprops := add :: remove :: !nonprops
+				| _ -> assert false (* shouldn't happen because Filters.check_cs_events makes sure methods are present *)
 			) events;
 
 			let evts = List.map (fun(_,v) -> !v) events in
@@ -2660,10 +2637,6 @@ let configure gen =
 				false
 	in
 
-	let module_gen w md_def =
-		List.fold_left (fun should md -> module_type_gen w md || should) false md_def.m_types
-	in
-
 	(* generate source code *)
 	init_ctx gen;
 
@@ -2699,7 +2672,7 @@ let configure gen =
 	gen.greal_type <- real_type;
 	gen.greal_type_param <- change_param_type;
 
-	SetHXGen.run_filter gen SetHXGen.default_hxgen_func;
+	SetHXGen.run_filter gen;
 
 	(* before running the filters, follow all possible types *)
 	(* this is needed so our module transformations don't break some core features *)
@@ -2729,29 +2702,17 @@ let configure gen =
 		| _ -> ()
 		) gen.gtypes_list;
 
-	let closure_t = ClosuresToClass.DoubleAndDynamicClosureImpl.get_ctx gen 6 in
-
-	(*let closure_t = ClosuresToClass.create gen 10 float_cl
-		(fun l -> l)
-		(fun l -> l)
-		(fun args -> args)
-		(fun args -> [])
-	in
-	ClosuresToClass.configure gen (ClosuresToClass.default_implementation closure_t (fun e _ _ -> e));
-
-	StubClosureImpl.configure gen (StubClosureImpl.default_implementation gen float_cl 10 (fun e _ _ -> e));*)
-
 	let tp_v = alloc_var "$type_param" t_dynamic in
 	let mk_tp t pos = { eexpr = TLocal(tp_v); etype = t; epos = pos } in
-	TypeParams.configure gen (fun ecall efield params elist ->
+	gen.gparam_func_call <- (fun ecall efield params elist ->
 		match efield.eexpr with
 		| TField(_, FEnum _) ->
-				{ ecall with eexpr = TCall(efield, elist) }
+			{ ecall with eexpr = TCall(efield, elist) }
 		| _ ->
-				{ ecall with eexpr = TCall(efield, (List.map (fun t -> mk_tp t ecall.epos ) params) @ elist) }
+			{ ecall with eexpr = TCall(efield, (List.map (fun t -> mk_tp t ecall.epos) params) @ elist) }
 	);
 
-	if not erase_generics then HardNullableSynf.configure gen (HardNullableSynf.traverse gen
+	if not erase_generics then HardNullableSynf.configure gen
 		(fun e ->
 			match e.eexpr, real_type e.etype with
 				| TConst TThis, _ when gen.gcurrent_path = (["haxe";"lang"], "Null") ->
@@ -2786,17 +2747,8 @@ let configure gen =
 			mk_field_access gen { e with etype = real_type e.etype } "hasValue" e.epos
 		)
 		(fun e1 e2 ->
-			{
-				eexpr = TCall(
-					mk_field_access gen e1 "Equals" e1.epos,
-					[e2]);
-				etype = basic.tbool;
-				epos = e1.epos;
-			}
-		)
-		true
-		false
-	);
+			mk (TCall(mk_field_access gen e1 "Equals" e1.epos, [e2])) basic.tbool e1.epos
+		);
 
 
 	let explicit_fn_name c tl fname =
@@ -2808,15 +2760,14 @@ let configure gen =
 
 	AbstractImplementationFix.configure gen;
 
-	IteratorsInterface.configure gen (fun e -> e);
-
-	OverrideFix.configure gen;
+	IteratorsInterface.configure gen;
 
-	ClosuresToClass.configure gen (ClosuresToClass.default_implementation closure_t (get_cl (get_type gen (["haxe";"lang"],"Function")) ));
+	let closure_t = ClosuresToClass.DoubleAndDynamicClosureImpl.get_ctx gen (get_cl (get_type gen (["haxe";"lang"],"Function"))) 6 in
+	ClosuresToClass.configure gen closure_t;
 
 	let enum_base = (get_cl (get_type gen (["haxe";"lang"],"Enum")) ) in
 	let param_enum_base = (get_cl (get_type gen (["haxe";"lang"],"ParamEnum")) ) in
-	EnumToClass.configure gen (Some (fun e -> mk_cast gen.gcon.basic.tint e)) true true enum_base param_enum_base false false;
+	EnumToClass.configure gen (Some (fun e -> mk_cast gen.gcon.basic.tint e)) true true enum_base param_enum_base;
 
 	InterfaceVarsDeleteModf.configure gen;
 	InterfaceProps.configure gen;
@@ -2825,20 +2776,14 @@ let configure gen =
 
 	let object_iface = get_cl (get_type gen (["haxe";"lang"],"IHxObject")) in
 
-	(*fixme: THIS IS A HACK. take this off *)
-	let empty_e = match (get_type gen (["haxe";"lang"], "EmptyObject")) with | TEnumDecl e -> e | _ -> assert false in
-	(*OverloadingCtor.set_new_create_empty gen ({eexpr=TEnumField(empty_e, "EMPTY"); etype=TEnum(empty_e,[]); epos=null_pos;});*)
+	let empty_en = match get_type gen (["haxe";"lang"], "EmptyObject") with TEnumDecl e -> e | _ -> assert false in
+	let empty_ctor_type = TEnum(empty_en, []) in
+	let empty_en_expr = mk (TTypeExpr (TEnumDecl empty_en)) (TAnon { a_fields = PMap.empty; a_status = ref (EnumStatics empty_en) }) null_pos in
+	let empty_ctor_expr = mk (TField (empty_en_expr, FEnum(empty_en, PMap.find "EMPTY" empty_en.e_constrs))) empty_ctor_type null_pos in
+	OverloadingConstructor.configure ~empty_ctor_type:empty_ctor_type ~empty_ctor_expr:empty_ctor_expr gen;
 
-	let empty_expr = { eexpr = (TTypeExpr (TEnumDecl empty_e)); etype = (TAnon { a_fields = PMap.empty; a_status = ref (EnumStatics empty_e) }); epos = null_pos } in
-	let empty_ef =
-		try
-			PMap.find "EMPTY" empty_e.e_constrs
-		with Not_found -> gen.gcon.error "Required enum field EMPTY was not found" empty_e.e_pos; assert false
-	in
-	OverloadingConstructor.configure ~empty_ctor_type:(TEnum(empty_e, [])) ~empty_ctor_expr:({ eexpr=TField(empty_expr, FEnum(empty_e, empty_ef)); etype=TEnum(empty_e,[]); epos=null_pos; }) ~supports_ctor_inheritance:false gen;
-
-	let rcf_static_find = mk_static_field_access_infer (get_cl (get_type gen (["haxe";"lang"], "FieldLookup"))) "findHash" Ast.null_pos [] in
-	let rcf_static_lookup = mk_static_field_access_infer (get_cl (get_type gen (["haxe";"lang"], "FieldLookup"))) "lookupHash" Ast.null_pos [] in
+	let rcf_static_find = mk_static_field_access_infer (get_cl (get_type gen (["haxe";"lang"], "FieldLookup"))) "findHash" null_pos [] in
+	let rcf_static_lookup = mk_static_field_access_infer (get_cl (get_type gen (["haxe";"lang"], "FieldLookup"))) "lookupHash" null_pos [] in
 
 	let rcf_static_insert, rcf_static_remove =
 		if erase_generics then begin
@@ -2847,11 +2792,11 @@ let configure gen =
 				| TAnon _ | TDynamic _ -> "Dynamic"
 				| _ -> print_endline (debug_type t); assert false
 			in
-			(fun t -> mk_static_field_access_infer (get_cl (get_type gen (["haxe";"lang"], "FieldLookup"))) ("insert" ^ get_specialized_postfix t) Ast.null_pos []),
-			(fun t -> mk_static_field_access_infer (get_cl (get_type gen (["haxe";"lang"], "FieldLookup"))) ("remove" ^ get_specialized_postfix t) Ast.null_pos [])
+			(fun t -> mk_static_field_access_infer (get_cl (get_type gen (["haxe";"lang"], "FieldLookup"))) ("insert" ^ get_specialized_postfix t) null_pos []),
+			(fun t -> mk_static_field_access_infer (get_cl (get_type gen (["haxe";"lang"], "FieldLookup"))) ("remove" ^ get_specialized_postfix t) null_pos [])
 		end else
-			(fun t -> mk_static_field_access_infer (get_cl (get_type gen (["haxe";"lang"], "FieldLookup"))) "insert" Ast.null_pos [t]),
-			(fun t -> mk_static_field_access_infer (get_cl (get_type gen (["haxe";"lang"], "FieldLookup"))) "remove" Ast.null_pos [t])
+			(fun t -> mk_static_field_access_infer (get_cl (get_type gen (["haxe";"lang"], "FieldLookup"))) "insert" null_pos [t]),
+			(fun t -> mk_static_field_access_infer (get_cl (get_type gen (["haxe";"lang"], "FieldLookup"))) "remove" null_pos [t])
 	in
 
 	let can_be_float = like_float in
@@ -2907,12 +2852,13 @@ let configure gen =
 		mk_cast ecall.etype { ecall with eexpr = TCall(infer, call_args) }
 	in
 
+	add_cast_handler gen;
 	if not erase_generics then
-		handle_type_params gen ifaces (get_cl (get_type gen (["haxe";"lang"], "IGenericObject")))
-	else begin
-		add_cast_handler gen;
-		TypeParams.RealTypeParams.RealTypeParamsModf.configure gen (TypeParams.RealTypeParams.RealTypeParamsModf.set_only_hxgeneric gen)
-	end;
+		TypeParams.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")))
+	else
+		TypeParams.RealTypeParams.RealTypeParamsModf.configure gen (TypeParams.RealTypeParams.RealTypeParamsModf.set_only_hxgeneric gen);
+
+	let flookup_cl = get_cl (get_type gen (["haxe";"lang"], "FieldLookup")) in
 
 	let rcf_ctx =
 		ReflectionCFs.new_ctx
@@ -2932,14 +2878,26 @@ let configure gen =
 				let t = gen.gclasses.nativearray_type hash_array.etype in
 				{ hash_array with eexpr = TCall(rcf_static_remove t, [hash_array; length; pos]); etype = gen.gcon.basic.tvoid }
 			)
-			false
+			(
+				let delete = mk_static_field_access_infer flookup_cl "deleteHashConflict" null_pos [] in
+				let get = mk_static_field_access_infer flookup_cl "getHashConflict" null_pos [] in
+				let set = mk_static_field_access_infer flookup_cl "setHashConflict" null_pos [] in
+				let add = mk_static_field_access_infer flookup_cl "addHashConflictNames" null_pos [] in
+				let conflict_t = TInst (get_cl (get_type gen (["haxe"; "lang"], "FieldHashConflict")), []) in
+				Some {
+					t = conflict_t;
+					get_conflict = (fun ehead ehash ename -> mk (TCall (get, [ehead; ehash; ename])) conflict_t ehead.epos);
+					set = (fun ehead ehash ename evalue -> mk (TCall (set, [ehead; ehash; ename; evalue])) basic.tvoid ehead.epos);
+					delete = (fun ehead ehash ename -> mk (TCall (delete, [ehead; ehash; ename])) basic.tbool ehead.epos);
+					add_names = (fun ehead earr -> mk (TCall (add, [ehead; earr])) basic.tvoid ehead.epos);
+				}
+			)
 	in
 
-	ReflectionCFs.UniversalBaseClass.default_config gen (get_cl (get_type gen (["haxe";"lang"],"HxObject")) ) object_iface dynamic_object;
+	ReflectionCFs.UniversalBaseClass.configure gen (get_cl (get_type gen (["haxe";"lang"],"HxObject")) ) object_iface dynamic_object;
 
-	ReflectionCFs.configure_dynamic_field_access rcf_ctx false;
+	ReflectionCFs.configure_dynamic_field_access rcf_ctx;
 
-	(* let closure_func = ReflectionCFs.implement_closure_cl rcf_ctx ( get_cl (get_type gen (["haxe";"lang"],"Closure")) ) in *)
 	let closure_cl = get_cl (get_type gen (["haxe";"lang"],"Closure")) in
 	let varargs_cl = get_cl (get_type gen (["haxe";"lang"],"VarArgsFunction")) in
 	let dynamic_name = gen.gmk_internal_name "hx" "invokeDynamic" in
@@ -2950,23 +2908,19 @@ let configure gen =
 		) cl.cl_ordered_fields
 	) [closure_cl; varargs_cl];
 
-	let closure_func = ReflectionCFs.get_closure_func rcf_ctx closure_cl in
-
 	ReflectionCFs.implement_varargs_cl rcf_ctx ( get_cl (get_type gen (["haxe";"lang"], "VarArgsBase")) );
 
-	let slow_invoke = mk_static_field_access_infer (runtime_cl) "slowCallField" Ast.null_pos [] in
+	let slow_invoke = mk_static_field_access_infer (runtime_cl) "slowCallField" null_pos [] in
 	ReflectionCFs.configure rcf_ctx ~slow_invoke:(fun ethis efield eargs -> {
 		eexpr = TCall(slow_invoke, [ethis; efield; eargs]);
 		etype = t_dynamic;
 		epos = ethis.epos;
 	} ) object_iface;
 
-	let objdecl_fn = ReflectionCFs.implement_dynamic_object_ctor rcf_ctx dynamic_object in
+	ObjectDeclMap.configure gen (ReflectionCFs.implement_dynamic_object_ctor rcf_ctx dynamic_object);
 
-	ObjectDeclMap.configure gen (ObjectDeclMap.traverse gen objdecl_fn);
-
-	InitFunction.configure gen true true;
-	TArrayTransform.configure gen (TArrayTransform.default_implementation gen (
+	InitFunction.configure gen;
+	TArrayTransform.configure gen (
 	fun e binop ->
 		match e.eexpr with
 			| TArray(e1, e2) ->
@@ -2983,7 +2937,7 @@ let configure gen =
 							true
 						| _ -> false)
 			| _ -> assert false
-	) "__get" "__set" );
+	) "__get" "__set";
 
 	let field_is_dynamic t field =
 		match field_access_esp gen (gen.greal_type t) field with
@@ -3051,7 +3005,7 @@ let configure gen =
 	in
 
 	DynamicOperators.configure gen
-		(DynamicOperators.abstract_implementation gen (fun e -> match e.eexpr with
+		(fun e -> match e.eexpr with
 			| TBinop (Ast.OpEq, e1, e2)
 			| TBinop (Ast.OpNotEq, e1, e2) ->
 				(
@@ -3120,9 +3074,10 @@ let configure gen =
 			end else begin
 				let static = mk_static_field_access_infer (runtime_cl) "compare" e1.epos [] in
 				{ eexpr = TCall(static, [e1; e2]); etype = gen.gcon.basic.tint; epos=e1.epos }
-			end) ~handle_strings:false);
+			end)
+		~handle_strings:false;
 
-	FilterClosures.configure gen (FilterClosures.traverse gen (fun e1 s -> true) closure_func);
+	FilterClosures.configure gen (fun e1 s -> true) (ReflectionCFs.get_closure_func rcf_ctx closure_cl);
 
 	let base_exception = get_cl (get_type gen (["System"], "Exception")) in
 	let base_exception_t = TInst(base_exception, []) in
@@ -3141,45 +3096,34 @@ let configure gen =
 	in
 
 	TryCatchWrapper.configure gen
-	(
-		TryCatchWrapper.traverse gen
-			(fun t -> not (is_exception (real_type t)))
-			(fun throwexpr expr ->
-				let wrap_static = mk_static_field_access (hx_exception) "wrap" (TFun([("obj",false,t_dynamic)], base_exception_t)) expr.epos in
-				{ throwexpr with eexpr = TThrow { expr with eexpr = TCall(wrap_static, [expr]); etype = hx_exception_t }; etype = gen.gcon.basic.tvoid }
-			)
-			(fun v_to_unwrap pos ->
-				let local = mk_cast hx_exception_t { eexpr = TLocal(v_to_unwrap); etype = v_to_unwrap.v_type; epos = pos } in
-				mk_field_access gen local "obj" pos
-			)
-			(fun rethrow ->
-				{ rethrow with eexpr = TCall(mk_local (alloc_var "__rethrow__" t_dynamic) rethrow.epos, [rethrow]); etype = gen.gcon.basic.tvoid }
-			)
-			(base_exception_t)
-			(hx_exception_t)
-			(fun v e ->
-
-				let exc_cl = get_cl (get_type gen (["haxe";"lang"],"Exceptions")) in
-				let exc_field = mk_static_field_access_infer exc_cl "exception" e.epos [] in
-				let esetstack = mk (TBinop(Ast.OpAssign, exc_field, mk_local v e.epos)) v.v_type e.epos in
-
-				Type.concat esetstack e;
-			)
-	);
+		(fun t -> not (is_exception (real_type t)))
+		(fun throwexpr expr ->
+			let wrap_static = mk_static_field_access (hx_exception) "wrap" (TFun([("obj",false,t_dynamic)], base_exception_t)) expr.epos in
+			{ throwexpr with eexpr = TThrow { expr with eexpr = TCall(wrap_static, [expr]); etype = hx_exception_t }; etype = gen.gcon.basic.tvoid }
+		)
+		(fun v_to_unwrap pos ->
+			let local = mk_cast hx_exception_t { eexpr = TLocal(v_to_unwrap); etype = v_to_unwrap.v_type; epos = pos } in
+			mk_field_access gen local "obj" pos
+		)
+		(fun rethrow ->
+			{ rethrow with eexpr = TCall(mk_local (alloc_var "__rethrow__" t_dynamic) rethrow.epos, [rethrow]); etype = gen.gcon.basic.tvoid }
+		)
+		(base_exception_t)
+		(hx_exception_t)
+		(fun v e ->
 
-	let get_typeof e =
-		{ e with eexpr = TCall( { eexpr = TLocal( alloc_var "__typeof__" t_dynamic ); etype = t_dynamic; epos = e.epos }, [e] ) }
-	in
+			let exc_cl = get_cl (get_type gen (["haxe";"lang"],"Exceptions")) in
+			let exc_field = mk_static_field_access_infer exc_cl "exception" e.epos [] in
+			let esetstack = mk (TBinop(Ast.OpAssign, exc_field, mk_local v e.epos)) v.v_type e.epos in
 
-	ClassInstance.configure gen (ClassInstance.traverse gen (fun e mt ->
-		get_typeof e
-	));
+			Type.concat esetstack e;
+		);
 
-	CastDetect.configure gen (CastDetect.default_implementation gen (Some (TEnum(empty_e, []))) (not erase_generics) ~native_string_cast:false ~overloads_cast_to_base:true);
+	ClassInstance.configure gen (fun e _ -> { e with eexpr = TCall({ eexpr = TLocal(alloc_var "__typeof__" t_dynamic); etype = t_dynamic; epos = e.epos }, [e]) });
 
-	(*FollowAll.configure gen;*)
+	CastDetect.configure gen (Some empty_ctor_type) (not erase_generics) ~overloads_cast_to_base:true;
 
-	SwitchToIf.configure gen (SwitchToIf.traverse gen (fun e ->
+	SwitchToIf.configure gen (fun e ->
 		match e.eexpr with
 			| TSwitch(cond, cases, def) ->
 				(match gen.gfollow#run_f cond.etype with
@@ -3191,35 +3135,34 @@ let configure gen =
 					| _ -> true
 				)
 			| _ -> assert false
-	) true ) ;
+	);
 
-	ExpressionUnwrap.configure gen (ExpressionUnwrap.traverse gen (fun e -> Some { eexpr = TVar(mk_temp gen "expr" e.etype, Some e); etype = gen.gcon.basic.tvoid; epos = e.epos }));
+	ExpressionUnwrap.configure gen (fun e -> Some { eexpr = TVar(mk_temp gen "expr" e.etype, Some e); etype = gen.gcon.basic.tvoid; epos = e.epos });
 
 	UnnecessaryCastsRemoval.configure gen;
 
-	IntDivisionSynf.configure gen (IntDivisionSynf.default_implementation gen true);
+	IntDivisionSynf.configure gen;
 
-	UnreachableCodeEliminationSynf.configure gen (UnreachableCodeEliminationSynf.traverse gen false true true false);
+	UnreachableCodeEliminationSynf.configure gen false;
 
-	ArrayDeclSynf.configure gen (ArrayDeclSynf.default_implementation gen native_arr_cl);
+	ArrayDeclSynf.configure gen native_arr_cl;
 
 	let goto_special = alloc_var "__goto__" t_dynamic in
 	let label_special = alloc_var "__label__" t_dynamic in
-	SwitchBreakSynf.configure gen (SwitchBreakSynf.traverse gen
+	SwitchBreakSynf.configure gen
 		(fun e_loop n api ->
-			api ({ eexpr = TCall( mk_local label_special e_loop.epos, [ mk_int gen n e_loop.epos ] ); etype = t_dynamic; epos = e_loop.epos }) false;
+			api ({ eexpr = TCall( mk_local label_special e_loop.epos, [ ExprBuilder.make_int gen.gcon n e_loop.epos ] ); etype = t_dynamic; epos = e_loop.epos }) false;
 			e_loop
 		)
 		(fun e_break n api ->
-			{ eexpr = TCall( mk_local goto_special e_break.epos, [ mk_int gen n e_break.epos ] ); etype = t_dynamic; epos = e_break.epos }
-		)
-	);
+			{ eexpr = TCall( mk_local goto_special e_break.epos, [ ExprBuilder.make_int gen.gcon n e_break.epos ] ); etype = t_dynamic; epos = e_break.epos }
+		);
 
-	DefaultArguments.configure gen (DefaultArguments.traverse gen);
+	DefaultArguments.configure gen;
 	InterfaceMetas.configure gen;
 
-	CSharpSpecificSynf.configure gen (CSharpSpecificSynf.traverse gen runtime_cl);
-	CSharpSpecificESynf.configure gen (CSharpSpecificESynf.traverse gen runtime_cl);
+	CSharpSpecificSynf.configure gen runtime_cl;
+	CSharpSpecificESynf.configure gen runtime_cl;
 
 	let out_files = ref [] in
 
@@ -3237,7 +3180,7 @@ let configure gen =
 			output_string f v;
 			close_out f;
 
-			out_files := (unique_full_path full_path) :: !out_files
+			out_files := (Path.unique_full_path full_path) :: !out_files
 		) gen.gcon.resources;
 	end;
 	(* add resources array *)
@@ -3246,9 +3189,9 @@ let configure gen =
 		let cf = PMap.find "content" res.cl_statics in
 		let res = ref [] in
 		Hashtbl.iter (fun name v ->
-			res := { eexpr = TConst(TString name); etype = gen.gcon.basic.tstring; epos = Ast.null_pos } :: !res;
+			res := { eexpr = TConst(TString name); etype = gen.gcon.basic.tstring; epos = null_pos } :: !res;
 		) gen.gcon.resources;
-		cf.cf_expr <- Some ({ eexpr = TArrayDecl(!res); etype = gen.gcon.basic.tarray gen.gcon.basic.tstring; epos = Ast.null_pos })
+		cf.cf_expr <- Some ({ eexpr = TArrayDecl(!res); etype = gen.gcon.basic.tarray gen.gcon.basic.tstring; epos = null_pos })
 	with | Not_found -> ());
 
 	run_filters gen;
@@ -3265,7 +3208,6 @@ let configure gen =
 	let hashes = Hashtbl.fold (fun i s acc -> incr nhash; (normalize_i i,s) :: acc) rcf_ctx.rcf_hash_fields [] in
 	let hashes = List.sort (fun (i,s) (i2,s2) -> compare i i2) hashes in
 
-	let flookup_cl = get_cl (get_type gen (["haxe";"lang"], "FieldLookup")) in
 	let haxe_libs = List.filter (function (_,_,_,lookup) -> is_some (lookup (["haxe";"lang"], "DceNo"))) gen.gcon.net_libs in
 	(try
 		(* first let's see if we're adding a -net-lib that has already a haxe.lang.FieldLookup *)
@@ -3337,12 +3279,21 @@ let configure gen =
 
 	let parts = Str.split_delim (Str.regexp "[\\/]+") gen.gcon.file in
 	mkdir_recursive "" parts;
-	generate_modules gen "cs" "src" module_gen out_files;
+
+	List.iter (fun md_def ->
+		let source_dir = gen.gcon.file ^ "/src/" ^ (String.concat "/" (fst (path_of_md_def md_def))) in
+		let w = SourceWriter.new_source_writer() in
+		let should_write = List.fold_left (fun should md -> module_type_gen w md || should) false md_def.m_types in
+		if should_write then begin
+			let path = path_of_md_def md_def in
+			write_file gen w source_dir path "cs" out_files
+		end
+	) gen.gmodules;
 
 	if not (Common.defined gen.gcon Define.KeepOldOutput) then
 		clean_files (gen.gcon.file ^ "/src") !out_files gen.gcon.verbose;
 
-	dump_descriptor gen ("hxcs_build.txt") path_s module_s;
+	dump_descriptor gen ("hxcs_build.txt") s_type_path module_s;
 	if ( not (Common.defined gen.gcon Define.NoCompilation) ) then begin
 		let old_dir = Sys.getcwd() in
 		Sys.chdir gen.gcon.file;
@@ -3371,15 +3322,15 @@ let generate con =
 		let type_cl = get_cl ( get_type gen (["System"], "Type")) in
 		let basic_fns =
 		[
-			mk_class_field "Equals" (TFun(["obj",false,t_dynamic], basic.tbool)) true Ast.null_pos (Method MethNormal) [];
-			mk_class_field "ToString" (TFun([], basic.tstring)) true Ast.null_pos (Method MethNormal) [];
-			mk_class_field "GetHashCode" (TFun([], basic.tint)) true Ast.null_pos (Method MethNormal) [];
-			mk_class_field "GetType" (TFun([], TInst(type_cl, []))) true Ast.null_pos (Method MethNormal) [];
+			mk_class_field "Equals" (TFun(["obj",false,t_dynamic], basic.tbool)) true null_pos (Method MethNormal) [];
+			mk_class_field "ToString" (TFun([], basic.tstring)) true null_pos (Method MethNormal) [];
+			mk_class_field "GetHashCode" (TFun([], basic.tint)) true null_pos (Method MethNormal) [];
+			mk_class_field "GetType" (TFun([], TInst(type_cl, []))) true null_pos (Method MethNormal) [];
 		] in
 		List.iter (fun cf -> gen.gbase_class_fields <- PMap.add cf.cf_name cf gen.gbase_class_fields) basic_fns;
 		configure gen
 	with | TypeNotFound path ->
-		con.error ("Error. Module '" ^ (path_s path) ^ "' is required and was not included in build.")	Ast.null_pos);
+		con.error ("Error. Module '" ^ (s_type_path path) ^ "' is required and was not included in build.")	null_pos);
 	debug_mode := false
 
 (* -net-lib implementation *)
@@ -3527,7 +3478,7 @@ let rec convert_signature ctx p = function
 	| _ -> mk_type_path ctx ([],[], "Dynamic") []
 
 let ilpath_s = function
-	| ns,[], name -> path_s (ns,name)
+	| ns,[], name -> s_type_path (ns,name)
 	| [],nested,name -> String.concat "." nested ^ "." ^ name
 	| ns, nested, name -> String.concat "." ns ^ "." ^ String.concat "." nested ^ "." ^ name
 
@@ -3638,7 +3589,7 @@ let convert_ilfield ctx p field =
 	let kind = match readonly with
 		| true ->
 			cff_meta := (Meta.ReadOnly, [], cff_pos) :: !cff_meta;
-			FProp ("default", "never", Some (convert_signature ctx p field.fsig.snorm,null_pos), None)
+			FProp (("default",null_pos), ("never",null_pos), Some (convert_signature ctx p field.fsig.snorm,null_pos), None)
 		| false ->
 			FVar (Some (convert_signature ctx p field.fsig.snorm,null_pos), None)
 	in
@@ -3874,7 +3825,7 @@ let convert_ilprop ctx p prop is_explicit_impl =
 	in
 
 	let kind =
-		FProp (get, set, Some(convert_signature ctx p ilsig,null_pos), None)
+		FProp ((get,null_pos), (set,null_pos), Some(convert_signature ctx p ilsig,null_pos), None)
 	in
 	{
 		cff_name = prop.pname,null_pos;
@@ -4452,7 +4403,7 @@ let add_net_lib com file std =
 			Hashtbl.iter (fun _ td ->
 				let path = IlMetaTools.get_path (TypeDef td) in
 				if PMap.mem "net_loader_debug" com.defines then
-					Printf.printf "found %s\n" (path_s (netpath_to_hx path));
+					Printf.printf "found %s\n" (s_type_path (netpath_to_hx path));
 				Hashtbl.replace com.net_path_map (netpath_to_hx path) path;
 				Hashtbl.replace meta.il_typedefs path td
 			) il_typedefs;
@@ -4484,12 +4435,12 @@ let add_net_lib com file std =
 	in
 
 	let build path =
-		let p = { pfile = !real_file ^ " @ " ^ path_s path; pmin = 0; pmax = 0; } in
+		let p = { pfile = !real_file ^ " @ " ^ s_type_path path; pmin = 0; pmax = 0; } in
 		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 then
-				Printf.printf "looking up %s\n" (path_s path);
+				Printf.printf "looking up %s\n" (s_type_path path);
 			match lookup path with
 			| Some({csuper = Some{snorm = LClass( (["System"],[],("Delegate"|"MulticastDelegate")),_)}} as cls)
 				when List.mem SSealed cls.cflags.tdf_semantics ->

文件差異過大導致無法顯示
+ 217 - 496
src/generators/genhl.ml


+ 108 - 152
src/generators/genjava.ml

@@ -17,6 +17,7 @@
 	Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  *)
 
+open Globals
 open JData
 open Unix
 open Ast
@@ -27,6 +28,7 @@ open Gencommon.SourceWriter
 open Printf
 open Option
 open ExtString
+
 module SS = Set.Make(String)
 
 let is_boxed_type t = match follow t with
@@ -154,12 +156,11 @@ let is_cl t = match follow t with
 	| TAnon(a) when is_some (anon_class t) -> true
 	| _ -> false
 
+
 (* ******************************************* *)
 (* JavaSpecificESynf *)
 (* ******************************************* *)
-
 (*
-
 	Some Java-specific syntax filters that must run before ExpressionUnwrap
 
 	dependencies:
@@ -167,13 +168,10 @@ let is_cl t = match follow t with
 		It must run before ClassInstance, as it will detect expressions that need unchanged TTypeExpr
 		It must run after CastDetect, as it changes casts
 		It must run after TryCatchWrapper, to change Std.is() calls inside there
-
 *)
 module JavaSpecificESynf =
 struct
-
 	let name = "java_specific_e"
-
 	let priority = solve_deps name [ DBefore ExpressionUnwrap.priority; DBefore ClassInstance.priority; DAfter CastDetect.priority; DAfter TryCatchWrapper.priority ]
 
 	let get_cl_from_t t =
@@ -181,7 +179,7 @@ struct
 			| TInst(cl,_) -> cl
 			| _ -> assert false
 
-	let traverse gen runtime_cl =
+	let configure gen runtime_cl =
 		let basic = gen.gcon.basic in
 		let float_cl = get_cl ( get_type gen (["java";"lang"], "Double")) in
 		let i8_md  = ( get_type gen (["java";"lang"], "Byte")) in
@@ -272,32 +270,24 @@ struct
 				(* end Std.is() *)
 				| _ -> Type.map_expr run e
 		in
-		run
-
-	let configure gen (mapping_func:texpr->texpr) =
-		let map e = Some(mapping_func e) in
+		let map e = Some(run e) in
 		gen.gsyntax_filters#add ~name:name ~priority:(PCustom priority) map
 
 end;;
 
+
 (* ******************************************* *)
 (* JavaSpecificSynf *)
 (* ******************************************* *)
-
 (*
-
 	Some Java-specific syntax filters that can run after ExprUnwrap
 
 	dependencies:
 		Runs after ExprUnwarp
-
 *)
-
 module JavaSpecificSynf =
 struct
-
 	let name = "java_specific"
-
 	let priority = solve_deps name [ DAfter ExpressionUnwrap.priority; DAfter ObjectDeclMap.priority; DAfter ArrayDeclSynf.priority; DBefore IntDivisionSynf.priority ]
 
 	let java_hash s =
@@ -337,8 +327,6 @@ struct
 			| TBlock bl -> is_final_return_block is_switch bl
 			| TSwitch (_, el_e_l, edef) ->
 				List.for_all (fun (_,e) -> is_final_return_expr e) el_e_l && Option.map_default is_final_return_expr false edef
-(*			 | TMatch (_, _, il_vl_e_l, edef) ->
-				List.for_all (fun (_,_,e) -> is_final_return_expr e)il_vl_e_l && Option.map_default is_final_return_expr false edef *)
 			| TIf (_,eif, Some eelse) ->
 				is_final_return_expr eif && is_final_return_expr eelse
 			| TFor (_,_,e) ->
@@ -564,10 +552,11 @@ struct
 
 	let get_cl_from_t t =
 		match follow t with
-			| TInst(cl,_) -> cl
-			| _ -> assert false
+		| TInst(cl,_) -> cl
+		| _ -> assert false
 
-	let traverse gen runtime_cl =
+	let configure gen runtime_cl =
+		(if java_hash "Testing string hashCode implementation from haXe" <> (Int32.of_int 545883604) then assert false);
 		let basic = gen.gcon.basic in
 		(* let tchar = mt_to_t_dyn ( get_type gen (["java"], "Char16") ) in *)
 		(* let tbyte = mt_to_t_dyn ( get_type gen (["java"], "Int8") ) in *)
@@ -696,11 +685,7 @@ struct
 					{ e with eexpr = TBinop(op, mk_cast t_empty (run e1), mk_cast t_empty (run e2)) }
 				| _ -> Type.map_expr run e
 		in
-		run
-
-	let configure gen (mapping_func:texpr->texpr) =
-		(if java_hash "Testing string hashCode implementation from haXe" <> (Int32.of_int 545883604) then assert false);
-		let map e = Some(mapping_func e) in
+		let map e = Some(run e) in
 		gen.gsyntax_filters#add ~name:name ~priority:(PCustom priority) map
 
 end;;
@@ -945,7 +930,7 @@ let configure gen =
 					pack ^ "." ^ name
 			| _ -> raise Not_found
 		with Not_found -> match path with
-			| (ns,clname) -> path_s (change_ns ns, change_clname clname)
+			| (ns,clname) -> s_type_path (change_ns ns, change_clname clname)
 	in
 
 	let cl_cl = get_cl (get_type gen (["java";"lang"],"Class")) in
@@ -1220,7 +1205,7 @@ let configure gen =
 			fun w p -> ()
 		else fun w p ->
 			let cur_line = Lexer.get_error_line p in
-			let file = Common.get_full_path p.pfile in
+			let file = Path.get_full_path p.pfile in
 			print w "//line %d \"%s\"" cur_line (Ast.s_escape file); newline w
 	in
 
@@ -1712,7 +1697,7 @@ let configure gen =
 								| [] -> acc
 								| _ -> acc) (* TODO
 								| _ -> (sprintf " where %s : %s" name (String.concat ", " (List.map (fun (cl,p) -> path_param_s (TClassDecl cl) cl.cl_path p) cl.cl_implements))) :: acc ) *)
-						| _ -> trace (t_s Ast.null_pos t); assert false (* FIXME it seems that a cl_params will never be anything other than cl.cl_params. I'll take the risk and fail if not, just to see if that confirms *)
+						| _ -> trace (t_s null_pos t); assert false (* FIXME it seems that a cl_params will never be anything other than cl.cl_params. I'll take the risk and fail if not, just to see if that confirms *)
 				) [] cl_params in
 				(params, String.concat " " params_extends)
 	in
@@ -1824,7 +1809,7 @@ let configure gen =
 						match meta with
 							| [] ->
 								let expr = match cf.cf_expr with
-									| None -> mk (TBlock([])) t_dynamic Ast.null_pos
+									| None -> mk (TBlock([])) t_dynamic null_pos
 									| Some s ->
 										match s.eexpr with
 											| TFunction tf ->
@@ -1968,7 +1953,11 @@ let configure gen =
 								| _ -> ()
 				with | Not_found -> ()
 				);
-				write w "main();";
+				(match gen.gcon.main with
+					| Some(expr) ->
+						expr_s w (mk_block expr)
+					| None ->
+						write w "main();");
 				end_block w;
 				newline w
 			| _ -> ()
@@ -2043,10 +2032,6 @@ let configure gen =
 				false
 	in
 
-	let module_gen w md =
-		module_type_gen w md
-	in
-
 	(* generate source code *)
 	init_ctx gen;
 
@@ -2061,7 +2046,7 @@ let configure gen =
 	gen.greal_type <- real_type;
 	gen.greal_type_param <- change_param_type;
 
-	SetHXGen.run_filter gen SetHXGen.default_hxgen_func;
+	SetHXGen.run_filter gen;
 
 	(* before running the filters, follow all possible types *)
 	(* this is needed so our module transformations don't break some core features *)
@@ -2084,18 +2069,6 @@ let configure gen =
 		| _ -> ()
 		) gen.gtypes_list;
 
-	let closure_t = ClosuresToClass.DoubleAndDynamicClosureImpl.get_ctx gen 6 in
-
-	(*let closure_t = ClosuresToClass.create gen 10 float_cl
-		(fun l -> l)
-		(fun l -> l)
-		(fun args -> args)
-		(fun args -> [])
-	in
-	ClosuresToClass.configure gen (ClosuresToClass.default_implementation closure_t (fun e _ _ -> e));
-
-	StubClosureImpl.configure gen (StubClosureImpl.default_implementation gen float_cl 10 (fun e _ _ -> e));*)
-
 	let get_vmtype t = match real_type t with
 		| TInst({ cl_path = ["java"],"NativeArray" }, tl) -> t
 		| TInst(c,tl) -> TInst(c,List.map (fun _ -> t_dynamic) tl)
@@ -2109,13 +2082,14 @@ let configure gen =
 	Normalize.configure gen ~metas:(Hashtbl.create 0);
 	AbstractImplementationFix.configure gen;
 
-	IteratorsInterface.configure gen (fun e -> e);
+	IteratorsInterface.configure gen;
 
-	ClosuresToClass.configure gen (ClosuresToClass.default_implementation closure_t (get_cl (get_type gen (["haxe";"lang"],"Function")) ));
+	let closure_t = ClosuresToClass.DoubleAndDynamicClosureImpl.get_ctx gen (get_cl (get_type gen (["haxe";"lang"],"Function"))) 6 in
+	ClosuresToClass.configure gen closure_t;
 
 	let enum_base = (get_cl (get_type gen (["haxe";"lang"],"Enum")) ) in
 	let param_enum_base = (get_cl (get_type gen (["haxe";"lang"],"ParamEnum")) ) in
-	EnumToClass.configure gen (None) false true enum_base param_enum_base false false;
+	EnumToClass.configure gen (None) false true enum_base param_enum_base;
 
 	InterfaceVarsDeleteModf.configure gen;
 
@@ -2123,28 +2097,22 @@ let configure gen =
 
 	let object_iface = get_cl (get_type gen (["haxe";"lang"],"IHxObject")) in
 
-	(*fixme: THIS IS A HACK. take this off *)
-	let empty_e = match (get_type gen (["haxe";"lang"], "EmptyObject")) with | TEnumDecl e -> e | _ -> assert false in
-	(*OverloadingCtor.set_new_create_empty gen ({eexpr=TEnumField(empty_e, "EMPTY"); etype=TEnum(empty_e,[]); epos=null_pos;});*)
+	let empty_en = match get_type gen (["haxe";"lang"], "EmptyObject") with TEnumDecl e -> e | _ -> assert false in
+	let empty_ctor_type = TEnum(empty_en, []) in
+	let empty_en_expr = mk (TTypeExpr (TEnumDecl empty_en)) (TAnon { a_fields = PMap.empty; a_status = ref (EnumStatics empty_en) }) null_pos in
+	let empty_ctor_expr = mk (TField (empty_en_expr, FEnum(empty_en, PMap.find "EMPTY" empty_en.e_constrs))) empty_ctor_type null_pos in
+	OverloadingConstructor.configure ~empty_ctor_type:empty_ctor_type ~empty_ctor_expr:empty_ctor_expr gen;
 
-	let empty_expr = { eexpr = (TTypeExpr (TEnumDecl empty_e)); etype = (TAnon { a_fields = PMap.empty; a_status = ref (EnumStatics empty_e) }); epos = null_pos } in
-	let empty_ef =
-		try
-			PMap.find "EMPTY" empty_e.e_constrs
-		with Not_found -> gen.gcon.error "Required enum field EMPTY was not found" empty_e.e_pos; assert false
-	in
-	OverloadingConstructor.configure ~empty_ctor_type:(TEnum(empty_e, [])) ~empty_ctor_expr:({ eexpr=TField(empty_expr, FEnum(empty_e, empty_ef)); etype=TEnum(empty_e,[]); epos=null_pos; }) ~supports_ctor_inheritance:false gen;
-
-	let rcf_static_find = mk_static_field_access_infer (get_cl (get_type gen (["haxe";"lang"], "FieldLookup"))) "findHash" Ast.null_pos [] in
-	(*let rcf_static_lookup = mk_static_field_access_infer (get_cl (get_type gen (["haxe";"lang"], "FieldLookup"))) "lookupHash" Ast.null_pos [] in*)
+	let rcf_static_find = mk_static_field_access_infer (get_cl (get_type gen (["haxe";"lang"], "FieldLookup"))) "findHash" null_pos [] in
+	(*let rcf_static_lookup = mk_static_field_access_infer (get_cl (get_type gen (["haxe";"lang"], "FieldLookup"))) "lookupHash" null_pos [] in*)
 	let get_specialized_postfix t = match t with
 		| TAbstract({a_path = [],"Float"}, _) -> "Float"
 		| TInst({cl_path = [],"String"},_) -> "String"
 		| TAnon _ | TDynamic _ -> "Dynamic"
 		| _ -> print_endline (debug_type t); assert false
 	in
-	let rcf_static_insert t = mk_static_field_access_infer (get_cl (get_type gen (["haxe";"lang"], "FieldLookup"))) ("insert" ^ get_specialized_postfix t) Ast.null_pos [] in
-	let rcf_static_remove t = mk_static_field_access_infer (get_cl (get_type gen (["haxe";"lang"], "FieldLookup"))) ("remove" ^ get_specialized_postfix t) Ast.null_pos [] in
+	let rcf_static_insert t = mk_static_field_access_infer (get_cl (get_type gen (["haxe";"lang"], "FieldLookup"))) ("insert" ^ get_specialized_postfix t) null_pos [] in
+	let rcf_static_remove t = mk_static_field_access_infer (get_cl (get_type gen (["haxe";"lang"], "FieldLookup"))) ("remove" ^ get_specialized_postfix t) null_pos [] in
 
 	let can_be_float t = like_float (real_type t) in
 
@@ -2220,33 +2188,26 @@ let configure gen =
 				let t = gen.gclasses.nativearray_type hash_array.etype in
 				{ hash_array with eexpr = TCall(rcf_static_remove t, [hash_array; length; pos]); etype = gen.gcon.basic.tvoid }
 			)
-			false
+			None
 		in
 
-	ReflectionCFs.UniversalBaseClass.default_config gen (get_cl (get_type gen (["haxe";"lang"],"HxObject")) ) object_iface dynamic_object;
+	ReflectionCFs.UniversalBaseClass.configure gen (get_cl (get_type gen (["haxe";"lang"],"HxObject")) ) object_iface dynamic_object;
 
-	ReflectionCFs.configure_dynamic_field_access rcf_ctx false;
-
-	(* let closure_func = ReflectionCFs.implement_closure_cl rcf_ctx ( get_cl (get_type gen (["haxe";"lang"],"Closure")) ) in *)
-	let closure_cl = get_cl (get_type gen (["haxe";"lang"],"Closure")) in
-
-	let closure_func = ReflectionCFs.get_closure_func rcf_ctx closure_cl in
+	ReflectionCFs.configure_dynamic_field_access rcf_ctx;
 
 	ReflectionCFs.implement_varargs_cl rcf_ctx ( get_cl (get_type gen (["haxe";"lang"], "VarArgsBase")) );
 
-	let slow_invoke = mk_static_field_access_infer (runtime_cl) "slowCallField" Ast.null_pos [] in
+	let slow_invoke = mk_static_field_access_infer (runtime_cl) "slowCallField" null_pos [] in
 	ReflectionCFs.configure rcf_ctx ~slow_invoke:(fun ethis efield eargs -> {
 		eexpr = TCall(slow_invoke, [ethis; efield; eargs]);
 		etype = t_dynamic;
 		epos = ethis.epos;
 	} ) object_iface;
 
-	let objdecl_fn = ReflectionCFs.implement_dynamic_object_ctor rcf_ctx dynamic_object in
-
-	ObjectDeclMap.configure gen (ObjectDeclMap.traverse gen objdecl_fn);
+	ObjectDeclMap.configure gen (ReflectionCFs.implement_dynamic_object_ctor rcf_ctx dynamic_object);
 
-	InitFunction.configure gen true true;
-	TArrayTransform.configure gen (TArrayTransform.default_implementation gen (
+	InitFunction.configure gen;
+	TArrayTransform.configure gen (
 	fun e _ ->
 		match e.eexpr with
 			| TArray ({ eexpr = TLocal { v_extra = Some( _ :: _, _) } }, _) -> (* captured transformation *)
@@ -2256,7 +2217,7 @@ let configure gen =
 					| TInst({ cl_path = (["java"], "NativeArray") }, _) -> false
 					| _ -> true )
 			| _ -> assert false
-	) "__get" "__set" );
+	) "__get" "__set";
 
 	let field_is_dynamic t field =
 		match field_access_esp gen (gen.greal_type t) field with
@@ -2298,7 +2259,7 @@ let configure gen =
 	let is_int t = like_int t in
 
 	DynamicOperators.configure gen
-		(DynamicOperators.abstract_implementation gen (fun e -> match e.eexpr with
+		(fun e -> match e.eexpr with
 			| TBinop (Ast.OpEq, e1, e2) ->
 				is_dynamic e1.etype || is_dynamic e2.etype || is_type_param e1.etype || is_type_param e2.etype
 			| TBinop (Ast.OpAdd, e1, e2)
@@ -2361,9 +2322,10 @@ let configure gen =
 			end else begin
 				let static = mk_static_field_access_infer (runtime_cl) "compare" e1.epos [] in
 				{ eexpr = TCall(static, [e1; e2]); etype = gen.gcon.basic.tint; epos=e1.epos }
-			end));
+			end);
 
-	FilterClosures.configure gen (FilterClosures.traverse gen (fun e1 s -> true) closure_func);
+	let closure_cl = get_cl (get_type gen (["haxe";"lang"],"Closure")) in
+	FilterClosures.configure gen (fun e1 s -> true) (ReflectionCFs.get_closure_func rcf_ctx closure_cl);
 
 	let base_exception = get_cl (get_type gen (["java"; "lang"], "Throwable")) in
 	let base_exception_t = TInst(base_exception, []) in
@@ -2382,49 +2344,35 @@ let configure gen =
 	in
 
 	TryCatchWrapper.configure gen
-	(
-		TryCatchWrapper.traverse gen
-			(fun t -> not (is_exception (real_type t)))
-			(fun throwexpr expr ->
-				let wrap_static = mk_static_field_access (hx_exception) "wrap" (TFun([("obj",false,t_dynamic)], hx_exception_t)) expr.epos in
-				{ throwexpr with eexpr = TThrow { expr with eexpr = TCall(wrap_static, [expr]); etype = hx_exception_t }; etype = gen.gcon.basic.tvoid }
-			)
-			(fun v_to_unwrap pos ->
-				let local = mk_cast hx_exception_t { eexpr = TLocal(v_to_unwrap); etype = v_to_unwrap.v_type; epos = pos } in
-				mk_field_access gen local "obj" pos
-			)
-			(fun rethrow ->
-				let wrap_static = mk_static_field_access (hx_exception) "wrap" (TFun([("obj",false,t_dynamic)], hx_exception_t)) rethrow.epos in
-				{ rethrow with eexpr = TThrow { rethrow with eexpr = TCall(wrap_static, [rethrow]); etype = hx_exception_t }; }
-			)
-			(base_exception_t)
-			(hx_exception_t)
-			(fun v e ->
-
-				let exc_cl = get_cl (get_type gen (["haxe";"lang"],"Exceptions")) in
-				let exc_field = mk_static_field_access_infer exc_cl "setException" e.epos [] in
-				let esetstack = { eexpr = TCall(exc_field,[mk_local v e.epos]); etype = gen.gcon.basic.tvoid; epos = e.epos } in
-
-				Type.concat esetstack e;
-			)
-	);
-
-	let get_typeof e =
-		{ e with eexpr = TCall( { eexpr = TLocal( alloc_var "__typeof__" t_dynamic ); etype = t_dynamic; epos = e.epos }, [e] ) }
-	in
+		(fun t -> not (is_exception (real_type t)))
+		(fun throwexpr expr ->
+			let wrap_static = mk_static_field_access (hx_exception) "wrap" (TFun([("obj",false,t_dynamic)], hx_exception_t)) expr.epos in
+			{ throwexpr with eexpr = TThrow { expr with eexpr = TCall(wrap_static, [expr]); etype = hx_exception_t }; etype = gen.gcon.basic.tvoid }
+		)
+		(fun v_to_unwrap pos ->
+			let local = mk_cast hx_exception_t { eexpr = TLocal(v_to_unwrap); etype = v_to_unwrap.v_type; epos = pos } in
+			mk_field_access gen local "obj" pos
+		)
+		(fun rethrow ->
+			let wrap_static = mk_static_field_access (hx_exception) "wrap" (TFun([("obj",false,t_dynamic)], hx_exception_t)) rethrow.epos in
+			{ rethrow with eexpr = TThrow { rethrow with eexpr = TCall(wrap_static, [rethrow]); etype = hx_exception_t }; }
+		)
+		(base_exception_t)
+		(hx_exception_t)
+		(fun v e ->
 
-	ClassInstance.configure gen (ClassInstance.traverse gen (fun e mt -> get_typeof e));
+			let exc_cl = get_cl (get_type gen (["haxe";"lang"],"Exceptions")) in
+			let exc_field = mk_static_field_access_infer exc_cl "setException" e.epos [] in
+			let esetstack = { eexpr = TCall(exc_field,[mk_local v e.epos]); etype = gen.gcon.basic.tvoid; epos = e.epos } in
 
-	(*let v = alloc_var "$type_param" t_dynamic in*)
-	TypeParams.configure gen (fun ecall efield params elist ->
-		{ ecall with eexpr = TCall(efield, elist) }
-	);
+			Type.concat esetstack e;
+		);
 
-	CastDetect.configure gen (CastDetect.default_implementation gen ~native_string_cast:false (Some (TEnum(empty_e, []))) false);
+	ClassInstance.configure gen (fun e _ -> { e with eexpr = TCall({ eexpr = TLocal(alloc_var "__typeof__" t_dynamic); etype = t_dynamic; epos = e.epos }, [e]) });
 
-	(*FollowAll.configure gen;*)
+	CastDetect.configure gen (Some empty_ctor_type) false;
 
-	SwitchToIf.configure gen (SwitchToIf.traverse gen (fun e ->
+	SwitchToIf.configure gen (fun e ->
 		match e.eexpr with
 			| TSwitch(cond, cases, def) ->
 				(match gen.gfollow#run_f cond.etype with
@@ -2437,34 +2385,33 @@ let configure gen =
 					| _ -> true
 				)
 			| _ -> assert false
-	) true );
+	);
 
-	ExpressionUnwrap.configure gen (ExpressionUnwrap.traverse gen (fun e -> Some { eexpr = TVar(mk_temp gen "expr" e.etype, Some e); etype = gen.gcon.basic.tvoid; epos = e.epos }));
+	ExpressionUnwrap.configure gen (fun e -> Some { eexpr = TVar(mk_temp gen "expr" e.etype, Some e); etype = gen.gcon.basic.tvoid; epos = e.epos });
 
 	UnnecessaryCastsRemoval.configure gen;
 
-	IntDivisionSynf.configure gen (IntDivisionSynf.default_implementation gen true);
+	IntDivisionSynf.configure gen;
 
-	UnreachableCodeEliminationSynf.configure gen (UnreachableCodeEliminationSynf.traverse gen false true true true);
+	UnreachableCodeEliminationSynf.configure gen true;
 
-	ArrayDeclSynf.configure gen (ArrayDeclSynf.default_implementation gen native_arr_cl);
+	ArrayDeclSynf.configure gen native_arr_cl;
 
 	let goto_special = alloc_var "__goto__" t_dynamic in
 	let label_special = alloc_var "__label__" t_dynamic in
-	SwitchBreakSynf.configure gen (SwitchBreakSynf.traverse gen
+	SwitchBreakSynf.configure gen
 		(fun e_loop n api ->
-			{ e_loop with eexpr = TBlock( { eexpr = TCall( mk_local label_special e_loop.epos, [ mk_int gen n e_loop.epos ] ); etype = t_dynamic; epos = e_loop.epos } :: [e_loop] ) };
+			{ e_loop with eexpr = TBlock( { eexpr = TCall( mk_local label_special e_loop.epos, [ Codegen.ExprBuilder.make_int gen.gcon n e_loop.epos ] ); etype = t_dynamic; epos = e_loop.epos } :: [e_loop] ) };
 		)
 		(fun e_break n api ->
-			{ eexpr = TCall( mk_local goto_special e_break.epos, [ mk_int gen n e_break.epos ] ); etype = t_dynamic; epos = e_break.epos }
-		)
-	);
+			{ eexpr = TCall( mk_local goto_special e_break.epos, [  Codegen.ExprBuilder.make_int gen.gcon n e_break.epos ] ); etype = t_dynamic; epos = e_break.epos }
+		);
 
-	DefaultArguments.configure gen (DefaultArguments.traverse gen);
+	DefaultArguments.configure gen;
 	InterfaceMetas.configure gen;
 
-	JavaSpecificSynf.configure gen (JavaSpecificSynf.traverse gen runtime_cl);
-	JavaSpecificESynf.configure gen (JavaSpecificESynf.traverse gen runtime_cl);
+	JavaSpecificSynf.configure gen runtime_cl;
+	JavaSpecificESynf.configure gen runtime_cl;
 
 	(* add native String as a String superclass *)
 	let str_cl = match gen.gcon.basic.tstring with | TInst(cl,_) -> cl | _ -> assert false in
@@ -2479,7 +2426,7 @@ let configure gen =
 	(* add resources array *)
 	let res = ref [] in
 	Hashtbl.iter (fun name v ->
-		res := { eexpr = TConst(TString name); etype = gen.gcon.basic.tstring; epos = Ast.null_pos } :: !res;
+		res := { eexpr = TConst(TString name); etype = gen.gcon.basic.tstring; epos = null_pos } :: !res;
 		let name = Codegen.escape_res_name name true in
 		let full_path = gen.gcon.file ^ "/src/" ^ name in
 		mkdir_from_path full_path;
@@ -2488,12 +2435,12 @@ let configure gen =
 		output_string f v;
 		close_out f;
 
-		out_files := (unique_full_path full_path) :: !out_files
+		out_files := (Path.unique_full_path full_path) :: !out_files
 	) gen.gcon.resources;
 	(try
 		let c = get_cl (Hashtbl.find gen.gtypes (["haxe"], "Resource")) in
 		let cf = PMap.find "content" c.cl_statics in
-		cf.cf_expr <- Some ({ eexpr = TArrayDecl(!res); etype = gen.gcon.basic.tarray gen.gcon.basic.tstring; epos = Ast.null_pos })
+		cf.cf_expr <- Some ({ eexpr = TArrayDecl(!res); etype = gen.gcon.basic.tarray gen.gcon.basic.tstring; epos = null_pos })
 	with | Not_found -> ());
 
 	run_filters gen;
@@ -2502,7 +2449,16 @@ let configure gen =
 
 	let parts = Str.split_delim (Str.regexp "[\\/]+") gen.gcon.file in
 	mkdir_recursive "" parts;
-	generate_modules_t gen "java" "src" change_path module_gen out_files;
+
+	let source_dir = gen.gcon.file ^ "/src" in
+	List.iter (fun md ->
+		let w = SourceWriter.new_source_writer () in
+		let should_write = module_type_gen w md in
+		if should_write then begin
+			let path = change_path (t_path md) in
+			write_file gen w (source_dir ^ "/" ^ (String.concat "/" (fst path))) path "java" out_files;
+		end
+	) gen.gtypes_list;
 
 	if not (Common.defined gen.gcon Define.KeepOldOutput) then
 		clean_files (gen.gcon.file ^ "/src") !out_files gen.gcon.verbose;
@@ -2553,19 +2509,19 @@ let generate con =
 	let cl_cl = get_cl (get_type gen (["java";"lang"],"Class")) in
 	let basic_fns =
 	[
-		mk_class_field "equals" (TFun(["obj",false,t_dynamic], basic.tbool)) true Ast.null_pos (Method MethNormal) [];
-		mk_class_field "toString" (TFun([], basic.tstring)) true Ast.null_pos (Method MethNormal) [];
-		mk_class_field "hashCode" (TFun([], basic.tint)) true Ast.null_pos (Method MethNormal) [];
-		mk_class_field "getClass" (TFun([], (TInst(cl_cl,[t_dynamic])))) true Ast.null_pos (Method MethNormal) [];
-		mk_class_field "wait" (TFun([], basic.tvoid)) true Ast.null_pos (Method MethNormal) [];
-		mk_class_field "notify" (TFun([], basic.tvoid)) true Ast.null_pos (Method MethNormal) [];
-		mk_class_field "notifyAll" (TFun([], basic.tvoid)) true Ast.null_pos (Method MethNormal) [];
+		mk_class_field "equals" (TFun(["obj",false,t_dynamic], basic.tbool)) true null_pos (Method MethNormal) [];
+		mk_class_field "toString" (TFun([], basic.tstring)) true null_pos (Method MethNormal) [];
+		mk_class_field "hashCode" (TFun([], basic.tint)) true null_pos (Method MethNormal) [];
+		mk_class_field "getClass" (TFun([], (TInst(cl_cl,[t_dynamic])))) true null_pos (Method MethNormal) [];
+		mk_class_field "wait" (TFun([], basic.tvoid)) true null_pos (Method MethNormal) [];
+		mk_class_field "notify" (TFun([], basic.tvoid)) true null_pos (Method MethNormal) [];
+		mk_class_field "notifyAll" (TFun([], basic.tvoid)) true null_pos (Method MethNormal) [];
 	] in
 	List.iter (fun cf -> gen.gbase_class_fields <- PMap.add cf.cf_name cf gen.gbase_class_fields) basic_fns;
 
 	(try
 		configure gen
-	with | TypeNotFound path -> con.error ("Error. Module '" ^ (path_s path) ^ "' is required and was not included in build.")	Ast.null_pos);
+	with | TypeNotFound path -> con.error ("Error. Module '" ^ (s_type_path path) ^ "' is required and was not included in build.")	null_pos);
 	debug_mode := false
 
 (** Java lib *)
@@ -2614,7 +2570,7 @@ let jpath_to_hx (pack,name) = match pack, name with
 	| pack, name -> normalize_pack pack, jname_to_hx name
 
 let real_java_path ctx (pack,name) =
-	path_s (pack, name)
+	s_type_path (pack, name)
 
 let lookup_jclass com path =
 	let path = jpath_to_hx path in
@@ -2831,13 +2787,13 @@ let convert_java_enum ctx p pe =
 		List.iter (fun jsig ->
 			match convert_signature ctx p jsig with
 				| CTPath path ->
-					cff_meta := (Meta.Throws, [Ast.EConst (Ast.String (path_s (path.tpackage,path.tname))), p],p) :: !cff_meta
+					cff_meta := (Meta.Throws, [Ast.EConst (Ast.String (s_type_path (path.tpackage,path.tname))), p],p) :: !cff_meta
 				| _ -> ()
 		) field.jf_throws;
 
 		let kind = match field.jf_kind with
 			| JKField when !readonly ->
-				FProp ("default", "null", Some (convert_signature ctx p field.jf_signature,null_pos), None)
+				FProp (("default",null_pos), ("null",null_pos), Some (convert_signature ctx p field.jf_signature,null_pos), None)
 			| JKField ->
 				FVar (Some (convert_signature ctx p field.jf_signature,null_pos), None)
 			| JKMethod ->
@@ -3132,7 +3088,7 @@ let jclass_with_params com cls params = try
 			cinterfaces = List.map (japply_params jparams) cls.cinterfaces;
 		}
 	with Invalid_argument("List.map2") ->
-		if com.verbose then prerr_endline ("Differing parameters for class: " ^ path_s cls.cpath);
+		if com.verbose then prerr_endline ("Differing parameters for class: " ^ s_type_path cls.cpath);
 		cls
 
 let is_object = function | TObject( (["java";"lang"], "Object"), [] ) -> true | _ -> false
@@ -3573,7 +3529,7 @@ let add_java_lib com file std =
 						if is_disallowed_inner then
 							None
 						else begin
-							if com.verbose then print_endline ("Parsed Java class " ^ (path_s cls.cpath));
+							if com.verbose then print_endline ("Parsed Java class " ^ (s_type_path cls.cpath));
 							let old_types = ctx.jtparams in
 							ctx.jtparams <- cls.ctypes :: ctx.jtparams;
 

+ 78 - 57
src/generators/genjs.ml

@@ -17,12 +17,11 @@
 	Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  *)
 
+open Globals
 open Ast
 open Type
 open Common
 
-type pos = Ast.pos
-
 type sourcemap = {
 	sources : (string) DynArray.t;
 	sources_hash : (string, int) Hashtbl.t;
@@ -42,7 +41,7 @@ type ctx = {
 	buf : Rbuffer.t;
 	chan : out_channel;
 	packages : (string list,unit) Hashtbl.t;
-	smap : sourcemap;
+	smap : sourcemap option;
 	js_modern : bool;
 	js_flatten : bool;
 	es_version : int;
@@ -71,10 +70,10 @@ let get_exposed ctx path meta =
 		(match args with
 			| [ EConst (String s), _ ] -> [s]
 			| [] -> [path]
-			| _ -> error "Invalid @:expose parameters" pos)
+			| _ -> abort "Invalid @:expose parameters" pos)
 	with Not_found -> []
 
-let dot_path = Ast.s_type_path
+let dot_path = s_type_path
 
 let flat_path (p,s) =
 	(* Replace _ with _$ in paths to prevent name collisions. *)
@@ -136,13 +135,12 @@ let static_field c s =
 let has_feature ctx = Common.has_feature ctx.com
 let add_feature ctx = Common.add_feature ctx.com
 
-let unsupported p = error "This expression cannot be compiled to Javascript" p
+let unsupported p = abort "This expression cannot be compiled to Javascript" p
 
 
-let add_mapping ctx force e =
-	if not ctx.com.debug || e.epos.pmin < 0 then () else
+let add_mapping smap force e =
+	if e.epos.pmin < 0 then () else
 	let pos = e.epos in
-	let smap = ctx.smap in
 	let file = try
 		Hashtbl.find smap.sources_hash pos.pfile
 	with Not_found ->
@@ -202,21 +200,21 @@ let add_mapping ctx force e =
 	end
 
 let handle_newlines ctx str =
-	if ctx.com.debug then
+	Option.may (fun smap ->
 		let rec loop from =
 			try begin
 				let next = String.index_from str from '\n' + 1 in
-				Rbuffer.add_char ctx.smap.mappings ';';
-				ctx.smap.output_last_col <- 0;
-				ctx.smap.output_current_col <- 0;
-				ctx.smap.print_comma <- false;
-				Option.may (fun e -> add_mapping ctx true e) ctx.smap.current_expr;
+				Rbuffer.add_char smap.mappings ';';
+				smap.output_last_col <- 0;
+				smap.output_current_col <- 0;
+				smap.print_comma <- false;
+				Option.may (fun e -> add_mapping smap true e) smap.current_expr;
 				loop next
 			end with Not_found ->
-				ctx.smap.output_current_col <- ctx.smap.output_current_col + (String.length str - from);
+				smap.output_current_col <- smap.output_current_col + (String.length str - from);
 		in
 		loop 0
-	else ()
+	) ctx.smap
 
 let flush ctx =
 	Rbuffer.output_buffer ctx.chan ctx.buf;
@@ -234,13 +232,13 @@ let print ctx =
 		Rbuffer.add_string ctx.buf s
 	end)
 
-let write_mappings ctx =
+let write_mappings ctx smap =
 	let basefile = Filename.basename ctx.com.file in
 	print ctx "\n//# sourceMappingURL=%s.map" basefile;
 	let channel = open_out_bin (ctx.com.file ^ ".map") in
-	let sources = DynArray.to_list ctx.smap.sources in
+	let sources = DynArray.to_list smap.sources in
 	let to_url file =
-		ExtString.String.map (fun c -> if c == '\\' then '/' else c) (Common.get_full_path file)
+		ExtString.String.map (fun c -> if c == '\\' then '/' else c) (Path.get_full_path file)
 	in
 	output_string channel "{\n";
 	output_string channel "\"version\":3,\n";
@@ -256,7 +254,7 @@ let write_mappings ctx =
 	end;
 	output_string channel "\"names\":[],\n";
 	output_string channel "\"mappings\":\"";
-	Rbuffer.output_buffer channel ctx.smap.mappings;
+	Rbuffer.output_buffer channel smap.mappings;
 	output_string channel "\"\n";
 	output_string channel "}";
 	close_out channel
@@ -339,15 +337,18 @@ let this ctx = match ctx.in_value with None -> "this" | Some _ -> "$this"
 
 let is_dynamic_iterator ctx e =
 	let check x =
-		has_feature ctx "HxOverrides.iter" && (match follow x.etype with
+		let rec loop t = match follow t with
 			| TInst ({ cl_path = [],"Array" },_)
 			| TInst ({ cl_kind = KTypeParameter _}, _)
 			| TAnon _
 			| TDynamic _
 			| TMono _ ->
 				true
+			| TAbstract(a,tl) when not (Meta.has Meta.CoreType a.a_meta) ->
+				loop (Abstract.get_underlying_type a tl)
 			| _ -> false
-		)
+		in
+		has_feature ctx "HxOverrides.iter" && loop x.etype
 	in
 	match e.eexpr with
 	| TField (x,f) when field_name f = "iterator" -> check x
@@ -367,7 +368,7 @@ let rec gen_call ctx e el in_value =
 	match e.eexpr , el with
 	| TConst TSuper , params ->
 		(match ctx.current.cl_super with
-		| None -> error "Missing api.setCurrentClass" e.epos
+		| None -> abort "Missing api.setCurrentClass" e.epos
 		| Some (c,_) ->
 			print ctx "%s.call(%s" (ctx.type_accessor (TClassDecl c)) (this ctx);
 			List.iter (fun p -> print ctx ","; gen_value ctx p) params;
@@ -375,7 +376,7 @@ let rec gen_call ctx e el in_value =
 		);
 	| TField ({ eexpr = TConst TSuper },f) , params ->
 		(match ctx.current.cl_super with
-		| None -> error "Missing api.setCurrentClass" e.epos
+		| None -> abort "Missing api.setCurrentClass" e.epos
 		| Some (c,_) ->
 			let name = field_name f in
 			print ctx "%s.prototype%s.call(%s" (ctx.type_accessor (TClassDecl c)) (field name) (this ctx);
@@ -470,16 +471,29 @@ let rec gen_call ctx e el in_value =
 		concat ctx "," (gen_value ctx) el;
 		spr ctx ")"
 
+(*
+	this wraps {} in parenthesis which is required to produce valid js code for field and array access to it.
+	the case itself is very rare and most probably comes from redundant code fused by the analyzer,
+	but we still have to support it.
+*)
+and add_objectdecl_parens e =
+	let rec loop e = match e.eexpr with
+		| TCast(e1,None) | TMeta(_,e1) -> loop e1 (* TODO: do we really want to lose these? *)
+		| TObjectDecl _ -> {e with eexpr = TParenthesis e}
+		| _ -> e
+	in
+	loop e
+
 and gen_expr ctx e =
-	add_mapping ctx false e;
+	Option.may (fun smap -> add_mapping smap false e) ctx.smap;
 	(match e.eexpr with
 	| TConst c -> gen_constant ctx e.epos c
 	| TLocal v -> spr ctx (ident v.v_name)
-	| TArray (e1,{ eexpr = TConst (TString s) }) when valid_js_ident s && (match e1.eexpr with TConst (TInt _|TFloat _) -> false | _ -> true) ->
-		gen_value ctx e1;
-		spr ctx (field s)
+	(*| TArray (e1,{ eexpr = TConst (TString s) }) when valid_js_ident s && (match e1.eexpr with TConst (TInt _|TFloat _) -> false | _ -> true) ->
+		gen_value ctx (add_objectdecl_parens e1);
+		spr ctx (field s)*)
 	| TArray (e1,e2) ->
-		gen_value ctx e1;
+		gen_value ctx (add_objectdecl_parens e1);
 		spr ctx "[";
 		gen_value ctx e2;
 		spr ctx "]";
@@ -499,10 +513,11 @@ and gen_expr ctx e =
 		print ctx ")";
 	| TField (x,FClosure (Some ({cl_path=[],"Array"},_), {cf_name="push"})) ->
 		(* see https://github.com/HaxeFoundation/haxe/issues/1997 *)
-		add_feature ctx "use.$arrayPushClosure";
-		print ctx "$arrayPushClosure(";
+		add_feature ctx "use.$arrayPush";
+		add_feature ctx "use.$bind";
+		print ctx "$bind(";
 		gen_value ctx x;
-		print ctx ")"
+		print ctx ",$arrayPush)"
 	| TField (x,FClosure (_,f)) ->
 		add_feature ctx "use.$bind";
 		(match x.eexpr with
@@ -524,7 +539,7 @@ and gen_expr ctx e =
 	| TField (x,f) ->
 		let rec skip e = match e.eexpr with
 			| TCast(e1,None) | TMeta(_,e1) -> skip e1
-			| TConst(TInt _ | TFloat _) -> {e with eexpr = TParenthesis e}
+			| TConst(TInt _ | TFloat _) | TObjectDecl _ -> {e with eexpr = TParenthesis e}
 			| _ -> e
 		in
 		let x = skip x in
@@ -810,7 +825,7 @@ and gen_expr ctx e =
 		spr ctx " , ";
 		spr ctx (ctx.type_accessor t);
 		spr ctx ")");
-	ctx.smap.current_expr <- None;
+	Option.may (fun smap -> smap.current_expr <- None) ctx.smap
 
 
 and gen_block_element ?(after=false) ctx e =
@@ -834,7 +849,7 @@ and gen_block_element ?(after=false) ctx e =
 		if after then newline ctx
 
 and gen_value ctx e =
-	add_mapping ctx false e;
+	Option.may (fun smap -> add_mapping smap false e) ctx.smap;
 	let assign e =
 		mk (TBinop (Ast.OpAssign,
 			mk (TLocal (match ctx.in_value with None -> assert false | Some v -> v)) t_dynamic e.epos,
@@ -946,7 +961,7 @@ and gen_value ctx e =
 			List.map (fun (v,e) -> v, block (assign e)) catchs
 		)) e.etype e.epos);
 		v());
-	ctx.smap.current_expr <- None
+	Option.may (fun smap -> smap.current_expr <- None) ctx.smap
 
 
 let generate_package_create ctx (p,_) =
@@ -979,7 +994,7 @@ let generate_package_create ctx (p,_) =
 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 JS") (match f.cf_expr with None -> c.cl_pos | Some e -> e.epos);
+		abort ("The field name '" ^ f.cf_name ^ "'  is not allowed in JS") (match f.cf_expr with None -> c.cl_pos | Some e -> e.epos);
 	| _ -> ()
 
 (* convert a.b.c to ["a"]["b"]["c"] *)
@@ -1044,7 +1059,7 @@ let generate_class ctx c =
 	ctx.current <- c;
 	ctx.id_counter <- 0;
 	(match c.cl_path with
-	| [],"Function" -> error "This class redefine a native one" c.cl_pos
+	| [],"Function" -> abort "This class redefine a native one" c.cl_pos
 	| _ -> ());
 	let p = s_path ctx c.cl_path in
 	let hxClasses = has_feature ctx "Type.resolveClass" in
@@ -1208,7 +1223,7 @@ let generate_require ctx path meta =
 	| [(EConst(String(module_name)),_) ; (EConst(String(object_path)),_)] ->
 		print ctx "%s = require(\"%s\").%s" p module_name object_path
 	| _ ->
-		error "Unsupported @:jsRequire format" mp);
+		abort "Unsupported @:jsRequire format" mp);
 
 	newline ctx
 
@@ -1242,23 +1257,29 @@ let set_current_class ctx c =
 	ctx.current <- c
 
 let alloc_ctx com =
+	let smap =
+		if com.debug || Common.defined com Define.JsSourceMap then
+			Some {
+				source_last_line = 0;
+				source_last_col = 0;
+				source_last_file = 0;
+				print_comma = false;
+				output_last_col = 0;
+				output_current_col = 0;
+				sources = DynArray.create();
+				sources_hash = Hashtbl.create 0;
+				mappings = Rbuffer.create 16;
+				current_expr = None;
+			}
+		else
+			None
+	in
 	let ctx = {
 		com = com;
 		buf = Rbuffer.create 16000;
 		chan = open_out_bin com.file;
 		packages = Hashtbl.create 0;
-		smap = {
-			source_last_line = 0;
-			source_last_col = 0;
-			source_last_file = 0;
-			print_comma = false;
-			output_last_col = 0;
-			output_current_col = 0;
-			sources = DynArray.create();
-			sources_hash = Hashtbl.create 0;
-			mappings = Rbuffer.create 16;
-			current_expr = None;
-		};
+		smap = smap;
 		js_modern = not (Common.defined com Define.JsClassic);
 		js_flatten = not (Common.defined com Define.JsUnflatten);
 		es_version = int_of_string (Common.defined_value com Define.JsEs);
@@ -1472,10 +1493,8 @@ let generate com =
 		print ctx "function $bind(o,m) { if( m == null ) return null; if( m.__id__ == null ) m.__id__ = $fid++; 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; }";
 		newline ctx;
 	end;
-	if has_feature ctx "use.$arrayPushClosure" then begin
-		print ctx "function $arrayPushClosure(a) {";
-		print ctx " return function(x) { a.push(x); }; ";
-		print ctx "}";
+	if has_feature ctx "use.$arrayPush" then begin
+		print ctx "function $arrayPush(x) { this.push(x); }";
 		newline ctx
 	end;
 	List.iter (gen_block_element ~after:true ctx) (List.rev ctx.inits);
@@ -1501,7 +1520,9 @@ let generate com =
 		) !toplevelExposed
 	);
 
-	if com.debug then write_mappings ctx else (try Sys.remove (com.file ^ ".map") with _ -> ());
+	(match ctx.smap with
+	| Some smap -> write_mappings ctx smap
+	| None -> try Sys.remove (com.file ^ ".map") with _ -> ());
 	flush ctx;
 	close_out ctx.chan)
 

文件差異過大導致無法顯示
+ 372 - 217
src/generators/genlua.ml


+ 4 - 3
src/generators/genneko.ml

@@ -18,6 +18,7 @@
  *)
 
 open Ast
+open Globals
 open Type
 open Nast
 open Common
@@ -169,7 +170,7 @@ let gen_constant ctx pe c =
 			if (h land 128 = 0) <> (h land 64 = 0) then raise Exit;
 			int p (Int32.to_int i)
 		with _ ->
-			if ctx.version < 2 then error "This integer is too big to be compiled to a Neko 31-bit integer. Please use a Float instead" pe;
+			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)
 	| TString s -> call p (field p (ident p "String") "new") [gen_big_string ctx p s]
@@ -235,7 +236,7 @@ and gen_expr ctx e =
 		(match follow e.etype with
 		| TFun (args,_) ->
 			let n = List.length args in
-			if n > 5 then error "Cannot create closure with more than 5 arguments" e.epos;
+			if n > 5 then abort "Cannot create closure with more than 5 arguments" e.epos;
 			let tmp = ident p "@tmp" in
 			EBlock [
 				(EVars ["@tmp", Some (gen_expr ctx e2); "@fun", Some (field p tmp f.cf_name)] , p);
@@ -802,7 +803,7 @@ let generate com =
 				else
 					loop (p + 1)
 			in
-			error msg (loop 0)
+			abort msg (loop 0)
 	end;
 	let command cmd = try com.run_command cmd with _ -> -1 in
 	let neko_file = (try Filename.chop_extension com.file with _ -> com.file) ^ ".neko" in

+ 11 - 10
src/generators/genphp.ml

@@ -20,6 +20,7 @@
 open Ast
 open Type
 open Common
+open Codegen
 
 type method_name = {
 	mutable mpath : path;
@@ -211,7 +212,7 @@ let is_string_expr e = is_string_type e.etype
 let to_string ctx e =
 	let v = alloc_var "__call__" t_dynamic e.epos in
 	let f = mk (TLocal v) t_dynamic e.epos in
-	mk (TCall (f, [ Codegen.string ctx.com "_hx_string_rec" e.epos; e; Codegen.string ctx.com "" e.epos])) ctx.com.basic.tstring e.epos
+	mk (TCall (f, [ ExprBuilder.make_string ctx.com "_hx_string_rec" e.epos; e; ExprBuilder.make_string ctx.com "" e.epos])) ctx.com.basic.tstring e.epos
 
 let as_string_expr ctx e =
 	match e.eexpr with
@@ -224,7 +225,7 @@ let as_string_expr ctx e =
 let to_string_null ctx e =
 	let v = alloc_var "__call__" t_dynamic e.epos in
 	let f = mk (TLocal v) t_dynamic e.epos in
-	mk (TCall (f, [ Codegen.string ctx.com "_hx_string_or_null" e.epos; e])) ctx.com.basic.tstring e.epos
+	mk (TCall (f, [ ExprBuilder.make_string ctx.com "_hx_string_or_null" e.epos; e])) ctx.com.basic.tstring e.epos
 
 
 let as_string_expr ctx e =	match e.eexpr with
@@ -343,7 +344,7 @@ let write_resource dir name data =
 	close_out ch
 
 let stack_init com use_add =
-	Codegen.stack_context_init com "GLOBALS['%s']" "GLOBALS['%e']" "__hx__spos" "tmp" use_add null_pos
+	Codegen.stack_context_init com "GLOBALS['%s']" "GLOBALS['%e']" "__hx__spos" "tmp" use_add Globals.null_pos
 
 let init com cwd path def_type =
 	let rec create acc = function
@@ -396,7 +397,7 @@ let init com cwd path def_type =
 	Codegen.map_source_header com (fun s -> print ctx "// %s\n" s);
 	ctx
 
-let unsupported msg p = error ("This expression cannot be generated to PHP: " ^ msg) p
+let unsupported msg p = abort ("This expression cannot be generated to PHP: " ^ msg) p
 
 let newline ctx =
 	match Buffer.nth ctx.buf (Buffer.length ctx.buf - 1) with
@@ -780,7 +781,7 @@ and get_constant_prefix meta =
 	(match args with
 		| [EConst(String prefix), _] -> prefix
 		| [] -> ""
-		| _ -> error "Invalid @:phpConstant parameters" pos)
+		| _ -> abort "Invalid @:phpConstant parameters" pos)
 
 and gen_member_access ctx isvar e s =
 	match follow e.etype with
@@ -987,9 +988,9 @@ and gen_tfield ctx e e1 s =
 				| _ ->
 					gen_expr ctx e1) in
 
-			spr ctx "(isset(";
-			gen_field_access ctx true e1 s;
-			spr ctx ") ? ";
+			spr ctx "(property_exists(";
+			ob e1.eexpr;
+			print ctx ", \"%s\") ? " (s_ident s);
 			gen_field_access ctx true e1 s;
 			spr ctx ": array(";
 			ob e1.eexpr;
@@ -2261,9 +2262,9 @@ let generate com =
 							else
 								((n = (prefixed_name false)) || (n = (prefixed_name true)))
 						) !lc_names in
-						unsupported ("method '" ^ (s_type_path c.cl_path) ^ "." ^ cf.cf_name ^ "' already exists here '" ^ (fst lc) ^ "' (different case?)") c.cl_pos
+						unsupported ("method '" ^ (Globals.s_type_path c.cl_path) ^ "." ^ cf.cf_name ^ "' already exists here '" ^ (fst lc) ^ "' (different case?)") c.cl_pos
 					with Not_found ->
-						lc_names := ((s_type_path c.cl_path) ^ "." ^ cf.cf_name, prefixed_name static) :: !lc_names)
+						lc_names := ((Globals.s_type_path c.cl_path) ^ "." ^ cf.cf_name, prefixed_name static) :: !lc_names)
 				| _ ->
 					()
 			) lst

+ 60 - 39
src/generators/genpy.ml

@@ -17,6 +17,7 @@
 	Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  *)
 
+open Globals
 open Ast
 open Type
 open Common
@@ -26,7 +27,7 @@ module Utils = struct
 	let class_of_module_type mt = match mt with
 		| TClassDecl c -> c
 		| _ -> failwith ("Not a class: " ^ (s_type_path (t_infos mt).mt_path))
-
+	
 	let find_type com path =
 		try
 			List.find (fun mt -> match mt with
@@ -34,7 +35,7 @@ module Utils = struct
 				| _ -> (t_infos mt).mt_path = path
 			) com.types
 		with Not_found ->
-			error (Printf.sprintf "Could not find type %s\n" (s_type_path path)) null_pos
+			abort (Printf.sprintf "Could not find type %s\n" (s_type_path path)) null_pos
 
 	let mk_static_field c cf p =
 			let ta = TAnon { a_fields = c.cl_statics; a_status = ref (Statics c) } in
@@ -78,7 +79,7 @@ module KeywordHandler = struct
 		List.iter (fun s -> Hashtbl.add h s ()) [
 			"len"; "int"; "float"; "list"; "bool"; "str"; "isinstance"; "print"; "min"; "max";
 			"hasattr"; "getattr"; "setattr"; "delattr"; "callable"; "type"; "ord"; "chr"; "iter"; "map"; "filter";
-			"tuple"; "dict"; "set"; "bytes"; "bytearray"
+			"tuple"; "dict"; "set"; "bytes"; "bytearray"; "self";
 		];
 		h
 
@@ -95,7 +96,8 @@ module KeywordHandler = struct
 		else s
 
 	let check_var_declaration v =
-		if Hashtbl.mem kwds2 v.v_name then v.v_name <- "_hx_" ^ v.v_name
+		if not (Meta.has Meta.This v.v_meta) then
+			if Hashtbl.mem kwds2 v.v_name then v.v_name <- "_hx_" ^ v.v_name
 end
 
 module Transformer = struct
@@ -123,12 +125,12 @@ module Transformer = struct
 
 	and debug_expr e =
 		let s_type = Type.s_type (print_context()) in
-		let s = Type.s_expr_pretty false "    " s_type e in
+		let s = Type.s_expr_pretty false "    " false s_type e in
 		Printf.printf "%s\n" s
 
 	and debug_expr_with_type e =
 		let s_type = Type.s_type (print_context()) in
-		let es = Type.s_expr_pretty false "    " s_type e in
+		let es = Type.s_expr_pretty false "    " false s_type e in
 		let t = s_type e.etype in
 		Printf.printf "%s : %s\n" es t
 
@@ -272,18 +274,21 @@ module Transformer = struct
 
 	let rec transform_function tf ae is_value =
 		let p = tf.tf_expr.epos in
-		let assigns = List.fold_left (fun acc (v,value) -> match value with
-			| None | Some TNull ->
-				acc
-			| Some ct ->
-				let a_local = mk (TLocal v) v.v_type p in
-				let a_null = mk (TConst TNull) v.v_type p in
-				let a_cmp = mk (TBinop(OpEq,a_local,a_null)) !t_bool p in
-				let a_value = mk (TConst(ct)) v.v_type p in
-				let a_assign = mk (TBinop(OpAssign,a_local,a_value)) v.v_type p in
-				let a_if = mk (TIf(a_cmp,a_assign,None)) !t_void p in
-				a_if :: acc
-		) [] tf.tf_args in
+		let assigns = List.fold_left (fun acc (v,value) ->
+			KeywordHandler.check_var_declaration v;
+			match value with
+				| None | Some TNull ->
+					acc
+				| Some ct ->
+					let a_local = mk (TLocal v) v.v_type p in
+					let a_null = mk (TConst TNull) v.v_type p in
+					let a_cmp = mk (TBinop(OpEq,a_local,a_null)) !t_bool p in
+					let a_value = mk (TConst(ct)) v.v_type p in
+					let a_assign = mk (TBinop(OpAssign,a_local,a_value)) v.v_type p in
+					let a_if = mk (TIf(a_cmp,a_assign,None)) !t_void p in
+					a_if :: acc
+			) [] tf.tf_args
+		in
 		let body = match assigns with
 			| [] ->
 				tf.tf_expr
@@ -308,6 +313,7 @@ module Transformer = struct
 			lift_expr fn
 
 	and transform_var_expr ae eo v =
+		KeywordHandler.check_var_declaration v;
 		let b,new_expr = match eo with
 			| None ->
 				[],None
@@ -840,9 +846,13 @@ module Transformer = struct
 		| (is_value, TBinop(OpAssignOp op,{eexpr = TField(e1,FDynamic s); etype = t},e2)) ->
 			let e = dynamic_field_read_write ae.a_next_id e1 s op e2 t in
 			transform_expr ~is_value:is_value e
-		| (is_value, TField(e1, FClosure(Some ({cl_path = [],("str" | "list")},_),cf))) ->
+		(*
+		| (is_value, TField(e1, FClosure(Some ({cl_path = [],("str")},_),cf))) ->
+
+		| (is_value, TField(e1, FClosure(Some ({cl_path = [],("list")},_),cf))) ->
 			let e = dynamic_field_read e1 cf.cf_name ae.a_expr.etype in
 			transform_expr ~is_value:is_value e
+		*)
 		| (is_value, TBinop(OpAssign, left, right))->
 			(let left = trans true [] left in
 			let right = trans true [] right in
@@ -888,7 +898,7 @@ module Transformer = struct
 			lift_expr ~blocks:blocks r
 		| (false, TTry(etry, catches)) ->
 			let etry = trans false [] etry in
-			let catches = List.map (fun(v,e) -> v, trans false [] e) catches in
+			let catches = List.map (fun(v,e) -> KeywordHandler.check_var_declaration v; v, trans false [] e) catches in
 			let blocks = List.flatten (List.map (fun (_,e) -> e.a_blocks) catches) in
 			let catches = List.map (fun(v,e) -> v, e.a_expr) catches in
 			let r = { a_expr with eexpr = TTry(etry.a_expr, catches)} in
@@ -902,7 +912,7 @@ module Transformer = struct
 			let temp_local = { a_expr with eexpr = TLocal(temp_var)} in
 			let mk_temp_assign right = { a_expr with eexpr = TBinop(OpAssign, temp_local, right)} in
 			let etry = mk_temp_assign etry in
-			let catches = List.map (fun (v,e)-> v, mk_temp_assign e) catches in
+			let catches = List.map (fun (v,e)-> KeywordHandler.check_var_declaration v; v, mk_temp_assign e) catches in
 			let new_try = { a_expr with eexpr = TTry(etry, catches)} in
 			let block = [temp_var_def; new_try; temp_local] in
 			let new_block = { a_expr with eexpr = TBlock(block)} in
@@ -1087,12 +1097,11 @@ module Printer = struct
 		let had_var_args = ref false in
 		let had_kw_args = ref false in
 		let sl = List.map (fun (v,cto) ->
-			let check_err () = if !had_var_args || !had_kw_args then error "Arguments after KwArgs/VarArgs are not allowed" p in
-			KeywordHandler.check_var_declaration v;
+			let check_err () = if !had_var_args || !had_kw_args then abort "Arguments after KwArgs/VarArgs are not allowed" p in
 			let name = handle_keywords v.v_name in
 			match follow v.v_type with
 				| TAbstract({a_path = ["python"],"KwArgs"},_) ->
-					if !had_kw_args then error "Arguments after KwArgs are not allowed" p;
+					if !had_kw_args then abort "Arguments after KwArgs are not allowed" p;
 					had_kw_args := true;
 					"**" ^ name
 				| TAbstract({a_path = ["python"],"VarArgs"},_) ->
@@ -1316,7 +1325,7 @@ module Printer = struct
 				print_call pctx e1 el e
 			| TNew(c,_,el) ->
 				let id = print_base_type (t_infos (TClassDecl c)) in
-				Printf.sprintf "%s(%s)" id (print_exprs pctx ", " el)
+				Printf.sprintf "%s(%s)" id (print_call_args pctx e el)
 			| TUnop(Not,Prefix,e1) ->
 				Printf.sprintf "(%s%s)" (print_unop Not) (print_expr pctx e1)
 			| TUnop(op,Prefix,e1) ->
@@ -1324,7 +1333,6 @@ module Printer = struct
 			| TFunction tf ->
 				print_function pctx tf None e.epos
 			| TVar (v,eo) ->
-				KeywordHandler.check_var_declaration v;
 				print_var pctx v eo
 			| TBlock [] ->
 				Printf.sprintf "pass"
@@ -1343,7 +1351,7 @@ module Printer = struct
 			| TWhile(econd,e1,NormalWhile) ->
 				Printf.sprintf "while %s:\n%s    %s" (print_expr pctx (remove_outer_parens econd)) indent (print_expr_indented e1)
 			| TWhile(econd,e1,DoWhile) ->
-				error "Currently not supported" e.epos
+				abort "Currently not supported" e.epos
 			| TTry(e1,catches) ->
 				print_try pctx e1 catches
 			| TReturn eo ->
@@ -1419,12 +1427,20 @@ module Printer = struct
 				Printf.sprintf "HxString.fromCharCode"
 			| FStatic({cl_path = ["python";"internal"],"UBuiltins"},{cf_name = s}) ->
 				s
+			| FClosure (Some(c,cf),_)  when call_override(name) && ((is_type "" "list")(TClassDecl c)) ->
+				Printf.sprintf "_hx_partial(python_internal_ArrayImpl.%s, %s)" name obj
+			| FInstance (c,_,cf) when call_override(name) && ((is_type "" "list")(TClassDecl c)) ->
+				Printf.sprintf "_hx_partial(python_internal_ArrayImpl.%s, %s)" name obj
+			| FClosure (Some(c,cf),_)  when call_override(name) && ((is_type "" "str")(TClassDecl c)) ->
+				Printf.sprintf "_hx_partial(HxString.%s, %s)" name obj
+			| FInstance (c,_,cf) when call_override(name) && ((is_type "" "str")(TClassDecl c)) ->
+				Printf.sprintf "_hx_partial(HxString.%s, %s)" name obj
 			| FInstance _ | FStatic _ ->
 				do_default ()
 			| FAnon cf when is_assign && call_override(name) ->
 				begin match follow cf.cf_type with
 					| TFun([],_) ->
-						Printf.sprintf "python_lib_FuncTools.partial(HxOverrides.%s, %s)" name obj
+						Printf.sprintf "_hx_partial(HxOverrides.%s, %s)" name obj
 					| _ ->
 						do_default()
 				end
@@ -1441,7 +1457,6 @@ module Printer = struct
 			| _ -> false
 		end in
 		let print_catch pctx i (v,e) =
-			KeywordHandler.check_var_declaration v;
 			let is_empty_expr = begin match e.eexpr with
 				| TBlock [] -> true
 				| _ -> false
@@ -1508,7 +1523,7 @@ module Printer = struct
 				let i = ref 0 in
 				let err msg =
 					let pos = { ecode.epos with pmin = ecode.epos.pmin + !i } in
-					error msg pos
+					abort msg pos
 				in
 				let regex = Str.regexp "[{}]" in
 				let rec loop m = match m with
@@ -1875,14 +1890,18 @@ module Generator = struct
 				else
 					print ctx "%s%s = %s" indent field expr_string_2
 
-	let gen_func_expr ctx e c name metas extra_args indent stat p =
+	let gen_func_expr ctx e c name metas add_self indent stat p =
 		let pctx = Printer.create_context indent ctx.com ctx.com.debug in
 		let e = match e.eexpr with
 			| TFunction(f) ->
-				let args = List.map (fun s ->
-					alloc_var s t_dynamic p,None
-				) extra_args in
-				{e with eexpr = TFunction {f with tf_args = args @ f.tf_args}}
+				let args = if add_self then
+					let v = alloc_var "self" t_dynamic p in
+					v.v_meta <- (Meta.This,[],p) :: v.v_meta;
+					(v,None) :: f.tf_args
+				else
+					f.tf_args
+				in
+				{e with eexpr = TFunction {f with tf_args = args}}
 			| _ ->
 				e
 		in
@@ -1941,7 +1960,7 @@ module Generator = struct
 
 				newline ctx;
 				newline ctx;
-				gen_func_expr ctx ef c "__init__" py_metas ["self"] "    " false cf.cf_pos
+				gen_func_expr ctx ef c "__init__" py_metas true "    " false cf.cf_pos
 			| _ ->
 				assert false
 		end
@@ -1957,7 +1976,7 @@ module Generator = struct
 				begin match cf.cf_kind with
 					| Method _ ->
 						let py_metas = filter_py_metas cf.cf_meta in
-						gen_func_expr ctx e c field py_metas ["self"] "    " false cf.cf_pos;
+						gen_func_expr ctx e c field py_metas true "    " false cf.cf_pos;
 
 					| _ ->
 						gen_expr ctx e (Printf.sprintf "# var %s" field) "    ";
@@ -2019,7 +2038,7 @@ module Generator = struct
 			let py_metas = filter_py_metas cf.cf_meta in
 			let e = match cf.cf_expr with Some e -> e | _ -> assert false in
 			newline ctx;
-			gen_func_expr ctx e c field py_metas [] "    " true cf.cf_pos;
+			gen_func_expr ctx e c field py_metas false "    " true cf.cf_pos;
 		) methods;
 
 		!has_static_methods || !has_empty_static_vars
@@ -2341,7 +2360,7 @@ module Generator = struct
 					| [(EConst(String(module_name)), _); (EConst(String(object_name)), _); (EBinop(OpAssign, (EConst(Ident("ignoreError")),_), (EConst(Ident("true")),_)),_)] ->
 						IObject (module_name,object_name), true
 					| _ ->
-						error "Unsupported @:pythonImport format" mp
+						abort "Unsupported @:pythonImport format" mp
 				in
 
 				let import = match import_type with
@@ -2443,6 +2462,8 @@ module Generator = struct
 		Transformer.init com;
 		let ctx = mk_context com in
 		Codegen.map_source_header com (fun s -> print ctx "# %s\n" s);
+		if has_feature ctx "closure_Array" || has_feature ctx "closure_String" then
+			spr ctx "from functools import partial as _hx_partial";
 		gen_imports ctx;
 		gen_resources ctx;
 		gen_types ctx;

+ 26 - 25
src/generators/genswf.ml

@@ -24,6 +24,7 @@ open Genswf9
 open Type
 open Common
 open Ast
+open Globals
 
 let rec make_tpath = function
 	| HMPath (pack,name) ->
@@ -109,7 +110,7 @@ let is_valid_path com pack name =
 		| [] ->
 			false
 		| load :: l ->
-			match load (pack,name) Ast.null_pos with
+			match load (pack,name) null_pos with
 			| None -> loop l
 			| Some (file,(_,a)) -> true
 	in
@@ -196,7 +197,7 @@ let build_class com c file =
 		match f.hlf_kind with
 		| HFVar v ->
 			if v.hlv_const then
-				cf.cff_kind <- FProp ("default","never",Some (make_type v.hlv_type,null_pos),None)
+				cf.cff_kind <- FProp (("default",null_pos),("never",null_pos),Some (make_type v.hlv_type,null_pos),None)
 			else
 				cf.cff_kind <- FVar (Some (make_dyn_type v.hlv_type,null_pos),None);
 			cf :: acc
@@ -301,7 +302,7 @@ let build_class com c file =
 			cff_doc = None;
 			cff_access = flags;
 			cff_meta = [];
-			cff_kind = if get && set then FVar (Some (make_dyn_type t,null_pos), None) else FProp ((if get then "default" else "never"),(if set then "default" else "never"),Some (make_dyn_type t,null_pos),None);
+			cff_kind = if get && set then FVar (Some (make_dyn_type t,null_pos), None) else FProp (((if get then "default" else "never"),null_pos),((if set then "default" else "never"),null_pos),Some (make_dyn_type t,null_pos),None);
 		}
 	in
 	let fields = Hashtbl.fold (fun (name,stat) t acc ->
@@ -324,7 +325,7 @@ let build_class com c file =
 			| f :: l ->
 				match f.cff_kind with
 				| FVar (Some (CTPath { tpackage = []; tname = ("String" | "Int" | "UInt") as tname },null_pos),None)
-				| FProp ("default","never",Some (CTPath { tpackage = []; tname = ("String" | "Int" | "UInt") as tname },null_pos),None) when List.mem AStatic f.cff_access ->
+				| FProp (("default",_),("never",_),Some (CTPath { tpackage = []; tname = ("String" | "Int" | "UInt") as tname },null_pos),None) when List.mem AStatic f.cff_access ->
 					if !real_type = "" then real_type := tname else if !real_type <> tname then raise Exit;
 					{
 						ec_name = f.cff_name;
@@ -363,7 +364,7 @@ let build_class com c file =
 	(path.tpackage, [(EClass class_data,pos)])
 
 let extract_data (_,tags) =
-	let t = Common.timer "read swf" in
+	let t = Common.timer ["read";"swf"] in
 	let h = Hashtbl.create 0 in
 	let rec loop_field f =
 		match f.hlf_kind with
@@ -455,7 +456,7 @@ let remove_debug_infos as3 =
 	As3hlparse.flatten (List.map loop_static hl)
 
 let parse_swf com file =
-	let t = Common.timer "read swf" in
+	let t = Common.timer ["read";"swf"] in
 	let is_swc = file_extension file = "swc" || file_extension file = "ane" in
 	let file = (try Common.find_file com file with Not_found -> failwith ((if is_swc then "SWC" else "SWF") ^ " Library not found : " ^ file)) in
 	let ch = if is_swc then begin
@@ -481,7 +482,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 && com.display = DMNone ->
+		| TActionScript3 (id,as3) when not com.debug && not com.display.DisplayMode.dms_display ->
 			t.tdata <- TActionScript3 (id,remove_debug_infos as3)
 		| _ -> ()
 	) tags;
@@ -744,7 +745,7 @@ let detect_format data p =
 	| '\xFF', i, _ when (int_of_char i) land 0xE2 = 0xE2 -> SMP3
 	| 'G', 'I', 'F' -> BGIF
 	| _ ->
-		error "Unknown file format" p
+		abort "Unknown file format" p
 
 open TTFData
 
@@ -786,7 +787,7 @@ let build_swf9 com file swc =
 		if String.length file > 5 && String.sub file 0 5 = "data:" then
 			String.sub file 5 (String.length file - 5)
 		else
-			(try Std.input_file ~bin:true file with Invalid_argument("String.create") -> error "File is too big (max 16MB allowed)" p | _  -> error "File not found" p)
+			(try Std.input_file ~bin:true file with Invalid_argument("String.create") -> abort "File is too big (max 16MB allowed)" p | _  -> abort "File not found" p)
 	in
 	let bmp = List.fold_left (fun acc t ->
 		match t with
@@ -795,8 +796,8 @@ let build_swf9 com file swc =
 				| [] -> acc
 				| (Meta.Font,(EConst (String file),p) :: args,_) :: l ->
 					let file = try Common.find_file com file with Not_found -> file in
-					let ch = try open_in_bin file with _ -> error "File not found" p in
-					let ttf = try TTFParser.parse ch with e -> error ("Error while parsing font " ^ file ^ " : " ^ Printexc.to_string e) p in
+					let ch = try open_in_bin file with _ -> abort "File not found" p in
+					let ttf = try TTFParser.parse ch with e -> abort ("Error while parsing font " ^ file ^ " : " ^ Printexc.to_string e) p in
 					close_in ch;
 					let get_string e = match fst e with
 						| EConst (String s) -> Some s
@@ -814,7 +815,7 @@ let build_swf9 com file swc =
 						| _ :: [e] ->
 							begin match fst e with
 								| EObjectDecl fl ->
-									begin try ttf_config.ttfc_font_name <- get_string (List.assoc "fontName" fl)
+									begin try ttf_config.ttfc_font_name <- get_string (Expr.field_assoc "fontName" fl)
 									with Not_found -> () end
 								| _ ->
 									()
@@ -874,10 +875,10 @@ let build_swf9 com file swc =
 					let adata = load_file_data afile p2 in
 					(match detect_format ddata p1 with
 					| BJPG -> ()
-					| _ -> error "RGB channel must be a JPG file" p1);
+					| _ -> abort "RGB channel must be a JPG file" p1);
 					(match detect_format adata p2 with
 					| BPNG -> ()
-					| _ -> error "Alpha channel must be a PNG file" p2);
+					| _ -> abort "Alpha channel must be a PNG file" p2);
 					let png = Png.parse (IO.input_string adata) in
 					let h = Png.header png in
 					let amask = (match h.Png.png_color with
@@ -889,7 +890,7 @@ let build_swf9 com file swc =
 								String.unsafe_set alpha i (String.unsafe_get raw_data (i lsl 2));
 							done;
 							Extc.zip alpha
-						| _ -> error "PNG file must contain 8 bit alpha channel" p2
+						| _ -> abort "PNG file must contain 8 bit alpha channel" p2
 					) in
 					incr cid;
 					classes := { f9_cid = Some !cid; f9_classname = s_type_path c.cl_path } :: !classes;
@@ -928,9 +929,9 @@ let build_swf9 com file swc =
 								let data = IO.nread i data_size in
 								make_flags 0 (chan = 1) freq bits, (data_size * 8 / (chan * bits)), data
 							with Exit | IO.No_more_input | IO.Overflow _ ->
-								error "Invalid WAV file" p
+								abort "Invalid WAV file" p
 							| Failure msg ->
-								error ("Invalid WAV file (" ^ msg ^ ")") p
+								abort ("Invalid WAV file (" ^ msg ^ ")") p
 							)
 						| SMP3 ->
 							(try
@@ -983,12 +984,12 @@ let build_swf9 com file swc =
 								read_frame();
 								make_flags 2 !mono !sampling 16, (!samples), ("\x00\x00" ^ data)
 							with Exit | IO.No_more_input | IO.Overflow _ ->
-								error "Invalid MP3 file" p
+								abort "Invalid MP3 file" p
 							| Failure msg ->
-								error ("Invalid MP3 file (" ^ msg ^ ")") p
+								abort ("Invalid MP3 file (" ^ msg ^ ")") p
 							)
 						| _ ->
-							error "Sound extension not supported (only WAV or MP3)" p
+							abort "Sound extension not supported (only WAV or MP3)" p
 					) in
 					incr cid;
 					classes := { f9_cid = Some !cid; f9_classname = s_type_path c.cl_path } :: !classes;
@@ -1024,7 +1025,7 @@ let merge com file priority (h1,tags1) (h2,tags2) =
 				let path = parse_path e.exp_name in
 				let b = List.exists (fun t -> t_path t = path) com.types in
 				if not b && fst path = [] then List.iter (fun t ->
-					if snd (t_path t) = snd path then error ("Linkage name '" ^ snd path ^ "' in '" ^ file ^  "' should be '" ^ s_type_path (t_path t) ^"'") (t_infos t).mt_pos;
+					if snd (t_path t) = snd path then abort ("Linkage name '" ^ snd path ^ "' in '" ^ file ^  "' should be '" ^ s_type_path (t_path t) ^"'") (t_infos t).mt_pos;
 				) com.types;
 				b
 			) el in
@@ -1100,9 +1101,9 @@ let generate swf_header com =
 								if Meta.has Meta.Bind c.cl_meta then
 									toremove := (t_path t) :: !toremove
 								else
-									error ("Class already exists in '" ^ file ^ "', use @:bind to redefine it") (t_infos t).mt_pos
+									abort ("Class already exists in '" ^ file ^ "', use @:bind to redefine it") (t_infos t).mt_pos
 							| _ ->
-								error ("Invalid redefinition of class defined in '" ^ file ^ "'") (t_infos t).mt_pos
+								abort ("Invalid redefinition of class defined in '" ^ file ^ "'") (t_infos t).mt_pos
 					) com.types;
 				) el
 			| _ -> ()
@@ -1140,7 +1141,7 @@ let generate swf_header com =
 	let fattr = if Common.defined com Define.AdvancedTelemetry then fattr @ [tag (TUnknown (0x5D,"\x00\x00"))] else fattr in
 	let swf_script_limits = try
 		let s = Common.defined_value com Define.SwfScriptTimeout in
-		let i = try int_of_string s with _ -> error "Argument to swf_script_timeout must be an integer" Ast.null_pos in
+		let i = try int_of_string s with _ -> abort "Argument to swf_script_timeout must be an integer" null_pos in
 		[tag(TScriptLimits (256, if i < 0 then 0 else if i > 65535 then 65535 else i))]
 	with Not_found ->
 		[]
@@ -1164,7 +1165,7 @@ let generate swf_header com =
 		{header with h_frame_count = header.h_frame_count + 1},loop tags
 	| _ -> swf in
 	(* write swf/swc *)
-	let t = Common.timer "write swf" in
+	let t = Common.timer ["write";"swf"] in
 	let level = (try int_of_string (Common.defined_value com Define.SwfCompressLevel) with Not_found -> 9) in
 	SwfParser.init Extc.input_zip (Extc.output_zip ~level);
 	(match swc with

+ 25 - 27
src/generators/genswf9.ml

@@ -17,6 +17,7 @@
 	Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  *)
 
+open Globals
 open Ast
 open Type
 open As3
@@ -107,8 +108,8 @@ let rec follow t = match Type.follow t with
 	| t ->
 		t
 
-let invalid_expr p = error "Invalid expression" p
-let stack_error p = error "Stack error" p
+let invalid_expr p = abort "Invalid expression" p
+let stack_error p = abort "Stack error" p
 
 let index_int (x : int) : 'a index = Obj.magic (x + 1)
 let index_nz_int (x : int) : 'a index_nz = Obj.magic x
@@ -225,8 +226,6 @@ let rec type_id ctx t =
 			(match l with
 			| [t] -> type_id ctx t
 			| _ -> type_path ctx ([],"Object"))
-		| KExtension (c,params) ->
-			type_id ctx (TInst (c,params))
 		| _ ->
 			type_path ctx c.cl_path)
 	| TAbstract (a,_) ->
@@ -346,13 +345,6 @@ let property ctx p t =
 			| "ffloor" | "fceil" | "fround" -> ident (String.sub p 1 (String.length p - 1)), None, false
 			| _ -> ident p, None, false)
 		| _ -> ident p, None, false)
-	| TInst ({ cl_kind = KExtension _ } as c,params) ->
-		(* cast type when accessing an extension field *)
-		(try
-			let f = PMap.find p c.cl_fields in
-			ident p, Some (classify ctx (apply_params c.cl_params params f.cf_type)), false
-		with Not_found ->
-			ident p, None, false)
 	| TInst ({ cl_interface = true } as c,_) ->
 		(* lookup the interface in which the field was actually declared *)
 		let rec loop c =
@@ -495,7 +487,7 @@ let define_local ctx ?(init=false) v p =
 let is_set v = (Obj.magic v) = Write
 
 let gen_local_access ctx v p (forset : 'a)  : 'a access =
-	match snd (try PMap.find v.v_id ctx.locals with Not_found -> error ("Unbound variable " ^ v.v_name) p) with
+	match snd (try PMap.find v.v_id ctx.locals with Not_found -> abort ("Unbound variable " ^ v.v_name) p) with
 	| LReg r ->
 		VReg r
 	| LScope n ->
@@ -617,7 +609,7 @@ let debug_infos ?(is_min=true) ctx p =
 	if ctx.debug then begin
 		let line = Lexer.get_error_line (if is_min then p else { p with pmin = p.pmax }) in
 		if ctx.last_file <> p.pfile then begin
-			write ctx (HDebugFile (if ctx.debugger then Common.get_full_path p.pfile else p.pfile));
+			write ctx (HDebugFile (if ctx.debugger then Path.get_full_path p.pfile else p.pfile));
 			ctx.last_file <- p.pfile;
 			ctx.last_line <- -1;
 		end;
@@ -718,7 +710,7 @@ let begin_fun ctx args tret el stat p =
 			| TInt i -> if kind = KUInt then HVUInt i else HVInt i
 			| TFloat s -> HVFloat (float_of_string s)
 			| TBool b -> HVBool b
-			| TNull -> error ("In Flash9, null can't be used as basic type " ^ s_type (print_context()) t) p
+			| TNull -> abort ("In Flash9, null can't be used as basic type " ^ s_type (print_context()) t) p
 			| _ -> assert false)
 		| _, Some TNull -> HVNone
 		| k, Some c ->
@@ -893,7 +885,7 @@ let rec gen_access ctx e (forset : 'a) : 'a access =
 	| TField (e1,fa) ->
 		let f = field_name fa in
 		let id, k, closure = property ctx f e1.etype in
-		if closure && not ctx.for_call then error "In Flash9, this method cannot be accessed this way : please define a local function" e1.epos;
+		if closure && not ctx.for_call then abort "In Flash9, this method cannot be accessed this way : please define a local function" e1.epos;
 		(match e1.eexpr with
 		| TConst (TThis|TSuper) when not ctx.in_static ->
 			write ctx (HFindProp id)
@@ -1081,6 +1073,10 @@ let rec gen_expr_content ctx retval e =
 	| TNew ({ cl_path = [],"Array" },_,[]) ->
 		(* it seems that [] is 4 time faster than new Array() *)
 		write ctx (HArray 0)
+	| TNew ({ cl_path = ["flash"],"Vector" },[t],_) when (match follow t with TInst({ cl_kind = KTypeParameter _ },_) -> true | _ -> false) ->
+		write ctx (HString "Cannot create Vector without knowing runtime type");
+		write ctx HThrow;
+		no_value ctx retval
 	| TNew (c,tl,pl) ->
 		let id = type_id ctx (TInst (c,tl)) in
 		(match id with
@@ -1128,7 +1124,7 @@ let rec gen_expr_content ctx retval e =
 	| TUnop (op,flag,e) ->
 		gen_unop ctx retval op flag e
 	| TTry (e2,cases) ->
-		if ctx.infos.istack <> 0 then error "Cannot compile try/catch as a right-side expression in Flash9" e.epos;
+		if ctx.infos.istack <> 0 then abort "Cannot compile try/catch as a right-side expression in Flash9" e.epos;
 		let branch = begin_branch ctx in
 		let p = ctx.infos.ipos in
 		gen_expr ctx retval e2;
@@ -1380,8 +1376,8 @@ and gen_call ctx retval e el r =
 		gen_expr ctx true counter;
 		write ctx HForIn
 	| TLocal { v_name = "__has_next__" }, [obj;counter] ->
-		let oreg = match gen_access ctx obj Read with VReg r -> r | _ -> error "Must be a local variable" obj.epos in
-		let creg = match gen_access ctx counter Read with VReg r -> r | _ -> error "Must be a local variable" obj.epos in
+		let oreg = match gen_access ctx obj Read with VReg r -> r | _ -> abort "Must be a local variable" obj.epos in
+		let creg = match gen_access ctx counter Read with VReg r -> r | _ -> abort "Must be a local variable" obj.epos in
 		write ctx (HNext (oreg.rid,creg.rid))
 	| TLocal { v_name = "__hkeys__" }, [e2]
 	| TLocal { v_name = "__foreach__" }, [e2]
@@ -1572,7 +1568,7 @@ and check_binop ctx e1 e2 =
 	let invalid = (match classify ctx e1.etype, classify ctx e2.etype with
 	| KInt, KUInt | KUInt, KInt -> (match e1.eexpr, e2.eexpr with TConst (TInt i) , _ | _ , TConst (TInt i) -> i < 0l | _ -> true)
 	| _ -> false) in
-	if invalid then error "Comparison of Int and UInt might lead to unexpected results" (punion e1.epos e2.epos);
+	if invalid then abort "Comparison of Int and UInt might lead to unexpected results" (punion e1.epos e2.epos);
 
 and gen_binop ctx retval op e1 e2 t p =
 	let write_op op =
@@ -1887,12 +1883,12 @@ let generate_class_init ctx c hc =
 	| None -> ()
 	| Some e ->
 		gen_expr ctx false e;
-		if ctx.block_vars <> [] then error "You can't have a local variable referenced from a closure inside __init__ (FP 10.1.53 crash)" e.epos;
+		if ctx.block_vars <> [] then abort "You can't have a local variable referenced from a closure inside __init__ (FP 10.1.53 crash)" e.epos;
 	);
 	generate_class_statics ctx c true;
 	if ctx.swc then begin
 		generate_class_statics ctx c false;
-		if ctx.block_vars <> [] then error "You can't have a local variable referenced from a closure inside a static (FP 10.1.53 crash)" c.cl_pos;
+		if ctx.block_vars <> [] then abort "You can't have a local variable referenced from a closure inside a static (FP 10.1.53 crash)" c.cl_pos;
 	end
 
 let generate_enum_init ctx e hc meta =
@@ -1936,7 +1932,7 @@ let extract_meta meta =
 				match a with
 				| EConst (String s) -> (None, s)
 				| EBinop (OpAssign,(EConst (Ident n),_),(EConst (String s),_)) -> (Some n, s)
-				| _ -> error "Invalid meta definition" p
+				| _ -> abort "Invalid meta definition" p
 			in
 			{ hlmeta_name = n; hlmeta_data = Array.of_list (List.map mk_arg args) } :: loop l
 		| _ :: l -> loop l
@@ -2017,7 +2013,7 @@ let check_constructor ctx c f =
 		match e.eexpr with
 		| TCall ({ eexpr = TConst TSuper },_) -> raise Exit
 		| TBinop (OpAssign,{ eexpr = TField({ eexpr = TConst TThis },FInstance (cc,_,cf)) },_) when c != cc && (match classify ctx cf.cf_type with KFloat | KDynamic -> true | _ -> false) ->
-			error "You cannot assign some super class vars before calling super() in flash, this will reset them to default value" e.epos
+			abort "You cannot assign some super class vars before calling super() in flash, this will reset them to default value" e.epos
 		| _ -> ()
 	in
 	(* only do so if we have a call to super() *)
@@ -2125,9 +2121,11 @@ let generate_class ctx c =
 				cf_meta = [];
 				cf_doc = None;
 				cf_pos = c.cl_pos;
+				cf_name_pos = null_pos;
 				cf_type = TFun ([],t_dynamic);
 				cf_params = [];
 				cf_expr = None;
+				cf_expr_unoptimized = None;
 				cf_kind = Method MethNormal;
 				cf_overloads = [];
 			} false;
@@ -2180,7 +2178,7 @@ let generate_class ctx c =
 		hlc_interface = c.cl_interface;
 		hlc_namespace = (match !has_protected with None -> None | Some p -> Some (HNProtected p));
 		hlc_implements = Array.of_list (List.map (fun (c,_) ->
-			if not c.cl_interface then error "Can't implement class in Flash9" c.cl_pos;
+			if not c.cl_interface then abort "Can't implement class in Flash9" c.cl_pos;
 			let pack, name = real_path c.cl_path in
 			HMMultiName (Some name,[HNPublic (Some (String.concat "." pack))])
 		) c.cl_implements);
@@ -2333,7 +2331,7 @@ let rec generate_type ctx t =
 				hlf_metas = extract_meta e.e_meta;
 			})
 	| TAbstractDecl ({ a_path = [],"Dynamic" } as a) ->
-		generate_type ctx (TClassDecl (mk_class a.a_module a.a_path a.a_pos))
+		generate_type ctx (TClassDecl (mk_class a.a_module a.a_path a.a_pos null_pos))
 	| TTypeDecl _ | TAbstractDecl _ ->
 		None
 
@@ -2341,8 +2339,8 @@ let resource_path name =
 	(["_res"],"_" ^ String.concat "_" (ExtString.String.nsplit name "."))
 
 let generate_resource ctx name =
-	let c = mk_class null_module (resource_path name) null_pos in
-	c.cl_super <- Some (mk_class null_module (["flash";"utils"],"ByteArray") null_pos,[]);
+	let c = mk_class null_module (resource_path name) null_pos null_pos in
+	c.cl_super <- Some (mk_class null_module (["flash";"utils"],"ByteArray") null_pos null_pos,[]);
 	let t = TClassDecl c in
 	match generate_type ctx t with
 	| Some (m,f) -> (t,m,f)

+ 6 - 5
src/generators/genxml.ml

@@ -17,6 +17,7 @@
 	Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  *)
 
+open Globals
 open Ast
 open Type
 open Common
@@ -85,7 +86,7 @@ let gen_meta meta =
 	| [] -> []
 	| _ ->
 		let nodes = List.map (fun (m,el,_) ->
-			node "m" ["n",fst (MetaInfo.to_string m)] (List.map (fun e -> node "e" [] [gen_string (Ast.s_expr e)]) el)
+			node "m" ["n",Meta.to_string m] (List.map (fun e -> node "e" [] [gen_string (Ast.s_expr e)]) el)
 		) meta in
 		[node "meta" [] nodes]
 
@@ -285,10 +286,10 @@ let rec write_xml ch tabs x =
 		IO.printf ch "<![CDATA[%s]]>" s
 
 let generate com file =
-	let t = Common.timer "construct xml" in
+	let t = Common.timer ["generate";"xml"] in
 	let x = node "haxe" [] (List.map (gen_type_decl com true) (List.filter (fun t -> not (Meta.has Meta.NoDoc (t_infos t).mt_meta)) com.types)) in
 	t();
-	let t = Common.timer "write xml" in
+	let t = Common.timer ["write";"xml"] in
 	let ch = IO.output_channel (open_out_bin file) in
 	write_xml ch "" x;
 	IO.close_out ch;
@@ -425,8 +426,8 @@ let generate_type com t =
 			| Meta.DefParam | Meta.CoreApi | Meta.Used | Meta.MaybeUsed | Meta.FlatEnum | Meta.Value | Meta.DirectlyUsed -> ()
 			| _ ->
 			match pl with
-			| [] -> p "@%s " (fst (MetaInfo.to_string m))
-			| l -> p "@%s(%s) " (fst (MetaInfo.to_string m)) (String.concat "," (List.map Ast.s_expr pl))
+			| [] -> p "@%s " (Meta.to_string m)
+			| l -> p "@%s(%s) " (Meta.to_string m) (String.concat "," (List.map Ast.s_expr pl))
 		) ml
 	in
 	let access is_read a = s_access is_read a in

+ 1218 - 0
src/generators/hl2c.ml

@@ -0,0 +1,1218 @@
+(*
+ * Copyright (C)2005-2016 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.
+ *)
+open Hlcode
+
+type comparison =
+	| CEq
+	| CNeq
+	| CLt
+	| CGt
+	| CLte
+	| CGte
+
+type output_options =
+	| OOLabel
+	| OOCase of int
+	| OODefault
+	| OOIncreaseIndent
+	| OODecreaseIndent
+	| OOBeginBlock
+	| OOEndBlock
+
+let c_kwds = [
+"auto";"break";"case";"char";"const";"continue";"default";"do";"double";"else";"enum";"extern";"float";"for";"goto";
+"if";"int";"long";"register";"return";"short";"signed";"sizeof";"static";"struct";"switch";"typedef";"union";"unsigned";
+"void";"volatile";"while";
+(* MS specific *)
+"__asm";"dllimport2";"__int8";"naked2";"__based1";"__except";"__int16";"__stdcall";"__cdecl";"__fastcall";"__int32";
+"thread2";"__declspec";"__finally";"__int64";"__try";"dllexport2";"__inline";"__leave";"asm";
+(* reserved by HLC *)
+"t";
+(* C11 *)
+"_Alignas";"_Alignof";"_Atomic";"_Bool";"_Complex";"_Generic";"_Imaginary";"_Noreturn";"_Static_assert";"_Thread_local";"_Pragma";
+"inline";"restrict"
+]
+
+let s_comp = function
+	| CLt -> "<"
+	| CGt -> ">"
+	| CEq -> "=="
+	| CLte -> "<="
+	| CGte -> ">="
+	| CNeq -> "!="
+
+let core_types =
+	let vp = { vfields = [||]; vindex = PMap.empty } in
+	let ep = { ename = ""; eid = 0; eglobal = None; efields = [||] } in
+	[HVoid;HUI8;HUI16;HI32;HF32;HF64;HBool;HBytes;HDyn;HFun ([],HVoid);HObj null_proto;HArray;HType;HRef HVoid;HVirtual vp;HDynObj;HAbstract ("",0);HEnum ep;HNull HVoid]
+
+let write_c version file (code:code) =
+	let tabs = ref "" in
+	let file_count = ref 1 in
+	let line_count = ref 0 in
+	let main_ch = IO.output_channel (open_out_bin file) in
+	let ch = ref main_ch in
+	let end_ch = ref [(fun() -> IO.close_out main_ch)] in
+	let block() = tabs := !tabs ^ "\t" in
+	let unblock() = tabs := String.sub (!tabs) 0 (String.length (!tabs) - 1) in
+	let line str =
+		incr line_count;
+		IO.write_line !ch (!tabs ^ str)
+	in
+	let expr str = line (str ^ ";") in
+	let sexpr fmt = Printf.ksprintf expr fmt in
+	let sline fmt = Printf.ksprintf line fmt in
+	let sprintf = Printf.sprintf in
+
+	let flush_file() =
+		if !line_count > 60000 then begin
+			incr file_count;
+			let nfile = String.sub file 0 (String.length file - 2) ^ string_of_int !file_count ^ ".c" in
+			ch := main_ch;
+			sline "#include \"%s\"" (match List.rev (ExtString.String.nsplit (String.concat "/" (ExtString.String.nsplit nfile "\\")) "/") with file :: _ -> file | _ -> assert false);
+			let nch = IO.output_channel (open_out_bin nfile) in
+ 			ch := nch;
+			sline "#ifdef HLC_H";
+			end_ch := (fun() -> IO.write_line nch "#endif"; IO.close_out nch) :: !end_ch;
+			line_count := 0;
+		end
+	in
+
+	let hash_cache = Hashtbl.create 0 in
+	let hash sid =
+		try
+			Hashtbl.find hash_cache sid
+		with Not_found ->
+			let h = hl_hash code.strings.(sid) in
+			Hashtbl.add hash_cache sid h;
+			h
+	in
+
+	let keywords =
+		let h = Hashtbl.create 0 in
+		List.iter (fun i -> Hashtbl.add h i ()) c_kwds;
+		h
+	in
+
+	let ident i = if Hashtbl.mem keywords i then "_" ^ i else i in
+
+	let tname str =
+		let n = String.concat "__" (ExtString.String.nsplit str ".") in
+		if Hashtbl.mem keywords ("_" ^ n) then "__" ^ n else n
+	in
+
+	let is_gc_ptr = function
+		| HVoid | HUI8 | HUI16 | HI32 | HF32 | HF64 | HBool | HType | HRef _ -> false
+		| HBytes | HDyn | HFun _ | HObj _ | HArray | HVirtual _ | HDynObj | HAbstract _ | HEnum _ | HNull _ -> true
+	in
+
+	let is_ptr = function
+		| HVoid | HUI8 | HUI16 | HI32 | HF32 | HF64 | HBool -> false
+		| _ -> true
+	in
+
+	let rec ctype_no_ptr = function
+		| HVoid -> "void",0
+		| HUI8 -> "unsigned char",0
+		| HUI16 -> "unsigned short",0
+		| HI32 -> "int",0
+		| HF32 -> "float",0
+		| HF64 -> "double",0
+		| HBool -> "bool",0
+		| HBytes -> "vbyte",1
+		| HDyn -> "vdynamic",1
+		| HFun _ -> "vclosure",1
+		| HObj p -> tname p.pname,0
+		| HArray -> "varray",1
+		| HType -> "hl_type",1
+		| HRef t -> let s,i = ctype_no_ptr t in s,i + 1
+		| HVirtual _ -> "vvirtual",1
+		| HDynObj -> "vdynobj",1
+		| HAbstract (name,_) -> name,1
+		| HEnum _ -> "venum",1
+		| HNull _ -> "vdynamic",1
+	in
+
+	let ctype t =
+		let t, nptr = ctype_no_ptr t in
+		if nptr = 0 then t else t ^ String.make nptr '*'
+	in
+
+	let type_id t =
+		match t with
+		| HVoid -> "HVOID"
+		| HUI8 -> "HUI8"
+		| HUI16 -> "HUI16"
+		| HI32 -> "HI32"
+		| HF32 -> "HF32"
+		| HF64 -> "HF64"
+		| HBool -> "HBOOL"
+		| HBytes -> "HBYTES"
+		| HDyn -> "HDYN"
+		| HFun _ -> "HFUN"
+		| HObj _ -> "HOBJ"
+		| HArray -> "HARRAY"
+		| HType -> "HTYPE"
+		| HRef _ -> "HREF"
+		| HVirtual _ -> "HVIRTUAL"
+		| HDynObj -> "HDYNOBJ"
+		| HAbstract _ -> "HABSTRACT"
+		| HEnum _ -> "HENUM"
+		| HNull _ -> "HNULL"
+	in
+
+	let var_type n t =
+		ctype t ^ " " ^ ident n
+	in
+
+	let version_major = version / 1000 in
+	let version_minor = (version mod 1000) / 100 in
+	let version_revision = (version mod 100) in
+	let ver_str = Printf.sprintf "%d.%d.%d" version_major version_minor version_revision in
+	line ("// Generated by HLC " ^ ver_str ^ " (HL v" ^ string_of_int code.version ^")");
+	line "#define HLC_BOOT";
+	line "#include <hlc.h>";
+	let all_types, htypes = gather_types code in
+	let tfuns = Array.create (Array.length code.functions + Array.length code.natives) ([],HVoid) in
+	let funnames = Array.create (Array.length code.functions + Array.length code.natives) "" in
+
+	let cast_fun s args t =
+		sprintf "((%s (*)(%s))%s)" (ctype t) (String.concat "," (List.map ctype args)) s
+	in
+
+	let enum_constr_type e i =
+		let cname,_, tl = e.efields.(i) in
+		if Array.length tl = 0 then
+			"venum"
+		else
+		let name = if e.eid = 0 then
+			let index = (try PMap.find (HEnum e) htypes with Not_found -> assert false) in
+			"Enum$" ^ string_of_int index
+		else
+			String.concat "_" (ExtString.String.nsplit e.ename ".")
+		in
+		if cname = "" then
+			name
+		else
+			name ^ "_" ^ cname
+	in
+
+	let dyn_value_field t =
+		"->v." ^ match t with
+		| HUI8 -> "ui8"
+		| HUI16 -> "ui16"
+		| HI32 -> "i"
+		| HF32 -> "f"
+		| HF64 -> "d"
+		| HBool -> "b"
+		| _ -> "ptr"
+	in
+
+	let used_closures = Hashtbl.create 0 in
+	let bytes_strings = Hashtbl.create 0 in
+	Array.iter (fun f ->
+		Array.iteri (fun i op ->
+			match op with
+			| OStaticClosure (_,fid) | OSetMethod (_,_,fid) ->
+				Hashtbl.replace used_closures fid ()
+			| OBytes (_,sid) ->
+				Hashtbl.replace bytes_strings sid ()
+			| _ ->
+				()
+		) f.code
+	) code.functions;
+
+
+	line "";
+	line "// Types definitions";
+	Array.iter (fun t ->
+		match t with
+		| HObj o ->
+			let name = tname o.pname in
+			expr ("typedef struct _" ^ name ^ " *" ^ name);
+		| HAbstract (name,_) ->
+			expr ("typedef struct _" ^ name ^ " "  ^ name);
+		| _ ->
+			()
+	) all_types;
+
+	line "";
+	line "// Types implementation";
+
+	let unamed_field fid = "$_f" ^ string_of_int fid in
+
+	let obj_field fid name =
+		if name = "" then unamed_field fid else ident name
+	in
+
+	Array.iter (fun t ->
+		match t with
+		| HObj o ->
+			let name = tname o.pname in
+			line ("struct _" ^ name ^ " {");
+			block();
+			let rec loop o =
+				(match o.psuper with
+				| None -> expr ("hl_type *$type");
+				| Some c -> loop c);
+				Array.iteri (fun i (n,_,t) ->
+					let rec abs_index p v =
+						match p with
+						| None -> v
+						| Some o -> abs_index o.psuper (Array.length o.pfields + v)
+					in
+					expr (var_type (if n = "" then unamed_field (abs_index o.psuper i) else n) t)
+				) o.pfields;
+			in
+			loop o;
+			unblock();
+			expr "}";
+		| HEnum e ->
+			Array.iteri (fun i (_,_,pl) ->
+				if Array.length pl <> 0 then begin
+					line ("typedef struct {");
+					block();
+					expr "int index";
+					Array.iteri (fun i t ->
+						expr (var_type ("p" ^ string_of_int i) t)
+					) pl;
+					unblock();
+					sexpr "} %s" (enum_constr_type e i);
+				end;
+			) e.efields
+		| _ ->
+			()
+	) all_types;
+
+	line "";
+	line "// Types values declaration";
+	Array.iteri (fun i t ->
+		sexpr "static hl_type type$%d = { %s } /* %s */" i (type_id t) (tstr t);
+		match t with
+		| HObj o ->
+			sline "#define %s__val &type$%d" (tname o.pname) i
+		| _ ->
+			()
+	) all_types;
+
+	line "";
+	line "// Globals";
+	Array.iteri (fun i t ->
+		let name = "global$" ^ string_of_int i in
+		sexpr "static %s = 0" (var_type name t)
+	) code.globals;
+
+	line "";
+	line "// Natives functions";
+	Array.iter (fun (lib,name,t,idx) ->
+		match t with
+		| HFun (args,t) ->
+			let fname =
+				let lib = code.strings.(lib) in
+				let lib = if lib = "std" then "hl" else lib in
+				lib ^ "_" ^ code.strings.(name)
+			in
+			sexpr "HL_API %s %s(%s)" (ctype t) fname (String.concat "," (List.map ctype args));
+			funnames.(idx) <- fname;
+			Array.set tfuns idx (args,t)
+		| _ ->
+			assert false
+	) code.natives;
+
+	line "";
+	line "// Functions declaration";
+	Array.iter (fun f ->
+		match f.ftype with
+		| HFun (args,t) ->
+			let fname = String.concat "_" (ExtString.String.nsplit (fundecl_name f) ".") in
+			sexpr "static %s %s(%s)" (ctype t) fname (String.concat "," (List.map ctype args));
+			Array.set tfuns f.findex (args,t);
+			funnames.(f.findex) <- fname;
+		| _ ->
+			assert false
+	) code.functions;
+
+	line "";
+	line "// Strings";
+	Array.iteri (fun i str ->
+		let rec loop s i =
+			if i = String.length s then [] else
+			let c = String.get s i in
+			string_of_int (int_of_char c) :: loop s (i+1)
+		in
+		if Hashtbl.mem bytes_strings i then
+			sexpr "static vbyte bytes$%d[] = {%s}" i (String.concat "," (loop str 0))
+		else
+			let s = utf8_to_utf16 str in
+			sexpr "static vbyte string$%d[] = {%s} /* %s */" i (String.concat "," (loop s 0)) (String.escaped (String.concat "* /" (ExtString.String.nsplit str "*/")))
+	) code.strings;
+
+	let type_value t =
+		let index = (try PMap.find t htypes with Not_found -> assert false) in
+		"&type$" ^ string_of_int index
+	in
+
+	line "";
+	line "// Types values data";
+	Array.iteri (fun i t ->
+		let field_value (name,name_id,t) =
+			sprintf "{(const uchar*)string$%d, %s, %ld}" name_id (type_value t) (hash name_id)
+		in
+		match t with
+		| HObj o ->
+			let proto_value p =
+				sprintf "{(const uchar*)string$%d, %d, %d, %ld}" p.fid p.fmethod (match p.fvirtual with None -> -1 | Some i -> i) (hash p.fid)
+			in
+			let fields =
+				if Array.length o.pfields = 0 then "NULL" else
+				let name = sprintf "fields$%d" i in
+				sexpr "static hl_obj_field %s[] = {%s}" name (String.concat "," (List.map field_value (Array.to_list o.pfields)));
+				name
+			in
+			let proto =
+				if Array.length o.pproto = 0 then "NULL" else
+				let name = sprintf "proto$%d" i in
+				sexpr "static hl_obj_proto %s[] = {%s}" name (String.concat "," (List.map proto_value (Array.to_list o.pproto)));
+				name
+			in
+			let ofields = [
+				string_of_int (Array.length o.pfields);
+				string_of_int (Array.length o.pproto);
+				sprintf "(const uchar*)string$%d" o.pid;
+				(match o.psuper with None -> "NULL" | Some c -> sprintf "%s__val" (tname c.pname));
+				fields;
+				proto
+			] in
+			sexpr "static hl_type_obj obj$%d = {%s}" i (String.concat "," ofields);
+		| HEnum e ->
+			let constr_name = sprintf "econstructs$%d" i in
+			let constr_value cid (_,nid,tl) =
+				let tval = if Array.length tl = 0 then "NULL" else
+					let name = sprintf "econstruct$%d_%d" i cid in
+					sexpr "static hl_type *%s[] = {%s}" name (String.concat "," (List.map type_value (Array.to_list tl)));
+					name
+				in
+				let size = if Array.length tl = 0 then "0" else sprintf "sizeof(%s)" (enum_constr_type e cid) in
+				let offsets = if Array.length tl = 0 then "NULL" else
+					let name = sprintf "eoffsets$%d_%d" i cid in
+					sexpr "static int %s[] = {%s}" name (String.concat "," (List.map (fun _ -> "0") (Array.to_list tl)));
+					name
+				in
+				let has_ptr = List.exists is_gc_ptr (Array.to_list tl) in
+				sprintf "{(const uchar*)string$%d, %d, %s, %s, %s, %s}" nid (Array.length tl) tval size (if has_ptr then "true" else "false") offsets
+			in
+			sexpr "static hl_enum_construct %s[] = {%s}" constr_name (String.concat "," (Array.to_list (Array.mapi constr_value e.efields)));
+			let efields = [
+				if e.eid = 0 then "NULL" else sprintf "(const uchar*)string$%d" e.eid;
+				string_of_int (Array.length e.efields);
+				constr_name
+			] in
+			sexpr "static hl_type_enum enum$%d = {%s}" i (String.concat "," efields);
+		| HVirtual v ->
+			let fields_name =
+				if Array.length v.vfields = 0 then "NULL" else
+				let name = sprintf "vfields$%d" i in
+				sexpr "static hl_obj_field %s[] = {%s}" name (String.concat "," (List.map field_value (Array.to_list v.vfields)));
+				name
+			in
+			let vfields = [
+				fields_name;
+				string_of_int (Array.length v.vfields)
+			] in
+			sexpr "static hl_type_virtual virt$%d = {%s}" i (String.concat "," vfields);
+		| HFun (args,t) ->
+			let aname = if args = [] then "NULL" else
+				let name = sprintf "fargs$%d" i in
+				sexpr "static hl_type *%s[] = {%s}" name (String.concat "," (List.map type_value args));
+				name
+			in
+			sexpr "static hl_type_fun tfun$%d = {%s,%s,%d}" i aname (type_value t) (List.length args)
+		| _ ->
+			()
+	) all_types;
+
+	line "";
+	line "// Static data";
+	Hashtbl.iter (fun fid _ ->
+		let args, t = tfuns.(fid) in
+		sexpr "static vclosure cl$%d = { %s, %s, 0 }" fid (type_value (HFun (args,t))) funnames.(fid);
+	) used_closures;
+
+	line "";
+	line "// Reflection helpers";
+	let funByArgs = Hashtbl.create 0 in
+	let type_kind t =
+		match t with
+		| HVoid | HF32 | HF64 -> t
+		| HBool | HUI8 | HUI16 | HI32 -> HI32
+		| HBytes | HDyn | HFun _ | HObj _ | HArray | HType | HRef _ | HVirtual _ | HDynObj | HAbstract _ | HEnum _ | HNull _ -> HDyn
+	in
+	let type_kind_id t =
+		match t with
+		| HVoid -> 0
+		| HBool | HUI8 | HUI16 | HI32 -> 1 (* same int representation *)
+		| HF32 -> 2
+		| HF64 -> 3
+		| _ -> 4
+	in
+	let add_fun args t =
+		let nargs = List.length args in
+		let kargs = List.map type_kind args in
+		let kt = type_kind t in
+		let h = try Hashtbl.find funByArgs nargs with Not_found -> let h = Hashtbl.create 0 in Hashtbl.add funByArgs nargs h; h in
+		Hashtbl.replace h (kargs,kt) ()
+	in
+	Array.iter (fun f ->
+		Array.iter (fun op ->
+			match op with
+			| OSafeCast (dst,_) | ODynGet (dst,_,_) ->
+				(match f.regs.(dst) with
+				| HFun (args, t) -> add_fun args t
+				| _ -> ())
+			| _ -> ()
+		) f.code
+	) code.functions;
+	Array.iter (fun (args,t) -> add_fun args t) tfuns;
+	let argsCounts = List.sort compare (Hashtbl.fold (fun i _ acc -> i :: acc) funByArgs []) in
+	sexpr "static int TKIND[] = {%s}" (String.concat "," (List.map (fun t -> string_of_int (type_kind_id (type_kind t))) core_types));
+	line "";
+	line "void *hlc_static_call( void *fun, hl_type *t, void **args, vdynamic *out ) {";
+	block();
+	sexpr "int chk = TKIND[t->fun->ret->kind]";
+	sexpr "vdynamic *d";
+	line "switch( t->fun->nargs ) {";
+	List.iter (fun nargs ->
+		sline "case %d:" nargs;
+		block();
+		if nargs > 9 then sexpr "hl_fatal(\"Too many arguments, TODO:use more bits\")" else begin
+		for i = 0 to nargs-1 do
+			sexpr "chk |= TKIND[t->fun->args[%d]->kind] << %d" i ((i + 1) * 3);
+		done;
+		line "switch( chk ) {";
+		Hashtbl.iter (fun (args,t) _ ->
+			let s = ref (-1) in
+			let chk = List.fold_left (fun chk t -> incr s; chk lor ((type_kind_id t) lsl (!s * 3))) 0 (t :: args) in
+			sline "case %d:" chk;
+			block();
+			let idx = ref (-1) in
+			let vargs = List.map (fun t ->
+				incr idx;
+				if is_ptr t then
+					sprintf "(%s)args[%d]" (ctype t) !idx
+				else
+					sprintf "*(%s*)args[%d]" (ctype t) !idx
+			) args in
+			let call = sprintf "%s(%s)" (cast_fun "fun" args t) (String.concat "," vargs) in
+			if is_ptr t then
+				sexpr "return %s" call
+			else if t = HVoid then begin
+				expr call;
+				expr "return NULL";
+			end else begin
+				sexpr "out%s = %s" (dyn_value_field t) call;
+				sexpr "return &out%s" (dyn_value_field t);
+			end;
+			unblock();
+		) (Hashtbl.find funByArgs nargs);
+		sline "}";
+		expr "break";
+		end;
+		unblock();
+	) argsCounts;
+	line "}";
+	sexpr "hl_fatal(\"Unsupported dynamic call\")";
+	sexpr "return NULL";
+	unblock();
+	line "}";
+	line "";
+	let wrap_char = function
+		| HVoid -> "v"
+		| HUI8 | HUI16 | HBool | HI32 -> "i"
+		| HF32 -> "f"
+		| HF64 -> "d"
+		| _ -> "p"
+	in
+	let make_wrap_name args t =
+		String.concat "" (List.map wrap_char args) ^ "_" ^ wrap_char t
+	in
+	List.iter (fun nargs ->
+		Hashtbl.iter (fun (args,t) _ ->
+			let name = make_wrap_name args t in
+			sline "static %s wrap_%s(void *value%s) {" (ctype t) name (String.concat "" (list_mapi (fun i t -> "," ^ var_type ("p" ^ string_of_int i) t) args));
+			block();
+			if args <> [] then sexpr "void *args[] = {%s}" (String.concat "," (list_mapi (fun i t ->
+				if not (is_ptr t) then
+					sprintf "&p%d" i
+				else
+					sprintf "p%d" i
+			) args));
+			let vargs = if args = [] then "NULL" else "args" in
+			if t = HVoid then
+				sexpr "hl_wrapper_call(value,%s,NULL)" vargs
+			else if is_ptr t then
+				sexpr "return hl_wrapper_call(value,%s,NULL)" vargs
+			else begin
+				expr "vdynamic ret";
+				sexpr "hl_wrapper_call(value,%s,&ret)" vargs;
+				sexpr "return ret.v.%s" (wrap_char t);
+			end;
+			unblock();
+			line "}";
+		) (Hashtbl.find funByArgs nargs);
+	) argsCounts;
+	line "";
+	line "void *hlc_get_wrapper( hl_type *t ) {";
+	block();
+	sexpr "int chk = TKIND[t->fun->ret->kind]";
+	line "switch( t->fun->nargs ) {";
+	List.iter (fun nargs ->
+		sline "case %d:" nargs;
+		block();
+		if nargs > 9 then sexpr "hl_fatal(\"Too many arguments, TODO:use more bits\")" else begin
+		for i = 0 to nargs-1 do
+			sexpr "chk |= TKIND[t->fun->args[%d]->kind] << %d" i ((i + 1) * 3);
+		done;
+		line "switch( chk ) {";
+		Hashtbl.iter (fun (args,t) _ ->
+			let s = ref (-1) in
+			let chk = List.fold_left (fun chk t -> incr s; chk lor ((type_kind_id t) lsl (!s * 3))) 0 (t :: args) in
+			sexpr "case %d: return wrap_%s" chk (make_wrap_name args t);
+		) (Hashtbl.find funByArgs nargs);
+		sline "}";
+		expr "break";
+		end;
+		unblock();
+	) argsCounts;
+	line "}";
+	sexpr "return NULL";
+	unblock();
+	line "}";
+	line "";
+	line "// Functions code";
+	Array.iter (fun f ->
+
+		flush_file();
+
+		let rid = ref (-1) in
+		let reg id = "r" ^ string_of_int id in
+
+		let label id = "label$" ^ string_of_int f.findex ^ "$" ^ string_of_int id in
+
+		let rtype r = f.regs.(r) in
+
+		let rcast r t =
+			if tsame (rtype r) t then (reg r)
+			else Printf.sprintf "((%s)%s)" (ctype t) (reg r)
+		in
+
+		let rfun r args t =
+			cast_fun (reg r ^ "->fun") args t
+		in
+
+		let rassign r t =
+			let rt = rtype r in
+			if t = HVoid then "" else
+			let assign = reg r ^ " = " in
+			if tsame t rt then assign else
+			if not (safe_cast t rt) then assert false
+			else assign ^ "(" ^ ctype rt ^ ")"
+		in
+
+		let ocall r fid args =
+			let targs, rt = tfuns.(fid) in
+			let rstr = rassign r rt in
+			sexpr "%s%s(%s)" rstr funnames.(fid) (String.concat "," (List.map2 rcast args targs))
+		in
+
+
+		let dyn_prefix = function
+			| HUI8 | HUI16 | HI32 | HBool -> "i"
+			| HF32 -> "f"
+			| HF64 -> "d"
+			| _ -> "p"
+		in
+
+		let type_value_opt t =
+			match t with HF32 | HF64 -> "" | _ -> "," ^ type_value t
+		in
+
+		let dyn_call r f pl =
+			line "{";
+			block();
+			if pl <> [] then sexpr "vdynamic *args[] = {%s}" (String.concat "," (List.map (fun p ->
+				match rtype p with
+				| HDyn ->
+					reg p
+				| t ->
+					if is_dynamic t then
+						sprintf "(vdynamic*)%s" (reg p)
+					else
+						sprintf "hl_make_dyn(&%s,%s)" (reg p) (type_value t)
+			) pl));
+			let rt = rtype r in
+			let ret = if rt = HVoid then "" else if is_dynamic rt then sprintf "%s = (%s)" (reg r) (ctype rt) else "vdynamic *ret = " in
+			sexpr "%shl_dyn_call((vclosure*)%s,%s,%d)" ret (reg f) (if pl = [] then "NULL" else "args") (List.length pl);
+			if rt <> HVoid && not (is_dynamic rt) then sexpr "%s = (%s)hl_dyn_cast%s(&ret,&hlt_dyn%s)" (reg r) (ctype rt) (dyn_prefix rt) (type_value_opt rt);
+			unblock();
+			line "}";
+		in
+
+		let mcall r fid = function
+			| [] -> assert false
+			| o :: args ->
+				match rtype o with
+				| HObj _ ->
+					let vfun = cast_fun (sprintf "%s->$type->vobj_proto[%d]" (reg o) fid) (rtype o :: List.map rtype args) (rtype r) in
+					sexpr "%s%s(%s)" (rassign r (rtype r)) vfun (String.concat "," (List.map reg (o::args)))
+				| HVirtual vp ->
+					let rt = rtype r in
+					let meth = sprintf "hl_vfields(%s)[%d]" (reg o) fid in
+					let meth = cast_fun meth (HDyn :: List.map rtype args) rt in
+					sline "if( hl_vfields(%s)[%d] ) %s%s(%s); else {" (reg o) fid (rassign r rt) meth (String.concat "," ((reg o ^ "->value") :: List.map reg args));
+					block();
+					if args <> [] then sexpr "void *args[] = {%s}" (String.concat "," (List.map (fun p ->
+						let t = rtype p in
+						if is_ptr t then
+							reg p
+						else
+							sprintf "&%s" (reg p)
+					) args));
+					let rt = rtype r in
+					let ret = if rt = HVoid then "" else if is_ptr rt then sprintf "%s = (%s)" (reg r) (ctype rt) else begin sexpr "vdynamic ret"; ""; end in
+					let fname, fid, ft = vp.vfields.(fid) in
+					sexpr "%shl_dyn_call_obj(%s->value,%s,%ld/*%s*/,%s,%s)" ret (reg o) (type_value ft) (hash fid) fname (if args = [] then "NULL" else "args") (if is_ptr rt || rt == HVoid then "NULL" else "&ret");
+					if rt <> HVoid && not (is_ptr rt) then sexpr "%s = (%s)ret.v.%s" (reg r) (ctype rt) (dyn_prefix rt);
+					unblock();
+					sline "}"
+				| _ ->
+					assert false
+		in
+
+		let set_field obj fid v =
+			match rtype obj with
+			| HObj o ->
+				let name, t = resolve_field o fid in
+				sexpr "%s->%s = %s" (reg obj) (obj_field fid name) (rcast v t)
+			| HVirtual vp ->
+				let name, nid, t = vp.vfields.(fid) in
+				let dset = sprintf "hl_dyn_set%s(%s->value,%ld/*%s*/%s,%s)" (dyn_prefix t) (reg obj) (hash nid) name (type_value_opt (rtype v)) (reg v) in
+				(match t with
+				| HFun _ -> expr dset
+				| _ -> sexpr "if( hl_vfields(%s)[%d] ) *(%s*)(hl_vfields(%s)[%d]) = (%s)%s; else %s" (reg obj) fid (ctype t) (reg obj) fid (ctype t) (reg v) dset)
+			| _ ->
+				assert false
+		in
+
+		let get_field r obj fid =
+			match rtype obj with
+			| HObj o ->
+				let name, t = resolve_field o fid in
+				sexpr "%s%s->%s" (rassign r t) (reg obj) (obj_field fid name)
+			| HVirtual v ->
+				let name, nid, t = v.vfields.(fid) in
+				let dget = sprintf "(%s)hl_dyn_get%s(%s->value,%ld/*%s*/%s)" (ctype t) (dyn_prefix t) (reg obj) (hash nid) name (type_value_opt t) in
+				(match t with
+				| HFun _ -> sexpr "%s%s" (rassign r t) dget
+				| _ -> sexpr "%shl_vfields(%s)[%d] ? (*(%s*)(hl_vfields(%s)[%d])) : %s" (rassign r t) (reg obj) fid (ctype t) (reg obj) fid dget)
+			| _ ->
+				assert false
+		in
+
+		let fret = (match f.ftype with
+		| HFun (args,t) ->
+			sline "static %s %s(%s) {" (ctype t) funnames.(f.findex) (String.concat "," (List.map (fun t -> incr rid; var_type (reg !rid) t) args));
+			t
+		| _ ->
+			assert false
+		) in
+		block();
+		let var_map = Hashtbl.create 0 in
+		Array.iteri (fun i t ->
+			if i <= !rid || t = HVoid then ()
+			else
+				let key = ctype_no_ptr t in
+				Hashtbl.replace var_map key (try (reg i) :: Hashtbl.find var_map key with Not_found -> [reg i])
+		) f.regs;
+		Hashtbl.iter (fun (s,i) il ->
+			let prefix = String.make i '*' in
+			let il = List.rev_map (fun s -> prefix ^ s) il in
+			sexpr "%s %s" s (String.concat ", " il)
+		) var_map;
+		let output_options = Array.make (Array.length f.code + 1) [] in
+		let output_at i oo = output_options.(i) <- oo :: output_options.(i) in
+		let output_at2 i ool = List.iter (output_at i) ool in
+		let has_label i = List.exists (function OOLabel -> true | _ -> false) output_options.(i) in
+
+		let trap_depth = ref 0 in
+		let max_trap_depth = ref 0 in
+		Array.iter (fun op ->
+			match op with
+			| OTrap _ ->
+				incr trap_depth;
+				if !trap_depth > !max_trap_depth then max_trap_depth := !trap_depth
+			| OEndTrap true ->
+				decr trap_depth
+			| _ ->
+				()
+		) f.code;
+		for i = 0 to !max_trap_depth - 1 do
+			sexpr "hl_trap_ctx trap$%d" i;
+		done;
+
+		let flush_options i =
+			match output_options.(i) with
+			| [] -> ()
+			| opts ->
+				(* put label after } *)
+				let opts = if has_label i && List.mem OOEndBlock opts then OOLabel :: List.filter (fun i -> i <> OOLabel) opts else opts in
+				let opts = List.rev opts in
+				List.iter (function
+					| OOLabel -> sline "%s:" (label i)
+					| OOCase i -> sline "case %i:" i
+					| OODefault -> line "default:"
+					| OOIncreaseIndent -> block()
+					| OODecreaseIndent -> unblock()
+					| OOBeginBlock ->  line "{"
+					| OOEndBlock -> line "}"
+				) opts
+		in
+
+		Array.iteri (fun i op ->
+			flush_options i;
+			let label delta =
+				let addr = delta + i + 1 in
+				let label = label addr in
+				if not (has_label addr) then output_at addr OOLabel;
+				label
+			in
+			let todo() =
+				sexpr "hl_fatal(\"%s\")" (ostr (fun id -> "f" ^ string_of_int id) op)
+			in
+			let rec compare_op op a b d =
+				let phys_compare() =
+					sexpr "if( %s %s %s ) goto %s" (reg a) (s_comp op) (rcast b (rtype a)) (label d)
+				in
+				(*
+					safe_cast is already checked
+					two ways (same type) for eq
+					one way for comparisons
+				*)
+				match rtype a, rtype b with
+				| (HUI8 | HUI16 | HI32 | HF32 | HF64 | HBool), (HUI8 | HUI16 | HI32 | HF32 | HF64 | HBool) ->
+					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)
+				| HNull t, HNull _ ->
+					let field = dyn_value_field t in
+					let pcompare = sprintf "(%s%s %s %s%s)" (reg a) field (s_comp op) (reg b) field in
+					if op = CEq then
+						sexpr "if( %s == %s || (%s && %s && %s) ) goto %s" (reg a) (reg b) (reg a) (reg b) pcompare (label d)
+					else if op = CNeq then
+						sexpr "if( %s != %s && (!%s || !%s || %s) ) goto %s" (reg a) (reg b) (reg a) (reg b) pcompare (label d)
+					else
+						sexpr "if( %s && %s && %s ) goto %s" (reg a) (reg b) pcompare (label d)
+				| HDyn , _ | _, HDyn ->
+					let inv = if op = CGt || op = CGte then "&& i != hl_invalid_comparison " else "" in
+					sexpr "{ int i = hl_dyn_compare((vdynamic*)%s,(vdynamic*)%s); if( i %s 0 %s) goto %s; }" (reg a) (reg b) (s_comp op) inv (label d)
+				| HObj oa, HObj _ ->
+					(try
+						let fid = PMap.find "__compare" oa.pfunctions in
+						if op = CEq then
+							sexpr "if( %s == %s || (%s && %s && %s(%s,%s) == 0) ) goto %s" (reg a) (reg b) (reg a) (reg b) funnames.(fid) (reg a) (reg b) (label d)
+						else if op = CNeq then
+							sexpr "if( %s != %s && (!%s || !%s || %s(%s,%s) != 0) ) goto %s" (reg a) (reg b) (reg a) (reg b) funnames.(fid) (reg a) (reg b) (label d)
+						else
+							sexpr "if( %s && %s && %s(%s,%s) %s 0 ) goto %s" (reg a) (reg b) funnames.(fid) (reg a) (reg b) (s_comp op) (label d)
+					with Not_found ->
+						phys_compare())
+				| HVirtual _, HVirtual _ ->
+					if op = CEq then
+						sexpr "if( %s == %s || (%s && %s && %s->value && %s->value && %s->value == %s->value) ) goto %s" (reg a) (reg b) (reg a) (reg b) (reg a) (reg b) (reg a) (reg b) (label d)
+					else if op = CNeq then
+						sexpr "if( %s != %s && (!%s || !%s || !%s->value || !%s->value || %s->value != %s->value) ) goto %s" (reg a) (reg b) (reg a) (reg b) (reg a) (reg b) (reg a) (reg b) (label d)
+					else
+						assert false
+				| HEnum _, HEnum _ | HDynObj, HDynObj ->
+					phys_compare()
+				| HVirtual _, HObj _->
+					if op = CEq then
+						sexpr "if( %s ? (%s && %s->value == (vdynamic*)%s) : (%s == NULL) ) goto %s" (reg a) (reg b) (reg a) (reg b) (reg b) (label d)
+					else if op = CNeq then
+						sexpr "if( %s ? (%s == NULL || %s->value != (vdynamic*)%s) : (%s != NULL) ) goto %s" (reg a) (reg b) (reg a) (reg b) (reg b) (label d)
+					else
+						assert false
+				| HObj _, HVirtual _ ->
+					compare_op op b a d
+				| HFun _, HFun _ ->
+					phys_compare()
+				| ta, tb ->
+					failwith ("Don't know how to compare " ^ tstr ta ^ " and " ^ tstr tb ^ " (hlc)")
+			in
+			match op with
+			| OMov (r,v) ->
+				if rtype r <> HVoid then sexpr "%s = %s" (reg r) (rcast v (rtype r))
+			| OInt (r,idx) ->
+				if code.ints.(idx) = 0x80000000l then
+					sexpr "%s = 0x80000000" (reg r)
+				else
+					sexpr "%s = %ld" (reg r) code.ints.(idx)
+			| OFloat (r,idx) ->
+				sexpr "%s = %.19g" (reg r) code.floats.(idx)
+			| OBool (r,b) ->
+				sexpr "%s = %s" (reg r) (if b then "true" else "false")
+			| OBytes (r,idx) ->
+				sexpr "%s = bytes$%d" (reg r) idx
+			| OString (r,idx) ->
+				sexpr "%s = string$%d" (reg r) idx
+			| ONull r ->
+				sexpr "%s = NULL" (reg r)
+			| OAdd (r,a,b) ->
+				sexpr "%s = %s + %s" (reg r) (reg a) (reg b)
+			| OSub (r,a,b) ->
+				sexpr "%s = %s - %s" (reg r) (reg a) (reg b)
+			| OMul (r,a,b) ->
+				sexpr "%s = %s * %s" (reg r) (reg a) (reg b)
+			| OSDiv (r,a,b) ->
+				(match rtype r with
+				| HUI8 | HUI16 | HI32 ->
+					sexpr "%s = %s == 0 ? 0 : %s / %s" (reg r) (reg b) (reg a) (reg b)
+				| _ ->
+					sexpr "%s = %s / %s" (reg r) (reg a) (reg b))
+			| OUDiv (r,a,b) ->
+				sexpr "%s = %s == 0 ? 0 : ((unsigned)%s) / ((unsigned)%s)" (reg r) (reg b) (reg a) (reg b)
+			| OSMod (r,a,b) ->
+				(match rtype r with
+				| HUI8 | HUI16 | HI32 ->
+					sexpr "%s = %s == 0 ? 0 : %s %% %s" (reg r) (reg b) (reg a) (reg b)
+				| HF32 ->
+					sexpr "%s = fmodf(%s,%s)" (reg r) (reg a) (reg b)
+				| HF64 ->
+					sexpr "%s = fmod(%s,%s)" (reg r) (reg a) (reg b)
+				| _ ->
+					assert false)
+			| OUMod (r,a,b) ->
+				sexpr "%s = %s == 0 ? 0 : ((unsigned)%s) %% ((unsigned)%s)" (reg r) (reg b) (reg a) (reg b)
+			| OShl (r,a,b) ->
+				sexpr "%s = %s << %s" (reg r) (reg a) (reg b)
+			| OSShr (r,a,b) ->
+				sexpr "%s = %s >> %s" (reg r) (reg a) (reg b)
+			| OUShr (r,a,b) ->
+				sexpr "%s = ((unsigned)%s) >> %s" (reg r) (reg a) (reg b)
+			| OAnd (r,a,b) ->
+				sexpr "%s = %s & %s" (reg r) (reg a) (reg b)
+			| OOr (r,a,b) ->
+				sexpr "%s = %s | %s" (reg r) (reg a) (reg b)
+			| OXor (r,a,b) ->
+				sexpr "%s = %s ^ %s" (reg r) (reg a) (reg b)
+			| ONeg (r,v) ->
+				sexpr "%s = -%s" (reg r) (reg v)
+			| ONot (r,v) ->
+				sexpr "%s = !%s" (reg r) (reg v)
+			| OIncr r ->
+				sexpr "++%s" (reg r)
+			| ODecr r ->
+				sexpr "--%s" (reg r)
+			| OCall0 (r,fid) ->
+				ocall r fid []
+			| OCall1 (r,fid,a) ->
+				ocall r fid [a]
+			| OCall2 (r,fid,a,b) ->
+				ocall r fid [a;b]
+			| OCall3 (r,fid,a,b,c) ->
+				ocall r fid [a;b;c]
+			| OCall4 (r,fid,a,b,c,d) ->
+				ocall r fid [a;b;c;d]
+			| OCallN (r,fid,rl) ->
+				ocall r fid rl
+			| OCallMethod (r,fid,pl) ->
+				mcall r fid pl
+			| OCallThis (r,fid,pl) ->
+				mcall r fid (0 :: pl)
+			| OCallClosure (r,cl,pl) ->
+				(match rtype cl with
+				| HDyn ->
+					dyn_call r cl pl
+				| HFun (args,ret) ->
+					let sargs = String.concat "," (List.map2 rcast pl args) in
+					sexpr "%s%s->hasValue ? %s((vdynamic*)%s->value%s) : %s(%s)" (rassign r ret) (reg cl) (rfun cl (HDyn :: args) ret) (reg cl) (if sargs = "" then "" else "," ^ sargs) (rfun cl args ret) sargs
+				| _ ->
+					assert false)
+			| OStaticClosure (r,fid) ->
+				sexpr "%s = &cl$%d" (reg r) fid
+			| OSetMethod (o,f,fid) ->
+				let name, t = resolve_field (match rtype o with HObj o -> o | _ -> assert false) f in
+				sexpr "%s->%s = (%s)&cl$%d" (reg o) (ident name) (ctype t) fid
+			| OInstanceClosure (r,fid,ptr) ->
+				let args, t = tfuns.(fid) in
+				sexpr "%s = hl_alloc_closure_ptr(%s,%s,%s)" (reg r) (type_value (HFun (args,t))) funnames.(fid) (reg ptr)
+			| OVirtualClosure (r,o,m) ->
+				(match rtype o with
+				| HObj p ->
+					let tl,t = tfuns.(p.pvirtuals.(m)) in
+					let s = sprintf "%s->$type->vobj_proto[%d]" (reg o) m in
+					sexpr "%s = hl_alloc_closure_ptr(%s,%s,%s)" (reg r) (type_value (HFun(tl,t))) s (reg o)
+				| _ ->
+					todo())
+			| OGetGlobal (r,g) ->
+				sexpr "%s = (%s)global$%d" (reg r) (ctype (rtype r)) g
+			| OSetGlobal (g,r) ->
+				sexpr "global$%d = (%s)%s" g (ctype code.globals.(g)) (reg r)
+			| ORet r ->
+				if rtype r = HVoid then expr "return" else sexpr "return %s" (rcast r fret)
+			| OJTrue (r,d) | OJNotNull (r,d) ->
+				sexpr "if( %s ) goto %s" (reg r) (label d)
+			| OJFalse (r,d) | OJNull (r,d) ->
+				sexpr "if( !%s ) goto %s" (reg r) (label d)
+			| OJSLt (a,b,d) ->
+				compare_op CLt a b d
+			| OJSGte (a,b,d) ->
+				compare_op CGte a b d
+			| OJSGt (a,b,d) ->
+				compare_op CGt a b d
+			| OJSLte (a,b,d) ->
+				compare_op CLte a b d
+			| OJULt (a,b,d) ->
+				sexpr "if( ((unsigned)%s) < ((unsigned)%s) ) goto %s" (reg a) (reg b) (label d)
+			| OJUGte (a,b,d) ->
+				sexpr "if( ((unsigned)%s) >= ((unsigned)%s) ) goto %s" (reg a) (reg b) (label d)
+			| OJEq (a,b,d) ->
+				compare_op CEq a b d
+			| OJNotEq (a,b,d) ->
+				compare_op CNeq a b d
+			| OJAlways d ->
+				sexpr "goto %s" (label d)
+			| OLabel _ ->
+				if not (has_label i) then sline "%s:" (label (-1))
+			| OToDyn (r,v) ->
+				if is_ptr (rtype v) then begin
+					sline "if( %s == NULL ) %s = NULL; else {" (reg v) (reg r);
+					block();
+				end;
+				sexpr "%s = hl_alloc_dynamic(%s)" (reg r) (type_value (rtype v));
+				(match rtype v with
+				| HUI8 | HUI16 | HI32 | HBool ->
+					sexpr "%s->v.i = %s" (reg r) (reg v)
+				| HF32 ->
+					sexpr "%s->v.f = %s" (reg r) (reg v)
+				| HF64 ->
+					sexpr "%s->v.d = %s" (reg r) (reg v)
+				| _ ->
+					sexpr "%s->v.ptr = %s" (reg r) (reg v));
+				if is_ptr (rtype v) then begin
+					unblock();
+					line "}";
+				end;
+			| OToSFloat (r,v) ->
+				sexpr "%s = (%s)%s" (reg r) (ctype (rtype r)) (reg v)
+			| OToUFloat (r,v) ->
+				sexpr "%s = (%s)(unsigned)%s" (reg r) (ctype (rtype r)) (reg v)
+			| OToInt (r,v) ->
+				sexpr "%s = (int)%s" (reg r) (reg v)
+			| ONew r ->
+				(match rtype r with
+				| HObj o -> sexpr "%s = (%s)hl_alloc_obj(%s)" (reg r) (tname o.pname) (tname o.pname ^ "__val")
+				| HDynObj -> sexpr "%s = hl_alloc_dynobj()" (reg r)
+				| HVirtual _ as t -> sexpr "%s = hl_alloc_virtual(%s)" (reg r) (type_value t)
+				| _ -> assert false)
+			| OField (r,obj,fid) ->
+				get_field r obj fid
+			| OSetField (obj,fid,v) ->
+				set_field obj fid v
+			| OGetThis (r,fid) ->
+				get_field r 0 fid
+			| OSetThis (fid,r) ->
+				set_field 0 fid r
+			| OThrow r ->
+				sexpr "hl_throw((vdynamic*)%s)" (reg r)
+			| ORethrow r ->
+				sexpr "hl_rethrow((vdynamic*)%s)" (reg r)
+			| OGetUI8 (r,b,idx) ->
+				sexpr "%s = *(unsigned char*)(%s + %s)" (reg r) (reg b) (reg idx)
+			| OGetUI16 (r,b,idx) ->
+				sexpr "%s = *(unsigned short*)(%s + %s)" (reg r) (reg b) (reg idx)
+			| OGetI32 (r,b,idx) ->
+				sexpr "%s = *(int*)(%s + %s)" (reg r) (reg b) (reg idx)
+			| OGetF32 (r,b,idx) ->
+				sexpr "%s = *(float*)(%s + %s)" (reg r) (reg b) (reg idx)
+			| OGetF64 (r,b,idx) ->
+				sexpr "%s = *(double*)(%s + %s)" (reg r) (reg b) (reg idx)
+			| OGetArray (r, arr, idx) ->
+				sexpr "%s = ((%s*)(%s + 1))[%s]" (reg r) (ctype (rtype r)) (reg arr) (reg idx)
+			| OSetUI8 (b,idx,r) ->
+				sexpr "*(unsigned char*)(%s + %s) = (unsigned char)%s" (reg b) (reg idx) (reg r)
+			| OSetUI16 (b,idx,r) ->
+				sexpr "*(unsigned short*)(%s + %s) = (unsigned short)%s" (reg b) (reg idx) (reg r)
+			| OSetI32 (b,idx,r) ->
+				sexpr "*(int*)(%s + %s) = %s" (reg b) (reg idx) (reg r)
+			| OSetF32 (b,idx,r) ->
+				sexpr "*(float*)(%s + %s) = (float)%s" (reg b) (reg idx) (reg r)
+			| OSetF64 (b,idx,r) ->
+				sexpr "*(double*)(%s + %s) = %s" (reg b) (reg idx) (reg r)
+			| OSetArray (arr,idx,v) ->
+				sexpr "((%s*)(%s + 1))[%s] = %s" (ctype (rtype v)) (reg arr) (reg idx) (reg v)
+			| OSafeCast (r,v) ->
+				let tsrc = rtype v in
+				let t = rtype r in
+				if tsrc = HNull t then
+					sexpr "%s = %s ? %s%s : 0" (reg r) (reg v) (reg v) (dyn_value_field t)
+				else
+					sexpr "%s = (%s)hl_dyn_cast%s(&%s,%s%s)" (reg r) (ctype t) (dyn_prefix t) (reg v) (type_value (rtype v)) (type_value_opt t)
+			| OUnsafeCast (r,v) ->
+				sexpr "%s = (%s)%s" (reg r) (ctype (rtype r)) (reg v)
+			| OArraySize (r,a) ->
+				sexpr "%s = %s->size" (reg r) (reg a)
+			| OType (r,t) ->
+				sexpr "%s = %s" (reg r) (type_value t)
+			| OGetType (r,v) ->
+				sexpr "%s = %s ? ((vdynamic*)%s)->t : &hlt_void" (reg r) (reg v) (reg v)
+			| OGetTID (r,v) ->
+				sexpr "%s = %s->kind" (reg r) (reg v)
+			| ORef (r,v) ->
+				sexpr "%s = &%s" (reg r) (reg v)
+			| OUnref (r,v) ->
+				sexpr "%s = *%s" (reg r) (reg v)
+			| OSetref (r,v) ->
+				sexpr "*%s = %s" (reg r) (reg v)
+			| OToVirtual (r,v) ->
+				sexpr "%s = hl_to_virtual(%s,(vdynamic*)%s)" (reg r) (type_value (rtype r)) (reg v)
+			| ODynGet (r,o,sid) ->
+				let t = rtype r in
+				let h = hash sid in
+				sexpr "%s = (%s)hl_dyn_get%s((vdynamic*)%s,%ld/*%s*/%s)" (reg r) (ctype t) (dyn_prefix t) (reg o) h code.strings.(sid) (type_value_opt t)
+			| ODynSet (o,sid,v) ->
+				let h = hash sid in
+				sexpr "hl_dyn_set%s((vdynamic*)%s,%ld/*%s*/%s,%s)" (dyn_prefix (rtype v)) (reg o) h code.strings.(sid) (type_value_opt (rtype v)) (reg v)
+			| OMakeEnum (r,cid,rl) ->
+				let e, et = (match rtype r with HEnum e -> e, enum_constr_type e cid | _ -> assert false) in
+				let has_ptr = List.exists (fun r -> is_gc_ptr (rtype r)) rl in
+				let need_tmp = List.mem r rl in
+				let tmp = if not need_tmp then reg r else begin
+					sexpr "{ venum *tmp";
+					"tmp"
+				end in
+				sexpr "%s = (venum*)hl_gc_alloc%s(sizeof(%s))" tmp (if has_ptr then "" else "_noptr") et;
+				sexpr "%s->index = %d" tmp cid;
+				let _,_,tl = e.efields.(cid) in
+				list_iteri (fun i v ->
+					sexpr "((%s*)%s)->p%d = %s" et tmp i (rcast v tl.(i))
+				) rl;
+				if need_tmp then sexpr "%s = tmp; }" (reg r)
+			| OEnumAlloc (r,cid) ->
+				let et, (_,_,tl) = (match rtype r with HEnum e -> enum_constr_type e cid, e.efields.(cid) | _ -> assert false) in
+				let has_ptr = List.exists is_gc_ptr (Array.to_list tl) in
+				sexpr "%s = (venum*)hl_gc_alloc%s(sizeof(%s))" (reg r) (if has_ptr then "" else "_noptr") et;
+				sexpr "memset(%s,0,sizeof(%s))" (reg r) et;
+				if cid <> 0 then sexpr "%s->index = %d" (reg r) cid
+			| OEnumIndex (r,v) ->
+				(match rtype v with
+				| HEnum _ ->
+					sexpr "%s = %s->index" (reg r) (reg v)
+				| HDyn ->
+					sexpr "%s = ((venum*)%s->v.ptr)->index" (reg r) (reg v)
+				| _ ->
+					assert false)
+			| OEnumField (r,e,cid,pid) ->
+				let tname,(_,_,tl) = (match rtype e with HEnum e -> enum_constr_type e cid, e.efields.(cid) | _ -> assert false) in
+				sexpr "%s((%s*)%s)->p%d" (rassign r tl.(pid)) tname (reg e) pid
+			| OSetEnumField (e,pid,r) ->
+				let tname, (_,_,tl) = (match rtype e with HEnum e -> enum_constr_type e 0, e.efields.(0) | _ -> assert false) in
+				sexpr "((%s*)%s)->p%d = (%s)%s" tname (reg e) pid (ctype tl.(pid)) (reg r)
+			| OSwitch (r,idx,eend) ->
+				sline "switch(%s) {" (reg r);
+				block();
+				output_at2 (i + 1) [OODefault;OOIncreaseIndent];
+				Array.iteri (fun k delta -> output_at2 (delta + i + 1) [OODecreaseIndent;OOCase k;OOIncreaseIndent]) idx;
+				let pend = i+1+eend in
+				(* insert at end if we have another switch case here *)
+				let old = output_options.(pend) in
+				output_options.(pend) <- [];
+				output_at2 pend ([OODecreaseIndent;OODecreaseIndent;OOEndBlock] @ List.rev old);
+			| ONullCheck r ->
+				sexpr "if( %s == NULL ) hl_null_access()" (reg r)
+			| OTrap (r,d) ->
+				sexpr "hl_trap(trap$%d,%s,%s)" !trap_depth (reg r) (label d);
+				incr trap_depth
+			| OEndTrap b ->
+				sexpr "hl_endtrap(trap$%d)" (!trap_depth - 1);
+				if b then decr trap_depth;
+			| ONop _ ->
+				()
+		) f.code;
+		flush_options (Array.length f.code);
+		unblock();
+		line "}";
+		line "";
+	) code.functions;
+
+	line "";
+	line "// Entry point";
+	line "void hl_entry_point() {";
+	block();
+	sexpr "static void *functions_ptrs[] = {%s}" (String.concat "," (Array.to_list funnames));
+	let rec loop i =
+		if i = Array.length code.functions + Array.length code.natives then [] else
+		let args, t = tfuns.(i) in
+		(type_value (HFun (args,t))) :: loop (i + 1)
+	in
+	sexpr "static hl_type *functions_types[] = {%s}" (String.concat "," (loop 0));
+	expr "hl_module_context ctx";
+	expr "hl_alloc_init(&ctx.alloc)";
+	expr "ctx.functions_ptrs = functions_ptrs";
+	expr "ctx.functions_types = functions_types";
+	Hashtbl.iter (fun i _ -> sexpr "hl_hash(string$%d)" i) hash_cache;
+	Array.iteri (fun i t ->
+		match t with
+		| HObj o ->
+			sexpr "obj$%d.m = &ctx" i;
+			(match o.pclassglobal with None -> () | Some g -> sexpr "obj$%d.global_value = (void**)&global$%d" i g);
+			sexpr "type$%d.obj = &obj$%d" i i
+		| HNull t | HRef t ->
+			sexpr "type$%d.tparam = %s" i (type_value t)
+		| HEnum e ->
+			sexpr "type$%d.tenum = &enum$%d" i i;
+			(match e.eglobal with None -> () | Some g -> sexpr "enum$%d.global_value = (void**)&global$%d" i g);
+			Array.iteri (fun cid (_,_,tl) ->
+				if Array.length tl > 0 then begin
+					line "{";
+					block();
+					sexpr "%s *_e = NULL" (enum_constr_type e cid);
+					Array.iteri (fun pid _ -> sexpr "eoffsets$%d_%d[%d] = (int)(int_val)&_e->p%d" i cid pid pid) tl;
+					unblock();
+					line "}";
+				end
+			) e.efields
+		| HVirtual _ ->
+			sexpr "type$%d.virt = &virt$%d" i i;
+			sexpr "hl_init_virtual(&type$%d,&ctx)" i;
+		| HFun _ ->
+			sexpr "type$%d.fun = &tfun$%d" i i
+		| _ ->
+			()
+	) all_types;
+	Array.iteri (fun i t ->
+		if is_ptr t then sexpr "hl_add_root((void**)&global$%d)" i;
+	) code.globals;
+	sexpr "%s()" funnames.(code.entrypoint);
+	unblock();
+	line "}";
+	line "";
+	List.iter (fun f -> f()) !end_ch

+ 650 - 0
src/generators/hlcode.ml

@@ -0,0 +1,650 @@
+(*
+ * Copyright (C)2005-2016 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.
+ *)
+
+type reg = int
+type global = int
+type 'a index = int
+type functable
+
+type ttype =
+	| HVoid
+	| HUI8
+	| HUI16
+	| HI32
+	| HF32
+	| HF64
+	| HBool
+	| HBytes
+	| HDyn
+	| HFun of ttype list * ttype
+	| HObj of class_proto
+	| HArray
+	| HType
+	| HRef of ttype
+	| HVirtual of virtual_proto
+	| HDynObj
+	| HAbstract of string * string index
+	| HEnum of enum_proto
+	| HNull of ttype
+
+and class_proto = {
+	pname : string;
+	pid : int;
+	mutable pclassglobal : int option;
+	mutable psuper : class_proto option;
+	mutable pvirtuals : int array;
+	mutable pproto : field_proto array;
+	mutable pnfields : int;
+	mutable pfields : (string * string index * ttype) array;
+	mutable pindex : (string, int * ttype) PMap.t;
+	mutable pfunctions : (string, int) PMap.t;
+	mutable pinterfaces : (ttype, int) PMap.t;
+}
+
+and enum_proto = {
+	ename : string;
+	eid : int;
+	mutable eglobal : int option;
+	mutable efields : (string * string index * ttype array) array;
+}
+
+and field_proto = {
+	fname : string;
+	fid : int;
+	fmethod : functable index;
+	fvirtual : int option;
+}
+
+and virtual_proto = {
+	mutable vfields : (string * string index * ttype) array;
+	mutable vindex : (string, int) PMap.t;
+}
+
+type unused = int
+type field
+
+type opcode =
+	(* register storing *)
+	| OMov of reg * reg
+	| OInt of reg * int index
+	| OFloat of reg * float index
+	| OBool of reg * bool
+	| OBytes of reg * string index
+	| OString of reg * string index
+	| ONull of reg
+	(* binops *)
+	| OAdd of reg * reg * reg
+	| OSub of reg * reg * reg
+	| OMul of reg * reg * reg
+	| OSDiv of reg * reg * reg
+	| OUDiv of reg * reg * reg
+	| OSMod of reg * reg * reg
+	| OUMod of reg * reg * reg
+	| OShl of reg * reg * reg
+	| OSShr of reg * reg * reg
+	| OUShr of reg * reg * reg
+	| OAnd of reg * reg * reg
+	| OOr of reg * reg * reg
+	| OXor of reg * reg * reg
+	(* unops *)
+	| ONeg of reg * reg
+	| ONot of reg * reg
+	| OIncr of reg
+	| ODecr of reg
+	(* calls *)
+	| OCall0 of reg * functable index
+	| OCall1 of reg * functable index * reg
+	| OCall2 of reg * functable index * reg * reg
+	| OCall3 of reg * functable index * reg * reg * reg
+	| OCall4 of reg * functable index * reg * reg * reg * reg
+	| OCallN of reg * functable index * reg list
+	| OCallMethod of reg * field index * reg list
+	| OCallThis of reg * field index * reg list
+	| OCallClosure of reg * reg * reg list
+	(* closures *)
+	| OStaticClosure of reg * functable index (* Class.method *)
+	| OInstanceClosure of reg * functable index * reg (* instance.method *)
+	| OVirtualClosure of reg * reg * field index (* instance.overriddenMethod *)
+	(* field access *)
+	| OGetGlobal of reg * global
+	| OSetGlobal of global * reg
+	| OField of reg * reg * field index
+	| OSetField of reg * field index * reg
+	| OGetThis of reg * field index
+	| OSetThis of field index * reg
+	| ODynGet of reg * reg * string index
+	| ODynSet of reg * string index * reg
+	| OSetMethod of reg * field index * functable index (* init static method *)
+	(* jumps *)
+	| OJTrue of reg * int
+	| OJFalse of reg * int
+	| OJNull of reg * int
+	| OJNotNull of reg * int
+	| OJSLt of reg * reg * int
+	| OJSGte of reg * reg * int
+	| OJSGt of reg * reg * int
+	| OJSLte of reg * reg * int
+	| OJULt of reg * reg * int
+	| OJUGte of reg * reg * int
+	| OJEq of reg * reg * int
+	| OJNotEq of reg * reg * int
+	| OJAlways of int
+	(* coerce *)
+	| OToDyn of reg * reg
+	| OToSFloat of reg * reg
+	| OToUFloat of reg * reg
+	| OToInt of reg * reg
+	| OSafeCast of reg * reg
+	| OUnsafeCast of reg * reg
+	| OToVirtual of reg * reg
+	(* control flow *)
+	| OLabel of unused
+	| ORet of reg
+	| OThrow of reg
+	| ORethrow of reg
+	| OSwitch of reg * int array * int
+	| ONullCheck of reg
+	| OTrap of reg * int
+	| OEndTrap of bool
+	(* memory access *)
+	| OGetUI8 of reg * reg * reg
+	| OGetUI16 of reg * reg * reg
+	| OGetI32 of reg * reg * reg
+	| OGetF32 of reg * reg * reg
+	| OGetF64 of reg * reg * reg
+	| OGetArray of reg * reg * reg
+	| OSetUI8 of reg * reg * reg
+	| OSetUI16 of reg * reg * reg
+	| OSetI32 of reg * reg * reg
+	| OSetF32 of reg * reg * reg
+	| OSetF64 of reg * reg * reg
+	| OSetArray of reg * reg * reg
+	(* type operations *)
+	| ONew of reg
+	| OArraySize of reg * reg
+	| OType of reg * ttype
+	| OGetType of reg * reg
+	| OGetTID of reg * reg
+	(* references *)
+	| ORef of reg * reg
+	| OUnref of reg * reg
+	| OSetref of reg * reg
+	(* enums *)
+	| OMakeEnum of reg * field index * reg list
+	| OEnumAlloc of reg * field index
+	| OEnumIndex of reg * reg
+	| OEnumField of reg * reg * field index * int
+	| OSetEnumField of reg * int * reg
+	(* misc *)
+	| ONop of string
+
+type fundecl = {
+	fpath : string * string;
+	findex : functable index;
+	ftype : ttype;
+	regs : ttype array;
+	code : opcode array;
+	debug : (int * int) array;
+}
+
+type code = {
+	version : int;
+	entrypoint : global;
+	strings : string array;
+	ints : int32 array;
+	floats : float array;
+	(* types : ttype array // only in bytecode, rebuilt on save() *)
+	globals : ttype array;
+	natives : (string index * string index * ttype * functable index) array;
+	functions : fundecl array;
+	debugfiles : string array;
+}
+
+let null_proto =
+	{
+		pname = "";
+		pid = 0;
+		pclassglobal = None;
+		psuper = None;
+		pvirtuals = [||];
+		pproto = [||];
+		pfields = [||];
+		pnfields = 0;
+		pindex = PMap.empty;
+		pfunctions = PMap.empty;
+		pinterfaces = PMap.empty;
+	}
+
+let list_iteri f l =
+	let p = ref (-1) in
+	List.iter (fun v -> incr p; f !p v) l
+
+let list_mapi f l =
+	let p = ref (-1) in
+	List.map (fun v -> incr p; f !p v) l
+
+(*
+	does the runtime value can be set to null
+*)
+let is_nullable t =
+	match t with
+	| HBytes | HDyn | HFun _ | HObj _ | HArray | HVirtual _ | HDynObj | HAbstract _ | HEnum _ | HNull _ | HRef _ -> true
+	| HUI8 | HUI16 | HI32 | HF32 | HF64 | HBool | HVoid | HType -> false
+
+
+let is_int = function
+	| HUI8 | HUI16 | HI32 -> true
+	| _ -> false
+
+let is_float = function
+	| HF32 | HF64 -> true
+	| _ -> false
+
+let is_number = function
+	| HUI8 | HUI16 | HI32 | HF32 | HF64 -> true
+	| _ -> false
+
+(*
+	does the runtime value carry its type
+*)
+let is_dynamic t =
+	match t with
+	| HDyn | HFun _ | HObj _ | HArray | HVirtual _ | HDynObj | HNull _ -> true
+	| _ -> false
+
+let rec tsame t1 t2 =
+	if t1 == t2 then true else
+	match t1, t2 with
+	| HFun (args1,ret1), HFun (args2,ret2) when List.length args1 = List.length args2 -> List.for_all2 tsame args1 args2 && tsame ret2 ret1
+	| HObj p1, HObj p2 -> p1 == p2
+	| HEnum e1, HEnum e2 -> e1 == e2
+	| HAbstract (_,a1), HAbstract (_,a2) -> a1 == a2
+	| HVirtual v1, HVirtual v2 ->
+		if v1 == v2 then true else
+		if Array.length v1.vfields <> Array.length v2.vfields then false else
+		let rec loop i =
+			if i = Array.length v1.vfields then true else
+			let _, i1, t1 = v1.vfields.(i) in
+			let _, i2, t2 = v2.vfields.(i) in
+			if i1 = i2 && tsame t1 t2 then loop (i + 1) else false
+		in
+		loop 0
+	| HNull t1, HNull t2 -> tsame t1 t2
+	| HRef t1, HRef t2 -> tsame t1 t2
+	| _ -> false
+
+(*
+	can we use a value of t1 as t2
+*)
+let rec safe_cast t1 t2 =
+	if t1 == t2 then true else
+	match t1, t2 with
+	| _, HDyn -> is_dynamic t1
+	| HVirtual v1, HVirtual v2 when Array.length v2.vfields < Array.length v1.vfields ->
+		let rec loop i =
+			if i = Array.length v2.vfields then true else
+			let n1, _, t1 = v1.vfields.(i) in
+			let n2, _, t2 = v2.vfields.(i) in
+			if n1 = n2 && tsame t1 t2 then loop (i + 1) else false
+		in
+		loop 0
+	| HObj p1, HObj p2 ->
+		(* allow subtyping *)
+		let rec loop p =
+			p.pname = p2.pname || (match p.psuper with None -> false | Some p -> loop p)
+		in
+		loop p1
+	| 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
+	| _ ->
+		tsame t1 t2
+
+let hl_hash b =
+	let h = ref Int32.zero in
+	let rec loop i =
+		let c = if i = String.length b then 0 else int_of_char b.[i] in
+		if c <> 0 then begin
+			h := Int32.add (Int32.mul !h 223l) (Int32.of_int c);
+			loop (i + 1)
+		end else
+			Int32.rem !h 0x1FFFFF7Bl
+	in
+	loop 0
+
+let utf16_add buf c =
+	let add c =
+		Buffer.add_char buf (char_of_int (c land 0xFF));
+		Buffer.add_char buf (char_of_int (c lsr 8));
+	in
+	if c >= 0 && c < 0x10000 then begin
+		if c >= 0xD800 && c <= 0xDFFF then failwith ("Invalid unicode char " ^ string_of_int c);
+		add c;
+	end else if c < 0x110000 then begin
+		let c = c - 0x10000 in
+		add ((c asr 10) + 0xD800);
+		add ((c land 1023) + 0xDC00);
+	end else
+		failwith ("Invalid unicode char " ^ string_of_int c)
+
+let utf8_to_utf16 str =
+	let b = Buffer.create (String.length str * 2) in
+	(try UTF8.iter (fun c -> utf16_add b (UChar.code c)) str with Invalid_argument _ | UChar.Out_of_range -> ()); (* if malformed *)
+	utf16_add b 0;
+	Buffer.contents b
+
+let rec get_index name p =
+	try
+		PMap.find name p.pindex
+	with Not_found ->
+		match p.psuper with
+		| None -> raise Not_found
+		| Some p -> get_index name p
+
+let resolve_field p fid =
+	let rec loop pl p =
+		let pl = p :: pl in
+		match p.psuper with
+		| None ->
+			let rec fetch id = function
+				| [] -> raise Not_found
+				| p :: pl ->
+					let d = id - Array.length p.pfields in
+					if d < 0 then
+						let name, _, t = p.pfields.(id) in
+						name, t
+					else
+						fetch d pl
+			in
+			fetch fid pl
+		| Some p ->
+			loop pl p
+	in
+	loop [] p
+
+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) | _ -> ());
+		if PMap.mem t !types then () else
+		let index = DynArray.length arr in
+		DynArray.add arr t;
+		types := PMap.add t index !types;
+		match t with
+		| HFun (args, ret) ->
+			List.iter get_type args;
+			get_type ret
+		| HObj p ->
+			Array.iter (fun (_,n,t) -> get_type t) p.pfields
+		| HNull t | HRef t ->
+			get_type t
+		| HVirtual v ->
+			Array.iter (fun (_,_,t) -> get_type t) v.vfields
+		| HEnum e ->
+			Array.iter (fun (_,_,tl) -> Array.iter get_type tl) e.efields
+		| _ ->
+			()
+	in
+	List.iter (fun t -> get_type t) [HVoid; HUI8; HUI16; HI32; HF32; HF64; HBool; HType; HDyn]; (* make sure all basic types get lower indexes *)
+	Array.iter (fun g -> get_type g) code.globals;
+	Array.iter (fun (_,_,t,_) -> get_type t) code.natives;
+	Array.iter (fun f ->
+		get_type f.ftype;
+		Array.iter (fun r -> get_type r) f.regs;
+		Array.iter (function
+			| OType (_,t) -> get_type t
+			| _ -> ()
+		) f.code;
+	) code.functions;
+	DynArray.to_array arr, !types
+
+let lookup_type types t =
+	try PMap.find t types with Not_found -> assert false
+
+(* --------------------------------------------------------------------------------------------------------------------- *)
+(* DUMP *)
+
+let rec tstr ?(stack=[]) ?(detailed=false) t =
+	match t with
+	| HVoid -> "void"
+	| HUI8 -> "ui8"
+	| HUI16 -> "ui16"
+	| HI32 -> "i32"
+	| HF32 -> "f32"
+	| HF64 -> "f64"
+	| HBool -> "bool"
+	| HBytes -> "bytes"
+	| HDyn  -> "dyn"
+	| HFun (args,ret) -> "(" ^ String.concat "," (List.map (tstr ~stack ~detailed) args) ^ "):" ^ tstr ~stack ~detailed ret
+	| HObj o when not detailed -> "#" ^ o.pname
+	| HObj o ->
+		let fields = "{" ^ String.concat "," (List.map (fun(s,_,t) -> s ^ " : " ^ tstr ~detailed:false t) (Array.to_list o.pfields)) ^ "}" in
+		let proto = "{"  ^ String.concat "," (List.map (fun p -> (match p.fvirtual with None -> "" | Some _ -> "virtual ") ^ p.fname ^ "@" ^  string_of_int p.fmethod) (Array.to_list o.pproto)) ^ "}" in
+		"#" ^ o.pname ^ "[" ^ (match o.psuper with None -> "" | Some p -> ">" ^ p.pname ^ " ") ^ "fields=" ^ fields ^ " proto=" ^ proto ^ "]"
+	| HArray ->
+		"array"
+	| HType ->
+		"type"
+	| HRef t ->
+		"ref(" ^ tstr t ^ ")"
+	| HVirtual v when List.memq v stack ->
+		"..."
+	| HVirtual v ->
+		"virtual(" ^ String.concat "," (List.map (fun (f,_,t) -> f ^":"^tstr ~stack:(v::stack) t) (Array.to_list v.vfields)) ^ ")"
+	| HDynObj ->
+		"dynobj"
+	| HAbstract (s,_) ->
+		"abstract(" ^ s ^ ")"
+	| HEnum e when e.eid = 0 ->
+		let _,_,fl = e.efields.(0) in
+		"enum(" ^ String.concat "," (List.map tstr (Array.to_list fl)) ^ ")"
+	| HEnum e ->
+		"enum(" ^ e.ename ^ ")"
+	| HNull t -> "null(" ^ tstr t ^ ")"
+
+let ostr fstr o =
+	match o with
+	| OMov (a,b) -> Printf.sprintf "mov %d,%d" a b
+	| OInt (r,i) -> Printf.sprintf "int %d,@%d" r i
+	| OFloat (r,i) -> Printf.sprintf "float %d,@%d" r i
+	| OString (r,s) -> Printf.sprintf "string %d,@%d" r s
+	| OBytes (r,s) -> Printf.sprintf "bytes %d,@%d" r s
+	| OBool (r,b) -> if b then Printf.sprintf "true %d" r else Printf.sprintf "false %d" r
+	| ONull r -> Printf.sprintf "null %d" r
+	| OAdd (r,a,b) -> Printf.sprintf "add %d,%d,%d" r a b
+	| OSub (r,a,b) -> Printf.sprintf "sub %d,%d,%d" r a b
+	| OMul (r,a,b) -> Printf.sprintf "mul %d,%d,%d" r a b
+	| OSDiv (r,a,b) -> Printf.sprintf "sdiv %d,%d,%d" r a b
+	| OUDiv (r,a,b) -> Printf.sprintf "udiv %d,%d,%d" r a b
+	| OSMod (r,a,b) -> Printf.sprintf "smod %d,%d,%d" r a b
+	| OUMod (r,a,b) -> Printf.sprintf "umod %d,%d,%d" r a b
+	| OShl (r,a,b) -> Printf.sprintf "shl %d,%d,%d" r a b
+	| OSShr (r,a,b) -> Printf.sprintf "sshr %d,%d,%d" r a b
+	| OUShr (r,a,b) -> Printf.sprintf "ushr %d,%d,%d" r a b
+	| OAnd (r,a,b) -> Printf.sprintf "and %d,%d,%d" r a b
+	| OOr (r,a,b) -> Printf.sprintf "or %d,%d,%d" r a b
+	| OXor (r,a,b) -> Printf.sprintf "xor %d,%d,%d" r a b
+	| ONeg (r,v) -> Printf.sprintf "neg %d,%d" r v
+	| ONot (r,v) -> Printf.sprintf "not %d,%d" r v
+	| OIncr r -> Printf.sprintf "incr %d" r
+	| ODecr r -> Printf.sprintf "decr %d" r
+	| OCall0 (r,g) -> Printf.sprintf "call %d, %s()" r (fstr g)
+	| OCall1 (r,g,a) -> Printf.sprintf "call %d, %s(%d)" r (fstr g) a
+	| OCall2 (r,g,a,b) -> Printf.sprintf "call %d, %s(%d,%d)" r (fstr g) a b
+	| OCall3 (r,g,a,b,c) -> Printf.sprintf "call %d, %s(%d,%d,%d)" r (fstr g) a b c
+	| OCall4 (r,g,a,b,c,d) -> Printf.sprintf "call %d, %s(%d,%d,%d,%d)" r (fstr g) a b c d
+	| OCallN (r,g,rl) -> Printf.sprintf "call %d, %s(%s)" r (fstr g) (String.concat "," (List.map string_of_int rl))
+	| OCallMethod (r,f,[]) -> "callmethod ???"
+	| OCallMethod (r,f,o :: rl) -> Printf.sprintf "callmethod %d, %d[%d](%s)" r o f (String.concat "," (List.map string_of_int rl))
+	| OCallClosure (r,f,rl) -> Printf.sprintf "callclosure %d, %d(%s)" r f (String.concat "," (List.map string_of_int rl))
+	| OCallThis (r,f,rl) -> Printf.sprintf "callthis %d, [%d](%s)" r f (String.concat "," (List.map string_of_int rl))
+	| OStaticClosure (r,f) -> Printf.sprintf "staticclosure %d, %s" r (fstr f)
+	| OInstanceClosure (r,f,v) -> Printf.sprintf "instanceclosure %d, %s(%d)" r (fstr f) v
+	| OSetMethod (o,f,fid) -> Printf.sprintf "setmethod %d[%d], %d" o f fid
+	| OGetGlobal (r,g) -> Printf.sprintf "global %d, %d" r g
+	| OSetGlobal (g,r) -> Printf.sprintf "setglobal %d, %d" g r
+	| ORet r -> Printf.sprintf "ret %d" r
+	| OJTrue (r,d) -> Printf.sprintf "jtrue %d,%d" r d
+	| OJFalse (r,d) -> Printf.sprintf "jfalse %d,%d" r d
+	| OJNull (r,d) -> Printf.sprintf "jnull %d,%d" r d
+	| OJNotNull (r,d) -> Printf.sprintf "jnotnull %d,%d" r d
+	| OJSLt (a,b,i) -> Printf.sprintf "jslt %d,%d,%d" a b i
+	| OJSGte (a,b,i) -> Printf.sprintf "jsgte %d,%d,%d" a b i
+	| OJSGt (r,a,b) -> Printf.sprintf "jsgt %d,%d,%d" r a b
+	| OJSLte (r,a,b) -> Printf.sprintf "jslte %d,%d,%d" r a b
+	| OJULt (a,b,i) -> Printf.sprintf "jult %d,%d,%d" a b i
+	| OJUGte (a,b,i) -> Printf.sprintf "jugte %d,%d,%d" a b i
+	| OJEq (a,b,i) -> Printf.sprintf "jeq %d,%d,%d" a b i
+	| OJNotEq (a,b,i) -> Printf.sprintf "jnoteq %d,%d,%d" a b i
+	| OJAlways d -> Printf.sprintf "jalways %d" d
+	| OToDyn (r,a) -> Printf.sprintf "todyn %d,%d" r a
+	| OToSFloat (r,a) -> Printf.sprintf "tosfloat %d,%d" r a
+	| OToUFloat (r,a) -> Printf.sprintf "toufloat %d,%d" r a
+	| OToInt (r,a) -> Printf.sprintf "toint %d,%d" r a
+	| OLabel _ -> "label"
+	| ONew r -> Printf.sprintf "new %d" r
+	| OField (r,o,i) -> Printf.sprintf "field %d,%d[%d]" r o i
+	| OVirtualClosure (r,o,m) -> Printf.sprintf "virtualclosure %d,%d[%d]" r o m
+	| OSetField (o,i,r) -> Printf.sprintf "setfield %d[%d],%d" o i r
+	| OGetThis (r,i) -> Printf.sprintf "getthis %d,[%d]" r i
+	| OSetThis (i,r) -> Printf.sprintf "setthis [%d],%d" i r
+	| OThrow r -> Printf.sprintf "throw %d" r
+	| ORethrow r -> Printf.sprintf "rethrow %d" r
+	| OGetUI8 (r,b,p) -> Printf.sprintf "getui8 %d,%d[%d]" r b p
+	| OGetUI16 (r,b,p) -> Printf.sprintf "getui16 %d,%d[%d]" r b p
+	| OGetI32 (r,b,p) -> Printf.sprintf "geti32 %d,%d[%d]" r b p
+	| OGetF32 (r,b,p) -> Printf.sprintf "getf32 %d,%d[%d]" r b p
+	| OGetF64 (r,b,p) -> Printf.sprintf "getf64 %d,%d[%d]" r b p
+	| OGetArray (r,a,i) -> Printf.sprintf "getarray %d,%d[%d]" r a i
+	| OSetUI8 (r,p,v) -> Printf.sprintf "setui8 %d,%d,%d" r p v
+	| OSetUI16 (r,p,v) -> Printf.sprintf "setui16 %d,%d,%d" r p v
+	| OSetI32 (r,p,v) -> Printf.sprintf "seti32 %d,%d,%d" r p v
+	| OSetF32 (r,p,v) -> Printf.sprintf "setf32 %d,%d,%d" r p v
+	| OSetF64 (r,p,v) -> Printf.sprintf "setf64 %d,%d,%d" r p v
+	| OSetArray (a,i,v) -> Printf.sprintf "setarray %d[%d],%d" a i v
+	| OSafeCast (r,v) -> Printf.sprintf "safecast %d,%d" r v
+	| OUnsafeCast (r,v) -> Printf.sprintf "unsafecast %d,%d" r v
+	| OArraySize (r,a) -> Printf.sprintf "arraysize %d,%d" r a
+	| OType (r,t) -> Printf.sprintf "type %d,%s" r (tstr t)
+	| OGetType (r,v) -> Printf.sprintf "gettype %d,%d" r v
+	| OGetTID (r,v) -> Printf.sprintf "gettid %d,%d" r v
+	| ORef (r,v) -> Printf.sprintf "ref %d,&%d" r v
+	| OUnref (v,r) -> Printf.sprintf "unref %d,*%d" v r
+	| OSetref (r,v) -> Printf.sprintf "setref *%d,%d" r v
+	| OToVirtual (r,v) -> Printf.sprintf "tovirtual %d,%d" r v
+	| ODynGet (r,o,f) -> Printf.sprintf "dynget %d,%d[@%d]" r o f
+	| ODynSet (o,f,v) -> Printf.sprintf "dynset %d[@%d],%d" o f v
+	| OMakeEnum (r,e,pl) -> Printf.sprintf "makeenum %d, %d(%s)" r e (String.concat "," (List.map string_of_int pl))
+	| OEnumAlloc (r,e) -> Printf.sprintf "enumalloc %d, %d" r e
+	| OEnumIndex (r,e) -> Printf.sprintf "enumindex %d, %d" r e
+	| OEnumField (r,e,i,n) -> Printf.sprintf "enumfield %d, %d[%d:%d]" r e i n
+	| OSetEnumField (e,i,r) -> Printf.sprintf "setenumfield %d[%d], %d" e i r
+	| OSwitch (r,idx,eend) -> Printf.sprintf "switch %d [%s] %d" r (String.concat "," (Array.to_list (Array.map string_of_int idx))) eend
+	| ONullCheck r -> Printf.sprintf "nullcheck %d" r
+	| OTrap (r,i) -> Printf.sprintf "trap %d, %d" r i
+	| OEndTrap b -> Printf.sprintf "endtrap %b" b
+	| 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 =
+	let all_protos = Hashtbl.create 0 in
+	let funnames = Hashtbl.create 0 in
+	let tstr t =
+		(match t with
+		| HObj p -> Hashtbl.replace all_protos p.pname p
+		| _ -> ());
+		tstr t
+	in
+	let str idx =
+		try
+			code.strings.(idx)
+		with _ ->
+			"INVALID:" ^ string_of_int idx
+	in
+	let fstr fid =
+		try
+			Hashtbl.find funnames fid
+		with _ ->
+			Printf.sprintf "f@%X" fid
+	in
+	let debug_infos (fid,line) =
+		(try code.debugfiles.(fid) with _ -> "???") ^ ":" ^ string_of_int line
+	in
+	pr ("hl v" ^ string_of_int code.version);
+	pr ("entry @" ^ string_of_int code.entrypoint);
+	pr (string_of_int (Array.length code.strings) ^ " strings");
+	Array.iteri (fun i s ->
+		pr ("	@" ^ string_of_int i ^ " : " ^ String.escaped s);
+	) code.strings;
+	pr (string_of_int (Array.length code.ints) ^ " ints");
+	Array.iteri (fun i v ->
+		pr ("	@" ^ string_of_int i ^ " : " ^ Int32.to_string v);
+	) code.ints;
+	pr (string_of_int (Array.length code.floats) ^ " floats");
+	Array.iteri (fun i f ->
+		pr ("	@" ^ string_of_int i ^ " : " ^ string_of_float f);
+	) code.floats;
+	pr (string_of_int (Array.length code.globals) ^ " globals");
+	Array.iteri (fun i g ->
+		pr ("	@" ^ string_of_int i ^ " : " ^ tstr g);
+	) code.globals;
+	pr (string_of_int (Array.length code.natives) ^ " natives");
+	Array.iter (fun (lib,name,t,fidx) ->
+		pr ("	@" ^ string_of_int fidx ^ " native " ^ str lib ^ "@" ^ str name ^ " " ^ tstr t);
+		Hashtbl.add funnames fidx (str lib ^ "@" ^ str name)
+	) code.natives;
+	Array.iter (fun f -> Hashtbl.add funnames f.findex (fundecl_name f)) code.functions;
+	pr (string_of_int (Array.length code.functions) ^ " functions");
+	Array.iter (fun f ->
+		pr (Printf.sprintf "	fun@%d(%Xh) %s" f.findex f.findex (tstr f.ftype));
+		let fid, _ = f.debug.(0) in
+		let cur_fid = ref fid in
+		pr (Printf.sprintf "	; %s (%s)" (debug_infos f.debug.(0)) (fundecl_name f));
+		Array.iteri (fun i r ->
+			pr ("		r" ^ string_of_int i ^ " " ^ tstr r);
+		) f.regs;
+		Array.iteri (fun i o ->
+			let fid, line = f.debug.(i) in
+			if fid <> !cur_fid then begin
+				cur_fid := fid;
+				pr (Printf.sprintf "	; %s" (debug_infos (fid,line)));
+			end;
+			pr (Printf.sprintf "		.%-5d @%X %s" line i (ostr fstr o))
+		) f.code;
+	) code.functions;
+	let protos = Hashtbl.fold (fun _ p acc -> p :: acc) all_protos [] in
+	pr (string_of_int (List.length protos) ^ " objects protos");
+	List.iter (fun p ->
+		pr ("	" ^ p.pname ^ " " ^ (match p.pclassglobal with None -> "no global" | Some i -> "@" ^ string_of_int i));
+		(match p.psuper with
+		| None -> ()
+		| Some p -> pr ("		extends " ^ p.pname));
+		pr ("		" ^ string_of_int (Array.length p.pfields) ^ " fields");
+		Array.iteri (fun i (_,id,t) ->
+			pr ("		  @" ^ string_of_int i ^ " " ^ str id ^ " " ^ tstr t)
+		) p.pfields;
+		pr ("		" ^ string_of_int (Array.length p.pproto) ^ " methods");
+		Array.iteri (fun i f ->
+			pr ("		  @" ^ string_of_int i ^ " " ^ str f.fid ^ " fun@" ^ string_of_int f.fmethod ^ (match f.fvirtual with None -> "" | Some p -> "[" ^ string_of_int p ^ "]"))
+		) p.pproto;
+	) protos

+ 2810 - 0
src/generators/hlinterp.ml

@@ -0,0 +1,2810 @@
+(*
+ * Copyright (C)2005-2016 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.
+ *)
+open Unix
+open Hlcode
+
+type value =
+	| VNull
+	| VInt of int32
+	| VFloat of float
+	| VBool of bool
+	| VDyn of value * ttype
+	| VObj of vobject
+	| VClosure of vfunction * value option
+	| VBytes of string
+	| VArray of value array * ttype
+	| VUndef
+	| VType of ttype
+	| VRef of ref_value * ttype
+	| VVirtual of vvirtual
+	| VDynObj of vdynobj
+	| VEnum of int * value array
+	| VAbstract of vabstract
+	| VVarArgs of vfunction * value option
+
+and ref_value =
+	| RStack of int
+	| RValue of value ref
+
+and vabstract =
+	| AHashBytes of (string, value) Hashtbl.t
+	| AHashInt of (int32, value) Hashtbl.t
+	| AHashObject of (value * value) list ref
+	| AReg of regexp
+	| ARandom
+	| APos of Globals.pos
+	| ATDecl of Type.module_type
+	| AUnsafe of Obj.t
+	| ALazyType of ((unit -> Type.t) ref) * (unit -> value)
+
+and vfunction =
+	| FFun of fundecl
+	| FNativeFun of string * (value list -> value) * ttype
+
+and vobject = {
+	oproto : vproto;
+	ofields : value array;
+}
+
+and vproto = {
+	pclass : class_proto;
+	pmethods : vfunction array;
+}
+
+and vvirtual = {
+	vtype : virtual_proto;
+	mutable vindexes : vfield array;
+	mutable vtable : value array;
+	mutable vvalue : value;
+}
+
+and vdynobj = {
+	dfields : (string, int) Hashtbl.t;
+	mutable dtypes : ttype array;
+	mutable dvalues : value array;
+	mutable dvirtuals : vvirtual list;
+}
+
+and vfield =
+	| VFNone
+	| VFIndex of int
+
+and regexp = {
+	r : Str.regexp;
+	mutable r_string : string;
+	mutable r_groups : (int * int) option array;
+}
+
+exception Return of value
+exception Runtime_error of string
+exception InterpThrow of value
+exception Sys_exit of int
+
+type context = {
+	mutable t_globals : value array;
+	mutable t_functions : vfunction array;
+	mutable call_stack : (fundecl * int ref) list;
+	mutable error_stack : (fundecl * int ref) list;
+	mutable stack : value array;
+	mutable stack_pos : int;
+	mutable fcall : vfunction -> value list -> value;
+	mutable code : code;
+	mutable on_error : value -> (fundecl * int ref) list -> unit;
+	mutable resolve_macro_api : string -> (value list -> value) option;
+	checked : bool;
+	cached_protos : (int, vproto * ttype array) Hashtbl.t;
+	cached_strings : (int, string) Hashtbl.t;
+	cached_hashes : (int32, string) Hashtbl.t;
+}
+
+let default t =
+	match t with
+	| HUI8 | HUI16 | HI32 -> VInt Int32.zero
+	| HF32 | HF64 -> VFloat 0.
+	| HBool -> VBool false
+	| _ -> if is_nullable t then VNull else VUndef
+
+let get_type = function
+	| VDyn (_,t) -> Some t
+	| VObj o -> Some (HObj o.oproto.pclass)
+	| VDynObj _ -> Some HDynObj
+	| VVirtual v -> Some (HVirtual v.vtype)
+	| VArray _ -> Some HArray
+	| VClosure (f,None) -> Some (match f with FFun f -> f.ftype | FNativeFun (_,_,t) -> t)
+	| VClosure (f,Some _) -> Some (match f with FFun { ftype = HFun(_::args,ret) } | FNativeFun (_,_,HFun(_::args,ret)) -> HFun (args,ret) | _ -> assert false)
+	| VVarArgs _ -> Some (HFun ([],HDyn))
+	| _ -> None
+
+let v_dynamic = function
+	| VNull	| VDyn _ | VObj _ | VClosure _ | VArray _ | VVirtual _ | VDynObj _ | VVarArgs _ -> true
+	| _ -> false
+
+let rec is_compatible v t =
+	match v, t with
+	| VInt _, (HUI8 | HUI16 | HI32) -> true
+	| VFloat _, (HF32 | HF64) -> true
+	| VBool _, HBool -> true
+	| _, HVoid -> true
+	| VNull, t -> is_nullable t
+	| VObj o, HObj _ -> safe_cast (HObj o.oproto.pclass) t
+	| VClosure _, HFun _ -> safe_cast (match get_type v with None -> assert false | Some t -> t) t
+	| VBytes _, HBytes -> true
+	| VDyn (_,t1), HNull t2 -> tsame t1 t2
+	| v, HNull t -> is_compatible v t
+	| v, HDyn -> v_dynamic v
+	| VType _, HType -> true
+	| VArray _, HArray -> true
+	| VDynObj _, HDynObj -> true
+	| VVirtual v, HVirtual _ -> safe_cast (HVirtual v.vtype) t
+	| VRef (_,t1), HRef t2 -> tsame t1 t2
+	| VAbstract _, HAbstract _ -> true
+	| VEnum _, HEnum _ -> true
+	| _ -> false
+
+type cast =
+	| CNo
+	| CDyn of ttype
+	| CUnDyn of ttype
+	| CCast of ttype * ttype
+
+let error msg = raise (Runtime_error msg)
+
+let get_function ctx f =
+	ctx.t_functions.(f)
+
+let rec get_proto ctx p =
+	try
+		Hashtbl.find ctx.cached_protos p.pid
+	with Not_found ->
+		let fields = (match p.psuper with None -> [||] | Some p -> snd(get_proto ctx p)) in
+		let meths = Array.map (get_function ctx) p.pvirtuals in
+		let fields = Array.append fields (Array.map (fun (_,_,t) -> t) p.pfields) in
+		let proto = ({ pclass = p; pmethods = meths },fields) in
+		Hashtbl.replace ctx.cached_protos p.pid proto;
+		proto
+
+let alloc_obj ctx t =
+	match t with
+	| HDynObj ->
+		VDynObj { dfields = Hashtbl.create 0; dvalues = [||]; dtypes = [||]; dvirtuals = []; }
+	| HObj p ->
+		let p, fields = get_proto ctx p in
+		VObj { oproto = p; ofields = Array.map default fields }
+	| HVirtual v ->
+		let o = {
+			dfields = Hashtbl.create 0;
+			dvalues = Array.map (fun (_,_,t) -> default t) v.vfields;
+			dtypes = Array.map (fun (_,_,t) -> t) v.vfields;
+			dvirtuals = [];
+		} in
+		Array.iteri (fun i (n,_,_) -> Hashtbl.add o.dfields n i) v.vfields;
+		let v = { vtype = v; vvalue = VDynObj o; vtable = o.dvalues; vindexes = Array.mapi (fun i _ -> VFIndex i) v.vfields } in
+		o.dvirtuals <- [v];
+		VVirtual v
+	| _ ->
+		assert false
+
+let float_to_string f =
+	let s = string_of_float f in
+	let len = String.length s in
+	if String.unsafe_get s (len - 1) = '.' then String.sub s 0 (len - 1) else s
+
+let rec get_method p name =
+	let m = ref None in
+	Array.iter (fun p -> if p.fname = name then m := Some p.fmethod) p.pproto;
+	match !m , p.psuper with
+	| Some i, _ -> Some i
+	| None, Some s -> get_method s name
+	| None, None -> None
+
+let get_to_string ctx p =
+	match get_method p "__string" with
+	| Some f ->
+		(match get_function ctx f with
+		| (FFun { ftype = HFun([_],HBytes) } as f) -> Some f
+		| _ -> None)
+	| None ->
+		None
+
+let set_i32 b p v =
+	try
+		String.set b p (char_of_int ((Int32.to_int v) land 0xFF));
+		String.set b (p+1) (char_of_int ((Int32.to_int (Int32.shift_right_logical v 8)) land 0xFF));
+		String.set b (p+2) (char_of_int ((Int32.to_int (Int32.shift_right_logical v 16)) land 0xFF));
+		String.set b (p+3) (char_of_int (Int32.to_int (Int32.shift_right_logical v 24)));
+	with _ ->
+		error "Set outside of bytes bounds"
+
+let get_i32 b p =
+	let i = int_of_char (String.get b p) in
+	let j = int_of_char (String.get b (p + 1)) in
+	let k = int_of_char (String.get b (p + 2)) in
+	let l = int_of_char (String.get b (p + 3)) in
+	Int32.logor (Int32.of_int (i lor (j lsl 8) lor (k lsl 16))) (Int32.shift_left (Int32.of_int l) 24)
+
+let make_dyn v t =
+	if v = VNull || is_dynamic t then
+		v
+	else
+		VDyn (v,t)
+
+let get_ref ctx = function
+	| RStack i -> ctx.stack.(i)
+	| RValue r -> !r
+
+let set_ref ctx r v =
+	match r with
+	| RStack i -> ctx.stack.(i) <- v
+	| RValue r -> r := v
+
+let fstr = function
+	| FFun f -> "function@" ^ string_of_int f.findex
+	| FNativeFun (s,_,_) -> "native[" ^ s ^ "]"
+
+let caml_to_hl str = utf8_to_utf16 str
+
+let hash ctx str =
+	let h = hl_hash str in
+	if not (Hashtbl.mem ctx.cached_hashes h) then Hashtbl.add ctx.cached_hashes h (String.sub str 0 (try String.index str '\000' with _ -> String.length str));
+	h
+
+let utf16_iter f s =
+	let get v = int_of_char s.[v] in
+	let rec loop p =
+		if p = String.length s then () else
+		let c = (get p) lor ((get (p+1)) lsl 8) in
+		if c >= 0xD800 && c <= 0xDFFF then begin
+			let c = c - 0xD800 in
+			let c2 = ((get (p+2)) lor ((get(p+3)) lsl 8)) - 0xDC00 in
+			f ((c2 lor (c lsl 10)) + 0x10000);
+			loop (p + 4);
+		end else begin
+			f c;
+			loop (p + 2);
+		end;
+	in
+	loop 0
+
+let utf16_char buf c =
+	utf16_add buf (int_of_char c)
+
+let hl_to_caml str =
+	let utf16_eof s =
+		let get v = int_of_char s.[v] in
+		let rec loop p =
+			let c = (get p) lor ((get (p+1)) lsl 8) in
+			if c = 0 then String.sub s 0 p else loop (p + 2);
+		in
+		loop 0
+	in
+	let b = UTF8.Buf.create (String.length str / 2) in
+	utf16_iter (fun c -> UTF8.Buf.add_char b (UChar.chr c)) (utf16_eof str);
+	UTF8.Buf.contents b
+
+let null_access() =
+	error "Null value bypass null pointer check"
+
+let throw ctx v =
+	ctx.error_stack <- [];
+	raise (InterpThrow v)
+
+let throw_msg ctx msg =
+	throw ctx (VDyn (VBytes (caml_to_hl msg),HBytes))
+
+let rec vstr_d ctx v =
+	let vstr_d = vstr_d ctx in
+	match v with
+	| VNull -> "null"
+	| VInt i -> Int32.to_string i ^ "i"
+	| VFloat f -> string_of_float f ^ "f"
+	| VBool b -> if b then "true" else "false"
+	| VDyn (v,t) -> "dyn(" ^ vstr_d v ^ ":" ^ tstr t ^ ")"
+	| VObj o ->
+		let p = "#" ^ o.oproto.pclass.pname in
+		(match get_to_string ctx o.oproto.pclass with
+		| Some f -> p ^ ":" ^ vstr_d (ctx.fcall f [v])
+		| None -> p)
+	| VBytes b -> "bytes(" ^ String.escaped b ^ ")"
+	| VClosure (f,o) ->
+		(match o with
+		| None -> fstr f
+		| Some v -> fstr f ^ "[" ^ vstr_d v ^ "]")
+	| VArray (a,t) -> "array<" ^ tstr t ^ ">(" ^ String.concat "," (Array.to_list (Array.map vstr_d a)) ^ ")"
+	| VUndef -> "undef"
+	| VType t -> "type(" ^ tstr t ^ ")"
+	| VRef (r,_) -> "ref(" ^ vstr_d (get_ref ctx r) ^ ")"
+	| VVirtual v -> "virtual(" ^ vstr_d v.vvalue ^ ")"
+	| VDynObj d -> "dynobj(" ^ String.concat "," (Hashtbl.fold (fun f i acc -> (f^":"^vstr_d d.dvalues.(i)) :: acc) d.dfields []) ^ ")"
+	| VEnum (i,vals) -> "enum#" ^ string_of_int i  ^ "(" ^ String.concat "," (Array.to_list (Array.map vstr_d vals)) ^ ")"
+	| VAbstract _ -> "abstract"
+	| VVarArgs _ -> "varargs"
+
+let rec to_virtual ctx v vp =
+	match v with
+	| VNull ->
+		VNull
+	| VObj o ->
+		let indexes = Array.mapi (fun i (n,_,t) ->
+			try
+				let idx, ft = get_index n o.oproto.pclass in
+				if idx < 0 || not (tsame t ft) then raise Not_found;
+				VFIndex idx
+			with Not_found ->
+				VFNone (* most likely a method *)
+		) vp.vfields in
+		let v = {
+			vtype = vp;
+			vindexes = indexes;
+			vtable = o.ofields;
+			vvalue = v;
+		} in
+		VVirtual v
+	| VDynObj d ->
+		(try
+			VVirtual (List.find (fun v -> v.vtype == vp) d.dvirtuals)
+		with Not_found ->
+			let indexes = Array.mapi (fun i (n,_,t) ->
+				try
+					let idx = Hashtbl.find d.dfields n in
+					if not (tsame t d.dtypes.(idx)) then raise Not_found;
+					VFIndex idx
+				with Not_found ->
+					VFNone
+			) vp.vfields in
+			let v = {
+				vtype = vp;
+				vindexes = indexes;
+				vtable = d.dvalues;
+				vvalue = v;
+			} in
+			d.dvirtuals <- v :: d.dvirtuals;
+			VVirtual v
+		)
+	| VVirtual vd ->
+		if vd.vtype == vp then
+			v
+		else if vd.vvalue = VNull then
+			assert false
+		else
+			to_virtual ctx vd.vvalue vp
+	| _ ->
+		throw_msg ctx ("Invalid ToVirtual " ^ vstr_d ctx v ^ " : " ^ tstr (HVirtual vp))
+
+let rec dyn_cast ctx v t rt =
+	let invalid() =
+		error ("Can't cast " ^ vstr_d ctx v ^ ":"  ^ tstr t ^ " to " ^ tstr rt)
+	in
+	let default() =
+		let v = default rt in
+		if v = VUndef then invalid();
+		v
+	in
+	if safe_cast t rt then
+		v
+	else if v = VNull then
+		default()
+	else match t, rt with
+	| (HUI8|HUI16|HI32), (HF32|HF64) ->
+		(match v with VInt i -> VFloat (Int32.to_float i) | _ -> assert false)
+	| (HF32|HF64), (HUI8|HUI16|HI32) ->
+		(match v with VFloat f -> VInt (Int32.of_float f) | _ -> assert false)
+	| (HUI8|HUI16|HI32|HF32|HF64), HNull ((HUI8|HUI16|HI32|HF32|HF64) as rt) ->
+		let v = dyn_cast ctx v t rt in
+		VDyn (v,rt)
+	| HBool, HNull HBool ->
+		VDyn (v,HBool)
+	| _, HDyn ->
+		make_dyn v t
+	| _, HRef t2 when t = t2 ->
+		VRef (RValue (ref v),t)
+	| HFun (args1,t1), HFun (args2,t2) when List.length args1 = List.length args2 ->
+		(match v with
+		| VClosure (fn,farg) ->
+			let get_conv t1 t2 =
+				if safe_cast t1 t2 || (t2 = HDyn && is_dynamic t1) then CNo
+				else if t2 = HDyn then CDyn t1
+				else if t1 = HDyn then CUnDyn t2
+				else CCast (t1,t2)
+			in
+			let conv = List.map2 get_conv args2 args1 in
+			let rconv = get_conv t1 t2 in
+			let convert v c =
+				match c with
+				| CNo -> v
+				| CDyn t -> make_dyn v t
+				| CUnDyn t -> dyn_cast ctx v HDyn t
+				| CCast (t1,t2) -> dyn_cast ctx v t1 t2
+			in
+			VClosure (FNativeFun ("~convert",(fun args ->
+				let args = List.map2 convert args conv in
+				let ret = ctx.fcall fn (match farg with None -> args | Some a -> a :: args) in
+				convert ret rconv
+			),rt),None)
+		| _ ->
+			assert false)
+	| HDyn, HFun (targs,tret) when (match v with VVarArgs _ -> true | _ -> false) ->
+		VClosure (FNativeFun ("~varargs",(fun args ->
+			dyn_call ctx v (List.map2 (fun v t -> (v,t)) args targs) tret
+		),rt),None)
+	| HDyn, _ ->
+		(match get_type v with
+		| None -> assert false
+		| Some t -> dyn_cast ctx (match v with VDyn (v,_) -> v | _ -> v) t rt)
+	| HNull t, _ ->
+		(match v with
+		| VDyn (v,t) -> dyn_cast ctx v t rt
+		| _ -> assert false)
+	| HObj _, HObj b when safe_cast rt t && (match get_type v with Some t -> safe_cast t rt | None -> assert false) ->
+		(* downcast *)
+		v
+	| (HObj _ | HDynObj | HVirtual _), HVirtual vp ->
+		to_virtual ctx v vp
+	| HVirtual _, _ ->
+		(match v with
+		| VVirtual v -> dyn_cast ctx v.vvalue (match get_type v.vvalue with None -> assert false | Some t -> t) rt
+		| _ -> assert false)
+	| HObj p, _ ->
+		(match get_method p "__cast" with
+		| None -> invalid()
+		| Some f ->
+			if v = VNull then VNull else
+			let ret = ctx.fcall (get_function ctx f) [v;VType rt] in
+			if ret <> VNull && (match get_type ret with None -> assert false | Some vt -> safe_cast vt rt) then ret else invalid())
+	| _ ->
+		invalid()
+
+and dyn_call ctx v args tret =
+	match v with
+	| VClosure (f,a) ->
+		let ft = (match f with FFun f -> f.ftype | FNativeFun (_,_,t) -> t) in
+		let fargs, fret = (match ft with HFun (a,t) -> a, t | _ -> assert false) in
+		let full_args = args and full_fargs = (match a with None -> fargs | Some _ -> List.tl fargs) in
+		let rec loop args fargs =
+			match args, fargs with
+			| [], [] -> []
+			| _, [] -> throw_msg ctx (Printf.sprintf "Too many arguments (%s) != (%s)" (String.concat "," (List.map (fun (v,_) -> vstr_d ctx v) full_args)) (String.concat "," (List.map tstr full_fargs)))
+			| (v,t) :: args, ft :: fargs -> dyn_cast ctx v t ft :: loop args fargs
+			| [], _ :: fargs -> default ft :: loop args fargs
+		in
+		let vargs = loop args full_fargs in
+		let v = ctx.fcall f (match a with None -> vargs | Some a -> a :: vargs) in
+		dyn_cast ctx v fret tret
+	| VNull ->
+		null_access()
+	| VVarArgs (f,a) ->
+		let arr = VArray (Array.of_list (List.map (fun (v,t) -> make_dyn v t) args),HDyn) in
+		dyn_call ctx (VClosure (f,a)) [arr,HArray] tret
+	| _ ->
+		throw_msg ctx (vstr_d ctx v ^ " cannot be called")
+
+let invalid_comparison = 255
+
+let rec dyn_compare ctx a at b bt =
+	if a == b && (match at with HF32 | HF64 -> false | _ -> true) then 0 else
+	let fcompare (a:float) (b:float) = if a = b then 0 else if a > b then 1 else if a < b then -1 else invalid_comparison in
+	match a, b with
+	| VInt a, VInt b -> Int32.compare a b
+	| VInt a, VFloat b -> fcompare (Int32.to_float a) b
+	| VFloat a, VInt b -> fcompare a (Int32.to_float b)
+	| VFloat a, VFloat b -> fcompare a b
+	| VBool a, VBool b -> compare a b
+	| VNull, VNull -> 0
+	| VType t1, VType t2 -> if tsame t1 t2 then 0 else 1
+	| VNull, _ -> 1
+	| _, VNull -> -1
+	| VObj oa, VObj ob ->
+		if oa == ob then 0 else
+		(match get_method oa.oproto.pclass "__compare" with
+		| None -> invalid_comparison
+		| Some f -> (match ctx.fcall (get_function ctx f) [a;b] with VInt i -> Int32.to_int i | _ -> assert false));
+	| VDyn (v,t), _ ->
+		dyn_compare ctx v t b bt
+	| _, VDyn (v,t) ->
+		dyn_compare ctx a at v t
+	| VVirtual v, _ ->
+		dyn_compare ctx v.vvalue HDyn b bt
+	| _, VVirtual v ->
+		dyn_compare ctx a at v.vvalue HDyn
+	| _ ->
+		invalid_comparison
+
+let rec dyn_get_field ctx obj field rt =
+	let get_with v t = dyn_cast ctx v t rt in
+	match obj with
+	| VDynObj d ->
+		(try
+			let idx = Hashtbl.find d.dfields field in
+			get_with d.dvalues.(idx) d.dtypes.(idx)
+		with Not_found ->
+			default rt)
+	| VObj o ->
+		let default rt =
+			match get_method o.oproto.pclass "__get_field" with
+			| None -> default rt
+			| Some f ->
+				get_with (ctx.fcall (get_function ctx f) [obj; VInt (hash ctx field)]) HDyn
+		in
+		let rec loop p =
+			try
+				let fid = PMap.find field p.pfunctions in
+				(match get_function ctx fid with
+				| FFun fd as f -> get_with (VClosure (f,Some obj)) (match fd.ftype with HFun (_::args,t) -> HFun(args,t) | _ -> assert false)
+				| FNativeFun _ -> assert false)
+			with Not_found ->
+				match p.psuper with
+				| None -> default rt
+				| Some p -> loop p
+		in
+		(try
+			let idx, t = get_index field o.oproto.pclass in
+			if idx < 0 then raise Not_found;
+			get_with o.ofields.(idx) t
+		with Not_found ->
+			loop o.oproto.pclass)
+	| VVirtual vp ->
+		(match vp.vvalue with
+		| VNull ->
+			(try
+				let idx = PMap.find field vp.vtype.vindex in
+				match vp.vindexes.(idx) with
+				| VFNone -> VNull
+				| VFIndex i -> vp.vtable.(i)
+			with Not_found ->
+				VNull)
+		| v -> dyn_get_field ctx v field rt)
+	| VNull ->
+		null_access()
+	| _ ->
+		throw_msg ctx "Invalid object access"
+
+let rebuild_virtuals ctx d =
+	let old = d.dvirtuals in
+	d.dvirtuals <- [];
+	List.iter (fun v ->
+		let v2 = (match to_virtual ctx (VDynObj d) v.vtype with VVirtual v -> v | _ -> assert false) in
+		v.vindexes <- v2.vindexes;
+		v.vtable <- d.dvalues;
+	) old;
+	d.dvirtuals <- old
+
+let rec dyn_set_field ctx obj field v vt =
+	let v, vt = (match vt with
+		| HDyn ->
+			(match get_type v with
+			| None -> if v = VNull then VNull, HDyn else assert false
+			| Some t -> (match v with VDyn (v,_) -> v | _ -> v), t)
+		| t -> v, t
+	) in
+	match obj with
+	| VDynObj d ->
+		(try
+			let idx = Hashtbl.find d.dfields field in
+			d.dvalues.(idx) <- v;
+			if not (tsame d.dtypes.(idx) vt) then begin
+				d.dtypes.(idx) <- vt;
+				rebuild_virtuals ctx d;
+			end;
+		with Not_found ->
+			let idx = Array.length d.dvalues in
+			Hashtbl.add d.dfields field idx;
+			let vals2 = Array.make (idx + 1) VNull in
+			let types2 = Array.make (idx + 1) HVoid in
+			Array.blit d.dvalues 0 vals2 0 idx;
+			Array.blit d.dtypes 0 types2 0 idx;
+			vals2.(idx) <- v;
+			types2.(idx) <- vt;
+			d.dvalues <- vals2;
+			d.dtypes <- types2;
+			rebuild_virtuals ctx d;
+		)
+	| VObj o ->
+		(try
+			let idx, t = get_index field o.oproto.pclass in
+			if idx < 0 then raise Not_found;
+			o.ofields.(idx) <- dyn_cast ctx v vt t
+		with Not_found ->
+			throw_msg ctx (o.oproto.pclass.pname ^ " has no field " ^ field))
+	| VVirtual vp ->
+		dyn_set_field ctx vp.vvalue field v vt
+	| VNull ->
+		null_access()
+	| _ ->
+		throw_msg ctx "Invalid object access"
+
+let make_stack ctx (f,pos) =
+	let pos = !pos - 1 in
+	try let fid, line = f.debug.(pos) in ctx.code.debugfiles.(fid), line with _ -> "???", 0
+
+let stack_frame ctx (f,pos) =
+	let file, line = make_stack ctx (f,pos) in
+	Printf.sprintf "%s:%d: Called from fun@%d @x%X" file line f.findex (!pos - 1)
+
+let virt_make_val v =
+	let hfields = Hashtbl.create 0 in
+	let ftypes = DynArray.create() in
+	let values = DynArray.create() in
+	Array.iteri (fun i idx ->
+		match idx with
+		| VFNone -> ()
+		| VFIndex k ->
+			let n, _, t = v.vtype.vfields.(i) in
+			Hashtbl.add hfields n (DynArray.length values);
+			DynArray.add values v.vtable.(k);
+			DynArray.add ftypes t;
+	) v.vindexes;
+	VDynObj {
+		dfields = hfields;
+		dtypes = DynArray.to_array ftypes;
+		dvalues = DynArray.to_array values;
+		dvirtuals = [v];
+	}
+
+let rec vstr ctx v t =
+	let vstr = vstr ctx in
+	match v with
+	| VNull -> "null"
+	| VInt i -> Int32.to_string i
+	| VFloat f -> float_to_string f
+	| VBool b -> if b then "true" else "false"
+	| VDyn (v,t) ->
+		vstr v t
+	| VObj o ->
+		(match get_to_string ctx o.oproto.pclass with
+		| None -> "#" ^ o.oproto.pclass.pname
+		| Some f -> vstr (ctx.fcall f [v]) HBytes)
+	| VBytes b -> (try hl_to_caml b with _ -> "?" ^ String.escaped b)
+	| VClosure (f,_) -> fstr f
+	| VArray (a,t) -> "[" ^ String.concat ", " (Array.to_list (Array.map (fun v -> vstr v t) a)) ^ "]"
+	| VUndef -> "undef"
+	| VType t -> tstr t
+	| VRef (r,t) -> "*" ^ (vstr (get_ref ctx r) t)
+	| VVirtual v ->
+		(match v.vvalue with
+		| VNull ->
+			vstr (virt_make_val v) HDyn
+		| _ ->
+			vstr v.vvalue HDyn)
+	| VDynObj d ->
+		(try
+			let fid = Hashtbl.find d.dfields "__string" in
+			(match d.dtypes.(fid) with HFun ([],HBytes) -> () | _ -> raise Not_found);
+			vstr (dyn_call ctx d.dvalues.(fid) [] HBytes) HBytes
+		with Not_found ->
+			"{" ^ String.concat ", " (Hashtbl.fold (fun f i acc -> (f^":"^vstr d.dvalues.(i) d.dtypes.(i)) :: acc) d.dfields []) ^ "}")
+	| VAbstract _ -> "abstract"
+	| VEnum (i,vals) ->
+		(match t with
+		| HEnum e ->
+			let n, _, pl = e.efields.(i) in
+			if Array.length pl = 0 then
+				n
+			else
+				let rec loop i =
+					if i = Array.length pl then []
+					else let v = vals.(i) in vstr v pl.(i) :: loop (i + 1)
+				in
+				n ^ "(" ^ String.concat "," (loop 0) ^ ")"
+		| _ ->
+			assert false)
+	| VVarArgs _ -> "varargs"
+
+let interp ctx f args =
+	let func = get_function ctx in
+	let spos = ctx.stack_pos in
+	if spos + Array.length f.regs > Array.length ctx.stack then begin
+		let nsize = spos + Array.length f.regs + 256 in
+		let nstack = Array.make nsize VUndef in
+		Array.blit ctx.stack 0 nstack 0 ctx.stack_pos;
+		ctx.stack <- nstack;
+	end;
+	if ctx.checked then for i = 0 to Array.length f.regs - 1 do ctx.stack.(i + spos) <- VUndef; done;
+	ctx.stack_pos <- spos + Array.length f.regs;
+
+	let pos = ref 1 in
+	ctx.call_stack <- (f,pos) :: ctx.call_stack;
+	let fret = (match f.ftype with
+		| HFun (fargs,fret) ->
+			if ctx.checked && List.length fargs <> List.length args then error (Printf.sprintf "Invalid args: (%s) should be (%s)" (String.concat "," (List.map (vstr_d ctx) args)) (String.concat "," (List.map tstr fargs)));
+			fret
+		| _ -> assert false
+	) in
+	let fcall = ctx.fcall in
+	let rtype i = Array.unsafe_get f.regs i in
+	let check v t id =
+		if ctx.checked && not (is_compatible v t) then error (Printf.sprintf "Can't set %s(%s) with %s" (id()) (tstr t) (vstr_d ctx v))
+	in
+	let cached_string idx =
+		try
+			Hashtbl.find ctx.cached_strings idx
+		with Not_found ->
+			let s = caml_to_hl ctx.code.strings.(idx) in
+			Hashtbl.add ctx.cached_strings idx s;
+			s
+	in
+	let check_obj v o fid =
+		if ctx.checked then match o with
+		| VObj o ->
+			let _, fields = get_proto ctx o.oproto.pclass in
+			check v fields.(fid) (fun() -> "obj field")
+		| VVirtual vp ->
+			let _,_, t = vp.vtype.vfields.(fid) in
+			check v t (fun() -> "virtual field")
+		| _ ->
+			()
+	in
+	let set r v =
+		check v (rtype r) (fun() -> "register " ^ string_of_int r);
+		Array.unsafe_set ctx.stack (r + spos) v
+	in
+	list_iteri set args;
+	let get r = Array.unsafe_get ctx.stack (r + spos) in
+	let global g = Array.unsafe_get ctx.t_globals g in
+	let traps = ref [] in
+	let numop iop fop a b =
+		match rtype a with
+		(* todo : sign-extend and mask after result for HUI8/16 *)
+		| HUI8 | HUI16 | HI32 ->
+			(match get a, get b with
+			| VInt a, VInt b -> VInt (iop a b)
+			| _ -> assert false)
+		| HF32 | HF64 ->
+			(match get a, get b with
+			| VFloat a, VFloat b -> VFloat (fop a b)
+			| _ -> assert false)
+		| _ ->
+			assert false
+	in
+	let iop f a b =
+		match rtype a with
+		(* todo : sign-extend and mask after result for HUI8/16 *)
+		| HUI8 | HUI16 | HI32 ->
+			(match get a, get b with
+			| VInt a, VInt b -> VInt (f a b)
+			| _ -> assert false)
+		| _ ->
+			assert false
+	in
+	let iunop iop r =
+		match rtype r with
+		| HUI8 | HUI16 | HI32 ->
+			(match get r with
+			| VInt a -> VInt (iop a)
+			| _ -> assert false)
+		| _ ->
+			assert false
+	in
+	let ucompare a b =
+		match a, b with
+		| VInt a, VInt b ->
+			let d = Int32.sub (Int32.shift_right_logical a 16) (Int32.shift_right_logical b 16) in
+			Int32.to_int (if d = 0l then Int32.sub (Int32.logand a 0xFFFFl) (Int32.logand b 0xFFFFl) else d)
+		| _ -> assert false
+	in
+	let vcompare ra rb op =
+		let a = get ra in
+		let b = get rb in
+		let t = rtype ra in
+		let r = dyn_compare ctx a t b t in
+		if r = invalid_comparison then false else op r 0
+	in
+	let ufloat v =
+		if v < 0l then Int32.to_float v +. 4294967296. else Int32.to_float v
+	in
+	let rec loop() =
+		let op = Array.unsafe_get f.code (!pos) in
+		incr pos;
+		(match op with
+		| OMov (a,b) -> set a (get b)
+		| OInt (r,i) -> set r (VInt ctx.code.ints.(i))
+		| OFloat (r,i) -> set r (VFloat (Array.unsafe_get ctx.code.floats i))
+		| OString (r,s) -> set r (VBytes (cached_string s))
+		| OBytes (r,s) -> set r (VBytes (ctx.code.strings.(s) ^ "\x00"))
+		| OBool (r,b) -> set r (VBool b)
+		| ONull r -> set r VNull
+		| OAdd (r,a,b) -> set r (numop Int32.add ( +. ) a b)
+		| OSub (r,a,b) -> set r (numop Int32.sub ( -. ) a b)
+		| OMul (r,a,b) -> set r (numop Int32.mul ( *. ) a b)
+		| OSDiv (r,a,b) -> set r (numop (fun a b -> if b = 0l then 0l else Int32.div a b) ( /. ) a b)
+		| OUDiv (r,a,b) -> set r (iop (fun a b -> if b = 0l then 0l else Int32.of_float ((ufloat a) /. (ufloat b))) a b)
+		| OSMod (r,a,b) -> set r (numop (fun a b -> if b = 0l then 0l else Int32.rem a b) mod_float a b)
+		| OUMod (r,a,b) -> set r (iop (fun a b -> if b = 0l then 0l else Int32.of_float (mod_float (ufloat a) (ufloat b))) a b)
+		| OShl (r,a,b) -> set r (iop (fun a b -> Int32.shift_left a (Int32.to_int b)) a b)
+		| OSShr (r,a,b) -> set r (iop (fun a b -> Int32.shift_right a (Int32.to_int b)) a b)
+		| OUShr (r,a,b) -> set r (iop (fun a b -> Int32.shift_right_logical a (Int32.to_int b)) a b)
+		| OAnd (r,a,b) -> set r (iop Int32.logand a b)
+		| OOr (r,a,b) -> set r (iop Int32.logor a b)
+		| OXor (r,a,b) -> set r (iop Int32.logxor a b)
+		| ONeg (r,v) -> set r (match get v with VInt v -> VInt (Int32.neg v) | VFloat f -> VFloat (-. f) | _ -> assert false)
+		| ONot (r,v) -> set r (match get v with VBool b -> VBool (not b) | _ -> assert false)
+		| OIncr r -> set r (iunop (fun i -> Int32.add i 1l) r)
+		| ODecr r -> set r (iunop (fun i -> Int32.sub i 1l) r)
+		| OCall0 (r,f) -> set r (fcall (func f) [])
+		| OCall1 (r,f,r1) -> set r (fcall (func f) [get r1])
+		| OCall2 (r,f,r1,r2) -> set r (fcall (func f) [get r1;get r2])
+		| OCall3 (r,f,r1,r2,r3) -> set r (fcall (func f) [get r1;get r2;get r3])
+		| OCall4 (r,f,r1,r2,r3,r4) -> set r (fcall (func f) [get r1;get r2;get r3;get r4])
+		| OCallN (r,f,rl) -> set r (fcall (func f) (List.map get rl))
+		| OGetGlobal (r,g) -> set r (global g)
+		| OSetGlobal (g,r) ->
+			let v = get r in
+			check v ctx.code.globals.(g) (fun() -> "global " ^ string_of_int g);
+			Array.unsafe_set ctx.t_globals g v
+		| OJTrue (r,i) -> if get r = VBool true then pos := !pos + i
+		| OJFalse (r,i) -> if get r = VBool false then pos := !pos + i
+		| ORet r -> raise (Return (get r))
+		| OJNull (r,i) -> if get r == VNull then pos := !pos + i
+		| OJNotNull (r,i) -> if get r != VNull then pos := !pos + i
+		| OJSLt (a,b,i) -> if vcompare a b (<) then pos := !pos + i
+		| OJSGte (a,b,i) -> if vcompare a b (>=) then pos := !pos + i
+		| OJSGt (a,b,i) -> if vcompare a b (>) then pos := !pos + i
+		| OJSLte (a,b,i) -> if vcompare a b (<=) then pos := !pos + i
+		| OJULt (a,b,i) -> if ucompare (get a) (get b) < 0 then pos := !pos + i
+		| OJUGte (a,b,i) -> if ucompare (get a) (get b) >= 0 then pos := !pos + i
+		| OJEq (a,b,i) -> if vcompare a b (=) then pos := !pos + i
+		| OJNotEq (a,b,i) -> if not (vcompare a b (=)) then pos := !pos + i
+		| OJAlways i -> pos := !pos + i
+		| OToDyn (r,a) -> set r (make_dyn (get a) f.regs.(a))
+		| OToSFloat (r,a) -> set r (match get a with VInt v -> VFloat (Int32.to_float v) | VFloat _ as v -> v | _ -> assert false)
+		| OToUFloat (r,a) -> set r (match get a with VInt v -> VFloat (ufloat v) | VFloat _ as v -> v | _ -> assert false)
+		| OToInt (r,a) -> set r (match get a with VFloat v -> VInt (Int32.of_float v) | VInt _ as v -> v | _ -> assert false)
+		| OLabel _ -> ()
+		| ONew r ->
+			set r (alloc_obj ctx (rtype r))
+		| OField (r,o,fid) ->
+			set r (match get o with
+				| VObj v -> v.ofields.(fid)
+				| VVirtual v as obj ->
+					(match v.vindexes.(fid) with
+					| VFNone -> dyn_get_field ctx obj (let n,_,_ = v.vtype.vfields.(fid) in n) (rtype r)
+					| VFIndex i -> v.vtable.(i))
+				| VNull -> null_access()
+				| _ -> assert false)
+		| OSetField (o,fid,r) ->
+			let rv = get r in
+			let o = get o in
+			(match o with
+			| VObj v ->
+				check_obj rv o fid;
+				v.ofields.(fid) <- rv
+			| VVirtual v ->
+				(match v.vindexes.(fid) with
+				| VFNone ->
+					dyn_set_field ctx o (let n,_,_ = v.vtype.vfields.(fid) in n) rv (rtype r)
+				| VFIndex i ->
+					check_obj rv o fid;
+					v.vtable.(i) <- rv)
+			| VNull -> null_access()
+			| _ -> assert false)
+		| OGetThis (r, fid) ->
+			set r (match get 0 with VObj v -> v.ofields.(fid) | _ -> assert false)
+		| OSetThis (fid, r) ->
+			(match get 0 with
+			| VObj v as o ->
+				let rv = get r in
+				check_obj rv o fid;
+				v.ofields.(fid) <- rv
+			| _ -> assert false)
+		| OSetMethod (o,fid,mid) ->
+			let o = get o in
+			(match o with
+			| VObj v -> v.ofields.(fid) <- VClosure (get_function ctx mid,None)
+			| _ -> assert false)
+		| OCallMethod (r,m,rl) ->
+			(match get (List.hd rl) with
+			| VObj v -> set r (fcall v.oproto.pmethods.(m) (List.map get rl))
+			| VVirtual v ->
+				let name, _, _ = v.vtype.vfields.(m) in
+				(match v.vvalue with
+				| VObj o as obj ->
+					(try
+						let m = PMap.find name o.oproto.pclass.pfunctions in
+						set r (dyn_call ctx (VClosure (get_function ctx m,Some obj)) (List.map (fun r -> get r, rtype r) (List.tl rl)) (rtype r))
+					with Not_found ->
+						assert false)
+				| VDynObj _ ->
+					set r (dyn_call ctx v.vvalue (List.map (fun r -> get r, rtype r) (List.tl rl)) (rtype r))
+				| _ ->
+					assert false)
+			| VNull -> null_access()
+			| _ -> assert false)
+		| OCallThis (r,m,rl) ->
+			(match get 0 with
+			| VObj v as o -> set r (fcall v.oproto.pmethods.(m) (o :: List.map get rl))
+			| _ -> assert false)
+		| OCallClosure (r,v,rl) ->
+			if rtype v = HDyn then
+				set r (dyn_call ctx (get v) (List.map (fun r -> get r, rtype r) rl) (rtype r))
+			else (match get v with
+			| VClosure (f,None) -> set r (fcall f (List.map get rl))
+			| VClosure (f,Some arg) -> set r (fcall f (arg :: List.map get rl))
+			| VNull -> null_access()
+			| _ -> throw_msg ctx (vstr_d ctx (get v)))
+		| OStaticClosure (r, fid) ->
+			let f = get_function ctx fid in
+			set r (VClosure (f,None))
+		| OInstanceClosure (r, fid, v) ->
+			let f = get_function ctx fid in
+			set r (VClosure (f,Some (get v)))
+		| OVirtualClosure (r, o, m) ->
+			let m = (match get o with
+			| VObj v as obj -> VClosure (v.oproto.pmethods.(m), Some obj)
+			| VNull -> null_access()
+			| VVirtual v ->
+				let name, _, _ = v.vtype.vfields.(m) in
+				(match v.vvalue with
+				| VObj o as obj ->
+					(try
+						let m = PMap.find name o.oproto.pclass.pfunctions in
+						VClosure (get_function ctx m, Some obj)
+					with Not_found ->
+						VNull)
+				| _ -> assert false)
+			| _ -> assert false
+			) in
+			set r (if m = VNull then m else dyn_cast ctx m (match get_type m with None -> assert false | Some v -> v) (rtype r))
+		| OThrow r ->
+			throw ctx (get r)
+		| ORethrow r ->
+			ctx.call_stack <- List.rev ctx.error_stack @ ctx.call_stack;
+			throw ctx (get r)
+		| OGetUI8 (r,b,p) ->
+			(match get b, get p with
+			| VBytes b, VInt p -> set r (VInt (Int32.of_int (int_of_char (String.get b (Int32.to_int p)))))
+			| _ -> assert false)
+		| OGetUI16 (r,b,p) ->
+			(match get b, get p with
+			| VBytes b, VInt p ->
+				let a = int_of_char (String.get b (Int32.to_int p)) in
+				let b = int_of_char (String.get b (Int32.to_int p + 1)) in
+				set r (VInt (Int32.of_int (a lor (b lsl 8))))
+			| _ -> assert false)
+		| OGetI32 (r,b,p) ->
+			(match get b, get p with
+			| VBytes b, VInt p -> set r (VInt (get_i32 b (Int32.to_int p)))
+			| _ -> assert false)
+		| OGetF32 (r,b,p) ->
+			(match get b, get p with
+			| VBytes b, VInt p -> set r (VFloat (Int32.float_of_bits (get_i32 b (Int32.to_int p))))
+			| _ -> assert false)
+		| OGetF64 (r,b,p) ->
+			(match get b, get p with
+			| VBytes b, VInt p ->
+				let p = Int32.to_int p in
+				let i64 = Int64.logor (Int64.logand (Int64.of_int32 (get_i32 b p)) 0xFFFFFFFFL) (Int64.shift_left (Int64.of_int32 (get_i32 b (p + 4))) 32) in
+				set r (VFloat (Int64.float_of_bits i64))
+			| _ -> assert false)
+		| OGetArray (r,a,i) ->
+			(match get a, get i with
+			| VArray (a,_), VInt i -> set r a.(Int32.to_int i)
+			| _ -> assert false);
+		| OSetUI8 (r,p,v) ->
+			(match get r, get p, get v with
+			| VBytes b, VInt p, VInt v -> String.set b (Int32.to_int p) (char_of_int ((Int32.to_int v) land 0xFF))
+			| _ -> assert false)
+		| OSetUI16 (r,p,v) ->
+			(match get r, get p, get v with
+			| VBytes b, VInt p, VInt v ->
+				String.set b (Int32.to_int p) (char_of_int ((Int32.to_int v) land 0xFF));
+				String.set b (Int32.to_int p + 1) (char_of_int (((Int32.to_int v) lsr 8) land 0xFF))
+			| _ -> assert false)
+		| OSetI32 (r,p,v) ->
+			(match get r, get p, get v with
+			| VBytes b, VInt p, VInt v -> set_i32 b (Int32.to_int p) v
+			| _ -> assert false)
+		| OSetF32 (r,p,v) ->
+			(match get r, get p, get v with
+			| VBytes b, VInt p, VFloat v -> set_i32 b (Int32.to_int p) (Int32.bits_of_float v)
+			| _ -> assert false)
+		| OSetF64 (r,p,v) ->
+			(match get r, get p, get v with
+			| VBytes b, VInt p, VFloat v ->
+				let p = Int32.to_int p in
+				let v64 = Int64.bits_of_float v in
+				set_i32 b p (Int64.to_int32 v64);
+				set_i32 b (p + 4) (Int64.to_int32 (Int64.shift_right_logical v64 32));
+			| _ -> assert false)
+		| OSetArray (a,i,v) ->
+			(match get a, get i with
+			| VArray (a,t), VInt i ->
+				let v = get v in
+				check v t (fun() -> "array");
+				a.(Int32.to_int i) <- v
+			| _ -> assert false);
+		| OSafeCast (r, v) ->
+			set r (dyn_cast ctx (get v) (rtype v) (rtype r))
+		| OUnsafeCast (r,v) ->
+			set r (get v)
+		| OArraySize (r,a) ->
+			(match get a with
+			| VArray (a,_) -> set r (VInt (Int32.of_int (Array.length a)));
+			| _ -> assert false)
+		| OType (r,t) ->
+			set r (VType t)
+		| OGetType (r,v) ->
+			let v = get v in
+			let v = (match v with VVirtual { vvalue = VNull } -> assert false | VVirtual v -> v.vvalue | _ -> v) in
+			set r (VType (if v = VNull then HVoid else match get_type v with None -> assert false | Some t -> t));
+		| OGetTID (r,v) ->
+			set r (match get v with
+				| VType t ->
+					(VInt (Int32.of_int (match t with
+					| HVoid -> 0
+					| HUI8 -> 1
+					| HUI16 -> 2
+					| HI32 -> 3
+					| HF32 -> 4
+					| HF64 -> 5
+					| HBool -> 6
+					| HBytes -> 7
+					| HDyn -> 8
+					| HFun _ -> 9
+					| HObj _ -> 10
+					| HArray -> 11
+					| HType -> 12
+					| HRef _ -> 13
+					| HVirtual _ -> 14
+					| HDynObj -> 15
+					| HAbstract _ -> 16
+					| HEnum _ -> 17
+					| HNull _ -> 18)))
+				| _ -> assert false);
+		| ORef (r,v) ->
+			set r (VRef (RStack (v + spos),rtype v))
+		| OUnref (v,r) ->
+			set v (match get r with
+			| VRef (r,_) -> get_ref ctx r
+			| _ -> assert false)
+		| OSetref (r,v) ->
+			(match get r with
+			| VRef (r,t) ->
+				let v = get v in
+				check v t (fun() -> "ref");
+				set_ref ctx r v
+			| _ -> assert false)
+		| OToVirtual (r,rv) ->
+			set r (to_virtual ctx (get rv) (match rtype r with HVirtual vp -> vp | _ -> assert false))
+		| ODynGet (r,o,f) ->
+			set r (dyn_get_field ctx (get o) ctx.code.strings.(f) (rtype r))
+		| ODynSet (o,fid,vr) ->
+			dyn_set_field ctx (get o) ctx.code.strings.(fid) (get vr) (rtype vr)
+		| OMakeEnum (r,e,pl) ->
+			set r (VEnum (e,Array.map get (Array.of_list pl)))
+		| OEnumAlloc (r,f) ->
+			(match rtype r with
+			| HEnum e ->
+				let _, _, fl = e.efields.(f) in
+				let vl = Array.create (Array.length fl) VUndef in
+				set r (VEnum (f, vl))
+			| _ -> assert false
+			)
+		| OEnumIndex (r,v) ->
+			(match get v with
+			| VEnum (i,_) | VDyn (VEnum (i,_),_) -> set r (VInt (Int32.of_int i))
+			| _ -> assert false)
+		| OEnumField (r, v, _, i) ->
+			(match get v with
+			| VEnum (_,vl) -> set r vl.(i)
+			| _ -> assert false)
+		| OSetEnumField (v, i, r) ->
+			(match get v, rtype v with
+			| VEnum (id,vl), HEnum e ->
+				let rv = get r in
+				let _, _, fields = e.efields.(id) in
+				check rv fields.(i) (fun() -> "enumfield");
+				vl.(i) <- rv
+			| _ -> assert false)
+		| OSwitch (r, indexes, _) ->
+			(match get r with
+			| VInt i ->
+				let i = Int32.to_int i in
+				if i >= 0 && i < Array.length indexes then pos := !pos + indexes.(i)
+			| _ -> assert false)
+		| ONullCheck r ->
+			if get r = VNull then throw_msg ctx "Null access"
+		| OTrap (r,j) ->
+			let target = !pos + j in
+			traps := (r,target) :: !traps
+		| OEndTrap _ ->
+			traps := List.tl !traps
+		| ONop _ ->
+			()
+		);
+		loop()
+	in
+	let rec exec() =
+		try
+			loop()
+		with
+			| Return v ->
+				check v fret (fun() -> "return value");
+				ctx.call_stack <- List.tl ctx.call_stack;
+				ctx.stack_pos <- spos;
+				v
+			| InterpThrow v ->
+				match !traps with
+				| [] ->
+					ctx.error_stack <- List.hd ctx.call_stack :: ctx.error_stack;
+					ctx.call_stack <- List.tl ctx.call_stack;
+					raise (InterpThrow v)
+				| (r,target) :: tl ->
+					traps := tl;
+					ctx.error_stack <- (f,ref !pos) :: ctx.error_stack;
+					pos := target;
+					ctx.stack_pos <- spos + Array.length f.regs;
+					set r v;
+					exec()
+	in
+	pos := 0;
+	exec()
+
+
+let call_fun ctx f args =
+	match f with
+	| FFun f -> interp ctx f args
+	| FNativeFun (_,f,_) ->
+		try
+			f args
+		with InterpThrow v ->
+			raise (InterpThrow v)
+		| Failure msg ->
+			throw_msg ctx msg
+		| e ->
+			throw_msg ctx (Printexc.to_string e)
+
+let call_wrap ?(final=(fun()->())) ctx f args =
+	let old_st = ctx.call_stack in
+	let old_pos = ctx.stack_pos in
+	let restore() =
+		ctx.call_stack <- old_st;
+		ctx.stack_pos <- old_pos;
+	in
+	try
+		let v = call_fun ctx f args in
+		final();
+		v
+	with
+		| InterpThrow v ->
+			restore();
+			final();
+			ctx.on_error v (List.rev ctx.error_stack);
+			VNull
+		| Runtime_error msg ->
+			let stack = ctx.call_stack in
+			restore();
+			final();
+			ctx.on_error (VBytes (caml_to_hl ("HL Interp error " ^ msg))) stack;
+			VNull
+
+(* ------------------------------- HL RUNTIME ---------------------------------------------- *)
+
+let load_native ctx lib name t =
+	let unresolved() = (fun args -> error ("Unresolved native " ^ lib ^ "@" ^ name)) in
+	let int = Int32.to_int in
+	let to_int i = VInt (Int32.of_int i) in
+	let date d =
+		Unix.localtime (Int32.to_float d)
+	in
+	let to_date d =
+		let t, _ = Unix.mktime d in
+		VInt (Int32.of_float t)
+	in
+	let hl_to_caml_sub str pos len =
+		hl_to_caml (String.sub str pos len ^ "\x00\x00")
+	in
+	let no_virtual v =
+		match v with
+		| VVirtual v when v.vvalue <> VNull -> v.vvalue
+		| _ -> v
+	in
+	let set_ref = set_ref ctx in
+	let f = (match lib with
+	| "std" ->
+		(match name with
+		| "alloc_bytes" ->
+			(function
+			| [VInt i] -> VBytes (String.create (int i))
+			| _ -> assert false)
+		| "alloc_array" ->
+			(function
+			| [VType t;VInt i] -> VArray (Array.create (int i) (default t),t)
+			| _ -> assert false)
+		| "alloc_obj" ->
+			(function
+			| [VType t] -> alloc_obj ctx t
+			| _ -> assert false)
+		| "alloc_enum" ->
+			(function
+			| [VType (HEnum e); VInt idx; VArray (vl,vt)] ->
+				let idx = int idx in
+				let _, _, args = e.efields.(idx) in
+				if Array.length args <> Array.length vl then
+					VNull
+				else
+					VDyn (VEnum (idx,Array.mapi (fun i v -> dyn_cast ctx v vt args.(i)) vl),HEnum e)
+			| _ -> assert false)
+		| "array_blit" ->
+			(function
+			| [VArray (dst,_); VInt dp; VArray (src,_); VInt sp; VInt len] ->
+				Array.blit src (int sp) dst (int dp) (int len);
+				VUndef
+			| _ -> assert false)
+		| "bytes_blit" ->
+			(function
+			| [VBytes dst; VInt dp; VBytes src; VInt sp; VInt len] ->
+				String.blit src (int sp) dst (int dp) (int len);
+				VUndef
+			| [(VBytes _ | VNull); VInt _; (VBytes _ | VNull); VInt _; VInt len] ->
+				if len = 0l then VUndef else error "bytes_blit to NULL bytes";
+			| _ -> assert false)
+		| "bsort_i32" ->
+			(function
+			| [VBytes b; VInt pos; VInt len; VClosure (f,c)] ->
+				let pos = int pos and len = int len in
+				let a = Array.init len (fun i -> get_i32 b (pos + i * 4)) in
+				Array.stable_sort (fun a b ->
+					match ctx.fcall f (match c with None -> [VInt a;VInt b] | Some v -> [v;VInt a;VInt b]) with
+					| VInt i -> int i
+					| _ -> assert false
+				) a;
+				Array.iteri (fun i v -> set_i32 b (pos + i * 4) v) a;
+				VUndef;
+			| _ ->
+				assert false)
+		| "bsort_f64" ->
+			(function
+			| [VBytes b; VInt pos; VInt len; VClosure _] ->
+				assert false
+			| _ ->
+				assert false)
+		| "itos" ->
+			(function
+			| [VInt v; VRef (r,_)] ->
+				let str = Int32.to_string v in
+				set_ref r (to_int (String.length str));
+				VBytes (caml_to_hl str)
+			| _ -> assert false);
+		| "ftos" ->
+			(function
+			| [VFloat f; VRef (r,_)] ->
+				let str = float_to_string f in
+				set_ref r (to_int (String.length str));
+				VBytes (caml_to_hl str)
+			| _ -> assert false);
+		| "value_to_string" ->
+			(function
+			| [v; VRef (r,_)] ->
+				let str = caml_to_hl (vstr ctx v HDyn) in
+				set_ref r (to_int ((String.length str) lsr 1 - 1));
+				VBytes str
+			| _ -> assert false);
+		| "math_isnan" -> (function [VFloat f] -> VBool (classify_float f = FP_nan) | _ -> assert false)
+		| "math_isfinite" -> (function [VFloat f] -> VBool (match classify_float f with FP_infinite | FP_nan -> false | _ -> true) | _ -> assert false)
+		| "math_round" -> (function [VFloat f] -> VInt (Int32.of_float (floor (f +. 0.5))) | _ -> assert false)
+		| "math_floor" -> (function [VFloat f] -> VInt (Int32.of_float (floor f)) | _ -> assert false)
+		| "math_ceil" -> (function [VFloat f] -> VInt (Int32.of_float (ceil f)) | _ -> assert false)
+		| "math_ffloor" -> (function [VFloat f] -> VFloat (floor f) | _ -> assert false)
+		| "math_fceil" -> (function [VFloat f] -> VFloat (ceil f) | _ -> assert false)
+		| "math_fround" -> (function [VFloat f] -> VFloat (floor (f +. 0.5)) | _ -> assert false)
+		| "math_abs" -> (function [VFloat f] -> VFloat (abs_float f) | _ -> assert false)
+		| "math_sqrt" -> (function [VFloat f] -> VFloat (if f < 0. then nan else sqrt f) | _ -> assert false)
+		| "math_cos" -> (function [VFloat f] -> VFloat (cos f) | _ -> assert false)
+		| "math_sin" -> (function [VFloat f] -> VFloat (sin f) | _ -> assert false)
+		| "math_tan" -> (function [VFloat f] -> VFloat (tan f) | _ -> assert false)
+		| "math_acos" -> (function [VFloat f] -> VFloat (acos f) | _ -> assert false)
+		| "math_asin" -> (function [VFloat f] -> VFloat (asin f) | _ -> assert false)
+		| "math_atan" -> (function [VFloat f] -> VFloat (atan f) | _ -> assert false)
+		| "math_atan2" -> (function [VFloat a; VFloat b] -> VFloat (atan2 a b) | _ -> assert false)
+		| "math_log" -> (function [VFloat f] -> VFloat (Pervasives.log f) | _ -> assert false)
+		| "math_exp" -> (function [VFloat f] -> VFloat (exp f) | _ -> assert false)
+		| "math_pow" -> (function [VFloat a; VFloat b] -> VFloat (a ** b) | _ -> assert false)
+		| "parse_int" ->
+			(function
+			| [VBytes str; VInt pos; VInt len] ->
+				(try
+					let i = (match Interp.parse_int (hl_to_caml_sub str (int pos) (int len)) with
+						| Interp.VInt v -> Int32.of_int v
+						| Interp.VInt32 v -> v
+						| _ -> assert false
+					) in
+					VDyn (VInt i,HI32)
+				with _ ->
+					VNull)
+			| l -> assert false)
+		| "parse_float" ->
+			(function
+			| [VBytes str; VInt pos; VInt len] -> (try VFloat (Interp.parse_float (hl_to_caml_sub str (int pos) (int len))) with _ -> VFloat nan)
+			| _ -> assert false)
+		| "dyn_compare" ->
+			(function
+			| [a;b] -> to_int (dyn_compare ctx a HDyn b HDyn)
+			| _ -> assert false)
+		| "fun_compare" ->
+			let ocompare o1 o2 =
+				match o1, o2 with
+				| None, None -> true
+				| Some o1, Some o2 -> o1 == o2
+				| _ -> false
+			in
+			(function
+			| [VClosure (FFun f1,o1);VClosure (FFun f2,o2)] -> VBool (f1 == f2 && ocompare o1 o2)
+			| [VClosure (FNativeFun (f1,_,_),o1);VClosure (FNativeFun (f2,_,_),o2)] -> VBool (f1 = f2 && ocompare o1 o2)
+			| _ -> VBool false)
+		| "array_type" ->
+			(function
+			| [VArray (_,t)] -> VType t
+			| _ -> assert false)
+		| "value_cast" ->
+			(function
+			| [v;VType t] -> if is_compatible v t then v else throw_msg ctx ("Cannot cast " ^ vstr_d ctx v ^ " to " ^ tstr t);
+			| _ -> assert false)
+		| "hballoc" ->
+			(function
+			| [] -> VAbstract (AHashBytes (Hashtbl.create 0))
+			| _ -> assert false)
+		| "hbset" ->
+			(function
+			| [VAbstract (AHashBytes h);VBytes b;v] ->
+				Hashtbl.replace h (hl_to_caml b) v;
+				VUndef
+			| _ -> assert false)
+		| "hbget" ->
+			(function
+			| [VAbstract (AHashBytes h);VBytes b] ->
+				(try Hashtbl.find h (hl_to_caml b) with Not_found -> VNull)
+			| _ -> assert false)
+		| "hbvalues" ->
+			(function
+			| [VAbstract (AHashBytes h)] ->
+				let values = Hashtbl.fold (fun _ v acc -> v :: acc) h [] in
+				VArray (Array.of_list values, HDyn)
+			| _ -> assert false)
+		| "hbkeys" ->
+			(function
+			| [VAbstract (AHashBytes h)] ->
+				let keys = Hashtbl.fold (fun s _ acc -> VBytes (caml_to_hl s) :: acc) h [] in
+				VArray (Array.of_list keys, HBytes)
+			| _ -> assert false)
+		| "hbexists" ->
+			(function
+			| [VAbstract (AHashBytes h);VBytes b] -> VBool (Hashtbl.mem h (hl_to_caml b))
+			| _ -> assert false)
+		| "hbremove" ->
+			(function
+			| [VAbstract (AHashBytes h);VBytes b] ->
+				let m = Hashtbl.mem h (hl_to_caml b) in
+				if m then Hashtbl.remove h (hl_to_caml b);
+				VBool m
+			| _ -> assert false)
+		| "hialloc" ->
+			(function
+			| [] -> VAbstract (AHashInt (Hashtbl.create 0))
+			| _ -> assert false)
+		| "hiset" ->
+			(function
+			| [VAbstract (AHashInt h);VInt i;v] ->
+				Hashtbl.replace h i v;
+				VUndef
+			| _ -> assert false)
+		| "higet" ->
+			(function
+			| [VAbstract (AHashInt h);VInt i] ->
+				(try Hashtbl.find h i with Not_found -> VNull)
+			| _ -> assert false)
+		| "hivalues" ->
+			(function
+			| [VAbstract (AHashInt h)] ->
+				let values = Hashtbl.fold (fun _ v acc -> v :: acc) h [] in
+				VArray (Array.of_list values, HDyn)
+			| _ -> assert false)
+		| "hikeys" ->
+			(function
+			| [VAbstract (AHashInt h)] ->
+				let keys = Hashtbl.fold (fun i _ acc -> VInt i :: acc) h [] in
+				VArray (Array.of_list keys, HI32)
+			| _ -> assert false)
+		| "hiexists" ->
+			(function
+			| [VAbstract (AHashInt h);VInt i] -> VBool (Hashtbl.mem h i)
+			| _ -> assert false)
+		| "hiremove" ->
+			(function
+			| [VAbstract (AHashInt h);VInt i] ->
+				let m = Hashtbl.mem h i in
+				if m then Hashtbl.remove h i;
+				VBool m
+			| _ -> assert false)
+		| "hoalloc" ->
+			(function
+			| [] -> VAbstract (AHashObject (ref []))
+			| _ -> assert false)
+		| "hoset" ->
+			(function
+			| [VAbstract (AHashObject l);o;v] ->
+				let o = no_virtual o in
+				let rec replace l =
+					match l with
+					| [] -> [o,v]
+					| (o2,_) :: l when o == o2 -> (o,v) :: l
+					| p :: l -> p :: replace l
+				in
+				l := replace !l;
+				VUndef
+			| _ -> assert false)
+		| "hoget" ->
+			(function
+			| [VAbstract (AHashObject l);o] ->
+				(try List.assq (no_virtual o) !l with Not_found -> VNull)
+			| _ -> assert false)
+		| "hovalues" ->
+			(function
+			| [VAbstract (AHashObject l)] ->
+				VArray (Array.of_list (List.map snd !l), HDyn)
+			| _ -> assert false)
+		| "hokeys" ->
+			(function
+			| [VAbstract (AHashObject l)] ->
+				VArray (Array.of_list (List.map fst !l), HDyn)
+			| _ -> assert false)
+		| "hoexists" ->
+			(function
+			| [VAbstract (AHashObject l);o] -> VBool (List.mem_assq (no_virtual o) !l)
+			| _ -> assert false)
+		| "horemove" ->
+			(function
+			| [VAbstract (AHashObject rl);o] ->
+				let rec loop acc = function
+					| [] -> false
+					| (o2,_) :: l when o == o2 ->
+						rl := (List.rev acc) @ l;
+						true
+					| p :: l -> loop (p :: acc) l
+				in
+				VBool (loop [] !rl)
+			| _ -> assert false)
+		| "sys_print" ->
+			(function
+			| [VBytes str] -> print_string (hl_to_caml str); VUndef
+			| _ -> assert false)
+		| "sys_time" ->
+			(function
+			| [] -> VFloat (Unix.gettimeofday())
+			| _ -> assert false)
+		| "sys_exit" ->
+			(function
+			| [VInt code] -> raise (Sys_exit (Int32.to_int code))
+			| _ -> assert false)
+		| "sys_utf8_path" ->
+			(function
+			| [] -> VBool true
+			| _ -> assert false)
+		| "hash" ->
+			(function
+			| [VBytes str] -> VInt (hash ctx (hl_to_caml str))
+			| _ -> assert false)
+		| "type_safe_cast" ->
+			(function
+			| [VType a; VType b] -> VBool (safe_cast a b)
+			| _ -> assert false)
+		| "type_super" ->
+			(function
+			| [VType t] -> VType (match t with HObj { psuper = Some o } -> HObj o | _ -> HVoid)
+			| _ -> assert false)
+		| "type_args_count" ->
+			(function
+			| [VType t] -> to_int (match t with HFun (args,_) -> List.length args | _ -> 0)
+			| _ -> assert false)
+		| "type_get_global" ->
+			(function
+			| [VType t] ->
+				(match t with
+				| HObj c -> (match c.pclassglobal with None -> VNull | Some g -> ctx.t_globals.(g))
+				| HEnum e -> (match e.eglobal with None -> VNull | Some g -> ctx.t_globals.(g))
+				| _ -> VNull)
+			| _ -> assert false)
+		| "type_name" ->
+			(function
+			| [VType t] ->
+				VBytes (caml_to_hl (match t with
+				| HObj o -> o.pname
+				| HEnum e -> e.ename
+				| _ -> assert false))
+			| _ -> assert false)
+		| "obj_fields" ->
+			let rec get_fields v isRec =
+				match v with
+				| VDynObj o ->
+					VArray (Array.of_list (Hashtbl.fold (fun n _ acc -> VBytes (caml_to_hl n) :: acc) o.dfields []), HBytes)
+				| VObj o ->
+					let rec loop p =
+						let fields = Array.map (fun (n,_,_) -> VBytes (caml_to_hl n)) p.pfields in
+						match p.psuper with Some p when isRec -> fields :: loop p | _ -> [fields]
+					in
+					VArray (Array.concat (loop o.oproto.pclass), HBytes)
+				| VVirtual v ->
+					get_fields v.vvalue isRec
+				| _ ->
+					VNull
+			in
+			(function
+			| [v] -> get_fields v true
+			| _ -> assert false)
+		| "obj_copy" ->
+			(function
+			| [VDynObj d | VVirtual { vvalue = VDynObj d }] ->
+				VDynObj { dfields = Hashtbl.copy d.dfields; dvalues = Array.copy d.dvalues; dtypes = Array.copy d.dtypes; dvirtuals = [] }
+			| [_] -> VNull
+			| _ -> assert false)
+		| "enum_parameters" ->
+			(function
+			| [VDyn (VEnum (idx,pl),HEnum e)] ->
+				let _,_, ptypes = e.efields.(idx) in
+				VArray (Array.mapi (fun i v -> make_dyn v ptypes.(i)) pl,HDyn)
+			| _ ->
+				assert false)
+		| "type_instance_fields" ->
+			(function
+			| [VType t] ->
+				(match t with
+				| HObj o ->
+					let rec fields o =
+						let sup = (match o.psuper with None -> [||] | Some o -> fields o) in
+						Array.concat [
+							sup;
+							Array.map (fun (s,_,_) -> VBytes (caml_to_hl s)) o.pfields;
+							Array.of_list (Array.fold_left (fun acc f ->
+								let is_override = (match o.psuper with None -> false | Some p -> try ignore(get_index f.fname p); true with Not_found -> false) in
+								if is_override then acc else VBytes (caml_to_hl f.fname) :: acc
+							) [] o.pproto)
+						]
+					in
+					VArray (fields o,HBytes)
+				| _ -> VNull)
+			| _ -> assert false)
+		| "type_enum_fields" ->
+			(function
+			| [VType t] ->
+				(match t with
+				| HEnum e -> VArray (Array.map (fun (f,_,_) -> VBytes (caml_to_hl f)) e.efields,HBytes)
+				| _ -> VNull)
+			| _ -> assert false)
+		| "type_enum_eq" ->
+			(function
+			| [VDyn (VEnum _, HEnum _); VNull] | [VNull; VDyn (VEnum _, HEnum _)] -> VBool false
+			| [VNull; VNull] -> VBool true
+			| [VDyn (VEnum _ as v1, HEnum e1); VDyn (VEnum _ as v2, HEnum e2)] ->
+				let rec loop v1 v2 e =
+					match v1, v2 with
+					| VEnum (t1,_), VEnum (t2,_) when t1 <> t2 -> false
+					| VEnum (t,vl1), VEnum (_,vl2) ->
+						let _, _, pl = e.efields.(t) in
+						let rec chk i =
+							if i = Array.length pl then true
+							else
+							(match pl.(i) with
+							| HEnum e -> loop vl1.(i) vl2.(i) e
+							| t -> dyn_compare ctx vl1.(i) t vl2.(i) t = 0) && chk (i + 1)
+						in
+						chk 0
+					| _ -> assert false
+				in
+				VBool (if e1 != e2 then false else loop v1 v2 e1)
+			| _ -> assert false)
+		| "obj_get_field" ->
+			(function
+			| [o;VInt hash] ->
+				let f = (try Hashtbl.find ctx.cached_hashes hash with Not_found -> assert false) in
+				(match o with
+				| VObj _ | VDynObj _ | VVirtual _ -> dyn_get_field ctx o f HDyn
+				| _ -> VNull)
+			| _ -> assert false)
+		| "obj_set_field" ->
+			(function
+			| [o;VInt hash;v] ->
+				let f = (try Hashtbl.find ctx.cached_hashes hash with Not_found -> assert false) in
+				dyn_set_field ctx o f v HDyn;
+				VUndef
+			| _ -> assert false)
+		| "obj_has_field" ->
+			(function
+			| [o;VInt hash] ->
+				let f = (try Hashtbl.find ctx.cached_hashes hash with Not_found -> assert false) in
+				let rec loop o =
+					match o with
+					| VDynObj d -> Hashtbl.mem d.dfields f
+					| VObj o ->
+						let rec loop p =
+							if PMap.mem f p.pindex then let idx, _ = PMap.find f p.pindex in idx >= 0 else match p.psuper with None -> false | Some p -> loop p
+						in
+						loop o.oproto.pclass
+					| VVirtual v -> loop v.vvalue
+					| _ -> false
+				in
+				VBool (loop o)
+			| _ -> assert false)
+		| "obj_delete_field" ->
+			(function
+			| [o;VInt hash] ->
+				let f = (try Hashtbl.find ctx.cached_hashes hash with Not_found -> assert false) in
+				let rec loop o =
+					match o with
+					| VDynObj d when Hashtbl.mem d.dfields f ->
+						let idx = Hashtbl.find d.dfields f in
+						let count = Array.length d.dvalues in
+						Hashtbl.remove d.dfields f;
+						let fields = Hashtbl.fold (fun name i acc -> (name,if i < idx then i else i - 1) :: acc) d.dfields [] in
+						Hashtbl.clear d.dfields;
+						List.iter (fun (n,i) -> Hashtbl.add d.dfields n i) fields;
+						let vals2 = Array.make (count - 1) VNull in
+						let types2 = Array.make (count - 1) HVoid in
+						let len = count - idx - 1 in
+						Array.blit d.dvalues 0 vals2 0 idx;
+						Array.blit d.dvalues (idx + 1) vals2 idx len;
+						Array.blit d.dtypes 0 types2 0 idx;
+						Array.blit d.dtypes (idx + 1) types2 idx len;
+						d.dvalues <- vals2;
+						d.dtypes <- types2;
+						rebuild_virtuals ctx d;
+						true
+					| VVirtual v -> loop v.vvalue
+					| _ -> false
+				in
+				VBool (loop o)
+			| _ -> assert false)
+		| "get_virtual_value" ->
+			(function
+			| [VVirtual v] -> v.vvalue
+			| _ -> assert false)
+		| "ucs2length" ->
+			(function
+			| [VBytes s; VInt pos] ->
+				let delta = int pos in
+				let rec loop p =
+					let c = int_of_char s.[p+delta] lor ((int_of_char s.[p+delta+1]) lsl 8) in
+					if c = 0 then p lsr 1 else loop (p + 2)
+				in
+				to_int (loop 0)
+			| _ -> assert false)
+		| "utf8_to_utf16" ->
+			(function
+			| [VBytes s; VInt pos; VRef (r,HI32)] ->
+				let s = String.sub s (int pos) (String.length s - (int pos)) in
+				let u16 = caml_to_hl (try String.sub s 0 (String.index s '\000') with Not_found -> assert false) in
+				set_ref r (to_int (String.length u16 - 2));
+				VBytes u16
+			| _ -> assert false)
+		| "utf16_to_utf8" ->
+			(function
+			| [VBytes s; VInt pos; VRef (r,HI32)] ->
+				let s = String.sub s (int pos) (String.length s - (int pos)) in
+				let u8 = hl_to_caml s in
+				set_ref r (to_int (String.length u8));
+				VBytes (u8 ^ "\x00")
+			| _ -> assert false)
+		| "ucs2_upper" ->
+			(function
+			| [VBytes s; VInt pos; VInt len] ->
+				let buf = Buffer.create 0 in
+				utf16_iter (fun c ->
+					let c =
+						if c >= int_of_char 'a' && c <= int_of_char 'z' then c + int_of_char 'A' - int_of_char 'a'
+						else c
+					in
+					utf16_add buf c
+				) (String.sub s (int pos) ((int len) lsl 1));
+				utf16_add buf 0;
+				VBytes (Buffer.contents buf)
+			| _ -> assert false)
+		| "ucs2_lower" ->
+			(function
+			| [VBytes s; VInt pos; VInt len] ->
+				let buf = Buffer.create 0 in
+				utf16_iter (fun c ->
+					let c =
+						if c >= int_of_char 'A' && c <= int_of_char 'Z' then c + int_of_char 'a' - int_of_char 'A'
+						else c
+					in
+					utf16_add buf c
+				) (String.sub s (int pos) ((int len) lsl 1));
+				utf16_add buf 0;
+				VBytes (Buffer.contents buf)
+			| _ -> assert false)
+		| "url_encode" ->
+			(function
+			| [VBytes s; VRef (r, HI32)] ->
+				let s = hl_to_caml s in
+				let buf = Buffer.create 0 in
+				let hex = "0123456789ABCDEF" in
+				for i = 0 to String.length s - 1 do
+					let c = String.unsafe_get s i in
+					match c with
+					| 'A'..'Z' | 'a'..'z' | '0'..'9' | '_' | '-' | '.' ->
+						utf16_char buf c
+					| _ ->
+						utf16_char buf '%';
+						utf16_char buf (String.unsafe_get hex (int_of_char c lsr 4));
+						utf16_char buf (String.unsafe_get hex (int_of_char c land 0xF));
+				done;
+				utf16_add buf 0;
+				let str = Buffer.contents buf in
+				set_ref r (to_int (String.length str lsr 1 - 1));
+				VBytes str
+			| _ -> assert false)
+		| "url_decode" ->
+			(function
+			| [VBytes s; VRef (r, HI32)] ->
+				let s = hl_to_caml s in
+				let b = Buffer.create 0 in
+				let len = String.length s in
+				let decode c =
+					match c with
+					| '0'..'9' -> Some (int_of_char c - int_of_char '0')
+					| 'a'..'f' -> Some (int_of_char c - int_of_char 'a' + 10)
+					| 'A'..'F' -> Some (int_of_char c - int_of_char 'A' + 10)
+					| _ -> None
+				in
+				let rec loop i =
+					if i = len then () else
+					let c = String.unsafe_get s i in
+					match c with
+					| '%' ->
+						let p1 = (try decode (String.get s (i + 1)) with _ -> None) in
+						let p2 = (try decode (String.get s (i + 2)) with _ -> None) in
+						(match p1, p2 with
+						| Some c1, Some c2 ->
+							Buffer.add_char b (char_of_int ((c1 lsl 4) lor c2));
+							loop (i + 3)
+						| _ ->
+							loop (i + 1));
+					| '+' ->
+						Buffer.add_char b ' ';
+						loop (i + 1)
+					| c ->
+						Buffer.add_char b c;
+						loop (i + 1)
+				in
+				loop 0;
+				let str = Buffer.contents b in
+				set_ref r (to_int (UTF8.length str));
+				VBytes (caml_to_hl str)
+			| _ -> assert false)
+		| "call_method" ->
+			(function
+			| [f;VArray (args,HDyn)] -> dyn_call ctx f (List.map (fun v -> v,HDyn) (Array.to_list args)) HDyn
+			| _ -> assert false)
+		| "no_closure" ->
+			(function
+			| [VClosure (f,_)] -> VClosure (f,None)
+			| _ -> assert false)
+		| "get_closure_value" ->
+			(function
+			| [VClosure (_,None)] -> VNull
+			| [VClosure (_,Some v)] -> v
+			| _ -> assert false)
+		| "make_var_args" ->
+			(function
+			| [VClosure (f,arg)] -> VVarArgs (f,arg)
+			| _ -> assert false)
+		| "bytes_find" ->
+			(function
+			| [VBytes src; VInt pos; VInt len; VBytes chk; VInt cpos; VInt clen; ] ->
+				to_int (try int pos + ExtString.String.find (String.sub src (int pos) (int len)) (String.sub chk (int cpos) (int clen)) with ExtString.Invalid_string -> -1)
+			| _ -> assert false)
+		| "bytes_compare" ->
+			(function
+			| [VBytes a; VInt apos; VBytes b; VInt bpos; VInt len] -> to_int (String.compare (String.sub a (int apos) (int len)) (String.sub b (int bpos) (int len)))
+			| _ -> assert false)
+		| "bytes_fill" ->
+			(function
+			| [VBytes a; VInt pos; VInt len; VInt v] ->
+				String.fill a (int pos) (int len) (char_of_int ((int v) land 0xFF));
+				VUndef
+			| _ -> assert false)
+		| "exception_stack" ->
+			(function
+			| [] -> VArray (Array.map (fun e -> VBytes (caml_to_hl (stack_frame ctx e))) (Array.of_list (List.rev ctx.error_stack)),HBytes)
+			| _ -> assert false)
+		| "date_new" ->
+			(function
+			| [VInt y; VInt mo; VInt d; VInt h; VInt m; VInt s] ->
+				let t = Unix.localtime (Unix.time()) in
+				let t = { t with
+					tm_year = int y - 1900;
+					tm_mon = int mo;
+					tm_mday = int d;
+					tm_hour = int h;
+					tm_min = int m;
+					tm_sec = int s;
+				} in
+				to_date t
+			| _ ->
+				assert false)
+		| "date_now" ->
+			(function
+			| [] -> to_date (Unix.localtime (Unix.time()))
+			| _ -> assert false)
+		| "date_get_time" ->
+			(function
+			| [VInt v] -> VFloat (fst (Unix.mktime (date v)) *. 1000.)
+			| _ -> assert false)
+		| "date_from_time" ->
+			(function
+			| [VFloat f] -> to_date (Unix.localtime (f /. 1000.))
+			| _ -> assert false)
+		| "date_get_inf" ->
+			(function
+			| [VInt d;year;month;day;hours;minutes;seconds;wday] ->
+				let d = date d in
+				let set r v =
+					match r with
+					| VNull -> ()
+					| VRef (r,HI32) -> set_ref r (to_int v)
+					| _ -> assert false
+				in
+				set year (d.tm_year + 1900);
+				set month d.tm_mon;
+				set day d.tm_mday;
+				set hours d.tm_hour;
+				set minutes d.tm_min;
+				set seconds d.tm_sec;
+				set wday d.tm_wday;
+				VUndef
+			| _ -> assert false)
+		| "date_to_string" ->
+			(function
+			| [VInt d; VRef (r,HI32)] ->
+				let t = date d in
+				let str = Printf.sprintf "%.4d-%.2d-%.2d %.2d:%.2d:%.2d" (t.tm_year + 1900) (t.tm_mon + 1) t.tm_mday t.tm_hour t.tm_min t.tm_sec in
+				set_ref r (to_int (String.length str));
+				VBytes (caml_to_hl str)
+			| _ -> assert false)
+		| "rnd_init_system" ->
+			(function
+			| [] -> Random.self_init(); VAbstract ARandom
+			| _ -> assert false)
+		| "rnd_int" ->
+			(function
+			| [VAbstract ARandom] -> VInt (Int32.of_int (Random.bits()))
+			| _ -> assert false)
+		| "rnd_float" ->
+			(function
+			| [VAbstract ARandom] -> VFloat (Random.float 1.)
+			| _ -> assert false)
+		| "regexp_new_options" ->
+			(function
+			| [VBytes str; VBytes opt] ->
+				let case_sensitive = ref true in
+				List.iter (function
+					| 'm' -> () (* always ON ? *)
+					| 'i' -> case_sensitive := false
+					| c -> failwith ("Unsupported regexp option '" ^ String.make 1 c ^ "'")
+				) (ExtString.String.explode (hl_to_caml opt));
+				let buf = Buffer.create 0 in
+				let rec loop prev esc = function
+					| [] -> ()
+					| c :: l when esc ->
+						(match c with
+						| 'n' -> Buffer.add_char buf '\n'
+						| 'r' -> Buffer.add_char buf '\r'
+						| 't' -> Buffer.add_char buf '\t'
+						| 's' -> Buffer.add_string buf "[ \t\r\n]"
+						| 'd' -> Buffer.add_string buf "[0-9]"
+						| '\\' -> Buffer.add_string buf "\\\\"
+						| '(' | ')' | '{' | '}' -> Buffer.add_char buf c
+						| '1'..'9' | '+' | '$' | '^' | '*' | '?' | '.' | '[' | ']' ->
+							Buffer.add_char buf '\\';
+							Buffer.add_char buf c;
+						| _ -> failwith ("Unsupported escaped char '" ^ String.make 1 c ^ "'"));
+						loop c false l
+					| c :: l ->
+						match c with
+						| '\\' -> loop prev true l
+						| '(' | '|' | ')' ->
+							Buffer.add_char buf '\\';
+							Buffer.add_char buf c;
+							loop c false l
+						| '?' when prev = '(' && (match l with ':' :: _ -> true | _ -> false) ->
+							failwith "Non capturing groups '(?:' are not supported in macros"
+						| '?' when prev = '*' ->
+							failwith "Ungreedy *? are not supported in macros"
+						| _ ->
+							Buffer.add_char buf c;
+							loop c false l
+				in
+				loop '\000' false (ExtString.String.explode (hl_to_caml str));
+				let str = Buffer.contents buf in
+				let r = {
+					r = if !case_sensitive then Str.regexp str else Str.regexp_case_fold str;
+					r_string = "";
+					r_groups = [||];
+				} in
+				VAbstract (AReg r)
+			| _ ->
+				assert false);
+		| "regexp_match" ->
+			(function
+			| [VAbstract (AReg r);VBytes str;VInt pos;VInt len] ->
+				let str = hl_to_caml str and pos = int pos and len = int len in
+				let nstr, npos, delta = (if len = String.length str - pos then str, pos, 0 else String.sub str pos len, 0, pos) in
+				(try
+					ignore(Str.search_forward r.r nstr npos);
+					let rec loop n =
+						if n = 9 then
+							[]
+						else try
+							(Some (Str.group_beginning n + delta, Str.group_end n + delta)) :: loop (n + 1)
+						with Not_found ->
+							None :: loop (n + 1)
+						| Invalid_argument _ ->
+							[]
+					in
+					r.r_string <- str;
+					r.r_groups <- Array.of_list (loop 0);
+					VBool true;
+				with Not_found ->
+					VBool false)
+			| _ -> assert false);
+		| "regexp_matched_pos" ->
+			(function
+			| [VAbstract (AReg r); VInt n; VRef (rr,HI32)] ->
+				let n = int n in
+				(match (try r.r_groups.(n) with _ -> failwith ("Invalid group " ^ string_of_int n)) with
+				| None -> to_int (-1)
+				| Some (pos,pend) -> set_ref rr (to_int (pend - pos)); to_int pos)
+			| [VAbstract (AReg r); VInt n; VNull] ->
+				let n = int n in
+				(match (try r.r_groups.(n) with _ -> failwith ("Invalid group " ^ string_of_int n)) with
+				| None -> to_int (-1)
+				| Some (pos,pend) -> to_int pos)
+			| _ -> assert false)
+		| "make_macro_pos" ->
+			(function
+			| [VBytes file;VInt min;VInt max] ->
+				VAbstract (APos { Globals.pfile = String.sub file 0 (String.length file - 1); pmin = Int32.to_int min; pmax = Int32.to_int max })
+			| _ -> assert false)
+		| _ ->
+			unresolved())
+	| "macro" ->
+		(match ctx.resolve_macro_api name with
+		| None -> unresolved()
+		| Some f -> f)
+	| _ ->
+		unresolved()
+	) in
+	FNativeFun (lib ^ "@" ^ name, f, t)
+
+let create checked =
+	let ctx = {
+		t_globals = [||];
+		t_functions = [||];
+		call_stack = [];
+		error_stack = [];
+		stack = [||];
+		stack_pos = 0;
+		cached_protos = Hashtbl.create 0;
+		cached_strings = Hashtbl.create 0;
+		cached_hashes = Hashtbl.create 0;
+		code = {
+			functions = [||];
+			globals = [||];
+			natives = [||];
+			strings = [||];
+			ints = [||];
+			debugfiles = [||];
+			floats = [||];
+			entrypoint = 0;
+			version = 0;
+		};
+		checked = checked;
+		fcall = (fun _ _ -> assert false);
+		on_error = (fun _ _ -> assert false);
+		resolve_macro_api = (fun _ -> None);
+	} in
+	ctx.on_error <- (fun msg stack -> failwith (vstr ctx msg HDyn ^ "\n" ^ String.concat "\n" (List.map (stack_frame ctx) stack)));
+	ctx.fcall <- call_fun ctx;
+	ctx
+
+let set_error_handler ctx e =
+	ctx.on_error <- e
+
+let set_macro_api ctx f =
+	ctx.resolve_macro_api <- f
+
+let add_code ctx code =
+	(* expand global table *)
+	let globals = Array.map default code.globals in
+	Array.blit ctx.t_globals 0 globals 0 (Array.length ctx.t_globals);
+	ctx.t_globals <- globals;
+	(* expand function table *)
+	let nfunctions = Array.length code.functions + Array.length code.natives in
+	let functions = Array.create nfunctions (FNativeFun ("",(fun _ -> assert false),HDyn)) in
+	Array.blit ctx.t_functions 0 functions 0 (Array.length ctx.t_functions);
+	let rec loop i =
+		if i = Array.length code.natives then () else
+		let lib, name, t, idx = code.natives.(i) in
+		functions.(idx) <- load_native ctx code.strings.(lib) code.strings.(name) t;
+		loop (i + 1)
+	in
+	loop (Array.length ctx.code.natives);
+	let rec loop i =
+		if i = Array.length code.functions then () else
+		let fd = code.functions.(i) in
+		functions.(fd.findex) <- FFun fd;
+		loop (i + 1)
+	in
+	loop  (Array.length ctx.code.functions);
+	ctx.t_functions <- functions;
+	ctx.code <- code;
+	(* call entrypoint *)
+	ignore(call_wrap ctx functions.(code.entrypoint) [])
+
+(* ------------------------------- CHECK ---------------------------------------------- *)
+
+let check code macros =
+	let ftypes = Array.create (Array.length code.natives + Array.length code.functions) HVoid in
+	let is_native_fun = Hashtbl.create 0 in
+
+	let check_fun f =
+		let pos = ref 0 in
+		let error msg =
+			let dfile, dline = f.debug.(!pos) in
+			let file = code.debugfiles.(dfile) in
+			let msg = Printf.sprintf "Check failure at fun@%d @%X - %s" f.findex (!pos) msg in
+			if macros then begin
+				let low = dline land 0xFFFFF in
+				let pos = {
+					Globals.pfile = file;
+					Globals.pmin = low;
+					Globals.pmax = low + (dline lsr 20);
+				} in
+				Common.abort msg pos
+			end else
+				failwith (Printf.sprintf "\n%s:%d: %s" file dline msg)
+		in
+		let targs, tret = (match f.ftype with HFun (args,ret) -> args, ret | _ -> assert false) in
+		let rtype i = try f.regs.(i) with _ -> HObj { null_proto with pname = "OUT_OF_BOUNDS:" ^ string_of_int i } in
+		let check t1 t2 =
+			if not (safe_cast t1 t2) then error (tstr t1 ^ " should be " ^ tstr t2)
+		in
+		let reg_inf r =
+			"Register " ^ string_of_int r ^ "(" ^ tstr (rtype r) ^ ")"
+		in
+		let reg r t =
+			if not (safe_cast (rtype r) t) then error (reg_inf r ^ " should be " ^ tstr t ^ " and not " ^ tstr (rtype r))
+		in
+		let numeric r =
+			match rtype r with
+			| HUI8 | HUI16 | HI32 | HF32 | HF64 -> ()
+			| _ -> error (reg_inf r ^ " should be numeric")
+		in
+		let int r =
+			match rtype r with
+			| HUI8 | HUI16 | HI32 -> ()
+			| _ -> error (reg_inf r ^ " should be integral")
+		in
+		let float r =
+			match rtype r with
+			| HF32 | HF64 -> ()
+			| _ -> error (reg_inf r ^ " should be float")
+		in
+		let call f args r =
+			match ftypes.(f) with
+			| HFun (targs, tret) ->
+				if List.length args <> List.length targs then error (tstr (HFun (List.map rtype args, rtype r)) ^ " should be " ^ tstr ftypes.(f));
+				List.iter2 reg args targs;
+				check tret (rtype r)
+			| _ -> assert false
+		in
+		let can_jump delta =
+			if !pos + 1 + delta < 0 || !pos + 1 + delta >= Array.length f.code then error "Jump outside function bounds";
+			if delta < 0 && Array.get f.code (!pos + 1 + delta) <> OLabel 0 then error "Jump back without Label";
+		in
+		let is_obj r =
+			match rtype r with
+			| HObj _ -> ()
+			| _ -> error (reg_inf r ^ " should be object")
+		in
+		let is_enum r =
+			match rtype r with
+			| HEnum _ -> ()
+			| _ -> error (reg_inf r ^ " should be enum")
+		in
+		let is_dyn r =
+			if not (is_dynamic (rtype r)) then error (reg_inf r ^ " should be castable to dynamic")
+		in
+		let tfield o fid proto =
+			if fid < 0 then error (reg_inf o ^ " does not have " ^ (if proto then "proto " else "") ^ "field " ^ string_of_int fid);
+			match rtype o with
+			| HObj p ->
+				if proto then ftypes.(p.pvirtuals.(fid)) else (try snd (resolve_field p fid) with Not_found -> error (reg_inf o ^ " does not have field " ^ string_of_int fid))
+			| HVirtual v when not proto ->
+				let _,_, t = v.vfields.(fid) in
+				t
+			| _ ->
+				is_obj o;
+				HVoid
+		in
+		list_iteri reg targs;
+		Array.iteri (fun i op ->
+			pos := i;
+			match op with
+			| OMov (a,b) ->
+				reg b (rtype a)
+			| OInt (r,i) ->
+				ignore(code.ints.(i));
+				int r
+			| OFloat (r,i) ->
+				if rtype r <> HF32 then reg r HF64;
+				if i < 0 || i >= Array.length code.floats then error "float outside range";
+			| OBool (r,_) ->
+				reg r HBool
+			| OString (r,i) | OBytes (r,i) ->
+				reg r HBytes;
+				if i < 0 || i >= Array.length code.strings then error "string outside range";
+			| ONull r ->
+				let t = rtype r in
+				if not (is_nullable t) then error (tstr t ^ " is not nullable")
+			| OAdd (r,a,b) | OSub (r,a,b) | OMul (r,a,b) | OSDiv (r,a,b) | OUDiv (r,a,b) | OSMod (r,a,b) | OUMod(r,a,b) ->
+				numeric r;
+				reg a (rtype r);
+				reg b (rtype r);
+			| ONeg (r,a) ->
+				numeric r;
+				reg a (rtype r);
+			| OShl (r,a,b) | OSShr (r,a,b) | OUShr (r,a,b) | OAnd (r,a,b) | OOr (r,a,b) | OXor (r,a,b) ->
+				int r;
+				reg a (rtype r);
+				reg b (rtype r);
+			| OIncr r ->
+				int r
+			| ODecr r ->
+				int r
+			| ONot (a,b) ->
+				reg a HBool;
+				reg b HBool;
+			| OCall0 (r,f) ->
+				call f [] r
+			| OCall1 (r, f, a) ->
+				call f [a] r
+			| OCall2 (r, f, a, b) ->
+				call f [a;b] r
+			| OCall3 (r, f, a, b, c) ->
+				call f [a;b;c] r
+			| OCall4 (r, f, a, b, c, d) ->
+				call f [a;b;c;d] r
+			| OCallN (r,f,rl) ->
+				call f rl r
+			| OCallThis (r, m, rl) ->
+				(match tfield 0 m true with
+				| HFun (tobj :: targs, tret) when List.length targs = List.length rl -> reg 0 tobj; List.iter2 reg rl targs; check tret (rtype r)
+				| t -> check t (HFun (rtype 0 :: List.map rtype rl, rtype r)));
+			| OCallMethod (r, m, rl) ->
+				(match rl with
+				| [] -> assert false
+				| obj :: rl2 ->
+					let t, rl = (match rtype obj with
+						| HVirtual v ->
+							let _, _, t = v.vfields.(m) in
+							t, rl2
+						| _ ->
+							tfield obj m true, rl
+					) in
+					match t with
+					| HFun (targs, tret) when List.length targs = List.length rl -> List.iter2 reg rl targs; check tret (rtype r)
+					| t -> check t (HFun (List.map rtype rl, rtype r)))
+			| OCallClosure (r,f,rl) ->
+				(match rtype f with
+				| HFun (targs,tret) when List.length targs = List.length rl -> List.iter2 reg rl targs; check tret (rtype r)
+				| HDyn -> List.iter (fun r -> ignore(rtype r)) rl;
+				| _ -> reg f (HFun(List.map rtype rl,rtype r)))
+			| OGetGlobal (r,g) ->
+				if not (safe_cast code.globals.(g) (rtype r)) then reg r code.globals.(g)
+			| OSetGlobal (g,r) ->
+				reg r code.globals.(g)
+			| ORet r ->
+				reg r tret
+			| OJTrue (r,delta) | OJFalse (r,delta) ->
+				reg r HBool;
+				can_jump delta
+			| OJNull (r,delta) | OJNotNull (r,delta) ->
+				ignore(rtype r);
+				can_jump delta
+			| OJUGte (a,b,delta) | OJULt (a,b,delta) | OJSGte (a,b,delta) | OJSLt (a,b,delta) | OJSGt (a,b,delta) | OJSLte (a,b,delta) ->
+				if not (safe_cast (rtype a) (rtype b)) then reg b (rtype a);
+				can_jump delta
+			| OJEq (a,b,delta) | OJNotEq (a,b,delta) ->
+				(match rtype a, rtype b with
+				| (HObj _ | HVirtual _), (HObj _ | HVirtual _) -> ()
+				| ta, tb when safe_cast tb ta -> ()
+				| _ -> reg a (rtype b));
+				can_jump delta
+			| OJAlways d ->
+				can_jump d
+			| OToDyn (r,a) ->
+				(* we can still use OToDyn on nullable if we want to turn them into dynamic *)
+				if is_dynamic (rtype a) then reg a HI32; (* don't wrap as dynamic types that can safely be cast to it *)
+				if rtype r <> HDyn then reg r (HNull (rtype a))
+			| OToSFloat (a,b) | OToUFloat (a,b) ->
+				float a;
+				(match rtype b with HF32 | HF64 -> () | _ -> int b);
+			| OToInt (a,b) ->
+				int a;
+				(match rtype b with HF32 | HF64 -> () | _ -> int b);
+			| OLabel _ ->
+				()
+			| ONew r ->
+				(match rtype r with
+				| HDynObj | HVirtual _ -> ()
+				| _ -> is_obj r)
+			| OField (r,o,fid) ->
+				check (tfield o fid false) (rtype r)
+			| OSetField (o,fid,r) ->
+				reg r (tfield o fid false)
+			| OGetThis (r,fid) ->
+				check (tfield 0 fid false) (rtype r)
+			| OSetThis(fid,r) ->
+				reg r (tfield 0 fid false)
+			| OStaticClosure (r,f) ->
+				reg r ftypes.(f)
+			| OSetMethod (o,f,fid) ->
+				check ftypes.(fid) (tfield o f false)
+			| OVirtualClosure (r,o,fid) ->
+				(match rtype o with
+				| HObj _ ->
+					(match tfield o fid true with
+					| HFun (t :: tl, tret) ->
+						reg o t;
+						reg r (HFun (tl,tret));
+					| _ ->
+						assert false)
+				| HVirtual v ->
+					let _,_, t = v.vfields.(fid) in
+					reg r t;
+				| _ ->
+					is_obj o)
+			| OInstanceClosure (r,f,arg) ->
+				(match ftypes.(f) with
+				| HFun (t :: tl, tret) ->
+					reg arg t;
+					if not (is_nullable t) then error (reg_inf r ^ " should be nullable");
+					reg r (HFun (tl,tret));
+				| _ -> assert false);
+			| OThrow r ->
+				reg r HDyn
+			| ORethrow r ->
+				reg r HDyn
+			| OGetArray (v,a,i) ->
+				reg a HArray;
+				reg i HI32;
+				ignore(rtype v);
+			| OGetUI8 (r,b,p) | OGetI32 (r,b,p) | OGetUI16(r,b,p) ->
+				reg r HI32;
+				reg b HBytes;
+				reg p HI32;
+			| OGetF32 (r,b,p) ->
+				reg r HF32;
+				reg b HBytes;
+				reg p HI32;
+			| OGetF64 (r,b,p) ->
+				reg r HF64;
+				reg b HBytes;
+				reg p HI32;
+			| OSetUI8 (r,p,v) | OSetI32 (r,p,v) | OSetUI16 (r,p,v) ->
+				reg r HBytes;
+				reg p HI32;
+				reg v HI32;
+			| OSetF32 (r,p,v) ->
+				reg r HBytes;
+				reg p HI32;
+				reg v HF32;
+			| OSetF64 (r,p,v) ->
+				reg r HBytes;
+				reg p HI32;
+				reg v HF64;
+			| OSetArray (a,i,v) ->
+				reg a HArray;
+				reg i HI32;
+				ignore(rtype v);
+			| OUnsafeCast (a,b) ->
+				is_dyn a;
+				is_dyn b;
+			| OSafeCast (a,b) ->
+				ignore(rtype a);
+				ignore(rtype b);
+			| OArraySize (r,a) ->
+				reg a HArray;
+				reg r HI32
+			| OType (r,_) ->
+				reg r HType
+			| OGetType (r,v) ->
+				reg r HType;
+				is_dyn v;
+			| OGetTID (r,v) ->
+				reg r HI32;
+				reg v HType;
+			| OUnref (v,r) ->
+				(match rtype r with
+				| HRef t -> check t (rtype v)
+				| _ -> reg r (HRef (rtype v)))
+			| ORef (r,v)
+			| OSetref (r,v) ->
+				(match rtype r with HRef t -> reg v t | _ -> reg r (HRef (rtype v)))
+			| OToVirtual (r,v) ->
+				(match rtype r with
+				| HVirtual _ -> ()
+				| _ -> reg r (HVirtual {vfields=[||];vindex=PMap.empty;}));
+				(match rtype v with
+				| HObj _ | HDynObj | HDyn | HVirtual _ -> ()
+				| _ -> reg v HDynObj)
+			| ODynGet (v,r,f) | ODynSet (r,f,v) ->
+				ignore(code.strings.(f));
+				ignore(rtype v);
+				(match rtype r with
+				| HObj _ | HDyn | HDynObj | HVirtual _ -> ()
+				| _ -> reg r HDynObj)
+			| OMakeEnum (r,index,pl) ->
+				(match rtype r with
+				| HEnum e ->
+					let _,_, fl = e.efields.(index) in
+					if Array.length fl <> List.length pl then error ("MakeEnum has " ^ (string_of_int (List.length pl)) ^ " params while " ^ (string_of_int (Array.length fl)) ^ " are required");
+					List.iter2 (fun r t -> reg r t) pl (Array.to_list fl)
+				| _ ->
+					is_enum r)
+			| OEnumAlloc (r,index) ->
+				(match rtype r with
+				| HEnum e ->
+					ignore(e.efields.(index))
+				| _ ->
+					is_enum r)
+			| OEnumIndex (r,v) ->
+				if rtype v <> HDyn then is_enum v;
+				reg r HI32;
+			| OEnumField (r,e,f,i) ->
+				(match rtype e with
+				| HEnum e ->
+					let _, _, tl = e.efields.(f) in
+					check tl.(i) (rtype r)
+				| _ -> is_enum e)
+			| OSetEnumField (e,i,r) ->
+				(match rtype e with
+				| HEnum e ->
+					let _, _, tl = e.efields.(0) in
+					check (rtype r) tl.(i)
+				| _ -> is_enum e)
+			| OSwitch (r,idx,eend) ->
+				reg r HI32;
+				Array.iter can_jump idx;
+				if eend + 1 + i <> Array.length f.code then can_jump eend
+			| ONullCheck r ->
+				ignore(rtype r)
+			| OTrap (r, idx) ->
+				reg r HDyn;
+				can_jump idx
+			| OEndTrap _ ->
+				()
+			| ONop _ ->
+				()
+		) f.code
+		(* TODO : check that all path correctly initialize NULL values and reach a return *)
+	in
+	Array.iter (fun fd ->
+		if fd.findex >= Array.length ftypes then failwith ("Invalid function index " ^ string_of_int fd.findex);
+		if ftypes.(fd.findex) <> HVoid then failwith ("Duplicate function bind " ^ string_of_int fd.findex ^ " " ^ fundecl_name fd);
+		ftypes.(fd.findex) <- fd.ftype;
+	) code.functions;
+	Array.iter (fun (lib,name,t,idx) ->
+		if idx >= Array.length ftypes then failwith ("Invalid native function index " ^ string_of_int idx ^ " for "^ code.strings.(lib) ^ "@" ^ code.strings.(name));
+		if ftypes.(idx) <> HVoid then failwith ("Duplicate native function bind " ^ string_of_int idx);
+		Hashtbl.add is_native_fun idx true;
+		ftypes.(idx) <- t
+	) code.natives;
+	(* TODO : check that no object type has a virtual native in his proto *)
+	Array.iter check_fun code.functions
+
+(* ------------------------------- SPEC ---------------------------------------------- *)
+
+open Hlopt
+
+type svalue =
+	| SUndef
+	| SArg of int
+	| SInt of int32
+	| SFloat of float
+	| SString of string
+	| SBool of bool
+	| SNull
+	| SType of ttype
+	| SOp of string * svalue * svalue
+	| SUnop of string * svalue
+	| SResult of string
+	| SFun of int * svalue option
+	| SMeth of svalue * int
+	| SGlobal of int
+	| SField of svalue * int
+	| SDField of svalue * string
+	| SConv of string * svalue
+	| SCast of svalue * ttype
+	| SMem of svalue * svalue * ttype
+	| SEnum of int * svalue list
+	| SEnumField of svalue * int * int
+	| SUnion of svalue list
+	| SRef of int
+	| SRefResult of string
+	| SUnreach
+	| SExc
+	| SDelayed of string * svalue list option ref
+
+type call_spec =
+	| SFid of int
+	| SMethod of int
+	| SClosure of svalue
+
+type spec =
+	| SCall of call_spec * svalue list
+	| SGlobalSet of int * svalue
+	| SFieldSet of svalue * int * svalue
+	| SFieldDSet of svalue * string * svalue
+	| SJEq of string * svalue
+	| SJComp of string * svalue * svalue
+	| SJump
+	| SRet of svalue
+	| SNullCheck of svalue
+	| SThrow of svalue
+	| SSwitch of svalue
+	| SWriteMem of svalue * svalue * svalue * ttype
+	| SSetRef of svalue * svalue
+	| SSetEnumField of svalue * int * svalue
+	| SStoreResult of string * spec
+	| SNew of ttype * int
+	| SVal of svalue
+
+let rec svalue_string v =
+	let sval = svalue_string in
+	match v with
+	| SUndef -> "undef"
+	| SArg i -> "arg" ^ string_of_int i
+	| SInt i -> Int32.to_string i
+	| SFloat f -> string_of_float f
+	| SString s -> "\"" ^ s ^ "\""
+	| SBool b -> if b then "true" else "false"
+	| SNull -> "null"
+	| SRef _ -> "ref"
+	| SRefResult s -> Printf.sprintf "refresult(%s)" s
+	| SType t -> tstr t
+	| SOp (op,a,b) -> Printf.sprintf "(%s %s %s)" (sval a) op (sval b)
+	| SUnop (op,v) -> op ^ sval v
+	| SResult i -> i
+	| SFun (i,None) -> "fun" ^ string_of_int i
+	| SFun (i,Some v) -> Printf.sprintf "fun%d(%s)" i (sval v)
+	| SMeth (v,i) -> Printf.sprintf "meth%d(%s)" i (sval v)
+	| SGlobal g -> Printf.sprintf "G[%d]" g
+	| SField (o,i) -> Printf.sprintf  "%s[%d]" (sval o) i
+	| SDField (o,f) -> Printf.sprintf  "%s.%s" (sval o) f
+	| SConv (f,v) -> Printf.sprintf "%s(%s)" f (sval v)
+	| SCast (v,t) -> Printf.sprintf "cast(%s,%s)" (sval v) (tstr t)
+	| SMem (m,idx,t) -> Printf.sprintf "(%s*)%s[%s]" (tstr t) (sval m) (sval idx)
+	| SEnum (i,vl) -> Printf.sprintf "enum%d(%s)" i (String.concat "," (List.map sval vl))
+	| SEnumField (v,k,i) -> Printf.sprintf "%s[%d:%d]" (sval v) k i
+	| SUnion vl -> Printf.sprintf "union(%s)" (String.concat " | " (List.map sval vl))
+	| SUnreach -> "unreach"
+	| SExc -> "exc"
+	| SDelayed (str,_) -> str
+
+let svalue_iter f = function
+	| SUndef | SArg _ | SInt _ | SFloat _ | SString _ | SBool _ | SNull | SType _ | SResult _
+	| SFun (_,None) | SGlobal _ | SRef _ | SRefResult _ | SUnreach | SExc | SDelayed _ ->
+		()
+	| SOp (_,a,b) | SMem (a,b,_) -> f a; f b
+	| SUnop (_,a) | SFun (_,Some a) | SMeth (a,_) | SField (a,_) | SDField (a,_) | SConv (_,a) | SCast (a,_) | SEnumField (a,_,_) -> f a
+	| SUnion vl | SEnum (_,vl) -> List.iter f vl
+
+let spec_iter fs fv = function
+	| SCall (c,vl) ->
+		(match c with SClosure v -> fv v | _ -> ());
+		List.iter fv vl
+	| SVal v
+	| SJEq (_,v)
+	| SRet v
+	| SNullCheck v
+	| SThrow v
+	| SSwitch v
+	| SGlobalSet (_,v) -> fv v
+	| SJComp (_,a,b)
+	| SSetRef (a,b)
+	| SSetEnumField (a,_,b)
+	| SFieldDSet (a,_,b) | SFieldSet (a,_,b) -> fv a; fv b
+	| SJump ->
+		()
+	| SWriteMem (m,a,b,_) ->
+		fv m; fv a; fv b
+	| SStoreResult (_,s) ->
+		fs s
+	| SNew _ ->
+		()
+
+let rec svalue_same a b =
+	let vsame = svalue_same in
+	match a, b with
+	| SType t1, SType t2 -> tsame t1 t2
+	| SOp (op1,a1,b1), SOp (op2,a2,b2) -> op1 = op2 && vsame a1 a2 && vsame b1 b2
+	| SUnop (op1,v1), SUnop (op2,v2) -> op1 = op2 && vsame v1 v2
+	| SFun (f1,Some v1), SFun (f2,Some v2) -> f1 = f2 && vsame v1 v2
+	| SMeth (v1,m1), SMeth (v2,m2) -> vsame v1 v2 && m1 = m2
+	| SField (v1,f1), SField (v2,f2) -> vsame v1 v2 && f1 = f2
+	| SDField (v1,f1), SDField (v2,f2) -> vsame v1 v2 && f1 = f2
+	| SConv (op1,v1), SConv (op2,v2) -> op1 = op2 && vsame v1 v2
+	| SCast (v1,t1), SCast (v2,t2) -> vsame v1 v2 && tsame t1 t2
+	| SMem (m1,i1,t1), SMem (m2,i2,t2) -> vsame m1 m2 && vsame i1 i2 && tsame t1 t2
+	| SEnum (i1,vl1), SEnum (i2,vl2) -> i1 = i2 && List.length vl1 = List.length vl2 && List.for_all2 vsame vl1 vl2
+	| SEnumField (v1,c1,i1), SEnumField (v2,c2,i2) -> vsame v1 v2 && c1 = c2 && i1 = i2
+	| SUnion vl1, SUnion vl2 -> List.length vl1 = List.length vl2 && List.for_all2 vsame vl1 vl2
+	| SDelayed (id1,_), SDelayed (id2,_) -> id1 = id2
+	| _ -> a = b
+
+let rec spec_string s =
+	let sval = svalue_string in
+	match s with
+	| SCall (c,vl) ->
+		let cstr = (match c with
+			| SFid i -> Printf.sprintf "fun%d" i
+			| SMethod i -> Printf.sprintf "meth%d" i
+			| SClosure v -> Printf.sprintf "closure(%s)" (sval v)
+		) in
+		Printf.sprintf "%s(%s)" cstr (String.concat "," (List.map sval vl))
+	| SGlobalSet (i,v) ->
+		Printf.sprintf "G[%d] = %s" i (sval v)
+	| SFieldSet (o,fid,v) | SSetEnumField (o,fid,v) ->
+		Printf.sprintf "%s[%d] = %s" (sval o) fid (sval v)
+	| SFieldDSet (o,f,v) ->
+		Printf.sprintf "%s.%s = %s" (sval o) f (sval v)
+	| SJEq (s,v) ->
+		Printf.sprintf "j%s(%s)" s (sval v)
+	| SJComp (s,a,b) ->
+		Printf.sprintf "jump(%s %s %s)" (sval a) s (sval b)
+	| SJump ->
+		"jump"
+	| SRet v ->
+		"ret " ^ sval v
+	| SNullCheck v ->
+		"nullcheck " ^ sval v
+	| SThrow v ->
+		"throw " ^ sval v
+	| SSwitch v ->
+		"switch " ^ sval v
+	| SWriteMem (m,idx,v,t) ->
+		Printf.sprintf "(%s*)%s[%s] = %s" (tstr t) (sval m) (sval idx) (sval v)
+	| SSetRef (r,v) ->
+		Printf.sprintf "*%s = %s" (sval r) (sval v)
+	| SStoreResult (r,s) ->
+		r ^ " <- " ^ spec_string s
+	| SNew (t,idx) ->
+		Printf.sprintf "new %s(%d)" (tstr t) idx
+	| SVal v ->
+		sval v
+
+let make_spec (code:code) (f:fundecl) =
+	let op = Array.get f.code in
+	let out_spec = ref [] in
+	let alloc_count = ref (-1) in
+
+	let digest str =
+		let d = Digest.to_hex (Digest.string str) in
+		String.sub d 0 4
+	in
+
+	let rec semit s =
+		let rec loop_spec s =
+			spec_iter loop_spec loop_val s
+
+		and loop_val v =
+			match v with
+			| SDelayed (r,used) ->
+				(match !used with
+				| None -> ()
+				| Some vl -> used := None; semit (SStoreResult (r,SVal (SUnion vl))))
+			| _ ->
+				svalue_iter loop_val v
+		in
+		loop_spec s;
+		out_spec := s :: !out_spec
+	in
+
+	let emit (s:spec) =
+		let d = digest (spec_string s) in
+		semit (SStoreResult (d,s));
+		SResult d
+	in
+
+	let big_unions = Hashtbl.create 0 in
+
+	let block_args = Hashtbl.create 0 in
+	let rec get_args b =
+		try
+			Hashtbl.find block_args b.bstart
+		with Not_found ->
+			assert false
+
+	and calc_spec b =
+		let bprev = List.filter (fun b2 -> b2.bstart < b.bstart) b.bprev in
+		let args = (match bprev with
+			| [] ->
+				let args = Array.make (Array.length f.regs) SUndef in
+				(match f.ftype with
+				| HFun (tl,_) -> list_iteri (fun i _ -> args.(i) <- SArg i) tl
+				| _ -> assert false);
+				args
+			| b2 :: l ->
+				let args = Array.copy (get_args b2) in
+				List.iter (fun b2 ->
+					let args2 = get_args b2 in
+					for i = 0 to Array.length args - 1 do
+						if not (svalue_same args.(i) args2.(i)) then begin
+							let l1 = (match args.(i) with SUnion l -> l | v -> [v]) in
+							let l2 = (match args2.(i) with SUnion l -> l | v -> [v]) in
+							let l = l1 @ List.filter (fun v -> not (List.exists (svalue_same v) l1)) l2 in
+							if List.length l > 10 then begin
+								(try
+									let ident, used = Hashtbl.find big_unions l in
+									args.(i) <- SDelayed (ident, used);
+								with Not_found ->
+									let ident = digest (String.concat "," (List.map svalue_string l)) in
+									let used = ref (Some l) in
+									Hashtbl.replace big_unions l (ident,used);
+									args.(i) <- SDelayed (ident, used))
+							end else
+								args.(i) <- SUnion l;
+						end
+					done;
+				) l;
+				if l = [] then (match op b2.bend with OTrap (r,_) -> args.(r) <- SExc | _ -> ());
+				args
+		) in
+		let make_call c vl =
+			let r = emit (SCall (c,vl)) in
+			(match r with
+			| SResult result -> List.iter (fun v -> match v with SRef r -> args.(r) <- SRefResult result | _ -> ()) vl
+			| _ -> assert false);
+			r
+		in
+		for i = b.bstart to b.bend do
+			match op i with
+			| OMov (d,r) -> args.(d) <- args.(r)
+			| OInt (d,i) -> args.(d) <- SInt code.ints.(i)
+			| OFloat (d,f) -> args.(d) <- SFloat code.floats.(f)
+			| OBool (d,b) -> args.(d) <- SBool b
+			| OBytes (d,s) | OString (d,s) -> args.(d) <- SString code.strings.(s)
+			| ONull d -> args.(d) <- SNull
+			| OAdd (d,a,b) -> args.(d) <- SOp ("+",args.(a),args.(b))
+			| OSub (d,a,b) -> args.(d) <- SOp ("-",args.(a),args.(b))
+			| OMul (d,a,b) -> args.(d) <- SOp ("*",args.(a),args.(b))
+			| OSDiv (d,a,b) -> args.(d) <- SOp ("/",args.(a),args.(b))
+			| OUDiv (d,a,b) -> args.(d) <- SOp ("//",args.(a),args.(b))
+			| OSMod (d,a,b) -> args.(d) <- SOp ("%",args.(a),args.(b))
+			| OUMod (d,a,b) -> args.(d) <- SOp ("%%",args.(a),args.(b))
+			| OShl (d,a,b) -> args.(d) <- SOp ("<<",args.(a),args.(b))
+			| OSShr (d,a,b) -> args.(d) <- SOp (">>",args.(a),args.(b))
+			| OUShr (d,a,b) -> args.(d) <- SOp (">>>",args.(a),args.(b))
+			| OAnd (d,a,b) -> args.(d) <- SOp ("&",args.(a),args.(b))
+			| OOr (d,a,b) -> args.(d) <- SOp ("|",args.(a),args.(b))
+			| OXor (d,a,b) -> args.(d) <- SOp ("^",args.(a),args.(b))
+			| ONeg (d,r) -> args.(d) <- SUnop ("-",args.(r))
+			| ONot (d,r) -> args.(d) <- SUnop ("!",args.(r))
+			| OIncr r -> args.(r) <- SUnop ("++",args.(r))
+			| ODecr r -> args.(r) <- SUnop ("++",args.(r))
+			| OCall0 (d,f) -> args.(d) <- make_call (SFid f) []
+			| OCall1 (d,f,a) -> args.(d) <- make_call (SFid f) [args.(a)]
+			| OCall2 (d,f,a,b) -> args.(d) <- make_call (SFid f) [args.(a);args.(b)]
+			| OCall3 (d,f,a,b,c) -> args.(d) <- make_call (SFid f) [args.(a);args.(b);args.(c)]
+			| OCall4 (d,f,a,b,c,k) -> args.(d) <- make_call (SFid f) [args.(a);args.(b);args.(c);args.(k)]
+			| OCallN (d,f,rl) -> args.(d) <- make_call (SFid f) (List.map (fun r -> args.(r)) rl)
+			| OCallMethod (d,fid,rl) -> args.(d) <- make_call (SMethod fid) (List.map (fun r -> args.(r)) rl)
+			| OCallThis (d,fid,rl) -> args.(d) <- make_call (SMethod fid) (List.map (fun r -> args.(r)) (0 :: rl))
+			| OCallClosure (d,r,rl) -> args.(d) <- make_call (SClosure args.(r)) (List.map (fun r -> args.(r)) rl)
+			| OStaticClosure (d,fid) -> args.(d) <- SFun (fid,None)
+			| OSetMethod (o,f,fid) -> semit (SFieldSet (args.(o),f,SFun(fid,None)))
+			| OInstanceClosure (d,fid,r) -> args.(d) <- SFun (fid,Some args.(r))
+			| OVirtualClosure (d,r,index) -> args.(d) <- SMeth (args.(r),index)
+			| OGetGlobal (d,g) -> args.(d) <- SGlobal g
+			| OSetGlobal (g,r) -> semit (SGlobalSet (g,args.(r)))
+			| OField (d,r,f) -> args.(d) <- SField (args.(r),f)
+			| OSetField (o,f,r) -> semit (SFieldSet (args.(o),f,args.(r)))
+			| OGetThis (d,fid) -> args.(d) <- SField (args.(0),fid)
+			| OSetThis (f,r) -> semit (SFieldSet (args.(0),f,args.(r)))
+			| ODynGet (d,o,f) -> args.(d) <- SDField (args.(o),code.strings.(f))
+			| ODynSet (o,f,v) -> semit (SFieldDSet (args.(o),code.strings.(f),args.(v)))
+			| OJTrue (r,_) -> semit (SJEq ("true",args.(r)))
+			| OJFalse (r,_) -> semit (SJEq ("false",args.(r)))
+			| OJNull (r,_) -> semit (SJEq ("null",args.(r)))
+			| OJNotNull (r,_) -> semit (SJEq ("not null",args.(r)))
+			| OJSLt (a,b,_) -> semit (SJComp ("<",args.(a),args.(b)))
+			| OJSGte (a,b,_) -> semit (SJComp (">=",args.(a),args.(b)))
+			| OJSGt (a,b,_) -> semit (SJComp (">",args.(a),args.(b)))
+			| OJSLte (a,b,_) -> semit (SJComp ("<=",args.(a),args.(b)))
+			| OJULt (a,b,_) -> semit (SJComp ("<!",args.(a),args.(b)))
+			| OJUGte (a,b,_) -> semit (SJComp (">=!",args.(a),args.(b)))
+			| OJEq (a,b,_) -> semit (SJComp ("==",args.(a),args.(b)))
+			| OJNotEq (a,b,_) -> semit (SJComp ("!=",args.(a),args.(b)))
+			| OJAlways _ -> semit SJump
+			| OToDyn (d,r) -> args.(d) <- SConv ("dyn",args.(r))
+			| OToSFloat (d,r) -> args.(d) <- SConv ("sfloat",args.(r))
+			| OToUFloat (d,r) -> args.(d) <- SConv ("ufloat",args.(r))
+			| OToInt (d,r) -> args.(d) <- SConv ("int",args.(r))
+			| OSafeCast (d,r) -> args.(d) <- SCast (args.(r),f.regs.(d))
+			| OUnsafeCast (d,r) -> args.(d) <- SConv ("cast", args.(r))
+			| OToVirtual (d,r) -> args.(d) <- SConv ("virtual",args.(r))
+			| OLabel _ -> ()
+			| ORet r ->
+				semit (SRet (if f.regs.(r) = HVoid then SUndef else args.(r)));
+				if i < b.bend then for i = 0 to Array.length args - 1 do args.(i) <- SUnreach done
+			| OThrow r | ORethrow r ->
+				semit (SThrow args.(r));
+				if i < b.bend then for i = 0 to Array.length args - 1 do args.(i) <- SUnreach done
+			| OSwitch (r,_,_) -> semit (SSwitch args.(r))
+			| ONullCheck r -> semit (SNullCheck args.(r))
+			| OTrap _ | OEndTrap _ -> ()
+			| OGetUI8 (d,b,i) -> args.(d) <- SMem (args.(b),args.(i),HUI8)
+			| OGetUI16 (d,b,i) -> args.(d) <- SMem (args.(b),args.(i),HUI16)
+			| OGetI32 (d,b,i) -> args.(d) <- SMem (args.(b),args.(i),HI32)
+			| OGetF32 (d,b,i) -> args.(d) <- SMem (args.(b),args.(i),HF32)
+			| OGetF64 (d,b,i) -> args.(d) <- SMem (args.(b),args.(i),HF64)
+			| OGetArray (d,b,i) -> args.(d) <- SMem (args.(b),args.(i),HArray)
+			| OSetUI8 (b,i,v) -> semit (SWriteMem (args.(b),args.(i),args.(v),HUI8))
+			| OSetUI16 (b,i,v) -> semit (SWriteMem (args.(b),args.(i),args.(v),HUI16))
+			| OSetI32 (b,i,v) -> semit (SWriteMem (args.(b),args.(i),args.(v),HI32))
+			| OSetF32 (b,i,v) -> semit (SWriteMem (args.(b),args.(i),args.(v),HF32))
+			| OSetF64 (b,i,v) -> semit (SWriteMem (args.(b),args.(i),args.(v),HF64))
+			| OSetArray (b,i,v) -> semit (SWriteMem (args.(b),args.(i),args.(v),HArray))
+			| ONew d ->
+				incr alloc_count;
+				args.(d) <- emit (SNew (f.regs.(d),!alloc_count))
+			| OArraySize (d,r) -> args.(d) <- SConv ("size",args.(r))
+			| OType (d,t) -> args.(d) <- SType t
+			| OGetType (d,r) -> args.(d) <- SConv ("type",args.(r))
+			| OGetTID (d,r) -> args.(d) <- SConv ("tid",args.(r))
+			| ORef (d,r) -> args.(d) <- SRef r
+			| OUnref (d,r) ->
+				(match args.(r) with
+				| SRef r -> args.(d) <- args.(r)
+				| _ -> args.(d) <- SConv ("unref",args.(r)))
+			| OSetref (r,v) ->
+				(match args.(r) with
+				| SRef r -> args.(r) <- args.(v)
+				| _ -> ());
+				semit (SSetRef (args.(r),args.(v)))
+			| OMakeEnum (d,fid,rl) -> args.(d) <- SEnum (fid, List.map (fun r -> args.(r)) rl)
+			| OEnumAlloc (d,fid) -> args.(d) <- SEnum (fid, [])
+			| OEnumIndex (d,r) -> args.(d) <- SConv ("index",args.(r))
+			| OEnumField (d,r,fid,cid) -> args.(d) <- SEnumField (args.(r),fid,cid)
+			| OSetEnumField (e,fid,r) -> semit (SSetEnumField (args.(e),fid,args.(r)))
+			| ONop _ -> ()
+		done;
+		Hashtbl.add block_args b.bstart args
+	in
+	let all_blocks, _ = Hlopt.code_graph f in
+	let rec loop i =
+		if i = Array.length f.code then () else
+		if not (Hashtbl.mem all_blocks i) then loop (i + 1) else (* unreachable code *)
+		let b = try Hashtbl.find all_blocks i with Not_found -> failwith (Printf.sprintf "Missing block %s(%d)" (fundecl_name f) i) in
+		calc_spec b;
+		loop (b.bend + 1)
+	in
+	loop 0;
+	List.rev !out_spec

+ 838 - 0
src/generators/hlopt.ml

@@ -0,0 +1,838 @@
+(*
+ * Copyright (C)2005-2016 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.
+ *)
+open Hlcode
+
+module ISet = Set.Make(struct
+	let compare = Pervasives.compare
+	type t = int
+end)
+
+type cur_value =
+	| VUndef
+	| VReg of int
+
+type reg_state = {
+	mutable rindex : int;
+	mutable ralias : reg_state;
+	mutable rbind : reg_state list;
+	mutable rnullcheck : bool;
+}
+
+type block = {
+	bstart : int;
+	mutable bend : int;
+	mutable bnext : block list;
+	mutable bprev : block list;
+	mutable bloop : bool;
+	mutable bstate : reg_state array option;
+	mutable bneed : ISet.t;
+	mutable bneed_all : ISet.t option;
+	mutable bwrite : (int, int) PMap.t;
+}
+
+type control =
+	| CNo
+	| CJCond of int
+	| CJAlways of int
+	| CTry of int
+	| CSwitch of int array
+	| CRet
+	| CThrow
+	| CLabel
+
+let control = function
+	| OJTrue (_,d) | OJFalse (_,d) | OJNull (_,d) | OJNotNull (_,d)
+	| OJSLt (_,_,d) | OJSGte (_,_,d) | OJSGt (_,_,d) | OJSLte (_,_,d) | OJULt (_,_,d) | OJUGte (_,_,d) | OJEq (_,_,d) | OJNotEq (_,_,d) ->
+		CJCond d
+	| OJAlways d ->
+		CJAlways d
+	| OLabel _ ->
+		CLabel
+	| ORet _ ->
+		CRet
+	| OThrow _ | ORethrow _ ->
+		CThrow
+	| OSwitch (_,cases,_) ->
+		CSwitch cases
+	| OTrap (_,d) ->
+		CTry d
+	| _ ->
+		CNo
+
+let opcode_fx frw op =
+	let read r = frw r true and write r = frw r false in
+	match op with
+	| OMov (d,a) | ONeg (d,a) | ONot (d,a) ->
+		read a; write d
+	| OInt (d,_) | OFloat (d,_) | OBool (d,_) | OBytes (d,_) | OString (d,_) | ONull d ->
+		write d
+	| OAdd (d,a,b) | OSub (d,a,b) | OMul (d,a,b) | OSDiv (d,a,b) | OUDiv (d,a,b) | OSMod (d,a,b)| OUMod (d,a,b) | OShl (d,a,b) | OSShr (d,a,b) | OUShr (d,a,b) | OAnd (d,a,b) | OOr (d,a,b) | OXor (d,a,b) ->
+		read a; read b; write d
+	| OIncr a | ODecr a ->
+		read a; write a
+	| OCall0 (d,_) ->
+		write d
+	| OCall1 (d,_,a) ->
+		read a; write d
+	| OCall2 (d,_,a,b) ->
+		read a; read b; write d
+	| OCall3 (d,_,a,b,c) ->
+		read a; read b; read c; write d
+	| OCall4 (d,_,a,b,c,k) ->
+		read a; read b; read c; read k; write d
+	| OCallN (d,_,rl) | OCallMethod (d,_,rl) | OCallThis (d,_,rl) ->
+		List.iter read rl; write d
+	| OCallClosure (d,f,rl) ->
+		read f; List.iter read rl; write d
+	| OStaticClosure (d,_) ->
+		write d
+	| OInstanceClosure (d, _, a) | OVirtualClosure (d,a,_) ->
+		read a; write d
+	| OGetGlobal (d,_) ->
+		write d
+	| OSetGlobal (_,a) ->
+		read a;
+	| OSetMethod (o,_,_) ->
+		read o;
+	| OField (d,a,_) | ODynGet (d,a,_) ->
+		read a; write d
+	| OSetField (a,_,b) | ODynSet (a,_,b)->
+		read a; read b
+	| OGetThis (d,_) ->
+		write d
+	| OSetThis (_,a) ->
+		read a
+	| OJTrue (r,_) | OJFalse (r,_) | OJNull (r,_) | OJNotNull (r,_) ->
+		read r
+	| OJSLt (a,b,_) | OJSGte (a,b,_) | OJSGt (a,b,_) | OJSLte (a,b,_) | OJULt (a,b,_) | OJUGte (a,b,_) | OJEq (a,b,_) | OJNotEq (a,b,_) ->
+		read a; read b;
+	| OJAlways _ | OLabel _ ->
+		()
+	| OToDyn (d, a) | OToSFloat (d,a) | OToUFloat (d,a) | OToInt (d,a) | OSafeCast (d,a) | OUnsafeCast (d,a) | OToVirtual (d,a) ->
+		read a; write d
+	| ORet r | OThrow r  | ORethrow r | OSwitch (r,_,_) | ONullCheck r ->
+		read r
+	| OTrap (r,_) ->
+		write r
+	| OEndTrap _ ->
+		() (* ??? *)
+	| OGetUI8 (d,a,b) | OGetUI16 (d,a,b) | OGetI32 (d,a,b) | OGetF32 (d,a,b) | OGetF64 (d,a,b) | OGetArray (d,a,b) ->
+		read a; read b; write d
+	| OSetUI8 (a,b,c) | OSetUI16 (a,b,c) | OSetI32 (a,b,c) | OSetF32 (a,b,c) | OSetF64 (a,b,c) | OSetArray (a,b,c) ->
+		read a; read b; read c
+	| ONew d ->
+		write d
+	| OArraySize (d, a)	| OGetType (d,a) | OGetTID (d,a) | ORef (d, a) | OUnref (d,a) | OSetref (d, a) | OEnumIndex (d, a) | OEnumField (d,a,_,_) ->
+		read a;
+		write d
+	| OType (d,_) | OEnumAlloc (d,_) ->
+		write d
+	| OMakeEnum (d,_,rl) ->
+		List.iter read rl;
+		write d
+	| OSetEnumField (a,_,b) ->
+		read a; read b
+	| ONop _ ->
+		()
+
+let opcode_eq a b =
+	match a, b with
+	| OType (r1,t1), OType (r2,t2) ->
+		r1 = r2 && t1 == t2
+	| _ ->
+		a = b
+
+let opcode_map read write op =
+	match op with
+	| OMov (d,a) ->
+		let a = read a in
+		OMov (write d, a)
+	| ONeg (d,a) ->
+		let a = read a in
+		ONeg (write d, a)
+	| ONot (d,a) ->
+		let a = read a in
+		ONot (write d, a)
+	| OInt (d,idx) ->
+		OInt (write d, idx)
+	| OFloat (d,idx) ->
+		OFloat (write d, idx)
+	| OBool (d,idx) ->
+		OBool (write d, idx)
+	| OBytes (d,idx) ->
+		OBytes (write d, idx)
+	| OString (d,idx) ->
+		OString (write d, idx)
+	| ONull d ->
+		ONull (write d)
+	| OAdd (d,a,b) ->
+		let a = read a and b = read b in
+		OAdd (write d, a, b)
+	| OSub (d,a,b) ->
+		let a = read a and b = read b in
+		OSub (write d, a, b)
+	| OMul (d,a,b) ->
+		let a = read a and b = read b in
+		OMul (write d, a, b)
+	| OSDiv (d,a,b) ->
+		let a = read a and b = read b in
+		OSDiv (write d, a, b)
+	| OUDiv (d,a,b) ->
+		let a = read a and b = read b in
+		OUDiv (write d, a, b)
+	| OSMod (d,a,b) ->
+		let a = read a and b = read b in
+		OSMod (write d, a, b)
+	| OUMod (d,a,b) ->
+		let a = read a and b = read b in
+		OUMod (write d, a, b)
+	| OShl (d,a,b) ->
+		let a = read a and b = read b in
+		OShl (write d, a, b)
+	| OSShr (d,a,b) ->
+		let a = read a and b = read b in
+		OSShr (write d, a, b)
+	| OUShr (d,a,b) ->
+		let a = read a and b = read b in
+		OUShr (write d, a, b)
+	| OAnd (d,a,b) ->
+		let a = read a and b = read b in
+		OAnd (write d, a, b)
+	| OOr (d,a,b) ->
+		let a = read a and b = read b in
+		OOr (write d, a, b)
+	| OXor (d,a,b) ->
+		let a = read a and b = read b in
+		OXor (write d, a, b)
+	| OIncr a ->
+		OIncr (write a)
+	| ODecr a ->
+		ODecr (write a)
+	| OCall0 (d,f) ->
+		OCall0 (write d, f)
+	| OCall1 (d,f,a) ->
+		let a = read a in
+		OCall1 (write d, f, a)
+	| OCall2 (d,f,a,b) ->
+		let a = read a in
+		let b = read b in
+		OCall2 (write d, f, a, b)
+	| OCall3 (d,f,a,b,c) ->
+		let a = read a in
+		let b = read b in
+		let c = read c in
+		OCall3 (write d, f, a, b, c)
+	| OCall4 (w,f,a,b,c,d) ->
+		let a = read a in
+		let b = read b in
+		let c = read c in
+		let d = read d in
+		OCall4 (write w, f, a, b, c, d)
+	| OCallN (d,f,rl) ->
+		let rl = List.map read rl in
+		OCallN (write d, f, rl)
+	| OCallMethod (d,f,rl) ->
+		let rl = List.map read rl in
+		OCallMethod (write d, f, rl)
+	| OCallThis (d,f,rl) ->
+		let rl = List.map read rl in
+		OCallThis (write d, f, rl)
+	| OCallClosure (d,f,rl) ->
+		let f = read f in
+		let rl = List.map read rl in
+		OCallClosure (write d, f, rl)
+	| OStaticClosure (d,f) ->
+		OStaticClosure (write d, f)
+	| OInstanceClosure (d, f, a) ->
+		let a = read a in
+		OInstanceClosure (write d, f, a)
+	| OVirtualClosure (d,a,f) ->
+		let a = read a in
+		OVirtualClosure (write d, a, f)
+	| OGetGlobal (d,g) ->
+		OGetGlobal (write d, g)
+	| OSetGlobal (g,r) ->
+		OSetGlobal (g, read r)
+	| OSetMethod (o,f,m) ->
+		OSetMethod (read o, f, m)
+	| OField (d,a,f) ->
+		let a = read a in
+		OField (write d, a, f)
+	| ODynGet (d,a,f) ->
+		let a = read a in
+		ODynGet (write d, a, f)
+	| OSetField (a,f,b) ->
+		OSetField (read a, f, read b)
+	| ODynSet (a,f,b) ->
+		ODynSet (read a, f, read b)
+	| OGetThis (d,f) ->
+		OGetThis (write d, f)
+	| OSetThis (f,a) ->
+		OSetThis (f, read a)
+	| OJTrue (r,d) ->
+		OJTrue (read r, d)
+	| OJFalse (r,d) ->
+		OJFalse (read r, d)
+	| OJNull (r,d) ->
+		OJNull (read r, d)
+	| OJNotNull (r,d) ->
+		OJNotNull (read r, d)
+	| OJSLt (a,b,d) ->
+		OJSLt (read a, read b, d)
+	| OJSGte (a,b,d) ->
+		OJSGte (read a, read b, d)
+	| OJSGt (a,b,d) ->
+		OJSGt (read a, read b, d)
+	| OJSLte (a,b,d) ->
+		OJSLte (read a, read b, d)
+	| OJULt (a,b,d) ->
+		OJULt (read a, read b, d)
+	| OJUGte (a,b,d) ->
+		OJUGte (read a, read b, d)
+	| OJEq (a,b,d) ->
+		OJEq (read a, read b, d)
+	| OJNotEq (a,b,d) ->
+		OJNotEq (read a, read b, d)
+	| OJAlways _ | OLabel _ ->
+		op
+	| OToDyn (d, a) ->
+		let a = read a in
+		OToDyn (write d, a)
+	| OToSFloat (d,a) ->
+		let a = read a in
+		OToSFloat (write d, a)
+	| OToUFloat (d,a) ->
+		let a = read a in
+		OToUFloat (write d, a)
+	| OToInt (d,a) ->
+		let a = read a in
+		OToInt (write d, a)
+	| OSafeCast (d,a) ->
+		let a = read a in
+		OSafeCast (write d, a)
+	| OUnsafeCast (d,a) ->
+		let a = read a in
+		OUnsafeCast (write d, a)
+	| OToVirtual (d,a) ->
+		let a = read a in
+		OToVirtual (write d, a)
+	| ORet r ->
+		ORet (read r)
+	| OThrow r ->
+		OThrow (read r)
+	| ORethrow r ->
+		ORethrow (read r)
+	| OSwitch (r,cases,def) ->
+		OSwitch (read r, cases, def)
+	| ONullCheck r ->
+		ONullCheck (read r)
+	| OTrap (r,d) ->
+		OTrap (write r, d)
+	| OEndTrap _ ->
+		op (* ??? *)
+	| OGetUI8 (d,a,b) ->
+		let a = read a and b = read b in
+		OGetUI8 (write d, a, b)
+	| OGetUI16 (d,a,b) ->
+		let a = read a and b = read b in
+		OGetUI16 (write d, a, b)
+	| OGetI32 (d,a,b) ->
+		let a = read a and b = read b in
+		OGetI32 (write d, a, b)
+	| OGetF32 (d,a,b) ->
+		let a = read a and b = read b in
+		OGetF32 (write d, a, b)
+	| OGetF64 (d,a,b) ->
+		let a = read a and b = read b in
+		OGetF64 (write d, a, b)
+	| OGetArray (d,a,b) ->
+		let a = read a and b = read b in
+		OGetArray (write d, a, b)
+	| OSetUI8 (a,b,c) ->
+		let a = read a and b = read b and c = read c in
+		OSetUI8 (a, b, c)
+	| OSetUI16 (a,b,c) ->
+		let a = read a and b = read b and c = read c in
+		OSetUI16 (a, b, c)
+	| OSetI32 (a,b,c) ->
+		let a = read a and b = read b and c = read c in
+		OSetI32 (a, b, c)
+	| OSetF32 (a,b,c) ->
+		let a = read a and b = read b and c = read c in
+		OSetF32 (a, b, c)
+	| OSetF64 (a,b,c) ->
+		let a = read a and b = read b and c = read c in
+		OSetF64 (a, b, c)
+	| OSetArray (a,b,c) ->
+		let a = read a and b = read b and c = read c in
+		OSetArray (a, b, c)
+	| ONew d ->
+		ONew (write d)
+	| OArraySize (d, a) ->
+		let a = read a in
+		OArraySize (write d, a)
+	| OGetType (d,a) ->
+		let a = read a in
+		OGetType (write d, a)
+	| OGetTID (d,a) ->
+		let a = read a in
+		OGetTID (write d, a)
+	| ORef (d, a) ->
+		let a = read a in
+		ORef (write d, a)
+	| OUnref (d,a) ->
+		let a = read a in
+		OUnref (write d, a)
+	| OSetref (d, a) ->
+		let a = read a in
+		OSetref (write d, a)
+	| OEnumIndex (d, a) ->
+		let a = read a in
+		OEnumIndex (write d, a)
+	| OEnumField (d,a,cs,idx) ->
+		let a = read a in
+		OEnumField (write d, a, cs, idx)
+	| OType (d,t) ->
+		OType (write d, t)
+	| OEnumAlloc (d,e) ->
+		OEnumAlloc (write d, e)
+	| OMakeEnum (d,e,rl) ->
+		let rl = List.map read rl in
+		OMakeEnum (write d, e, rl)
+	| OSetEnumField (a,f,b) ->
+		OSetEnumField (read a, f, read b)
+	| ONop _ ->
+		op
+
+(* build code graph *)
+
+let code_graph (f:fundecl) =
+	let op index = f.code.(index) in
+	let blocks_pos = Hashtbl.create 0 in
+	let all_blocks = Hashtbl.create 0 in
+	for i = 0 to Array.length f.code - 1 do
+		match control (op i) with
+		| CJAlways d | CJCond d -> Hashtbl.replace all_blocks (i + 1 + d) true
+		| _ -> ()
+	done;
+	let rec make_block pos =
+		try
+			Hashtbl.find blocks_pos pos
+		with Not_found ->
+			let b = {
+				bstart = pos;
+				bend = 0;
+				bnext = [];
+				bprev = [];
+				bloop = false;
+				bstate = None;
+				bneed = ISet.empty;
+				bwrite = PMap.empty;
+				bneed_all = None;
+			} in
+			Hashtbl.add blocks_pos pos b;
+			let rec loop i =
+				let goto d =
+					let b2 = make_block (i + 1 + d) in
+					b2.bprev <- b :: b2.bprev;
+					b2
+				in
+				if i > pos && Hashtbl.mem all_blocks i then begin
+					b.bend <- i - 1;
+					b.bnext <- [goto (-1)];
+				end else match control (op i) with
+				| CNo ->
+					loop (i + 1)
+				| CRet | CThrow ->
+					b.bend <- i
+				| CJAlways d ->
+					b.bend <- i;
+					b.bnext <- [goto d];
+				| CSwitch pl ->
+					b.bend <- i;
+					b.bnext <- goto 0 :: Array.to_list (Array.map goto pl)
+				| CJCond d | CTry d ->
+					b.bend <- i;
+					b.bnext <- [goto 0; goto d];
+				| CLabel ->
+					b.bloop <- true;
+					loop (i + 1)
+			in
+			loop pos;
+			b
+	in
+	blocks_pos, make_block 0
+
+let optimize dump (f:fundecl) =
+	let nregs = Array.length f.regs in
+	let old_code = match dump with None -> f.code | Some _ -> Array.copy f.code in
+	let op index = f.code.(index) in
+	let set_op index op = f.code.(index) <- op in
+	let nop_count = ref 0 in
+	let set_nop index r = f.code.(index) <- (ONop r); incr nop_count in
+	let write str = match dump with None -> () | Some ch -> IO.nwrite ch (str ^ "\n") in
+
+	let blocks_pos, root = code_graph f in
+
+	let read_counts = Array.make nregs 0 in
+	let write_counts = Array.make nregs 0 in
+	let last_write = Array.make nregs (-1) in
+
+	let bit_regs = 30 in
+	let stride = (nregs + bit_regs - 1) / bit_regs in
+	let live_bits = Array.make (Array.length f.code * stride) 0 in
+
+	let set_live r min max =
+		let offset = r / bit_regs in
+		let mask = 1 lsl (r - offset * bit_regs) in
+		if min < 0 || max >= Array.length f.code then assert false;
+		for i=min to max do
+			let p = i * stride + offset in
+			Array.unsafe_set live_bits p ((Array.unsafe_get live_bits p) lor mask);
+		done;
+	in
+	let is_live r i =
+		let offset = r / bit_regs in
+		let mask = 1 lsl (r - offset * bit_regs) in
+		live_bits.(i * stride + offset) land mask <> 0
+	in
+
+	let read_count r = read_counts.(r) <- read_counts.(r) + 1 in
+	let write_count r = write_counts.(r) <- write_counts.(r) + 1 in
+
+	let empty_state() = Array.init nregs (fun i ->
+		let r = { rindex = i; ralias = Obj.magic 0; rbind = []; rnullcheck = false } in
+		r.ralias <- r;
+		r
+	) in
+
+	let print_state i s =
+		let state_str s =
+			if s.ralias == s && s.rbind == [] then "" else
+			Printf.sprintf "%d%s[%s]" s.rindex (if s.ralias == s then "" else "=" ^ string_of_int s.ralias.rindex) (String.concat "," (List.map (fun s -> string_of_int s.rindex) s.rbind))
+		in
+		write (Printf.sprintf "@%X %s" i (String.concat " " (Array.to_list (Array.map state_str s))))
+	in
+
+	let dstate = false in
+
+	let rec propagate b =
+		let state = if b.bloop then
+			empty_state()
+		else match b.bprev with
+		| [] -> empty_state()
+		| b2 :: l ->
+			let s = get_state b2 in
+			let s = (match b2.bnext with
+			| [] -> assert false
+			| [_] -> s (* reuse *)
+			| _ :: l ->
+				let s2 = empty_state() in
+				for i = 0 to nregs - 1 do
+					let sold = s.(i) and snew = s2.(i) in
+					snew.ralias <- s2.(sold.ralias.rindex);
+					snew.rbind <- List.map (fun b -> s2.(b.rindex)) sold.rbind;
+					snew.rnullcheck <- sold.rnullcheck;
+				done;
+				s2
+			) in
+			List.iter (fun b2 ->
+				let s2 = get_state b2 in
+				for i = 0 to nregs - 1 do
+					let s1 = s.(i) and s2 = s2.(i) in
+					if s1.ralias.rindex <> s2.ralias.rindex then s1.ralias <- s1
+				done;
+				for i = 0 to nregs - 1 do
+					let s1 = s.(i) and s2 = s2.(i) in
+					s1.rbind <- List.filter (fun s -> s.ralias == s1) s1.rbind;
+					s1.rnullcheck <- s1.rnullcheck && s2.rnullcheck;
+					(match s2.rbind with
+					| [] -> ()
+					| l -> s1.rbind <- List.fold_left (fun acc sb2 -> let s = s.(sb2.rindex) in if s.ralias == s1 && not (List.memq s s1.rbind) then s :: acc else acc) s1.rbind s2.rbind)
+				done;
+			) l;
+			s
+		in
+		let unalias r =
+			r.ralias.rbind <- List.filter (fun r2 -> r2 != r) r.ralias.rbind;
+			r.ralias <- r
+		in
+		let rec loop i =
+			let do_read r =
+				let w = last_write.(r) in
+				if w < b.bstart || w > i then begin
+					last_write.(r) <- i;
+					set_live r b.bstart i;
+					b.bneed <- ISet.add r b.bneed;
+				end else
+					set_live r (w + 1) i;
+				read_count r
+			in
+			let do_write r =
+				let s = state.(r) in
+				List.iter (fun s2 -> s2.ralias <- s2) s.rbind;
+				s.rbind <- [];
+				s.rnullcheck <- false;
+				last_write.(r) <- i;
+				b.bwrite <- PMap.add r i b.bwrite;
+				write_count r;
+				unalias s
+			in
+			if i > b.bend then () else
+			let op = op i in
+			if dstate then print_state i state;
+			(match op with
+			| OIncr r | ODecr r | ORef (_,r) -> unalias state.(r)
+			| OCallClosure (_,r,_) when f.regs.(r) = HDyn && (match f.regs.(state.(r).ralias.rindex) with HFun (_,rt) -> not (is_dynamic rt) | HDyn -> false | _ -> true) -> unalias state.(r) (* Issue3218.hx *)
+			| _ -> ());
+			let op = opcode_map (fun r ->
+				let s = state.(r) in
+				s.ralias.rindex
+			) (fun w ->	w) op in
+			set_op i op;
+			(match op with
+			| OMov (d, v) when d = v ->
+				set_nop i "mov"
+			| OMov (d, v) ->
+				let sv = state.(v) in
+				let sd = state.(d) in
+				do_read v;
+				do_write d;
+				sd.ralias <- sv;
+				sd.rnullcheck <- sv.rnullcheck;
+				if not (List.memq sd sv.rbind) then sv.rbind <- sd :: sv.rbind;
+			| OIncr r | ODecr r ->
+				do_read r;
+				do_write r;
+			| ONullCheck r ->
+				let s = state.(r) in
+				if s.rnullcheck then set_nop i "nullcheck" else begin do_read r; s.rnullcheck <- true; end;
+			| _ ->
+				opcode_fx (fun r read ->
+					if read then do_read r else do_write r
+				) op
+			);
+			loop (i + 1)
+		in
+		loop b.bstart;
+		b.bstate <- Some state;
+		List.iter (fun b2 -> ignore (get_state b2)) b.bnext
+
+	and get_state b =
+		match b.bstate with
+		| None ->
+			(* recurse before calculating *)
+			List.iter (fun b2 -> if b2.bstart < b.bstart then ignore(get_state b2)) b.bprev;
+			(match b.bstate with
+			| None ->
+				propagate b;
+				get_state b
+			| Some b -> b);
+		| Some state ->
+			state
+	in
+	propagate root;
+
+	(* unreachable code *)
+
+	let rec loop i =
+		if i = Array.length f.code then () else
+		try
+			let b = Hashtbl.find blocks_pos i in
+			loop (b.bend + 1)
+		with Not_found ->
+			(match op i with
+			| OEndTrap true -> ()
+			| _ -> set_nop i "unreach");
+			loop (i + 1)
+	in
+	loop 0;
+
+	(* liveness *)
+
+	let rec live b =
+		match b.bneed_all with
+		| Some a -> a
+		| None ->
+			let need_sub = List.fold_left (fun acc b2 ->
+				(* loop : first pass does not recurse, second pass uses cache *)
+				if b2.bloop && b2.bstart < b.bstart then (match b2.bneed_all with None -> acc | Some s -> ISet.union acc s) else
+				ISet.union acc (live b2)
+			) ISet.empty b.bnext in
+			let need_sub = ISet.filter (fun r ->
+				try
+					let w = PMap.find r b.bwrite in
+					set_live r (w + 1) b.bend;
+					false
+				with Not_found ->
+					set_live r b.bstart b.bend;
+					true
+			) need_sub in
+			let need = ISet.union b.bneed need_sub in
+			b.bneed_all <- Some need;
+			if b.bloop then begin
+				(*
+					if we are a loop, we need a second pass to perform fixed point
+					first clear the cache within the loop from backward
+					then rebuild the cache to make sure liveness ranges are correctly set
+				*)
+				let rec clear b2 =
+					match b2.bneed_all with
+					| Some _ when b2.bstart > b.bstart ->
+						b2.bneed_all <- None;
+						List.iter clear b2.bprev
+					| _ -> ()
+				in
+				List.iter (fun b2 -> if b2.bstart > b.bstart then clear b2) b.bprev;
+				List.iter (fun b -> ignore(live b)) b.bnext;
+			end;
+			need
+	in
+	ignore(live root);
+
+	(* nop *)
+
+	for i=0 to Array.length f.code - 1 do
+		(match op i with
+		| OMov (d,r) when not (is_live d (i + 1)) ->
+			let n = read_counts.(r) - 1 in
+			read_counts.(r) <- n;
+			write_counts.(d) <- write_counts.(d) - 1;
+			set_nop i "unused"
+		| _ -> ());
+	done;
+
+	(* reg map *)
+
+	let used_regs = ref 0 in
+	let reg_map = read_counts in
+	let nargs = (match f.ftype with HFun (args,_) -> List.length args | _ -> assert false) in
+	for i=0 to nregs-1 do
+		if read_counts.(i) > 0 || write_counts.(i) > 0 || i < nargs then begin
+			reg_map.(i) <- !used_regs;
+			incr used_regs;
+		end else
+			reg_map.(i) <- -1;
+	done;
+	let reg_remap = !used_regs <> nregs in
+
+	(* done *)
+	if dump <> None then begin
+		let rec loop i block =
+			if i = Array.length f.code then () else
+			let block = try
+				let b = Hashtbl.find blocks_pos i in
+				write (Printf.sprintf "\t----- [%s] (%X)"
+					(String.concat "," (List.map (fun b -> Printf.sprintf "%X" b.bstart) b.bnext))
+					b.bend
+				);
+				let need = String.concat "," (List.map string_of_int (ISet.elements b.bneed)) in
+				let wr = String.concat " " (List.rev (PMap.foldi (fun r p acc -> Printf.sprintf "r%d:%X" r p :: acc) b.bwrite [])) in
+				write ("\t" ^ (if b.bloop then "LOOP " else "") ^ "NEED=" ^ need ^ "\tWRITE=" ^ wr);
+				b
+			with Not_found ->
+				block
+			in
+			let old = old_code.(i) in
+			let op = op i in
+			let rec live_loop r l =
+				if r = nregs then List.rev l else
+				live_loop (r + 1) (if is_live r i then r :: l else l)
+			in
+			let live = "LIVE=" ^ String.concat "," (List.map string_of_int (live_loop 0 [])) in
+			write (Printf.sprintf "\t@%-3X %-20s %-20s%s" i (ostr string_of_int old) (if opcode_eq old op then "" else ostr string_of_int op) live);
+			loop (i + 1) block
+		in
+		write (Printf.sprintf "%s@%d" (fundecl_name f) f.findex);
+		if reg_remap then begin
+			for i=0 to nregs-1 do
+				write (Printf.sprintf "\tr%-2d %-10s%s" i (tstr f.regs.(i)) (if reg_map.(i) < 0 then " unused" else if reg_map.(i) = i then "" else Printf.sprintf " r%-2d" reg_map.(i)))
+			done;
+		end;
+		loop 0 root;
+		write "";
+		write "";
+		(match dump with None -> () | Some ch -> IO.flush ch);
+	end;
+
+	(* remap *)
+
+	let code = ref f.code in
+	let regs = ref f.regs in
+	let debug = ref f.debug in
+
+	if !nop_count > 0 || reg_remap then begin
+		let new_pos = Array.make (Array.length f.code) 0 in
+		let jumps = ref [] in
+		let out_pos = ref 0 in
+		let out_code = Array.make (Array.length f.code - !nop_count) (ONop "") in
+		let new_debug = Array.make (Array.length f.code - !nop_count) (0,0) in
+		Array.iteri (fun i op ->
+			Array.unsafe_set new_pos i !out_pos;
+			match op with
+			| ONop _ -> ()
+			| _ ->
+				(match op with
+				| OJTrue _ | OJFalse _ | OJNull _ | OJNotNull _  | OJSLt _ | OJSGte _ | OJSGt _ | OJSLte _ | OJULt _ | OJUGte _ | OJEq _ | OJNotEq _ | OJAlways _ | OSwitch _  | OTrap _ ->
+					jumps := i :: !jumps
+				| _ -> ());
+				let op = if reg_remap then opcode_map (fun r -> reg_map.(r)) (fun r -> reg_map.(r)) op else op in
+				out_code.(!out_pos) <- op;
+				new_debug.(!out_pos) <- f.debug.(i);
+				incr out_pos
+		) f.code;
+		List.iter (fun j ->
+			let pos d =
+				new_pos.(j + 1 + d) - new_pos.(j + 1)
+			in
+			let p = new_pos.(j) in
+			out_code.(p) <- (match out_code.(p) with
+			| OJTrue (r,d) -> OJTrue (r,pos d)
+			| OJFalse (r,d) -> OJFalse (r,pos d)
+			| OJNull (r,d) -> OJNull (r, pos d)
+			| OJNotNull (r,d) -> OJNotNull (r, pos d)
+			| OJSLt (a,b,d) -> OJSLt (a,b,pos d)
+			| OJSGte (a,b,d) -> OJSGte (a,b,pos d)
+			| OJSLte (a,b,d) -> OJSLte (a,b,pos d)
+			| OJSGt (a,b,d) -> OJSGt (a,b,pos d)
+			| OJULt (a,b,d) -> OJULt (a,b,pos d)
+			| OJUGte (a,b,d) -> OJUGte (a,b,pos d)
+			| OJEq (a,b,d) -> OJEq (a,b,pos d)
+			| OJNotEq (a,b,d) -> OJNotEq (a,b,pos d)
+			| OJAlways d -> OJAlways (pos d)
+			| OSwitch (r,cases,send) -> OSwitch (r, Array.map pos cases, pos send)
+			| OTrap (r,d) -> OTrap (r,pos d)
+			| _ -> assert false)
+		) !jumps;
+		code := out_code;
+		debug := new_debug;
+		if reg_remap then begin
+			let new_regs = Array.make !used_regs HVoid in
+			for i=0 to nregs-1 do
+				let p = reg_map.(i) in
+				if p >= 0 then new_regs.(p) <- f.regs.(i)
+			done;
+			regs := new_regs;
+		end;
+	end;
+
+	{ f with code = !code; regs = !regs; debug = !debug }

+ 64 - 0
src/globals.ml

@@ -0,0 +1,64 @@
+type pos = {
+	pfile : string;
+	pmin : int;
+	pmax : int;
+}
+
+module IntMap = Map.Make(struct type t = int let compare a b = a - b end)
+module StringMap = Map.Make(struct type t = string let compare = String.compare end)
+
+type platform =
+	| Cross
+	| Js
+	| Lua
+	| Neko
+	| Flash
+	| Php
+	| Cpp
+	| Cs
+	| Java
+	| Python
+	| Hl
+
+let version = 3400
+let version_major = version / 1000
+let version_minor = (version mod 1000) / 100
+let version_revision = (version mod 100)
+let version_is_stable = version_minor land 1 = 0
+
+let macro_platform = ref Neko
+
+let is_windows = Sys.os_type = "Win32" || Sys.os_type = "Cygwin"
+
+let s_version =
+	Printf.sprintf "%d.%d.%d%s" version_major version_minor version_revision (match Version.version_extra with None -> "" | Some v -> " " ^ v)
+
+let platforms = [
+	Js;
+	Lua;
+	Neko;
+	Flash;
+	Php;
+	Cpp;
+	Cs;
+	Java;
+	Python;
+	Hl;
+]
+
+let platform_name = function
+	| Cross -> "cross"
+	| Js -> "js"
+	| Lua -> "lua"
+	| Neko -> "neko"
+	| Flash -> "flash"
+	| Php -> "php"
+	| Cpp -> "cpp"
+	| Cs -> "cs"
+	| Java -> "java"
+	| Python -> "python"
+	| Hl -> "hl"
+
+let null_pos = { pfile = "?"; pmin = -1; pmax = -1 }
+
+let s_type_path (p,s) = match p with [] -> s | _ -> String.concat "." p ^ "." ^ s

+ 554 - 0
src/macro/hlmacro.ml

@@ -0,0 +1,554 @@
+(*
+	The Haxe Compiler
+	Copyright (C) 2005-2016  Haxe Foundation
+
+	This program is free software; you can redistribute it and/or
+	modify it under the terms of the GNU General Public License
+	as published by the Free Software Foundation; either version 2
+	of the License, or (at your option) any later version.
+
+	This program 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 General Public License for more details.
+
+	You should have received a copy of the GNU General Public License
+	along with this program; if not, write to the Free Software
+	Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ *)
+open Globals
+open Type
+open Ast
+open Hlcode
+open Hlinterp
+open MacroApi
+
+type value = Hlinterp.value
+
+type context = {
+	com : Common.context; (* macro one *)
+	mutable gen : Genhl.context option;
+	interp : Hlinterp.context;
+	types : (Type.path,int) Hashtbl.t;
+	cached_protos : (obj_type, (virtual_proto * vfield array)) Hashtbl.t;
+	cached_enums : (enum_type, ttype) Hashtbl.t;
+	mutable curapi : value MacroApi.compiler_api;
+	mutable has_error : bool;
+}
+
+exception Error of string * Globals.pos list
+
+let debug = true (* TODO !!! set to false for speed++ ! *)
+
+let get_ctx_ref = ref (fun() -> assert false)
+let get_ctx() : context = (!get_ctx_ref)()
+let macro_lib = Hashtbl.create 0
+let interp() = (get_ctx()).interp
+
+let setup get_api =
+	let api = get_api (fun() -> (get_ctx()).curapi.get_com()) (fun() -> (get_ctx()).curapi) in
+	List.iter (fun (n,v) -> Hashtbl.replace macro_lib n (match v with VClosure (FNativeFun (_,v,_),None) -> v | _ -> assert false)) api;
+	Globals.macro_platform := Globals.Hl
+
+let select ctx =
+	get_ctx_ref := (fun() -> ctx)
+
+let error_handler ctx v stack =
+	let make_pos st =
+		let file, line = Hlinterp.make_stack ctx.interp st in
+		let low = line land 0xFFFFF in
+		{
+			Globals.pfile = file;
+			Globals.pmin = low;
+			Globals.pmax = low + (line lsr 20);
+		}
+	in
+	(*let rec loop o =
+		if o == ctx.error_proto then true else match o.oproto with None -> false | Some p -> loop p
+	in
+	(match v with
+	| VObject o when loop o ->
+		(match get_field o (hash "message"), get_field o (hash "pos") with
+		| VObject msg, VAbstract (APos pos) ->
+			(match get_field msg h_s with
+			| VString msg -> raise (Error.Error (Error.Custom msg,pos))
+			| _ -> ());
+		| _ -> ());
+	| _ -> ());*)
+	raise (Error (Hlinterp.vstr ctx.interp v Hlcode.HDyn,List.map make_pos stack))
+
+let create com api =
+	let ctx = {
+		com = com;
+		gen = None;
+		interp = Hlinterp.create debug;
+		curapi = api;
+		types = Hashtbl.create 0;
+		has_error = false;
+		cached_protos = Hashtbl.create 0;
+		cached_enums = Hashtbl.create 0;
+	} in
+	select ctx;
+	Hlinterp.set_error_handler ctx.interp (error_handler ctx);
+	Hlinterp.set_macro_api ctx.interp (fun name -> try Some (Hashtbl.find macro_lib name) with Not_found -> None);
+	ctx
+
+let init ctx =
+	if ctx.gen = None then ctx.gen <- Some (Genhl.create_context ctx.com true false)
+
+let set_error ctx e =
+	ctx.has_error <- e
+
+let add_types ctx types ready =
+	let types = List.filter (fun t -> match t with
+		| TAbstractDecl a when not (Meta.has Meta.CoreType a.a_meta) ->
+			(* A @:native on an abstract causes the implementation class and the abstract
+			   to have the same path. Let's skip all abstracts so this doesn't matter. *)
+			false
+		| _ ->
+			let path = Type.t_path t in
+			if Hashtbl.mem ctx.types path then false else begin
+				Hashtbl.add ctx.types path (Type.t_infos t).mt_module.m_id;
+				true;
+			end
+	) types in
+	List.iter ready types;
+	if ctx.gen = None then init ctx;
+	match ctx.gen with
+	| None -> assert false
+	| Some gen ->
+		Genhl.add_types gen types;
+		if debug then Genhl.check gen;
+		let code = Genhl.build_code gen types None in
+		if debug then begin
+			try
+				Hlinterp.check code true
+			with Failure _ | Common.Abort _ as exn ->
+				let ch = open_out_bin "hlcode.txt" in
+				Hlcode.dump (fun s -> output_string ch (s ^ "\n")) code;
+				close_out ch;
+				raise exn
+		end;
+		Hlinterp.add_code ctx.interp code
+
+let do_reuse ctx api =
+	ctx.curapi <- api
+
+let can_reuse ctx types =
+	let has_old_version t =
+		let inf = Type.t_infos t in
+		try
+			Hashtbl.find ctx.types inf.mt_path <> inf.mt_module.m_id
+		with Not_found ->
+			false
+	in
+	not (List.exists has_old_version types)
+
+let catch_errors ctx ?(final=(fun() -> ())) f =
+	let prev = get_ctx() in (* switch context in case we have an older one, see #5676 *)
+	select ctx;
+	try
+		let v = f() in
+		final();
+		select prev;
+		Some v
+	with Error _ as e ->
+		final();
+		select prev;
+		raise e
+	|  Abort ->
+		final();
+		select prev;
+		None
+
+let call_path ctx cpath (f:string) args api =
+	if ctx.has_error then
+		None
+	else let old = ctx.curapi in
+	ctx.curapi <- api;
+	let gid = Genhl.resolve_class_global (match ctx.gen with None -> assert false | Some ctx -> ctx) (String.concat "." cpath) in
+	let gval = ctx.interp.Hlinterp.t_globals.(gid) in
+	let fval = Hlinterp.dyn_get_field ctx.interp gval f Hlcode.HDyn in
+	match fval with
+	| Hlinterp.VClosure (f,None) ->
+		catch_errors ~final:(fun() -> ctx.curapi <- old) ctx (fun() -> call_wrap ctx.interp f args)
+	| _ ->
+		prerr_endline (Hlinterp.vstr_d ctx.interp gval);
+		assert false
+
+let vnull = VNull
+let vbool b = VBool b
+let vint i = VInt (Int32.of_int i)
+let vint32 i = VInt i
+let vfloat f = VFloat f
+
+let vfun0 f =
+	let callb args = match args with [] -> f() | _ -> assert false in
+	VClosure(FNativeFun ("fun0",callb,HVoid), None)
+
+let vfun1 f =
+	let callb args = match args with [a] -> f a | _ -> assert false in
+	VClosure(FNativeFun ("fun1",callb,HVoid), None)
+
+let vfun2 f =
+	let callb args = match args with [a;b] -> f a b | _ -> assert false in
+	VClosure(FNativeFun ("fun2",callb,HVoid), None)
+
+let vfun3 f =
+	let callb args = match args with [a;b;c] -> f a b c | _ -> assert false in
+	VClosure(FNativeFun ("fun3",callb,HVoid), None)
+
+let vfun4 f =
+	let callb args = match args with [a;b;c;d] -> f a b c d | _ -> assert false in
+	VClosure(FNativeFun ("fun4",callb,HVoid), None)
+
+let vfun5 f =
+	let callb args = match args with [a;b;c;d;e] -> f a b c d e | _ -> assert false in
+	VClosure(FNativeFun ("fun5",callb,HVoid), None)
+
+let exc_string msg =
+	Hlinterp.throw_msg (interp()) msg
+
+let compiler_error msg pos =
+	assert false (* TODO : raise haxe.macro.Error(msg,pos) *)
+
+let eval_expr ctx (e:texpr) =
+	assert false
+
+let eval_delayed _ _ =
+	assert false (* macro - in - macro *)
+
+let prepare_callback f n =
+	assert false
+
+let encode_pos p = VAbstract (APos p)
+
+let decode_pos = function
+	| VAbstract (APos p) -> p
+	| _ -> raise Invalid_expr
+
+let last_enum_type = ref IExpr
+
+let encode_enum t pos tag pl =
+	last_enum_type := t;
+	match pos with
+	| None -> VEnum (tag,Array.of_list pl)
+	| Some p -> VEnum (tag,Array.of_list (List.rev (encode_pos p :: List.rev pl)))
+
+let decode_enum = function
+	| VEnum (tag,arr) -> tag, Array.to_list arr
+	| _ -> raise Invalid_expr
+
+let decode_enum_with_pos = function
+	| VEnum (tag,arr) when Array.length arr > 0 && (match arr.(Array.length arr - 1) with VAbstract (APos _) -> true | _ -> false) ->
+		let rec loop i =
+			if i = Array.length arr - 1 then [] else arr.(i) :: loop (i + 1)
+		in
+		(tag, loop 0), decode_pos arr.(Array.length arr - 1)
+	| e ->
+		decode_enum e, Globals.null_pos
+
+
+let encode_tdecl t = VAbstract (ATDecl t)
+let encode_unsafe o = VAbstract (AUnsafe o)
+
+let decode_unsafe = function
+	| VAbstract (AUnsafe v) -> v
+	| _ -> raise Invalid_expr
+
+let decode_tdecl = function
+	| VAbstract (ATDecl t) -> t
+	| _ -> raise Invalid_expr
+
+let dec_int = function
+	| VInt i -> Int32.to_int i
+	| _ -> raise Invalid_expr
+
+let dec_i32 = function
+	| VInt i -> i
+	| _ -> raise Invalid_expr
+
+let dec_bool = function
+	| VBool b -> b
+	| _ -> raise Invalid_expr
+
+let field o n =
+	match o with
+	| VDynObj _ | VVirtual _ | VObj _ -> Hlinterp.dyn_get_field (interp()) o n HDyn
+	| _ -> raise Invalid_expr
+
+let dec_string = function
+	| VObj { ofields = [|VBytes s;VInt _|] } -> Hlinterp.hl_to_caml s
+	| _ -> raise Invalid_expr
+
+let dec_array = function
+	| VObj { ofields = [|VInt len;VArray (arr,_)|] } ->
+		let len = Int32.to_int len in
+		let rec loop i =
+			if i = len then [] else arr.(i) :: loop (i+1)
+		in
+		loop 0
+	| v ->
+		raise Invalid_expr
+
+let encode_lazytype f t = VAbstract (ALazyType (f,t))
+
+let decode_lazytype = function
+	| VAbstract (ALazyType (t,_)) -> t
+	| _ -> raise Invalid_expr
+
+let enc_obj t fields =
+	match t with
+	| OMetaAccess ->
+		let h = Hashtbl.create 0 in
+		let rec loop i = function
+			| [] -> ()
+			| (n,_) :: l ->
+				Hashtbl.add h n i;
+				loop (i + 1) l
+		in
+		loop 0 fields;
+		let values = Array.of_list (List.map snd fields) in
+		VDynObj {
+			dfields = h;
+			dvalues = values;
+			dtypes = Array.make (Array.length values) HDyn;
+			dvirtuals = [];
+		}
+	| _ ->
+	let ctx = get_ctx() in
+	let to_str (name,f) =
+		match f with
+		| None -> name
+		| Some f -> name ^ "." ^ f
+	in
+	let vp, idx = try
+		Hashtbl.find ctx.cached_protos t
+	with Not_found ->
+		let name, field = proto_name t in
+		let gen = (match ctx.gen with None -> assert false | Some gen -> gen) in
+		let vt = (try
+			let t = Hashtbl.find gen.Genhl.macro_typedefs name in
+			(match t, field with
+			| _, None -> t
+			| HVirtual v, Some f ->
+				let idx = (try PMap.find f v.vindex with Not_found -> failwith (name ^ " has no field definition " ^ f)) in
+				let _,_, t = v.vfields.(idx) in
+				(match t with
+				| HVirtual _ -> t
+				| _ -> failwith ("Unexpected type " ^ tstr t ^ " for definition " ^ to_str (name,field)))
+			| _ ->
+				assert false
+			)
+		with Not_found -> try
+			let t = PMap.find (["haxe";"macro"],name) gen.Genhl.cached_types in
+			(match t, field with
+			| HEnum e, Some f ->
+				let rec loop i =
+					if i = Array.length e.efields then raise Not_found;
+					let n, _, tl = e.efields.(i) in
+					if n = f then
+						tl.(0)
+					else
+						loop (i + 1)
+				in
+				loop 0
+			| _ ->
+				failwith ("Unexpected type " ^ tstr t ^ " for definition " ^ to_str (name,field)))
+		with Not_found ->
+			failwith ("Macro definition missing " ^ to_str (name,field))
+		) in
+		match vt with
+		| HVirtual vp ->
+			let vindexes = Array.map (fun (n,_,_) ->
+				let rec loop i = function
+					| [] -> VFNone
+					| (n2,_) :: _ when n = n2 -> VFIndex i
+					| _ :: l -> loop (i + 1) l
+				in
+				loop 0 fields
+			) vp.vfields in
+			Hashtbl.replace ctx.cached_protos t (vp, vindexes);
+			vp, vindexes
+		| _ ->
+			failwith (to_str (name,field) ^ " returned invalid type " ^ tstr vt)
+	in
+	if debug then begin
+		let farr = Array.of_list fields in
+		Array.iteri (fun i idx ->
+			let name, _ ,_ = vp.vfields.(i) in
+			match idx with
+			| VFNone ->
+				if List.mem_assoc name fields then failwith ("Field " ^ name ^ " is present in "  ^ to_str (proto_name t))
+			| VFIndex i when i >= Array.length farr ->
+				failwith ("Missing field " ^ name ^ " of "  ^ to_str (proto_name t))
+			| VFIndex i when fst farr.(i) <> name ->
+				failwith ("Field " ^ name ^ " of "  ^ to_str (proto_name t) ^ " is wrongly mapped on " ^ fst farr.(i))
+			| _ ->
+				()
+		) idx;
+		List.iter (fun (n,_) ->
+			if n <> "name_pos" && not (PMap.mem n vp.vindex) then failwith ("Field " ^ n ^ " has data but is not part of type " ^ to_str (proto_name t));
+		) fields;
+	end;
+	VVirtual {
+		vtype = vp;
+		vindexes = idx;
+		vtable = Array.map snd (Array.of_list fields);
+		vvalue = VNull;
+	}
+
+let enc_inst path fields =
+	let ctx = get_ctx() in
+	let t = (match ctx.gen with None -> assert false | Some gen -> try Genhl.resolve_type gen path with Not_found -> assert false) in
+	match t with
+	| HObj o ->
+		let proto, _ = Hlinterp.get_proto ctx.interp o in
+		VObj { oproto = proto; ofields = fields }
+	| _ ->
+		assert false
+
+let enc_string s =
+	enc_inst ([],"String") [|VBytes (caml_to_hl s);VInt (Int32.of_int (String.length s))|]
+
+let enc_array vl =
+	let arr = Array.of_list (List.map (fun v ->
+		match v with
+		| VNull | VObj _ | VVirtual _ -> v
+		| VEnum _ ->
+			let ctx = get_ctx() in
+			let et = !last_enum_type in
+			let t = try
+				Hashtbl.find ctx.cached_enums et
+			with Not_found ->
+				let name = enum_name et in
+				let t = (match ctx.gen with
+				| None -> assert false
+				| Some gen -> try PMap.find (["haxe";"macro"],name) gen.Genhl.cached_types with Not_found -> failwith ("Missing enum type " ^ name)
+				) in
+				Hashtbl.replace ctx.cached_enums et t;
+				t
+			in
+			VDyn (v,t)
+		| _ -> failwith "Invalid array value"
+	) vl) in
+	enc_inst (["hl";"types"],"ArrayObj") [|VInt (Int32.of_int (Array.length arr));VArray (arr,HDyn)|]
+
+let encode_bytes s =
+	enc_inst (["haxe";"io"],"Bytes") [|VInt (Int32.of_int (String.length s)); VBytes s|]
+
+let encode_string_map convert pmap =
+	let h = Hashtbl.create 0 in
+	PMap.iter (fun k v -> Hashtbl.add h k (convert v)) pmap;
+	enc_inst (["haxe";"ds"],"StringMap") [|VAbstract (AHashBytes h)|]
+
+let decode_bytes = function
+	| VObj { ofields = [|VInt _;VBytes s|] } -> s
+	| _ -> raise Invalid_expr
+
+let encode_ref v convert tostr =
+	let h = Hashtbl.create 0 in
+	Hashtbl.add h "get" 0;
+	Hashtbl.add h "__string" 1;
+	Hashtbl.add h "toString" 2;
+	Hashtbl.add h "$" 3;
+	VDynObj {
+		dfields = h;
+		dvalues = [|
+			vfun0 (fun() -> convert v);
+			vfun0 (fun() -> VBytes (caml_to_hl (tostr())));
+			vfun0 (fun() -> enc_string (tostr()));
+			VAbstract (AUnsafe (Obj.repr v))
+		|];
+		dtypes = [|
+			HFun ([],HDyn);
+			HFun ([],HBytes);
+			HFun ([],HDyn);
+			HDyn;
+		|];
+		dvirtuals = [];
+	}
+
+let decode_ref v : 'a =
+	match field v "$" with
+	| VAbstract (AUnsafe t) -> Obj.obj t
+	| _ -> raise Invalid_expr
+
+let value_string v =
+	Hlinterp.vstr (get_ctx()).interp v HDyn
+
+let value_signature v =
+	failwith "signature() not supported in HL macros"
+
+let value_to_expr v p =
+	let ctx = get_ctx() in
+	let error v = failwith ("Unsupported value " ^ vstr ctx.interp v Hlcode.HDyn) in
+	(*
+	let h_enum = hash "__enum__" and h_et = hash "__et__" and h_ct = hash "__ct__" in
+	let h_tag = hash "tag" and h_args = hash "args" in
+	let h_length = hash "length" in
+	let make_path t =
+		let rec loop = function
+			| [] -> assert false
+			| [name] -> (Ast.EConst (Ast.Ident name),p)
+			| name :: l -> (Ast.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]))
+	in*)
+	let rec loop = function
+		| VNull -> (Ast.EConst (Ast.Ident "null"),p)
+		| VBool b -> (Ast.EConst (Ast.Ident (if b then "true" else "false")),p)
+		| VInt i -> (Ast.EConst (Ast.Int (Int32.to_string i)),p)
+		| VFloat f ->
+			let std = (Ast.EConst (Ast.Ident "std"), p) in
+			let math = (Ast.EField (std, "Math"), p) in
+			if (f = infinity) then
+				(Ast.EField (math, "POSITIVE_INFINITY"), p)
+			else if (f = neg_infinity) then
+				(Ast.EField (math, "NEGATIVE_INFINITY"), p)
+			else if (f <> f) then
+				(Ast.EField (math, "NaN"), p)
+			else
+				(Ast.EConst (Ast.Float (Common.float_repres f)), p)
+		| VAbstract (APos p) ->
+			(Ast.EObjectDecl (
+				(("fileName",Globals.null_pos) , (Ast.EConst (Ast.String p.Globals.pfile) , p)) ::
+				(("lineNumber",Globals.null_pos) , (Ast.EConst (Ast.Int (string_of_int (Lexer.get_error_line p))),p)) ::
+				(("className",Globals.null_pos) , (Ast.EConst (Ast.String ("")),p)) ::
+				[]
+			), p)
+		| VObj { oproto = { pclass = { pname = "String" } }; ofields = [|VBytes content;VInt _|] } ->
+			(Ast.EConst (Ast.String (hl_to_caml content)),p)
+		| v ->
+			error v
+		(*
+		| VObject o as v ->
+			match o.oproto with
+			| None ->
+				(match get_field_opt o h_ct with
+				| Some (VAbstract (ATDecl t)) ->
+					make_path t
+				| _ ->
+					let fields = List.fold_left (fun acc (fid,v) -> ((field_name ctx fid,Globals.null_pos), loop v) :: acc) [] (Array.to_list o.ofields) in
+					(Ast.EObjectDecl fields, p))
+			| Some proto ->
+				match get_field_opt proto h_enum, get_field_opt o h_a, get_field_opt o h_s, get_field_opt o h_length with
+				| _, Some (VArray a), _, Some (VInt len) ->
+					(Ast.EArrayDecl (List.map loop (Array.to_list (Array.sub a 0 len))),p)
+				| Some (VObject en), _, _, _ ->
+					(match get_field en h_et, get_field o h_tag with
+					| VAbstract (ATDecl t), VString tag ->
+						let e = (Ast.EField (make_path t,tag),p) in
+						(match get_field_opt o h_args with
+						| Some (VArray args) ->
+							let args = List.map loop (Array.to_list args) in
+							(Ast.ECall (e,args),p)
+						| _ -> e)
+					| _ ->
+						error v)
+				| _ ->
+					error v
+			*)
+	in
+	loop v

文件差異過大導致無法顯示
+ 63 - 734
src/macro/interp.ml


+ 1869 - 0
src/macro/macroApi.ml

@@ -0,0 +1,1869 @@
+open Ast
+open Type
+open Common
+
+exception Invalid_expr
+exception Abort
+
+(**
+	Our access to the compiler from the macro api
+**)
+
+type 'value compiler_api = {
+	pos : Globals.pos;
+	get_com : unit -> Common.context;
+	get_type : string -> Type.t option;
+	get_module : string -> Type.t list;
+	after_typing : (module_type list -> unit) -> unit;
+	on_generate : (Type.t list -> unit) -> unit;
+	after_generate : (unit -> unit) -> unit;
+	on_type_not_found : (string -> 'value) -> unit;
+	parse_string : string -> Globals.pos -> bool -> Ast.expr;
+	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;
+	meta_patch : string -> string -> string option -> bool -> unit;
+	set_js_generator : (Genjs.ctx -> unit) -> unit;
+	get_local_type : unit -> t option;
+	get_expected_type : unit -> t option;
+	get_call_arguments : unit -> Ast.expr list option;
+	get_local_method : unit -> string;
+	get_local_imports : unit -> Ast.import list;
+	get_local_using : unit -> tclass list;
+	get_local_vars : unit -> (string, Type.tvar) PMap.t;
+	get_build_fields : unit -> 'value;
+	get_pattern_locals : Ast.expr -> Type.t -> (string,Type.tvar * Globals.pos) PMap.t;
+	define_type : 'value -> unit;
+	define_module : string -> 'value list -> ((string * Globals.pos) list * Ast.import_mode) list -> Ast.type_path list -> unit;
+	module_dependency : string -> string -> bool -> unit;
+	current_module : unit -> module_def;
+	on_reuse : (unit -> bool) -> unit;
+	mutable current_macro_module : unit -> module_def;
+	delayed_macro : int -> (unit -> (unit -> 'value));
+	use_cache : unit -> bool;
+	format_string : string -> Globals.pos -> Ast.expr;
+	cast_or_unify : Type.t -> texpr -> Globals.pos -> Type.texpr;
+	add_global_metadata : string -> string -> (bool * bool * bool) -> unit;
+	add_module_check_policy : string list -> int list -> bool -> int -> unit;
+}
+
+
+type enum_type =
+	| IExpr
+	| IBinop
+	| IUnop
+	| IConst
+	| ITParam
+	| ICType
+	| IField
+	| IType
+	| IFieldKind
+	| IMethodKind
+	| IVarAccess
+	| IAccess
+	| IClassKind
+	| ITypedExpr
+	| ITConstant
+	| IModuleType
+	| IFieldAccess
+	| IAnonStatus
+	| IImportMode
+
+type obj_type =
+	(* make_const *)
+	| O__Const
+	(* Expr *)
+	| OImportExpr
+	| OImportExpr_path
+	| OTypePath
+	| OMetadataEntry
+	| OField
+	| OTypeParamDecl
+	| OFunction
+	| OFunctionArg
+	| OExprDef_fields
+	| OVar
+	| OCase
+	| OCatch
+	| OExpr
+	(* Type *)
+	| OMetaAccess
+	| OTypeParameter
+	| OClassType
+	| OAbstractType
+	| OAnonType
+	| ODefType
+	| OEnumType
+	| OClassField
+	| OAbstractType_binops
+	| OAbstractType_unops
+	| OAbstractType_from
+	| OAbstractType_to
+	| OEnumField
+	| OClassType_superClass
+	| OClassType_interfaces
+	| OType_args
+	| OTVar
+	| OTVar_extra
+	| OTFunc
+	| OTFunc_args
+	| OFieldAccess_c
+	| OTypedExprDef
+	| OTypedExprDef_fields
+	| OTypedExprDef_cases
+	| OTypedExprDef_catches
+	| OJSGenApi
+	| OContext_getPosInfos
+	| OCompiler_getDisplayPos
+	| ORef
+(* ---- ^^^^^ please exactly match the name of the typedef or use TypeName_field if it's a anonymous *)
+
+(**
+	Our access to the interpreter from the macro api
+**)
+
+module type InterpApi = sig
+	type value
+
+	val vnull : value
+	val vint : int -> value
+	val vfloat : float -> value
+	val vint32 : int32 -> value
+	val vbool : bool -> value
+
+	val enc_array : value list -> value
+	val enc_string  : string -> value
+	val enc_obj : obj_type -> (string * value) list -> value
+
+	val vfun0 : (unit -> value) -> value
+	val vfun1 : (value -> value) -> value
+	val vfun2 : (value -> value -> value) -> value
+	val vfun3 : (value -> value -> value -> value) -> value
+	val vfun4 : (value -> value -> value -> value -> value) -> value
+	val vfun5 : (value -> value -> value -> value -> value -> value) -> value
+
+	val encode_pos : Globals.pos -> value
+	val encode_enum : enum_type -> Globals.pos option -> int -> value list -> value
+	val encode_string_map : ('a -> value) -> (string, 'a) PMap.t -> value
+
+	val encode_tdecl : Type.module_type -> value
+	val encode_lazytype : (unit -> Type.t) ref -> (unit -> value) -> value
+	val encode_unsafe : Obj.t -> value
+
+	val field : value -> string -> value
+
+	val dec_bool : value -> bool
+	val dec_int : value -> int
+	val dec_i32 : value -> int32
+	val dec_string : value -> string
+	val dec_array : value -> value list
+
+	val decode_pos : value -> Globals.pos
+	val decode_enum : value -> int * value list
+	val decode_tdecl : value -> Type.module_type
+	val decode_lazytype : value -> (unit -> Type.t) ref
+	val decode_unsafe : value -> Obj.t
+
+	val decode_enum_with_pos : value -> (int * value list) * Globals.pos
+
+	val encode_ref : 'a -> ('a -> value) -> (unit -> string) -> value
+	val decode_ref : value -> 'a
+
+	val compiler_error : string -> Globals.pos -> 'a
+	val value_to_expr : value -> Globals.pos -> Ast.expr
+	val value_signature : value -> string
+
+	val encode_bytes : string -> value
+	val decode_bytes : value -> string (* haxe.io.Bytes *)
+
+	val prepare_callback : value -> int -> (value list -> value)
+
+	val value_string : value -> string
+
+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"
+
+let proto_name = function
+	| O__Const -> assert false
+	| OImportExpr -> "ImportExpr", None
+	| OImportExpr_path -> "ImportExpr", Some "path"
+	| OTypePath -> "TypePath", None
+	| OMetadataEntry -> "MetadataEntry", None
+	| OField -> "Field", None
+	| OTypeParamDecl -> "TypeParamDecl", None
+	| OFunction -> "Function", None
+	| OFunctionArg -> "FunctionArg", None
+	| OExprDef_fields -> "ExprDef", Some "fields"
+	| OVar -> "Var", None
+	| OCase -> "Case", None
+	| OCatch -> "Catch", None
+	| OExpr -> "Expr", None
+	| OMetaAccess -> "MetaAccess", None
+	| OTypeParameter -> "TypeParameter", None
+	| OClassType -> "ClassType", None
+	| OAbstractType -> "AbstracType", None
+	| OAnonType -> "AnonType", None
+	| ODefType -> "DefType", None
+	| OEnumType -> "EnumType", None
+	| OClassField -> "ClassField", None
+	| OAbstractType_binops -> "AbstractType", Some "binops"
+	| OAbstractType_unops -> "AbstractType", Some "unops"
+	| OAbstractType_from -> "AbstractType", Some "from"
+	| OAbstractType_to -> "AbstractType", Some "to"
+	| OEnumField -> "EnumField", None
+	| OClassType_superClass -> "ClassType", Some "superClass"
+	| OClassType_interfaces -> "ClassType", Some "interfaces"
+	| OType_args -> "Type", Some "args"
+	| OTVar -> "TVar", None
+	| OTVar_extra -> "TVar", Some "extra"
+	| OTFunc -> "TFunc", None
+	| OTFunc_args -> "TFunc", Some "args"
+	| OFieldAccess_c -> "FieldAccess", Some "c"
+	| OTypedExprDef -> "TypedExprDef", None
+	| OTypedExprDef_fields -> "TypedExprDef", Some "fields"
+	| OTypedExprDef_cases -> "TypedExprDef", Some "cases"
+	| OTypedExprDef_catches -> "TypedExprDef", Some "catches"
+	| OJSGenApi -> "JSGenApi", None
+	| OContext_getPosInfos -> "Context", Some "getPosInfos"
+	| OCompiler_getDisplayPos -> "Compiler", Some "getDisplayPos"
+	| ORef -> "Ref", None
+
+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
+	if (f = infinity) then
+		(Ast.EField (math, "POSITIVE_INFINITY"), p)
+	else if (f = neg_infinity) then
+		(Ast.EField (math, "NEGATIVE_INFINITY"), p)
+	else if (f <> f) then
+		(Ast.EField (math, "NaN"), p)
+	else
+		(Ast.EConst (Ast.Float (float_repres f)), p)
+
+(* ------------------------------------------------------------------------------------------------------------- *)
+(* Our macro api functor *)
+
+module MacroApiImpl(InterpImpl:InterpApi) = struct
+
+open InterpImpl
+
+(*
+	The whole encoding/decoding of compiler values from/to interpreter values based on interpreter api
+*)
+
+let null f = function
+	| None -> vnull
+	| Some v -> f v
+
+let encode_enum ?(pos=None) k tag vl = encode_enum k pos tag vl
+
+let encode_const c =
+	let tag, pl = match c with
+	| Int s -> 0, [enc_string s]
+	| Float s -> 1, [enc_string s]
+	| String s -> 2, [enc_string s]
+	| Ident s -> 3, [enc_string s]
+	| Regexp (s,opt) -> 4, [enc_string s;enc_string opt]
+	in
+	encode_enum IConst tag pl
+
+let rec encode_binop op =
+	let tag, pl = match op with
+	| OpAdd -> 0, []
+	| OpMult -> 1, []
+	| OpDiv -> 2, []
+	| OpSub -> 3, []
+	| OpAssign -> 4, []
+	| OpEq -> 5, []
+	| OpNotEq -> 6, []
+	| OpGt -> 7, []
+	| OpGte -> 8, []
+	| OpLt -> 9, []
+	| OpLte -> 10, []
+	| OpAnd -> 11, []
+	| OpOr -> 12, []
+	| OpXor -> 13, []
+	| OpBoolAnd -> 14, []
+	| OpBoolOr -> 15, []
+	| OpShl -> 16, []
+	| OpShr -> 17, []
+	| OpUShr -> 18, []
+	| OpMod -> 19, []
+	| OpAssignOp op -> 20, [encode_binop op]
+	| OpInterval -> 21, []
+	| OpArrow -> 22, []
+	in
+	encode_enum IBinop tag pl
+
+let encode_unop op =
+	let tag = match op with
+	| Increment -> 0
+	| Decrement -> 1
+	| Not -> 2
+	| Neg -> 3
+	| NegBits -> 4
+	in
+	encode_enum IUnop tag []
+
+let encode_import (path,mode) =
+	let tag,pl = match mode with
+		| INormal -> 0, []
+		| IAsName s -> 1, [enc_string s]
+		| IAll -> 2,[]
+	in
+	let mode = encode_enum IImportMode tag pl in
+	enc_obj OImportExpr [
+		"path", enc_array (List.map (fun (name,p) -> enc_obj OImportExpr_path [ "pos", encode_pos p; "name", enc_string name]) path);
+		"mode", mode
+	]
+
+let encode_placed_name (s,p) =
+	enc_string s
+
+let rec encode_path (t,_) =
+	let fields = [
+		"pack", enc_array (List.map enc_string t.tpackage);
+		"name", enc_string t.tname;
+		"params", enc_array (List.map encode_tparam t.tparams);
+	] in
+	enc_obj OTypePath (match t.tsub with
+		| None -> fields
+		| Some s -> ("sub", enc_string s) :: fields)
+
+and encode_tparam = function
+	| TPType t -> encode_enum ITParam 0 [encode_ctype t]
+	| TPExpr e -> encode_enum ITParam 1 [encode_expr e]
+
+and encode_access a =
+	let tag = match a with
+		| APublic -> 0
+		| APrivate -> 1
+		| AStatic -> 2
+		| AOverride -> 3
+		| ADynamic -> 4
+		| AInline -> 5
+		| AMacro -> 6
+	in
+	encode_enum IAccess tag []
+
+and encode_meta_entry (m,ml,p) =
+	enc_obj OMetadataEntry [
+		"name", enc_string (Meta.to_string m);
+		"params", enc_array (List.map encode_expr ml);
+		"pos", encode_pos p;
+	]
+
+and encode_meta_content m =
+	enc_array (List.map encode_meta_entry m)
+
+and encode_field (f:class_field) =
+	let tag, pl = match f.cff_kind with
+		| FVar (t,e) -> 0, [null encode_ctype t; null encode_expr e]
+		| FFun f -> 1, [encode_fun f]
+		| FProp (get,set, t, e) -> 2, [encode_placed_name get; encode_placed_name set; null encode_ctype t; null encode_expr e]
+	in
+	enc_obj OField [
+		"name",encode_placed_name f.cff_name;
+		"name_pos", encode_pos (pos f.cff_name);
+		"doc", null enc_string f.cff_doc;
+		"pos", encode_pos f.cff_pos;
+		"kind", encode_enum IField tag pl;
+		"meta", encode_meta_content f.cff_meta;
+		"access", enc_array (List.map encode_access f.cff_access);
+	]
+
+and encode_ctype t =
+	let tag, pl = match fst t with
+	| CTPath p ->
+		0, [encode_path (p,Globals.null_pos)]
+	| CTFunction (pl,r) ->
+		1, [enc_array (List.map encode_ctype pl);encode_ctype r]
+	| CTAnonymous fl ->
+		2, [enc_array (List.map encode_field fl)]
+	| CTParent t ->
+		3, [encode_ctype t]
+	| CTExtend (tl,fields) ->
+		4, [enc_array (List.map encode_path tl); enc_array (List.map encode_field fields)]
+	| CTOptional t ->
+		5, [encode_ctype t]
+	in
+	encode_enum ~pos:(Some (pos t)) ICType tag pl
+
+and encode_tparam_decl tp =
+	enc_obj OTypeParamDecl [
+		"name", encode_placed_name tp.tp_name;
+		"name_pos", encode_pos (pos tp.tp_name);
+		"params", enc_array (List.map encode_tparam_decl tp.tp_params);
+		"constraints", enc_array (List.map encode_ctype tp.tp_constraints);
+		"meta", encode_meta_content tp.tp_meta;
+	]
+
+and encode_fun f =
+	enc_obj OFunction [
+		"params", enc_array (List.map encode_tparam_decl f.f_params);
+		"args", enc_array (List.map (fun (n,opt,m,t,e) ->
+			enc_obj OFunctionArg [
+				"name", encode_placed_name n;
+				"name_pos", encode_pos (pos n);
+				"opt", vbool opt;
+				"meta", encode_meta_content m;
+				"type", null encode_ctype t;
+				"value", null encode_expr e;
+			]
+		) f.f_args);
+		"ret", null encode_ctype f.f_type;
+		"expr", null encode_expr f.f_expr
+	]
+
+and encode_expr e =
+	let rec loop (e,p) =
+		let tag, pl = match e with
+			| EConst c ->
+				0, [encode_const c]
+			| EArray (e1,e2) ->
+				1, [loop e1;loop e2]
+			| EBinop (op,e1,e2) ->
+				2, [encode_binop op;loop e1;loop e2]
+			| EField (e,f) ->
+				3, [loop e;enc_string f]
+			| EParenthesis e ->
+				4, [loop e]
+			| EObjectDecl fl ->
+				5, [enc_array (List.map (fun ((f,p),e) -> enc_obj OExprDef_fields [
+					"field",enc_string f;
+					"name_pos",encode_pos p;
+					"expr",loop e;
+				]) fl)]
+			| EArrayDecl el ->
+				6, [enc_array (List.map loop el)]
+			| ECall (e,el) ->
+				7, [loop e;enc_array (List.map loop el)]
+			| ENew (p,el) ->
+				8, [encode_path p; enc_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 ->
+				10, [enc_array (List.map (fun (v,t,eo) ->
+					enc_obj OVar [
+						"name",encode_placed_name v;
+						"name_pos",encode_pos (pos v);
+						"type",null encode_ctype t;
+						"expr",null loop eo;
+					]
+				) vl)]
+			| EFunction (name,f) ->
+				11, [null enc_string name; encode_fun f]
+			| EBlock el ->
+				12, [enc_array (List.map loop el)]
+			| EFor (e,eloop) ->
+				13, [loop e;loop eloop]
+			| EIn (e1,e2) ->
+				14, [loop e1;loop e2]
+			| EIf (econd,e,eelse) ->
+				15, [loop econd;loop e;null loop eelse]
+			| EWhile (econd,e,flag) ->
+				16, [loop econd;loop e;vbool (match flag with NormalWhile -> true | DoWhile -> false)]
+			| ESwitch (e,cases,eopt) ->
+				17, [loop e;enc_array (List.map (fun (ecl,eg,e,p) ->
+					enc_obj OCase [
+						"values",enc_array (List.map loop ecl);
+						"guard",null loop eg;
+						"expr",null loop e;
+						"pos",encode_pos p;
+					]
+				) cases);null (fun (e,_) -> encode_null_expr e) eopt]
+			| ETry (e,catches) ->
+				18, [loop e;enc_array (List.map (fun (v,t,e,p) ->
+					enc_obj OCatch [
+						"name",encode_placed_name v;
+						"name_pos",encode_pos (pos v);
+						"type",encode_ctype t;
+						"expr",loop e;
+						"pos",encode_pos p
+					]
+				) catches)]
+			| EReturn eo ->
+				19, [null loop eo]
+			| EBreak ->
+				20, []
+			| EContinue ->
+				21, []
+			| EUntyped e ->
+				22, [loop e]
+			| EThrow e ->
+				23, [loop e]
+			| ECast (e,t) ->
+				24, [loop e; null encode_ctype t]
+			| EDisplay (e,flag) ->
+				25, [loop e; vbool flag]
+			| EDisplayNew t ->
+				26, [encode_path t]
+			| ETernary (econd,e1,e2) ->
+				27, [loop econd;loop e1;loop e2]
+			| ECheckType (e,t) ->
+				28, [loop e; encode_ctype t]
+			| EMeta (m,e) ->
+				29, [encode_meta_entry m;loop e]
+		in
+		enc_obj OExpr [
+			"pos", encode_pos p;
+			"expr", encode_enum IExpr tag pl;
+		]
+	in
+	loop e
+
+and encode_null_expr e =
+	match e with
+	| None ->
+		enc_obj OExpr ["pos", vnull;"expr",vnull]
+	| Some e ->
+		encode_expr e
+
+(* ---------------------------------------------------------------------- *)
+(* EXPR DECODING *)
+
+let opt f v =
+	if v = vnull then None else Some (f v)
+
+let opt_list f v =
+	if v = vnull then [] else f v
+
+let dec_opt_bool v =
+	if v = vnull then false else dec_bool v
+
+let decode_const c =
+	match decode_enum c with
+	| 0, [s] -> Int (dec_string s)
+	| 1, [s] -> Float (dec_string s)
+	| 2, [s] -> String (dec_string s)
+	| 3, [s] -> Ident (dec_string s)
+	| 4, [s;opt] -> Regexp (dec_string s, dec_string opt)
+	| 5, [s] -> Ident (dec_string s) (** deprecated CType, keep until 3.0 release **)
+	| _ -> raise Invalid_expr
+
+let rec decode_op op =
+	match decode_enum op with
+	| 0, [] -> OpAdd
+	| 1, [] -> OpMult
+	| 2, [] -> OpDiv
+	| 3, [] -> OpSub
+	| 4, [] -> OpAssign
+	| 5, [] -> OpEq
+	| 6, [] -> OpNotEq
+	| 7, [] -> OpGt
+	| 8, [] -> OpGte
+	| 9, [] -> OpLt
+	| 10, [] -> OpLte
+	| 11, [] -> OpAnd
+	| 12, [] -> OpOr
+	| 13, [] -> OpXor
+	| 14, [] -> OpBoolAnd
+	| 15, [] -> OpBoolOr
+	| 16, [] -> OpShl
+	| 17, [] -> OpShr
+	| 18, [] -> OpUShr
+	| 19, [] -> OpMod
+	| 20, [op] -> OpAssignOp (decode_op op)
+	| 21, [] -> OpInterval
+	| 22,[] -> OpArrow
+	| _ -> raise Invalid_expr
+
+let decode_unop op =
+	match decode_enum op with
+	| 0, [] -> Increment
+	| 1, [] -> Decrement
+	| 2, [] -> Not
+	| 3, [] -> Neg
+	| 4, [] -> NegBits
+	| _ -> raise Invalid_expr
+
+let decode_import_mode t =
+	match decode_enum t with
+	| 0, [] -> INormal
+	| 1, [alias] -> IAsName (dec_string alias)
+	| 2, [] -> IAll
+	| _ -> raise Invalid_expr
+
+let decode_import t = (List.map (fun o -> ((dec_string (field o "name")), (decode_pos (field o "pos")))) (dec_array (field t "path")), decode_import_mode (field t "mode"))
+
+let maybe_decode_pos p = try decode_pos p with Invalid_expr -> Globals.null_pos
+
+let decode_placed_name vp v =
+	dec_string v,maybe_decode_pos vp
+
+let dec_opt_array f v =
+	if v = vnull then [] else List.map f (dec_array v)
+
+let rec decode_path t =
+	{
+		tpackage = List.map dec_string (dec_array (field t "pack"));
+		tname = dec_string (field t "name");
+		tparams = dec_opt_array decode_tparam (field t "params");
+		tsub = opt dec_string (field t "sub");
+	},Globals.null_pos
+
+and decode_tparam v =
+	match decode_enum v with
+	| 0,[t] -> TPType (decode_ctype t)
+	| 1,[e] -> TPExpr (decode_expr e)
+	| _ -> raise Invalid_expr
+
+and decode_tparams v =
+	dec_opt_array decode_tparam_decl v
+
+and decode_tparam_decl v =
+	{
+		tp_name = decode_placed_name (field v "name_pos") (field v "name");
+		tp_constraints = dec_opt_array decode_ctype (field v "constraints");
+		tp_params = decode_tparams (field v "params");
+		tp_meta = decode_meta_content (field v "meta");
+	}
+
+and decode_fun v =
+	{
+		f_params = decode_tparams (field v "params");
+		f_args = List.map (fun o ->
+			decode_placed_name (field o "name_pos") (field o "name"),
+			dec_opt_bool (field o "opt"),
+			decode_meta_content (field o "meta"),
+			opt decode_ctype (field o "type"),
+			opt decode_expr (field o "value")
+		) (dec_array (field v "args"));
+		f_type = opt decode_ctype (field v "ret");
+		f_expr = opt decode_expr (field v "expr");
+	}
+
+and decode_access v =
+	match decode_enum v with
+	| 0, [] -> APublic
+	| 1, [] -> APrivate
+	| 2, [] -> AStatic
+	| 3, [] -> AOverride
+	| 4, [] -> ADynamic
+	| 5, [] -> AInline
+	| 6, [] -> AMacro
+	| _ -> raise Invalid_expr
+
+and decode_meta_entry v =
+	Meta.from_string (dec_string (field v "name")), dec_opt_array decode_expr (field v "params"), decode_pos (field v "pos")
+
+and decode_meta_content m = dec_opt_array decode_meta_entry m
+
+and decode_field v =
+	let fkind = match decode_enum (field v "kind") with
+		| 0, [t;e] ->
+			FVar (opt decode_ctype t, opt decode_expr e)
+		| 1, [f] ->
+			FFun (decode_fun f)
+		| 2, [get;set; t; e] ->
+			FProp (decode_placed_name vnull get, decode_placed_name vnull set, opt decode_ctype t, opt decode_expr e)
+		| _ ->
+			raise Invalid_expr
+	in
+	{
+		cff_name = decode_placed_name (field v "name_pos") (field v "name");
+		cff_doc = opt dec_string (field v "doc");
+		cff_pos = decode_pos (field v "pos");
+		cff_kind = fkind;
+		cff_access = List.map decode_access (opt_list dec_array (field v "access"));
+		cff_meta = opt_list decode_meta_content (field v "meta");
+	}
+
+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))
+	| 1, [a;r] ->
+		CTFunction (List.map decode_ctype (dec_array a), decode_ctype r)
+	| 2, [fl] ->
+		CTAnonymous (List.map decode_field (dec_array fl))
+	| 3, [t] ->
+		CTParent (decode_ctype t)
+	| 4, [tl;fl] ->
+		CTExtend (List.map decode_path (dec_array tl), List.map decode_field (dec_array fl))
+	| 5, [t] ->
+		CTOptional (decode_ctype t)
+	| _ ->
+		raise Invalid_expr),p
+
+and decode_expr v =
+	let rec loop v =
+		let p = decode_pos (field v "pos") in
+		(decode (field v "expr") p, p)
+	and decode e p =
+		match decode_enum e with
+		| 0, [c] ->
+			EConst (decode_const c)
+		| 1, [e1;e2] ->
+			EArray (loop e1, loop e2)
+		| 2, [op;e1;e2] ->
+			EBinop (decode_op op, loop e1, loop e2)
+		| 3, [e;f] ->
+			EField (loop e, dec_string f)
+		| 4, [e] ->
+			EParenthesis (loop e)
+		| 5, [a] ->
+			EObjectDecl (List.map (fun o ->
+				(decode_placed_name (field o "name_pos") (field o "field")),loop (field o "expr")
+			) (dec_array a))
+		| 6, [a] ->
+			EArrayDecl (List.map loop (dec_array a))
+		| 7, [e;el] ->
+			ECall (loop e,List.map loop (dec_array el))
+		| 8, [t;el] ->
+			ENew (decode_path t,List.map loop (dec_array el))
+		| 9, [op;f;e] ->
+			EUnop (decode_unop op,(if dec_bool f then Postfix else Prefix),loop e)
+		| 10, [vl] ->
+			EVars (List.map (fun v ->
+				((decode_placed_name (field v "name_pos") (field v "name")),opt decode_ctype (field v "type"),opt loop (field v "expr"))
+			) (dec_array vl))
+		| 11, [fname;f] ->
+			EFunction (opt dec_string fname,decode_fun f)
+		| 12, [el] ->
+			EBlock (List.map loop (dec_array el))
+		| 13, [e1;e2] ->
+			EFor (loop e1, loop e2)
+		| 14, [e1;e2] ->
+			EIn (loop e1, loop e2)
+		| 15, [e1;e2;e3] ->
+			EIf (loop e1, loop e2, opt loop e3)
+		| 16, [e1;e2;flag] ->
+			EWhile (loop e1,loop e2,if dec_bool flag then NormalWhile else DoWhile)
+		| 17, [e;cases;eo] ->
+			let cases = List.map (fun c ->
+				(List.map loop (dec_array (field c "values")),opt loop (field c "guard"),opt loop (field c "expr"),maybe_decode_pos (field c "pos"))
+			) (dec_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)
+		| 18, [e;catches] ->
+			let catches = List.map (fun c ->
+				((decode_placed_name (field c "name_pos") (field c "name")),(decode_ctype (field c "type")),loop (field c "expr"),maybe_decode_pos (field c "pos"))
+			) (dec_array catches) in
+			ETry (loop e, catches)
+		| 19, [e] ->
+			EReturn (opt loop e)
+		| 20, [] ->
+			EBreak
+		| 21, [] ->
+			EContinue
+		| 22, [e] ->
+			EUntyped (loop e)
+		| 23, [e] ->
+			EThrow (loop e)
+		| 24, [e;t] ->
+			ECast (loop e,opt decode_ctype t)
+		| 25, [e;f] ->
+			EDisplay (loop e,dec_bool f)
+		| 26, [t] ->
+			EDisplayNew (decode_path t)
+		| 27, [e1;e2;e3] ->
+			ETernary (loop e1,loop e2,loop e3)
+		| 28, [e;t] ->
+			ECheckType (loop e, (decode_ctype t))
+		| 29, [m;e] ->
+			EMeta (decode_meta_entry m,loop e)
+		| 30, [e;f] ->
+			EField (loop e, dec_string f) (*** deprecated EType, keep until haxe 3 **)
+		| _ ->
+			raise Invalid_expr
+	in
+	try
+		loop v
+	with Stack_overflow ->
+		raise Invalid_expr
+
+(* ---------------------------------------------------------------------- *)
+(* TYPE ENCODING *)
+
+let encode_pmap_array convert m =
+	let l = ref [] in
+	PMap.iter (fun _ v -> l := !l @ [(convert v)]) m;
+	enc_array !l
+
+let encode_array convert l =
+	enc_array (List.map convert l)
+
+let vopt f v = match v with
+	| None -> vnull
+	| Some v -> f v
+
+let encode_meta m set =
+	let meta = ref m in
+	enc_obj OMetaAccess [
+		"get", vfun0 (fun() ->
+			encode_meta_content (!meta)
+		);
+		"add", vfun3 (fun k vl p ->
+			(try
+				let el = List.map decode_expr (dec_array vl) in
+				meta := (Meta.from_string (dec_string k), el, decode_pos p) :: !meta;
+				set (!meta)
+			with Invalid_expr ->
+				failwith "Invalid expression");
+			vnull
+		);
+		"extract", vfun1 (fun k ->
+			let k = Meta.from_string (dec_string k) in
+			encode_array encode_meta_entry (List.filter (fun (m,_,_) -> m = k) (!meta))
+		);
+		"remove", vfun1 (fun k ->
+			let k = Meta.from_string (dec_string k) in
+			meta := List.filter (fun (m,_,_) -> m <> k) (!meta);
+			set (!meta);
+			vnull
+		);
+		"has", vfun1 (fun k ->
+			let k = Meta.from_string (dec_string k) in
+			vbool (List.exists (fun (m,_,_) -> m = k) (!meta));
+		);
+	]
+
+let rec encode_mtype ot t fields =
+	let i = t_infos t in
+	enc_obj ot ([
+		"__t", 	encode_tdecl t;
+		"pack", enc_array (List.map enc_string (fst i.mt_path));
+		"name", enc_string (snd i.mt_path);
+		"pos", encode_pos i.mt_pos;
+		"module", enc_string (s_type_path i.mt_module.m_path);
+		"isPrivate", vbool i.mt_private;
+		"meta", encode_meta i.mt_meta (fun m -> i.mt_meta <- m);
+		"doc", null enc_string i.mt_doc;
+		"params", encode_type_params i.mt_params;
+	] @ fields)
+
+and encode_type_params tl =
+	enc_array (List.map (fun (n,t) -> enc_obj OTypeParameter ["name",enc_string n;"t",encode_type t]) tl)
+
+and encode_tenum e =
+	encode_mtype OEnumType (TEnumDecl e) [
+		"isExtern", vbool e.e_extern;
+		"exclude", vfun0 (fun() -> e.e_extern <- true; vnull);
+		"constructs", encode_string_map encode_efield e.e_constrs;
+		"names", enc_array (List.map enc_string e.e_names);
+	]
+
+and encode_tabstract a =
+	encode_mtype OAbstractType (TAbstractDecl a) [
+		"type", encode_type a.a_this;
+		"impl", (match a.a_impl with None -> vnull | Some c -> encode_clref c);
+		"binops", enc_array (List.map (fun (op,cf) -> enc_obj OAbstractType_binops [ "op",encode_binop op; "field",encode_cfield cf]) a.a_ops);
+		"unops", enc_array (List.map (fun (op,postfix,cf) -> enc_obj OAbstractType_unops [ "op",encode_unop op; "isPostfix",vbool (match postfix with Postfix -> true | Prefix -> false); "field",encode_cfield cf]) a.a_unops);
+		"from", enc_array ((List.map (fun t -> enc_obj OAbstractType_from [ "t",encode_type t; "field",vnull]) a.a_from) @ (List.map (fun (t,cf) -> enc_obj OAbstractType_from [ "t",encode_type t; "field",encode_cfield cf]) a.a_from_field));
+		"to", enc_array ((List.map (fun t -> enc_obj OAbstractType_to [ "t",encode_type t; "field",vnull]) a.a_to) @ (List.map (fun (t,cf) -> enc_obj OAbstractType_to [ "t",encode_type t; "field",encode_cfield cf]) a.a_to_field));
+		"array", enc_array (List.map encode_cfield a.a_array);
+		"resolve", (match a.a_resolve with None -> vnull | Some cf -> encode_cfref cf)
+	]
+
+and encode_efield f =
+	enc_obj OEnumField [
+		"name", enc_string f.ef_name;
+		"type", encode_type f.ef_type;
+		"pos", encode_pos f.ef_pos;
+		"namePos", encode_pos f.ef_name_pos;
+		"index", vint f.ef_index;
+		"meta", encode_meta f.ef_meta (fun m -> f.ef_meta <- m);
+		"doc", null enc_string f.ef_doc;
+		"params", encode_type_params f.ef_params;
+	]
+
+and encode_cfield f =
+	enc_obj OClassField [
+		"name", enc_string f.cf_name;
+		"type", (match f.cf_kind with Method _ -> encode_lazy_type f.cf_type | _ -> encode_type f.cf_type);
+		"isPublic", vbool f.cf_public;
+		"params", encode_type_params f.cf_params;
+		"meta", encode_meta f.cf_meta (fun m -> f.cf_meta <- m);
+		"expr", vfun0 (fun() -> ignore(follow f.cf_type); (match f.cf_expr with None -> vnull | Some e -> encode_texpr e));
+		"kind", encode_field_kind f.cf_kind;
+		"pos", encode_pos f.cf_pos;
+		"namePos",encode_pos f.cf_name_pos;
+		"doc", null enc_string f.cf_doc;
+		"overloads", encode_ref f.cf_overloads (encode_array encode_cfield) (fun() -> "overloads");
+	]
+
+and encode_field_kind k =
+	let tag, pl = (match k with
+		| Type.Var v -> 0, [encode_var_access v.v_read; encode_var_access v.v_write]
+		| Method m -> 1, [encode_method_kind m]
+	) in
+	encode_enum IFieldKind tag pl
+
+and encode_var_access a =
+	let tag, pl = (match a with
+		| AccNormal -> 0, []
+		| AccNo -> 1, []
+		| AccNever -> 2, []
+		| AccResolve -> 3, []
+		| AccCall -> 4, []
+		| AccInline	-> 5, []
+		| AccRequire (s,msg) -> 6, [enc_string s; null enc_string msg]
+	) in
+	encode_enum IVarAccess tag pl
+
+and encode_method_kind m =
+	let tag, pl = (match m with
+		| MethNormal -> 0, []
+		| MethInline -> 1, []
+		| MethDynamic -> 2, []
+		| MethMacro -> 3, []
+	) in
+	encode_enum IMethodKind tag pl
+
+and encode_class_kind k =
+	let tag, pl = (match k with
+		| KNormal -> 0, []
+		| KTypeParameter pl -> 1, [encode_tparams pl]
+		(* KExtension was here *)
+		| KExpr e -> 3, [encode_expr e]
+		| KGeneric -> 4, []
+		| KGenericInstance (cl, params) -> 5, [encode_clref cl; encode_tparams params]
+		| KMacroType -> 6, []
+		| KAbstractImpl a -> 7, [encode_abref a]
+		| KGenericBuild cfl -> 8, []
+	) in
+	encode_enum IClassKind tag pl
+
+and encode_tclass c =
+	ignore(c.cl_build());
+	encode_mtype OClassType (TClassDecl c) [
+		"kind", encode_class_kind c.cl_kind;
+		"isExtern", vbool c.cl_extern;
+		"exclude", vfun0 (fun() -> c.cl_extern <- true; c.cl_init <- None; vnull);
+		"isInterface", vbool c.cl_interface;
+		"superClass", (match c.cl_super with
+			| None -> vnull
+			| Some (c,pl) -> enc_obj OClassType_superClass ["t",encode_clref c;"params",encode_tparams pl]
+		);
+		"interfaces", enc_array (List.map (fun (c,pl) -> enc_obj OClassType_interfaces ["t",encode_clref c;"params",encode_tparams pl]) c.cl_implements);
+		"fields", encode_ref c.cl_ordered_fields (encode_array encode_cfield) (fun() -> "class fields");
+		"statics", encode_ref c.cl_ordered_statics (encode_array encode_cfield) (fun() -> "class fields");
+		"constructor", (match c.cl_constructor with None -> vnull | Some cf -> encode_cfref cf);
+		"init", (match c.cl_init with None -> vnull | Some e -> encode_texpr e);
+		"overrides", (enc_array (List.map encode_cfref c.cl_overrides))
+	]
+
+and encode_ttype t =
+	encode_mtype ODefType (TTypeDecl t) [
+		"isExtern", vbool false;
+		"exclude", vfun0 (fun() -> vnull);
+		"type", encode_type t.t_type;
+	]
+
+and encode_tanon a =
+	enc_obj OAnonType [
+		"fields", encode_pmap_array encode_cfield a.a_fields;
+		"status", encode_anon_status !(a.a_status);
+	]
+
+and encode_anon_status s =
+	let tag, pl = (match s with
+		| Closed -> 0, []
+		| Opened -> 1, []
+		| Type.Const -> 2, []
+		| Extend tl -> 3, [encode_ref tl (fun tl -> enc_array (List.map encode_type tl)) (fun() -> "<extended types>")]
+		| Statics cl -> 4, [encode_clref cl]
+		| EnumStatics en -> 5, [encode_enref en]
+		| AbstractStatics ab -> 6, [encode_abref ab]
+	)
+	in
+	encode_enum IAnonStatus tag pl
+
+and encode_tparams pl =
+	enc_array (List.map encode_type pl)
+
+and encode_clref c =
+	encode_ref c encode_tclass (fun() -> s_type_path c.cl_path)
+
+and encode_enref en =
+	encode_ref en encode_tenum (fun() -> s_type_path en.e_path)
+
+and encode_cfref cf =
+	encode_ref cf encode_cfield (fun() -> cf.cf_name)
+
+and encode_abref ab =
+	encode_ref ab encode_tabstract (fun() -> s_type_path ab.a_path)
+
+and encode_type t =
+	let rec loop = function
+		| TMono r ->
+			(match !r with
+			| None -> 0, [encode_ref r (fun r -> match !r with None -> vnull | Some t -> encode_type t) (fun() -> "<mono>")]
+			| Some t -> loop t)
+		| TEnum (e, pl) ->
+			1 , [encode_ref e encode_tenum (fun() -> s_type_path e.e_path); encode_tparams pl]
+		| TInst (c, pl) ->
+			2 , [encode_clref c; encode_tparams pl]
+		| TType (t,pl) ->
+			3 , [encode_ref t encode_ttype (fun() -> s_type_path t.t_path); encode_tparams pl]
+		| TFun (pl,ret) ->
+			let pl = List.map (fun (n,o,t) ->
+				enc_obj OType_args [
+					"name",enc_string n;
+					"opt",vbool o;
+					"t",encode_type t
+				]
+			) pl in
+			4 , [enc_array pl; encode_type ret]
+		| TAnon a ->
+			5, [encode_ref a encode_tanon (fun() -> "<anonymous>")]
+		| TDynamic tsub as t ->
+			if t == t_dynamic then
+				6, [vnull]
+			else
+				6, [encode_type tsub]
+		| TLazy f ->
+			loop (!f())
+		| TAbstract (a, pl) ->
+			8, [encode_abref a; encode_tparams pl]
+	in
+	let tag, pl = loop t in
+	encode_enum IType tag pl
+
+and encode_lazy_type t =
+	let rec loop = function
+		| TMono r ->
+			(match !r with
+			| Some t -> loop t
+			| _ -> encode_type t)
+		| TLazy f ->
+			encode_enum IType 7 [encode_lazytype f (fun() -> encode_type (!f()))]
+		| _ ->
+			encode_type t
+	in
+	loop t
+
+and decode_type t =
+	match decode_enum t with
+	| 0, [r] -> TMono (decode_ref r)
+	| 1, [e; pl] -> TEnum (decode_ref e, List.map decode_type (dec_array pl))
+	| 2, [c; pl] -> TInst (decode_ref c, List.map decode_type (dec_array pl))
+	| 3, [t; pl] -> TType (decode_ref t, List.map decode_type (dec_array pl))
+	| 4, [pl; r] -> TFun (List.map (fun p -> dec_string (field p "name"), dec_bool (field p "opt"), decode_type (field p "t")) (dec_array pl), decode_type r)
+	| 5, [a] -> TAnon (decode_ref a)
+	| 6, [t] -> if t = vnull then t_dynamic else TDynamic (decode_type t)
+	| 7, [f] -> TLazy (decode_lazytype f)
+	| 8, [a; pl] -> TAbstract (decode_ref a, List.map decode_type (dec_array pl))
+	| _ -> raise Invalid_expr
+
+(* ---------------------------------------------------------------------- *)
+(* TEXPR Encoding *)
+
+and encode_tconst c =
+	let tag, pl = match c with
+		| TInt i -> 0,[vint32 i]
+		| TFloat f -> 1,[enc_string f]
+		| TString s -> 2,[enc_string s]
+		| TBool b -> 3,[vbool b]
+		| TNull -> 4,[]
+		| TThis -> 5,[]
+		| TSuper -> 6,[]
+	in
+	encode_enum ITConstant tag pl
+
+and encode_tvar v =
+	let f_extra (pl,e) =
+		enc_obj OTVar_extra [
+			"params",encode_type_params pl;
+			"expr",vopt encode_texpr e
+		]
+	in
+	enc_obj OTVar [
+		"id", vint v.v_id;
+		"name", enc_string v.v_name;
+		"t", encode_type v.v_type;
+		"capture", vbool v.v_capture;
+		"extra", vopt f_extra v.v_extra;
+		"meta", encode_meta v.v_meta (fun m -> v.v_meta <- m);
+		"$", encode_unsafe (Obj.repr v);
+	]
+
+and encode_module_type mt =
+	let tag,pl = match mt with
+		| TClassDecl c -> 0,[encode_clref c]
+		| TEnumDecl e -> 1,[encode_enref e]
+		| TTypeDecl t -> 2,[encode_ref t encode_ttype (fun () -> s_type_path t.t_path)]
+		| TAbstractDecl a -> 3,[encode_abref a]
+	in
+	encode_enum IModuleType tag pl
+
+and encode_tfunc func =
+	enc_obj OTFunc [
+		"args",enc_array (List.map (fun (v,c) ->
+			enc_obj OTFunc_args [
+				"v",encode_tvar v;
+				"value",match c with None -> vnull | Some c -> encode_tconst c
+			]
+		) func.tf_args);
+		"t",encode_type func.tf_type;
+		"expr",encode_texpr func.tf_expr
+	]
+
+and encode_field_access fa =
+	let encode_instance c tl =
+		enc_obj OFieldAccess_c [
+			"c",encode_clref c;
+			"params",encode_tparams tl
+		]
+	in
+	let tag,pl = match fa with
+		| FInstance(c,tl,cf) -> 0,[encode_clref c;encode_tparams tl;encode_cfref cf]
+		| FStatic(c,cf) -> 1,[encode_clref c;encode_cfref cf]
+		| FAnon(cf) -> 2,[encode_cfref cf]
+		| FDynamic(s) -> 3,[enc_string s]
+		| FClosure(co,cf) -> 4,[(match co with Some (c,tl) -> encode_instance c tl | None -> vnull);encode_cfref cf]
+		| FEnum(en,ef) -> 5,[encode_enref en;encode_efield ef]
+	in
+	encode_enum IFieldAccess tag pl
+
+and encode_texpr e =
+	let rec loop e =
+		let tag, pl = match e.eexpr with
+			| TConst c -> 0,[encode_tconst c]
+			| TLocal v -> 1,[encode_tvar v]
+			| TArray(e1,e2) -> 2,[loop e1; loop e2]
+			| TBinop(op,e1,e2) -> 3,[encode_binop op;loop e1;loop e2]
+			| TField(e1,fa) -> 4,[loop e1;encode_field_access fa]
+			| TTypeExpr mt -> 5,[encode_module_type mt]
+			| TParenthesis e1 -> 6,[loop e1]
+			| TObjectDecl fl -> 7, [enc_array (List.map (fun (f,e) ->
+				enc_obj OTypedExprDef_fields [
+					"name",enc_string f;
+					"expr",loop e;
+				]) fl)]
+			| TArrayDecl el -> 8,[encode_texpr_list el]
+			| TCall(e1,el) -> 9,[loop e1;encode_texpr_list el]
+			| TNew(c,pl,el) -> 10,[encode_clref c;encode_tparams pl;encode_texpr_list el]
+			| TUnop(op,flag,e1) -> 11,[encode_unop op;vbool (flag = Postfix);loop e1]
+			| TFunction func -> 12,[encode_tfunc func]
+			| TVar (v,eo) -> 13,[encode_tvar v;vopt encode_texpr eo]
+			| TBlock el -> 14,[encode_texpr_list el]
+			| TFor(v,e1,e2) -> 15,[encode_tvar v;loop e1;loop e2]
+			| TIf(eif,ethen,eelse) -> 16,[loop eif;loop ethen;vopt encode_texpr eelse]
+			| TWhile(econd,e1,flag) -> 17,[loop econd;loop e1;vbool (flag = NormalWhile)]
+			| TSwitch(e1,cases,edef) -> 18,[
+				loop e1;
+				enc_array (List.map (fun (el,e) -> enc_obj OTypedExprDef_cases ["values",encode_texpr_list el;"expr",loop e]) cases);
+				vopt encode_texpr edef
+				]
+			| TTry(e1,catches) -> 19,[
+				loop e1;
+				enc_array (List.map (fun (v,e) ->
+					enc_obj OTypedExprDef_catches [
+						"v",encode_tvar v;
+						"expr",loop e
+					]) catches
+				)]
+			| TReturn e1 -> 20,[vopt encode_texpr e1]
+			| TBreak -> 21,[]
+			| TContinue -> 22,[]
+			| TThrow e1 -> 23,[loop e1]
+			| TCast(e1,mt) -> 24,[loop e1;match mt with None -> vnull | Some mt -> encode_module_type mt]
+			| TMeta(m,e1) -> 25,[encode_meta_entry m;loop e1]
+			| TEnumParameter(e1,ef,i) -> 26,[loop e1;encode_efield ef;vint i]
+		in
+		enc_obj OTypedExprDef [
+			"pos", encode_pos e.epos;
+			"expr", encode_enum ITypedExpr tag pl;
+			"t", encode_type e.etype
+		]
+	in
+	loop e
+
+and encode_texpr_list el =
+	enc_array (List.map encode_texpr el)
+
+(* ---------------------------------------------------------------------- *)
+(* TEXPR Decoding *)
+
+let decode_tconst c =
+	match decode_enum c with
+	| 0, [s] -> TInt (dec_i32 s)
+	| 1, [s] -> TFloat (dec_string s)
+	| 2, [s] -> TString (dec_string s)
+	| 3, [s] -> TBool (dec_bool s)
+	| 4, [] -> TNull
+	| 5, [] -> TThis
+	| 6, [] -> TSuper
+	| _ -> raise Invalid_expr
+
+let decode_type_params v =
+	List.map (fun v -> dec_string (field v "name"),decode_type (field v "t")) (dec_array v)
+
+let decode_tvar v =
+	(Obj.obj (decode_unsafe (field v "$")) : tvar)
+
+let decode_var_access v =
+	match decode_enum v with
+	| 0, [] -> AccNormal
+	| 1, [] -> AccNo
+	| 2, [] -> AccNever
+	| 3, [] -> AccResolve
+	| 4, [] -> AccCall
+	| 5, [] -> AccInline
+	| 6, [s1;s2] -> AccRequire(dec_string s1, opt dec_string s2)
+	| _ -> raise Invalid_expr
+
+let decode_method_kind v =
+	match decode_enum v with
+	| 0, [] -> MethNormal
+	| 1, [] -> MethInline
+	| 2, [] -> MethDynamic
+	| 3, [] -> MethMacro
+	| _ -> raise Invalid_expr
+
+let decode_field_kind v =
+	match decode_enum v with
+	| 0, [vr;vw] -> Type.Var({v_read = decode_var_access vr; v_write = decode_var_access vw})
+	| 1, [m] -> Method (decode_method_kind m)
+	| _ -> raise Invalid_expr
+
+let decode_cfield v =
+	{
+		cf_name = dec_string (field v "name");
+		cf_type = decode_type (field v "type");
+		cf_public = dec_bool (field v "isPublic");
+		cf_pos = decode_pos (field v "pos");
+		cf_name_pos = decode_pos (field v "namePos");
+		cf_doc = opt dec_string (field v "doc");
+		cf_meta = []; (* TODO *)
+		cf_kind = decode_field_kind (field v "kind");
+		cf_params = decode_type_params (field v "params");
+		cf_expr = None;
+		cf_expr_unoptimized = None;
+		cf_overloads = decode_ref (field v "overloads");
+	}
+
+let decode_efield v =
+	{
+		ef_name = dec_string (field v "name");
+		ef_type = decode_type (field v "type");
+		ef_pos = decode_pos (field v "pos");
+		ef_name_pos = decode_pos (field v "namePos");
+		ef_index = dec_int (field v "index");
+		ef_meta = []; (* TODO *)
+		ef_doc = opt dec_string (field v "doc");
+		ef_params = decode_type_params (field v "params")
+	}
+
+let decode_field_access v =
+	match decode_enum v with
+	| 0, [c;tl;cf] ->
+		let c = decode_ref c in
+		FInstance(c,List.map decode_type (dec_array tl),decode_ref cf)
+	| 1, [c;cf] -> FStatic(decode_ref c,decode_ref cf)
+	| 2, [cf] -> FAnon(decode_ref cf)
+	| 3, [s] -> FDynamic(dec_string s)
+	| 4, [co;cf] ->
+		let co = if co = vnull then None else Some (decode_ref (field co "c"),List.map decode_type (dec_array (field co "params"))) in
+		FClosure(co,decode_ref cf)
+	| 5, [e;ef] -> FEnum(decode_ref e,decode_efield ef)
+	| _ -> raise Invalid_expr
+
+let decode_module_type v =
+	match decode_enum v with
+	| 0, [c] -> TClassDecl (decode_ref c)
+	| 1, [en] -> TEnumDecl (decode_ref en)
+	| 2, [t] -> TTypeDecl (decode_ref t)
+	| 3, [a] -> TAbstractDecl (decode_ref a)
+	| _ -> raise Invalid_expr
+
+let rec decode_tfunc v =
+	{
+		tf_args = List.map (fun v -> decode_tvar (field v "v"),opt decode_tconst (field v "value")) (dec_array (field v "args"));
+		tf_type = decode_type (field v "t");
+		tf_expr = decode_texpr (field v "expr")
+	}
+
+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 =
+		match decode_enum e with
+		| 0, [c] ->	TConst(decode_tconst c)
+		| 1, [v] -> TLocal(decode_tvar v)
+		| 2, [v1;v2] -> TArray(loop v1,loop v2)
+		| 3, [op;v1;v2] -> TBinop(decode_op op,loop v1,loop v2)
+		| 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 -> dec_string (field v "name"),loop (field v "expr")) (dec_array v))
+		| 8, [vl] -> TArrayDecl(List.map loop (dec_array vl))
+		| 9, [v1;vl] -> TCall(loop v1,List.map loop (dec_array vl))
+		| 10, [c;tl;vl] -> TNew(decode_ref c,List.map decode_type (dec_array tl),List.map loop (dec_array vl))
+		| 11, [op;pf;v1] -> TUnop(decode_unop op,(if dec_bool pf then Postfix else Prefix),loop v1)
+		| 12, [f] -> TFunction(decode_tfunc f)
+		| 13, [v;eo] -> TVar(decode_tvar v,opt loop eo)
+		| 14, [vl] -> TBlock(List.map loop (dec_array vl))
+		| 15, [v;v1;v2] -> TFor(decode_tvar v,loop v1,loop v2)
+		| 16, [vif;vthen;velse] -> TIf(loop vif,loop vthen,opt loop velse)
+		| 17, [vcond;v1;b] -> TWhile(loop vcond,loop v1,if dec_bool b then NormalWhile else DoWhile)
+		| 18, [v1;cl;vdef] -> TSwitch(loop v1,List.map (fun v -> List.map loop (dec_array (field v "values")),loop (field v "expr")) (dec_array cl),opt loop vdef)
+		| 19, [v1;cl] -> TTry(loop v1,List.map (fun v -> decode_tvar (field v "v"),loop (field v "expr")) (dec_array cl))
+		| 20, [vo] -> TReturn(opt loop vo)
+		| 21, [] -> TBreak
+		| 22, [] -> TContinue
+		| 23, [v1] -> TThrow(loop v1)
+		| 24, [v1;mto] -> TCast(loop v1,opt decode_module_type mto)
+		| 25, [m;v1] -> TMeta(decode_meta_entry m,loop v1)
+		| 26, [v1;ef;i] -> TEnumParameter(loop v1,decode_efield ef,dec_int i)
+		| i,el -> Printf.printf "%i %i\n" i (List.length el); raise Invalid_expr
+	in
+	try
+		loop v
+	with Stack_overflow ->
+		raise Invalid_expr
+
+(* ---------------------------------------------------------------------- *)
+(* TYPE DEFINITION *)
+
+let decode_type_def v =
+	let pack = List.map dec_string (dec_array (field v "pack")) in
+	let name = decode_placed_name (field v "name_pos") (field v "name") in
+	let meta = decode_meta_content (field v "meta") in
+	let pos = decode_pos (field v "pos") in
+	let isExtern = dec_opt_bool (field v "isExtern") in
+	let fields = List.map decode_field (dec_array (field v "fields")) in
+	let mk fl dl =
+		{
+			d_name = name;
+			d_doc = None;
+			d_params = decode_tparams (field v "params");
+			d_meta = meta;
+			d_flags = fl;
+			d_data = dl;
+		}
+	in
+	let tdef = (match decode_enum (field v "kind") with
+	| 0, [] ->
+		let conv f =
+			let loop ((n,_),opt,_,t,_) =
+				match t with
+				| None -> raise Invalid_expr
+				| Some t -> n, opt, t
+			in
+			let args, params, t = (match f.cff_kind with
+				| FVar (t,None) -> [], [], t
+				| FFun f -> List.map loop f.f_args, f.f_params, f.f_type
+				| _ -> raise Invalid_expr
+			) in
+			{
+				ec_name = f.cff_name;
+				ec_doc = f.cff_doc;
+				ec_meta = f.cff_meta;
+				ec_pos = f.cff_pos;
+				ec_args = args;
+				ec_params = params;
+				ec_type = t;
+			}
+		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))
+	| 2, [ext;impl;interf] ->
+		let flags = if isExtern then [HExtern] else [] in
+		let is_interface = dec_opt_bool interf in
+		let interfaces = (match opt (fun v -> List.map decode_path (dec_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 flags = if is_interface then begin
+				let flags = HInterface :: flags in
+				List.map (fun t -> HExtends t) interfaces @ flags
+			end else begin
+				List.map (fun t -> HImplements t) interfaces @ flags
+			end
+		in
+		EClass (mk flags fields)
+	| 3, [t] ->
+		ETypedef (mk (if isExtern then [EExtern] else []) (decode_ctype t))
+	| 4, [tthis;tfrom;tto] ->
+		let flags = match opt dec_array tfrom with None -> [] | Some ta -> List.map (fun t -> AFromType (decode_ctype t)) ta in
+		let flags = match opt dec_array tto with None -> flags | Some ta -> (List.map (fun t -> AToType (decode_ctype t)) ta) @ flags in
+		let flags = match opt decode_ctype tthis with None -> flags | Some t -> (AIsType t) :: flags in
+		EAbstract(mk flags fields)
+	| _ ->
+		raise Invalid_expr
+	) in
+	(* if our package ends with an uppercase letter, then it's the module name *)
+	let pack,name = (match List.rev pack with
+		| last :: l when not (is_lower_ident last) -> List.rev l, last
+		| _ -> pack, fst name
+	) in
+	(pack, name), tdef, pos
+
+(* ---------------------------------------------------------------------- *)
+(* VALUE-TO-CONSTANT *)
+
+let rec make_const e =
+	match e.eexpr with
+	| TConst c ->
+		(match c with
+		| TInt i -> vint32 i
+		| TFloat s -> vfloat (float_of_string s)
+		| TString s -> enc_string s
+		| TBool b -> vbool b
+		| TNull -> vnull
+		| TThis | TSuper -> raise Exit)
+	| TParenthesis e | TMeta(_,e) | TCast(e,None) ->
+		make_const e
+	| TObjectDecl el ->
+		enc_obj O__Const (List.map (fun (f,e) -> f, make_const e) el)
+	| TArrayDecl al ->
+		enc_array (List.map make_const al)
+	| _ ->
+		raise Exit
+
+
+(* ------------------------------------------------------------------------- *)
+(* MACRO API *)
+
+(**
+
+	Our macro API implementation. It gets called by the interpreter and
+	accesses the compiler with the compiler_api
+
+**)
+
+let macro_api ccom get_api =
+	[
+		"current_pos", vfun0 (fun() ->
+			encode_pos (get_api()).pos
+		);
+		"error", vfun2 (fun msg p ->
+			let msg = dec_string msg in
+			let p = decode_pos p in
+			(ccom()).error msg p;
+			raise Abort
+		);
+		"fatal_error", vfun2 (fun msg p ->
+			let msg = dec_string msg in
+			let p = decode_pos p in
+			raise (Error.Fatal_error (msg,p))
+		);
+		"warning", vfun2 (fun msg p ->
+			let msg = dec_string msg in
+			let p = decode_pos p in
+			(ccom()).warning msg p;
+			vnull
+		);
+		"class_path", vfun0 (fun() ->
+			enc_array (List.map enc_string (ccom()).class_path);
+		);
+		"resolve_path", vfun1 (fun file ->
+			let file = dec_string file in
+			enc_string (try Common.find_file (ccom()) file with Not_found -> failwith ("File not found '" ^ file ^ "'"))
+		);
+		"define", vfun2 (fun s v ->
+			let v = if v = vnull then "" else "=" ^ (dec_string v) in
+			Common.raw_define (ccom()) ((dec_string s) ^ v);
+			vnull
+		);
+		"defined", vfun1 (fun s ->
+			vbool (Common.raw_defined (ccom()) (dec_string s))
+		);
+		"defined_value", vfun1 (fun s ->
+			try enc_string (Common.raw_defined_value (ccom()) (dec_string s)) with Not_found -> vnull
+		);
+		"get_defines", vfun0 (fun() ->
+			encode_string_map enc_string (ccom()).defines
+		);
+		"get_type", vfun1 (fun s ->
+			let tname = dec_string s in
+			match (get_api()).get_type tname with
+			| None -> failwith ("Type not found '" ^ tname ^ "'")
+			| Some t -> encode_type t
+		);
+		"get_module", vfun1 (fun s ->
+			enc_array (List.map encode_type ((get_api()).get_module (dec_string s)))
+		);
+		"on_after_typing", vfun1 (fun f ->
+			let f = prepare_callback f 1 in
+			(get_api()).after_typing (fun tl -> ignore(f [enc_array (List.map encode_module_type tl)]));
+			vnull
+		);
+		"on_generate", vfun1 (fun f ->
+			let f = prepare_callback f 1 in
+			(get_api()).on_generate (fun tl -> ignore(f [enc_array (List.map encode_type tl)]));
+			vnull
+		);
+		"on_after_generate", vfun1 (fun f ->
+			let f = prepare_callback f 0 in
+			(get_api()).after_generate (fun () -> ignore(f []));
+			vnull
+		);
+		"on_type_not_found", vfun1 (fun f ->
+			let f = prepare_callback f 1 in
+			(get_api()).on_type_not_found (fun path -> f [enc_string path]);
+			vnull
+		);
+		"do_parse", vfun3 (fun s p b ->
+			let s = dec_string s in
+			if s = "" then raise Invalid_expr;
+			encode_expr ((get_api()).parse_string s (decode_pos p) (dec_bool b))
+		);
+		"make_expr", vfun2 (fun v p ->
+			encode_expr (value_to_expr v (decode_pos p))
+		);
+		"signature", vfun1 (fun v ->
+			enc_string (Digest.to_hex (value_signature v))
+		);
+		"to_complex_type", vfun1 (fun v ->
+			try	encode_ctype (TExprToExpr.convert_type' (decode_type v))
+			with Exit -> vnull
+		);
+		"unify", vfun2 (fun t1 t2 ->
+			let e1 = mk (TObjectDecl []) (decode_type t1) Globals.null_pos in
+			try ignore(((get_api()).cast_or_unify) (decode_type t2) e1 Globals.null_pos); vbool true
+			with Error.Error (Error.Unify _,_) -> vbool false
+		);
+		"typeof", vfun1 (fun v ->
+			encode_type ((get_api()).type_expr (decode_expr v)).etype
+		);
+		"type_expr", vfun1 (fun v ->
+			encode_texpr ((get_api()).type_expr (decode_expr v))
+		);
+		"resolve_type", vfun2 (fun t p ->
+			encode_type ((get_api()).resolve_type (fst (decode_ctype t)) (decode_pos p));
+		);
+		"s_type", vfun1 (fun v ->
+			enc_string (Type.s_type (print_context()) (decode_type v))
+		);
+		"s_expr", vfun2 (fun v b ->
+			let f = if dec_opt_bool b then Type.s_expr_pretty false "" false else Type.s_expr_ast true "" in
+			enc_string (f (Type.s_type (print_context())) (decode_texpr v))
+		);
+		"is_fmt_string", vfun1 (fun p ->
+			vbool (Lexer.is_fmt_string (decode_pos p))
+		);
+		"format_string", vfun2 (fun s p ->
+			encode_expr ((get_api()).format_string (dec_string s) (decode_pos p))
+		);
+		"allow_package", vfun1 (fun s ->
+			(get_api()).allow_package (dec_string s);
+			vnull
+		);
+		"type_patch", vfun4 (fun t f s v ->
+			(get_api()).type_patch (dec_string t) (dec_string f) (dec_bool s) (opt dec_string v);
+			vnull
+		);
+		"meta_patch", vfun4 (fun m t f s ->
+			(get_api()).meta_patch (dec_string m) (dec_string t) (opt dec_string f) (dec_bool s);
+			vnull
+		);
+		"add_global_metadata_impl", vfun5 (fun s1 s2 b1 b2 b3 ->
+			(get_api()).add_global_metadata (dec_string s1) (dec_string s2) (dec_bool b1,dec_bool b2,dec_bool b3);
+			vnull
+		);
+		"set_custom_js_generator", vfun1 (fun f ->
+			let f = prepare_callback f 1 in
+			(get_api()).set_js_generator (fun js_ctx ->
+				let com = ccom() in
+				let api = enc_obj OJSGenApi [
+					"outputFile", enc_string com.file;
+					"types", enc_array (List.map (fun t -> encode_type (type_of_module_type t)) com.types);
+					"main", (match com.main with None -> vnull | Some e -> encode_texpr e);
+					"generateValue", vfun1 (fun v ->
+						let e = decode_texpr v in
+						let str = Genjs.gen_single_expr js_ctx e false in
+						enc_string str
+					);
+					"isKeyword", vfun1 (fun v ->
+						vbool (Hashtbl.mem Genjs.kwds (dec_string v))
+					);
+					"hasFeature", vfun1 (fun v ->
+						vbool (Common.has_feature com (dec_string v))
+					);
+					"addFeature", vfun1 (fun v ->
+						Common.add_feature com (dec_string v);
+						vnull
+					);
+					"quoteString", vfun1 (fun v ->
+						enc_string ("\"" ^ Ast.s_escape (dec_string v) ^ "\"")
+					);
+					"buildMetaData", vfun1 (fun t ->
+						match Codegen.build_metadata com (decode_tdecl t) with
+						| None -> vnull
+						| Some e -> encode_texpr e
+					);
+					"generateStatement", vfun1 (fun v ->
+						let e = decode_texpr v in
+						let str = Genjs.gen_single_expr js_ctx e true in
+						enc_string str
+					);
+					"setTypeAccessor", vfun1 (fun callb ->
+						let callb = prepare_callback callb 1 in
+						js_ctx.Genjs.type_accessor <- (fun t ->
+							dec_string (callb [encode_type (type_of_module_type t)])
+						);
+						vnull
+					);
+					"setCurrentClass", vfun1 (fun c ->
+						Genjs.set_current_class js_ctx (match decode_tdecl c with TClassDecl c -> c | _ -> assert false);
+						vnull
+					);
+				] in
+				ignore(f [api]);
+			);
+			vnull
+		);
+		"get_pos_infos", vfun1 (fun p ->
+			let p = decode_pos p in
+			enc_obj OContext_getPosInfos ["min",vint p.Globals.pmin;"max",vint p.Globals.pmax;"file",enc_string p.Globals.pfile]
+		);
+		"make_position", vfun3 (fun min max file ->
+			encode_pos { Globals.pmin = dec_int min; Globals.pmax = dec_int max; Globals.pfile = dec_string file }
+		);
+		"add_resource", vfun2 (fun name data ->
+			let name = dec_string name in
+			let data = decode_bytes data in
+			if name = "" then failwith "Empty resource name";
+			Hashtbl.replace (ccom()).resources name data;
+			let m = if name.[0] = '$' then (get_api()).current_macro_module() else (get_api()).current_module() in
+			m.m_extra.m_binded_res <- PMap.add name data m.m_extra.m_binded_res;
+			vnull
+		);
+		"get_resources", vfun0 (fun() ->
+			encode_string_map encode_bytes (Hashtbl.fold (fun k v acc -> PMap.add k v acc) (ccom()).resources PMap.empty)
+		);
+		"get_local_module", vfun0 (fun() ->
+			let m = (get_api()).current_module() in
+			enc_string (s_type_path m.m_path);
+		);
+		"get_local_type", vfun0 (fun() ->
+			match (get_api()).get_local_type() with
+			| None -> vnull
+			| Some t -> encode_type t
+		);
+		"get_expected_type", vfun0 (fun() ->
+			match (get_api()).get_expected_type() with
+			| None -> vnull
+			| Some t -> encode_type t
+		);
+		"get_call_arguments", vfun0 (fun() ->
+			match (get_api()).get_call_arguments() with
+			| None -> vnull
+			| Some el -> enc_array (List.map encode_expr el)
+		);
+		"get_local_method", vfun0 (fun() ->
+			enc_string ((get_api()).get_local_method())
+		);
+		"get_local_using", vfun0 (fun() ->
+			enc_array (List.map encode_clref ((get_api()).get_local_using()))
+		);
+		"get_local_imports", vfun0 (fun() ->
+			enc_array (List.map encode_import ((get_api()).get_local_imports()))
+		);
+		"local_vars", vfun1 (fun as_var ->
+			let as_var = dec_opt_bool as_var in
+			let vars = (get_api()).get_local_vars() in
+			encode_string_map (if as_var then encode_tvar else (fun v -> encode_type v.v_type)) vars
+		);
+		"follow_with_abstracts", vfun2 (fun v once ->
+			let t = decode_type v in
+			let follow_once t =
+				match t with
+				| TMono r ->
+					(match !r with
+					| None -> t
+					| Some t -> t)
+				| TAbstract (a,tl) when not (Meta.has Meta.CoreType a.a_meta) ->
+					Abstract.get_underlying_type a tl
+				| TAbstract _ | TEnum _ | TInst _ | TFun _ | TAnon _ | TDynamic _ ->
+					t
+				| TType (t,tl) ->
+					apply_params t.t_params tl t.t_type
+				| TLazy f ->
+					(!f)()
+			in
+			encode_type (if dec_opt_bool once then follow_once t else Abstract.follow_with_abstracts t)
+		);
+		"follow", vfun2 (fun v once ->
+			let t = decode_type v in
+			let follow_once t =
+				match t with
+				| TMono r ->
+					(match !r with
+					| None -> t
+					| Some t -> t)
+				| TAbstract _ | TEnum _ | TInst _ | TFun _ | TAnon _ | TDynamic _ ->
+					t
+				| TType (t,tl) ->
+					apply_params t.t_params tl t.t_type
+				| TLazy f ->
+					(!f)()
+			in
+			encode_type (if dec_opt_bool once then follow_once t else follow t)
+		);
+		"get_build_fields", vfun0 (fun() ->
+			(get_api()).get_build_fields()
+		);
+		"define_type", vfun1 (fun v ->
+			(get_api()).define_type v;
+			vnull
+		);
+		"define_module", vfun4 (fun path vl ui ul ->
+			(get_api()).define_module (dec_string path) (dec_array vl) (List.map decode_import (dec_array ui)) (List.map fst (List.map decode_path (dec_array ul)));
+			vnull
+		);
+		"add_class_path", vfun1 (fun cp ->
+			let com = ccom() in
+			let cp = dec_string cp in
+			let cp = Path.add_trailing_slash cp in
+			com.class_path <- cp :: com.class_path;
+			(match com.get_macros() with
+			| Some(mcom) ->
+				mcom.class_path <- cp :: com.class_path;
+			| None ->
+				());
+			Hashtbl.clear com.file_lookup_cache;
+			vnull
+		);
+		"add_native_lib", vfun1 (fun file ->
+			let file = dec_string file in
+			let com = ccom() in
+			(match com.platform with
+			| Globals.Flash -> Genswf.add_swf_lib com file false
+			| Globals.Java -> Genjava.add_java_lib com file false
+			| Globals.Cs ->
+				let file, is_std = match ExtString.String.nsplit file "@" with
+					| [file] ->
+						file,false
+					| [file;"std"] ->
+						file,true
+					| _ -> failwith ("unsupported file@`std` format: " ^ file)
+				in
+				Gencs.add_net_lib com file is_std
+			| _ -> failwith "Unsupported platform");
+			vnull
+		);
+		"add_native_arg", vfun1 (fun arg ->
+			let arg = dec_string arg in
+			let com = ccom() in
+			(match com.platform with
+			| Globals.Java | Globals.Cs | Globals.Cpp ->
+				com.c_args <- arg :: com.c_args
+			| _ -> failwith "Unsupported platform");
+			vnull
+		);
+		"register_module_dependency", vfun2 (fun m file ->
+			(get_api()).module_dependency (dec_string m) (dec_string file) false;
+			vnull
+		);
+		"register_module_reuse_call", vfun2 (fun m mcall ->
+			(get_api()).module_dependency (dec_string m) (dec_string mcall) true;
+			vnull
+		);
+		"get_typed_expr", vfun1 (fun e ->
+			let e = decode_texpr e in
+			encode_expr (TExprToExpr.convert_expr e)
+		);
+		"store_typed_expr", vfun1 (fun e ->
+			let e = decode_texpr e in
+			encode_expr ((get_api()).store_typed_expr e)
+		);
+		"get_output", vfun0 (fun() ->
+			enc_string (ccom()).file
+		);
+		"set_output", vfun1 (fun s ->
+			(ccom()).file <- dec_string s;
+			vnull
+		);
+		"get_display_pos", vfun0 (fun() ->
+			let p = !Parser.resume_display in
+			if p = Globals.null_pos then
+				vnull
+			else
+				enc_obj OCompiler_getDisplayPos ["file",enc_string p.Globals.pfile;"pos",vint p.Globals.pmin]
+		);
+		"pattern_locals", vfun2 (fun e t ->
+			let loc = (get_api()).get_pattern_locals (decode_expr e) (decode_type t) in
+			encode_string_map (fun (v,_) -> encode_type v.v_type) loc
+		);
+		"on_macro_context_reused", vfun1 (fun c ->
+			let c = prepare_callback c 0 in
+			(get_api()).on_reuse (fun() -> dec_bool (c []));
+			vnull
+		);
+		"apply_params", vfun3 (fun tpl tl t ->
+			let tl = List.map decode_type (dec_array tl) in
+			let tpl = List.map (fun v -> dec_string (field v "name"), decode_type (field v "t")) (dec_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)
+					with Not_found ->
+						Type.map map t
+					end
+				| _ -> Type.map map t
+			in
+			encode_type (apply_params tpl tl (map (decode_type t)))
+		);
+		"include_file", vfun2 (fun file position ->
+			let file = dec_string file in
+			let position = dec_string position in
+			let file = if Sys.file_exists file then
+				file
+			else try Common.find_file (ccom()) file with
+				| Not_found ->
+					failwith ("unable to find file for inclusion: " ^ file)
+			in
+			(ccom()).include_files <- (file, position) :: (ccom()).include_files;
+			vnull
+		);
+		(* Compilation server *)
+		"server_add_module_check_policy", vfun4 (fun filter policy recursive context_options ->
+			let filter = List.map dec_string (dec_array filter) in
+			let policy = List.map dec_int (dec_array policy) in
+			(get_api()).add_module_check_policy filter policy (dec_bool recursive) (dec_int context_options);
+			vnull
+		);
+		"server_invalidate_files", vfun1 (fun a ->
+			let cs = match CompilationServer.get() with Some cs -> cs | None -> failwith "compilation server not running" in
+			List.iter (fun v ->
+				let s = dec_string v in
+				let s = Path.unique_full_path s in
+				CompilationServer.taint_modules cs s;
+				CompilationServer.remove_files cs s;
+			) (dec_array a);
+			vnull
+		);
+	]
+
+
+end

+ 765 - 0
src/macro/macroContext.ml

@@ -0,0 +1,765 @@
+(*
+	The Haxe Compiler
+	Copyright (C) 2005-2016  Haxe Foundation
+
+	This program is free software; you can redistribute it and/or
+	modify it under the terms of the GNU General Public License
+	as published by the Free Software Foundation; either version 2
+	of the License, or (at your option) any later version.
+
+	This program 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 General Public License for more details.
+
+	You should have received a copy of the GNU General Public License
+	along with this program; if not, write to the Free Software
+	Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ *)
+
+open Ast
+open Common.DisplayMode
+open Common
+open Type
+open Typecore
+open Error
+open Globals
+
+module InterpImpl = Interp (* Hlmacro *)
+
+module Interp = struct
+	module BuiltApi = MacroApi.MacroApiImpl(InterpImpl)
+	include InterpImpl
+	include BuiltApi
+end
+
+let macro_enable_cache = ref false
+let macro_interp_cache = ref None
+let macro_interp_on_reuse = ref []
+let macro_interp_reused = ref false
+
+let delayed_macro_result = ref ((fun() -> assert false) : unit -> unit -> Interp.value)
+let unify_call_args_ref = ref (fun _ _ _ _ _ _ _-> assert false)
+let unify_call_args a b c d e f g : (texpr list * t) = !unify_call_args_ref a b c d e f g
+
+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
+
+let get_type_patch ctx t sub =
+	let new_patch() =
+		{ tp_type = None; tp_remove = false; tp_meta = [] }
+	in
+	let path = Ast.parse_path t in
+	let h, tp = (try
+		Hashtbl.find ctx.g.type_patches path
+	with Not_found ->
+		let h = Hashtbl.create 0 in
+		let tp = new_patch() in
+		Hashtbl.add ctx.g.type_patches path (h,tp);
+		h, tp
+	) in
+	match sub with
+	| None -> tp
+	| Some k ->
+		try
+			Hashtbl.find h k
+		with Not_found ->
+			let tp = new_patch() in
+			Hashtbl.add h k tp;
+			tp
+
+let macro_timer ctx l =
+	Common.timer (if Common.defined ctx.com Define.MacroTimes then ("macro" :: l) else ["macro"])
+
+let typing_timer ctx need_type f =
+	let t = Common.timer ["typing"] in
+	let old = ctx.com.error and oldp = ctx.pass 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 need_type && ctx.pass < PTypeField then ctx.pass <- PTypeField;
+	let exit() =
+		t();
+		ctx.com.error <- old;
+		ctx.pass <- oldp;
+	in
+	try
+		let r = f() in
+		exit();
+		r
+	with Error (ekind,p) ->
+			exit();
+			Interp.compiler_error (error_msg ekind) p
+		| WithTypeError (l,p) ->
+			exit();
+			Interp.compiler_error (error_msg l) p
+		| e ->
+			exit();
+			raise e
+
+let load_macro_ref : (typer -> bool -> path -> string -> pos -> (typer * ((string * bool * t) list * t * tclass * Type.tclass_field) * (Interp.value list -> Interp.value option))) ref = ref (fun _ _ _ _ -> assert false)
+
+let make_macro_api ctx p =
+	let parse_expr_string s p inl =
+		typing_timer ctx false (fun() -> try Parser.parse_expr_string ctx.com s p error inl with Exit -> raise MacroApi.Invalid_expr)
+	in
+	{
+		MacroApi.pos = p;
+		MacroApi.get_com = (fun() -> ctx.com);
+		MacroApi.get_type = (fun s ->
+			typing_timer ctx true (fun() ->
+				let path = parse_path s in
+				let tp = match List.rev (fst path) with
+					| s :: sl when String.length s > 0 && (match s.[0] with 'A'..'Z' -> true | _ -> false) ->
+						{ tpackage = List.rev sl; tname = s; tparams = []; tsub = Some (snd path) }
+					| _ ->
+						{ tpackage = fst path; tname = snd path; tparams = []; tsub = None }
+				in
+				try
+					let m = Some (Typeload.load_instance ctx (tp,null_pos) true p) in
+					m
+				with Error (Module_not_found _,p2) when p == p2 ->
+					None
+			)
+		);
+		MacroApi.resolve_type = (fun t p ->
+			typing_timer ctx true (fun() -> Typeload.load_complex_type ctx false p (t,null_pos))
+		);
+		MacroApi.get_module = (fun s ->
+			typing_timer ctx true (fun() ->
+				let path = parse_path s in
+				let m = List.map type_of_module_type (Typeload.load_module ctx path p).m_types in
+				m
+			)
+		);
+		MacroApi.after_typing = (fun f ->
+			Common.add_typing_filter ctx.com (fun tl ->
+				let t = macro_timer ctx ["afterTyping"] in
+				f tl;
+				t()
+			)
+		);
+		MacroApi.on_generate = (fun f ->
+			Common.add_filter ctx.com (fun() ->
+				let t = macro_timer ctx ["onGenerate"] in
+				f (List.map type_of_module_type ctx.com.types);
+				t()
+			)
+		);
+		MacroApi.after_generate = (fun f ->
+			Common.add_final_filter ctx.com (fun() ->
+				let t = macro_timer ctx ["afterGenerate"] in
+				f();
+				t()
+			)
+		);
+		MacroApi.on_type_not_found = (fun f ->
+			ctx.com.load_extern_type <- ctx.com.load_extern_type @ [fun path p ->
+				let td = f (s_type_path path) in
+				if td = Interp.vnull then
+					None
+				else
+					let (pack,name),tdef,p = Interp.decode_type_def td in
+					Some (name,(pack,[tdef,p]))
+			];
+		);
+		MacroApi.parse_string = parse_expr_string;
+		MacroApi.type_expr = (fun e ->
+			typing_timer ctx true (fun() -> type_expr ctx e Value)
+		);
+		MacroApi.type_macro_expr = (fun e ->
+			let e = typing_timer ctx true (fun() -> type_expr ctx e 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.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
+		);
+		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 Parser.parse_string ctx.com ("typedef T = " ^ s) null_pos error false with
+					| _,[ETypedef { d_data = ct },_] -> Some ct
+					| _ -> assert false
+				) in
+				let tp = get_type_patch ctx t (Some (f,s)) in
+				match v with
+				| None -> tp.tp_remove <- true
+				| Some _ -> tp.tp_type <- Option.map fst v
+			);
+		);
+		MacroApi.meta_patch = (fun m t f s ->
+			let m = (match Parser.parse_string ctx.com (m ^ " typedef T = T") null_pos error false with
+				| _,[ETypedef t,_] -> t.d_meta
+				| _ -> assert false
+			) in
+			let tp = get_type_patch ctx t (match f with None -> None | Some f -> Some (f,s)) in
+			tp.tp_meta <- tp.tp_meta @ m;
+		);
+		MacroApi.set_js_generator = (fun gen ->
+			let js_ctx = Genjs.alloc_ctx ctx.com in
+			ctx.com.js_gen <- Some (fun() ->
+				let t = macro_timer ctx ["jsGenerator"] in
+				gen js_ctx;
+				t()
+			);
+		);
+		MacroApi.get_local_type = (fun() ->
+			match ctx.g.get_build_infos() with
+			| Some (mt,tl,_) ->
+				Some (match mt with
+					| TClassDecl c -> TInst (c,tl)
+					| TEnumDecl e -> TEnum (e,tl)
+					| TTypeDecl t -> TType (t,tl)
+					| TAbstractDecl a -> TAbstract(a,tl))
+			| None ->
+				if ctx.curclass == null_class then
+					None
+				else
+					Some (TInst (ctx.curclass,[]))
+		);
+		MacroApi.get_expected_type = (fun() ->
+			match ctx.with_type_stack with
+				| (WithType t) :: _ -> Some t
+				| _ -> None
+		);
+		MacroApi.get_call_arguments = (fun() ->
+			match ctx.call_argument_stack with
+				| [] -> None
+				| el :: _ -> Some el
+		);
+		MacroApi.get_local_method = (fun() ->
+			ctx.curfield.cf_name;
+		);
+		MacroApi.get_local_using = (fun() ->
+			List.map fst ctx.m.module_using;
+		);
+		MacroApi.get_local_imports = (fun() ->
+			ctx.m.module_imports;
+		);
+		MacroApi.get_local_vars = (fun () ->
+			ctx.locals;
+		);
+		MacroApi.get_build_fields = (fun() ->
+			match ctx.g.get_build_infos() with
+			| None -> Interp.vnull
+			| Some (_,_,fields) -> Interp.enc_array (List.map Interp.encode_field fields)
+		);
+		MacroApi.get_pattern_locals = (fun e t ->
+			!get_pattern_locals_ref ctx e t
+		);
+		MacroApi.define_type = (fun v ->
+			let m, tdef, pos = (try Interp.decode_type_def v with MacroApi.Invalid_expr -> Interp.exc_string "Invalid type definition") in
+			let add is_macro ctx =
+				let mnew = Typeload.type_module ctx m ctx.m.curmod.m_extra.m_file [tdef,pos] pos in
+				mnew.m_extra.m_kind <- if is_macro then MMacro else MFake;
+				add_dependency mnew ctx.m.curmod;
+			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,ctx.g.macros with
+			| EClass c,Some (_,mctx) when List.exists (fun cff -> (Meta.has Meta.Macro cff.cff_meta || List.mem AMacro cff.cff_access)) c.d_data ->
+				add true mctx
+			| _ ->
+				()
+		);
+		MacroApi.define_module = (fun m types imports usings ->
+			let types = List.map (fun v ->
+				let _, tdef, pos = (try Interp.decode_type_def v with MacroApi.Invalid_expr -> Interp.exc_string "Invalid type definition") in
+				tdef, pos
+			) types in
+			let pos = (match types with [] -> null_pos | (_,p) :: _ -> p) in
+			let imports = List.map (fun (il,ik) -> EImport(il,ik),pos) imports in
+			let usings = List.map (fun tp ->
+				let sl = tp.tpackage @ [tp.tname] @ (match tp.tsub with None -> [] | Some s -> [s]) in
+				EUsing (List.map (fun s -> s,null_pos) sl),pos
+			) usings in
+			let types = imports @ usings @ types in
+			let mpath = Ast.parse_path m in
+			begin try
+				let m = Hashtbl.find ctx.g.modules mpath in
+				ignore(Typeload.type_types_into_module ctx m types pos)
+			with Not_found ->
+				let mnew = Typeload.type_module ctx mpath ctx.m.curmod.m_extra.m_file types pos in
+				mnew.m_extra.m_kind <- MFake;
+				add_dependency mnew ctx.m.curmod;
+			end
+		);
+		MacroApi.module_dependency = (fun mpath file ismacro ->
+			let m = typing_timer ctx false (fun() -> Typeload.load_module ctx (parse_path mpath) p) in
+			if ismacro then
+				m.m_extra.m_macro_calls <- file :: List.filter ((<>) file) m.m_extra.m_macro_calls
+			else
+				add_dependency m (create_fake_module ctx file);
+		);
+		MacroApi.current_module = (fun() ->
+			ctx.m.curmod
+		);
+		MacroApi.current_macro_module = (fun () -> assert false);
+		MacroApi.delayed_macro = (fun i ->
+			let mctx = (match ctx.g.macros with None -> assert false | Some (_,mctx) -> mctx) in
+			let f = (try DynArray.get mctx.g.delayed_macros i with _ -> failwith "Delayed macro retrieve failure") in
+			f();
+			let ret = !delayed_macro_result in
+			delayed_macro_result := (fun() -> assert false);
+			ret
+		);
+		MacroApi.use_cache = (fun() ->
+			!macro_enable_cache
+		);
+		MacroApi.format_string = (fun s p ->
+			ctx.g.do_format_string ctx s p
+		);
+		MacroApi.cast_or_unify = (fun t e p ->
+			AbstractCast.cast_or_unify_raise ctx t e p
+		);
+		MacroApi.add_global_metadata = (fun s1 s2 config ->
+			let meta = (match Parser.parse_string ctx.com (s2 ^ " typedef T = T") null_pos error false with
+				| _,[ETypedef t,_] -> t.d_meta
+				| _ -> assert false
+			) in
+			List.iter (fun m ->
+				ctx.g.global_metadata <- (ExtString.String.nsplit s1 ".",m,config) :: ctx.g.global_metadata;
+			) meta;
+		);
+		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 <- Typeload.get_policy ctx m.m_path) ctx.g.modules;
+			in
+			let add_macro ctx = match ctx.g.macros with
+				| None -> ()
+				| Some(_,mctx) -> add mctx;
+			in
+			match Obj.magic i with
+			| CompilationServer.NormalContext -> add ctx
+			| CompilationServer.MacroContext -> add_macro ctx
+			| CompilationServer.NormalAndMacroContext -> add ctx; add_macro ctx;
+		);
+		MacroApi.on_reuse = (fun f ->
+			macro_interp_on_reuse := f :: !macro_interp_on_reuse
+		);
+	}
+
+let rec init_macro_interp ctx mctx mint =
+	let p = null_pos in
+	ignore(Typeload.load_module mctx (["haxe";"macro"],"Expr") p);
+	ignore(Typeload.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;
+		macro_interp_on_reuse := [];
+		macro_interp_reused := true;
+	end
+
+and flush_macro_context mint ctx =
+	let t = macro_timer ctx ["flush"] in
+	let mctx = (match ctx.g.macros with None -> assert false | Some (_,mctx) -> mctx) in
+	ctx.g.do_finalize mctx;
+	let _, types, modules = ctx.g.do_generate mctx in
+	mctx.com.types <- types;
+	mctx.com.Common.modules <- modules;
+	let check_reuse() =
+		if !macro_interp_reused then
+			true
+		else if not (List.for_all (fun f -> f())  !macro_interp_on_reuse) then
+			false
+		else begin
+			macro_interp_reused := true;
+			true;
+		end
+	in
+	(* if one of the type we are using has been modified, we need to create a new macro context from scratch *)
+	let mint = if not (Interp.can_reuse mint types && check_reuse()) then begin
+		let com2 = mctx.com in
+		let mint = Interp.create com2 (make_macro_api ctx Globals.null_pos) in
+		let macro = ((fun() -> Interp.select mint), mctx) in
+		ctx.g.macros <- Some macro;
+		mctx.g.macros <- Some macro;
+		init_macro_interp ctx mctx mint;
+		mint
+	end else mint in
+	(* we should maybe ensure that all filters in Main are applied. Not urgent atm *)
+	let expr_filters = [AbstractCast.handle_abstract_casts mctx; Filters.captured_vars mctx.com; Filters.rename_local_vars mctx] in
+
+	(*
+		some filters here might cause side effects that would break compilation server.
+		let's save the minimal amount of information we need
+	*)
+	let minimal_restore t =
+		match t with
+		| TClassDecl c ->
+			let meta = c.cl_meta in
+			let path = c.cl_path in
+			c.cl_restore <- (fun() -> c.cl_meta <- meta; c.cl_path <- path);
+		| _ ->
+			()
+	in
+	let type_filters = [
+		Filters.add_field_inits mctx;
+		minimal_restore;
+		Filters.apply_native_paths mctx
+	] 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()
+
+let create_macro_interp ctx mctx =
+	let com2 = mctx.com in
+	let mint, init = (match !macro_interp_cache with
+		| None ->
+			let mint = Interp.create com2 (make_macro_api ctx null_pos) in
+			mint, (fun() -> init_macro_interp ctx mctx mint)
+		| Some mint ->
+			macro_interp_reused := false;
+			Interp.do_reuse mint (make_macro_api ctx null_pos);
+			mint, (fun() -> ())
+	) in
+	let on_error = com2.error in
+	com2.error <- (fun e p ->
+		Interp.set_error (Interp.get_ctx()) true;
+		macro_interp_cache := None;
+		on_error e p
+	);
+	let macro = ((fun() -> Interp.select mint), mctx) in
+	ctx.g.macros <- Some macro;
+	mctx.g.macros <- Some macro;
+	(* mctx.g.core_api <- ctx.g.core_api; // causes some issues because of optional args and Null type in Flash9 *)
+	init()
+
+let get_macro_context ctx p =
+	let api = make_macro_api ctx p in
+	match ctx.g.macros with
+	| Some (select,ctx) ->
+		select();
+		api, ctx
+	| None ->
+		let com2 = Common.clone ctx.com in
+		ctx.com.get_macros <- (fun() -> Some com2);
+		com2.package_rules <- PMap.empty;
+		com2.main_class <- None;
+		com2.display <- DisplayMode.create DMNone;
+		List.iter (fun p -> com2.defines <- PMap.remove (Globals.platform_name p) com2.defines) Globals.platforms;
+		com2.defines_signature <- None;
+		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;
+		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) Common.flash_versions in
+		com2.defines <- PMap.foldi (fun k v acc -> if List.mem k to_remove then acc else PMap.add k v acc) com2.defines PMap.empty;
+		Common.define com2 Define.Macro;
+		Common.init_platform com2 !Globals.macro_platform;
+		let mctx = ctx.g.do_create com2 in
+		mctx.is_display_file <- ctx.is_display_file;
+		create_macro_interp ctx mctx;
+		api, mctx
+
+let load_macro ctx display cpath f p =
+	let t = macro_timer ctx ["typing";s_type_path cpath ^ "." ^ f] in
+	let api, mctx = get_macro_context ctx p in
+	let mint = Interp.get_ctx() in
+	let cpath, sub = (match List.rev (fst cpath) with
+		| name :: pack when name.[0] >= 'A' && name.[0] <= 'Z' -> (List.rev pack,name), Some (snd cpath)
+		| _ -> cpath, None
+	) in
+	let meth = try Hashtbl.find mctx.com.cached_macros (cpath,f) with Not_found ->
+		(* Temporarily enter display mode while typing the macro. *)
+		if display then mctx.com.display <- ctx.com.display;
+		let m = (try Hashtbl.find ctx.g.types_module cpath with Not_found -> cpath) in
+		let mloaded = Typeload.load_module mctx m p in
+		api.MacroApi.current_macro_module <- (fun() -> mloaded);
+		mctx.m <- {
+			curmod = mloaded;
+			module_types = [];
+			module_using = [];
+			module_globals = PMap.empty;
+			wildcard_packages = [];
+			module_imports = [];
+		};
+		add_dependency ctx.m.curmod mloaded;
+		let mt = Typeload.load_type_def mctx p { tpackage = fst cpath; tname = snd cpath; tparams = []; tsub = sub } in
+		let cl, meth = (match mt with
+			| TClassDecl c ->
+				let t = macro_timer ctx ["finalize"] in
+				mctx.g.do_finalize mctx;
+				t();
+				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
+		) in
+		if not (Common.defined ctx.com Define.NoDeprecationWarnings) then
+			Display.DeprecationCheck.check_cf mctx.com meth p;
+		let meth = (match follow meth.cf_type with TFun (args,ret) -> args,ret,cl,meth | _ -> error "Macro call should be a method" p) in
+		mctx.com.display <- DisplayMode.create DMNone;
+		if not ctx.in_macro then flush_macro_context mint ctx;
+		Hashtbl.add mctx.com.cached_macros (cpath,f) meth;
+		mctx.m <- {
+			curmod = null_module;
+			module_types = [];
+			module_using = [];
+			module_globals = PMap.empty;
+			wildcard_packages = [];
+			module_imports = [];
+		};
+		meth
+	in
+	t();
+	let call args =
+		let t = macro_timer ctx ["execution";s_type_path cpath ^ "." ^ f] in
+		incr stats.s_macros_called;
+		let r = Interp.call_path (Interp.get_ctx()) ((fst cpath) @ [(match sub with None -> snd cpath | Some s -> s)]) f args api in
+		t();
+		r
+	in
+	mctx, meth, call
+
+type macro_arg_type =
+	| MAExpr
+	| MAFunction
+	| MAOther
+
+let type_macro ctx mode cpath f (el:Ast.expr list) p =
+	let mctx, (margs,mret,mclass,mfield), call_macro = load_macro ctx (mode = MDisplay) cpath f p in
+	let mpos = mfield.cf_pos in
+	let ctexpr = { tpackage = ["haxe";"macro"]; tname = "Expr"; tparams = []; tsub = None } in
+	let expr = Typeload.load_instance mctx (ctexpr,null_pos) false p in
+	(match mode with
+	| MDisplay ->
+		raise Exit (* We don't have to actually call the macro. *)
+	| MExpr ->
+		unify mctx mret expr mpos;
+	| MBuild ->
+		let ctfields = { tpackage = []; tname = "Array"; tparams = [TPType (CTPath { tpackage = ["haxe";"macro"]; tname = "Expr"; tparams = []; tsub = Some "Field" },null_pos)]; tsub = None } in
+		let tfields = Typeload.load_instance mctx (ctfields,null_pos) false p in
+		unify mctx mret tfields mpos
+	| MMacroType ->
+		let cttype = { tpackage = ["haxe";"macro"]; tname = "Type"; tparams = []; tsub = None } in
+		let ttype = Typeload.load_instance mctx (cttype,null_pos) false p in
+		try
+			unify_raise mctx 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 _,_) ->
+			let cttype = { tpackage = ["haxe";"macro"]; tname = "Expr"; tparams = []; tsub = Some ("ComplexType") } in
+			let ttype = Typeload.load_instance mctx (cttype,null_pos) false p in
+			unify_raise mctx mret ttype mpos;
+	);
+	(*
+		if the function's last argument is of Array<Expr>, split the argument list and use [] for unify_call_args
+	*)
+	let el,el2 = match List.rev margs with
+		| (_,_,TInst({cl_path=([], "Array")},[e])) :: rest when (try Type.type_eq EqStrict e expr; true with Unify_error _ -> false) ->
+			let rec loop (acc1,acc2) el1 el2 = match el1,el2 with
+				| [],[] ->
+					List.rev acc1, List.rev acc2
+				| [], e2 :: [] ->
+					(List.rev ((EArrayDecl [],p) :: acc1), [])
+				| [], _ ->
+					(* not enough arguments, will be handled by unify_call_args *)
+					List.rev acc1, List.rev acc2
+				| e1 :: l1, e2 :: [] ->
+					loop (((EArrayDecl [],p) :: acc1), [e1]) l1 []
+				| e1 :: l1, [] ->
+					loop (acc1, e1 :: acc2) l1 []
+				| e1 :: l1, e2 :: l2 ->
+					loop (e1 :: acc1, acc2) l1 l2
+			in
+			loop ([],[]) el margs
+		| _ ->
+			el,[]
+	in
+	let todo = ref [] in
+	let args =
+		(*
+			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
+				| TFun _ ->
+					(n,o,t_dynamic), MAFunction
+				| _ ->
+					(n,o,t), MAOther
+			) margs in
+		(*
+			this is quite tricky here : we want to use unify_call_args which will type our AST expr
+			but we want to be able to get it back after it's been padded with nulls
+		*)
+		let index = ref (-1) in
+		let constants = List.map (fun e ->
+			let p = snd e in
+			let e = (try
+				(match Codegen.type_constant_value ctx.com e with
+				| { eexpr = TConst (TString _); epos = p } when Lexer.is_fmt_string p ->
+					Lexer.remove_fmt_string p;
+					todo := (fun() -> Lexer.add_fmt_string p) :: !todo;
+				| _ -> ());
+				e
+			with Error (Custom _,_) ->
+				(* if it's not a constant, let's make something that is typed as haxe.macro.Expr - for nice error reporting *)
+				(EBlock [
+					(EVars [("__tmp",null_pos),Some (CTPath ctexpr,p),Some (EConst (Ident "null"),p)],p);
+					(EConst (Ident "__tmp"),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)
+		) el in
+		let elt, _ = unify_call_args mctx constants (List.map fst eargs) t_dynamic p false false in
+		List.iter (fun f -> f()) (!todo);
+		List.map2 (fun (_,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
+				(* added by unify_call_args *)
+				| TConst TNull -> (EConst (Ident "null"),e.epos), e
+				| _ -> assert false
+			) in
+			let ictx = Interp.get_ctx() in
+			match mct with
+			| MAExpr ->
+				Interp.encode_expr e
+			| MAFunction ->
+				let e = ictx.Interp.curapi.MacroApi.type_macro_expr e in
+				begin match Interp.eval_expr ictx e with
+				| Some v -> v
+				| None -> Interp.vnull
+				end
+			| MAOther -> match Interp.eval_expr ictx et with
+				| None -> assert false
+				| Some v -> v
+		) eargs elt
+	in
+	let args = match el2 with
+		| [] -> args
+		| _ -> (match List.rev args with _::args -> List.rev args | [] -> []) @ [Interp.enc_array (List.map Interp.encode_expr el2)]
+	in
+	let call() =
+		match call_macro args with
+		| None -> None
+		| Some v ->
+			try
+				Some (match mode with
+				| MExpr | MDisplay -> Interp.decode_expr v
+				| MBuild ->
+					let fields = if v = Interp.vnull then
+							(match ctx.g.get_build_infos() with
+							| None -> assert false
+							| Some (_,_,fields) -> fields)
+						else
+							List.map Interp.decode_field (Interp.dec_array v)
+					in
+					(EVars [("fields",null_pos),Some (CTAnonymous fields,p),None],p)
+				| MMacroType ->
+					let t = if v = Interp.vnull then
+						mk_mono()
+					else try
+						let ct = Interp.decode_ctype v in
+						Typeload.load_complex_type ctx false p ct;
+					with MacroApi.Invalid_expr ->
+						Interp.decode_type v
+					in
+					ctx.ret <- t;
+					(EBlock [],p)
+				)
+			with MacroApi.Invalid_expr ->
+				if v = Interp.vnull then
+					error "Unexpected null value returned from macro" p
+				else
+					error "The macro didn't return a valid result" p
+	in
+	let e = (if ctx.in_macro then begin
+		(*
+			this is super-tricky : we can't evaluate a macro inside a macro because we might trigger some cycles.
+			So instead, we generate a haxe.macro.Context.delayedCalled(i) expression that will only evaluate the
+			macro if/when it is called.
+
+			The tricky part is that the whole delayed-evaluation process has to use the same contextual informations
+			as if it was evaluated now.
+		*)
+		let ctx = {
+			ctx with locals = ctx.locals;
+		} in
+		let pos = DynArray.length mctx.g.delayed_macros in
+		DynArray.add mctx.g.delayed_macros (fun() ->
+			delayed_macro_result := (fun() ->
+				let mint = Interp.get_ctx() in
+				match call() with
+				| None -> (fun() -> raise MacroApi.Abort)
+				| Some e -> Interp.eval_delayed mint (type_expr ctx e Value)
+			);
+		);
+		ctx.m.curmod.m_extra.m_time <- -1.; (* disable caching for modules having macro-in-macro *)
+		if Common.defined ctx.com Define.MacroDebug then
+			ctx.com.warning "Macro-in-macro call detected" p;
+		let e = (EConst (Ident "$__delayed_call__"),p) in
+		Some (EUntyped (ECall (e,[EConst (Int (string_of_int pos)),p]),p),p)
+	end else
+		call()
+	) in
+	e
+
+let call_macro ctx path meth args p =
+	let mctx, (margs,_,mclass,mfield), call = load_macro ctx false path meth p in
+	let el, _ = unify_call_args mctx args margs t_dynamic p false false in
+	call (List.map (fun e -> try Interp.make_const e with Exit -> error "Parameter should be a constant" e.epos) el)
+
+let call_init_macro ctx e =
+	let p = { pfile = "--macro"; pmin = 0; pmax = 0 } in
+	let e = try
+		Parser.parse_expr_string ctx.com e p error false
+	with err ->
+		display_error ctx ("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
+			| EConst (Ident i) -> [i]
+			| _ -> 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
+		ignore(call_macro ctx path meth args p);
+	| _ ->
+		error "Invalid macro call" p
+
+let interpret ctx =
+	let mctx = Interp.create ctx.com (make_macro_api ctx null_pos) in
+	Interp.add_types mctx ctx.com.types (fun t -> ());
+	match ctx.com.main with
+	| None -> ()
+	| Some e -> ignore(Interp.eval_expr mctx e)
+
+let setup() =
+	Interp.setup Interp.macro_api
+
+;;
+load_macro_ref := load_macro;

文件差異過大導致無法顯示
+ 187 - 767
src/main.ml


+ 45 - 28
src/optimization/analyzer.ml

@@ -22,6 +22,8 @@ open Type
 open Common
 open AnalyzerTexpr
 open AnalyzerTypes
+open OptimizerTexpr
+open Globals
 
 (* File organization:
 	* analyzer.ml: The controlling file with all graph-based optimizations
@@ -394,7 +396,7 @@ module ConstPropagation = DataFlow(struct
 				let e1 = wrap cl1 in
 				let e2 = wrap cl2 in
 				let e = {e with eexpr = TBinop(op,e1,e2)} in
-				let e' = Optimizer.optimize_binop e op e1 e2 in
+				let e' = optimize_binop e op e1 e2 in
 				if e != e' then
 					eval bb e'
 				else
@@ -403,7 +405,7 @@ module ConstPropagation = DataFlow(struct
 				let cl1 = eval bb e1 in
 				let e1 = wrap cl1 in
 				let e = {e with eexpr = TUnop(op,flag,e1)} in
-				let e' = Optimizer.optimize_unop e op flag e1 in
+				let e' = optimize_unop e op flag e1 in
 				if e != e' then
 					eval bb e'
 				else
@@ -456,8 +458,9 @@ module ConstPropagation = DataFlow(struct
 				if not (type_change_ok ctx.com e'.etype e.etype) then raise Not_found;
 				e'
 		in
+		let is_special_var v = v.v_capture || is_asvar_type v.v_type in
 		let rec commit e = match e.eexpr with
-			| TLocal v when not v.v_capture ->
+			| TLocal v when not (is_special_var v) ->
 				begin try
 					inline e v.v_id
 				with Not_found ->
@@ -465,13 +468,13 @@ module ConstPropagation = DataFlow(struct
 				end
 			| TBinop((OpAssign | OpAssignOp _ as op),({eexpr = TLocal v} as e1),e2) ->
 				let e2 = try
-					if (Optimizer.has_side_effect e2) then raise Not_found;
+					if (has_side_effect e2) then raise Not_found;
 					inline e2 v.v_id
 				with Not_found ->
 					commit e2
 				in
 				{e with eexpr = TBinop(op,e1,e2)}
-			| TVar(v,Some e1) when not (Optimizer.has_side_effect e1) ->
+			| TVar(v,Some e1) when not (has_side_effect e1) ->
 				let e1 = try inline e1 v.v_id with Not_found -> commit e1 in
 				{e with eexpr = TVar(v,Some e1)}
 			| _ ->
@@ -814,7 +817,7 @@ module LocalDce = struct
 			| TCall ({ eexpr = TField(_,FStatic({ cl_path = ([],"Std") },{ cf_name = "string" })) },args) -> Type.iter loop e
 			| TCall ({eexpr = TField(_,FEnum _)},_) -> Type.iter loop e
 			| TCall ({eexpr = TConst (TString ("phi" | "fun"))},_) -> ()
-			| TCall({eexpr = TField(e1,fa)},el) -> Optimizer.field_call_has_side_effect loop e1 fa el
+			| TCall({eexpr = TField(e1,fa)},el) when PurityState.is_pure_field_access fa -> loop e1; List.iter loop el
 			| TNew _ | TCall _ | TBinop ((OpAssignOp _ | OpAssign),_,_) | TUnop ((Increment|Decrement),_,_) -> raise Exit
 			| TReturn _ | TBreak | TContinue | TThrow _ | TCast (_,Some _) -> raise Exit
 			| TFor _ -> raise Exit
@@ -1004,7 +1007,7 @@ module Debug = struct
 		) g.g_var_infos
 
 	let get_dump_path ctx c cf =
-		"dump" :: [Common.platform_name ctx.com.platform] @ (fst c.cl_path) @ [Printf.sprintf "%s.%s" (snd c.cl_path) cf.cf_name]
+		"dump" :: [platform_name ctx.com.platform] @ (fst c.cl_path) @ [Printf.sprintf "%s.%s" (snd c.cl_path) cf.cf_name]
 
 	let dot_debug ctx c cf =
 		let g = ctx.graph in
@@ -1100,8 +1103,8 @@ module Run = struct
 	open AnalyzerConfig
 	open Graph
 
-	let with_timer s f =
-		let timer = timer s in
+	let with_timer actx s f =
+		let timer = timer (if actx.config.detail_times then ["analyzer";s] else ["analyzer"]) in
 		let r = f() in
 		timer();
 		r
@@ -1120,6 +1123,7 @@ module Run = struct
 			loop_counter = 0;
 			loop_stack = [];
 			debug_exprs = [];
+			name_stack = [];
 		} in
 		ctx
 
@@ -1128,37 +1132,46 @@ module Run = struct
 
 	let there actx e =
 		if actx.com.debug then add_debug_expr actx "initial" e;
-		let e = with_timer "analyzer-filter-apply" (fun () -> TexprFilter.apply actx.com e) in
+		let e = with_timer actx "var-lazifier" (fun () -> VarLazifier.apply actx.com e) in
+		if actx.com.debug then add_debug_expr actx "after var-lazifier" e;
+		let e = with_timer actx "filter-apply" (fun () -> TexprFilter.apply actx.com e) in
 		if actx.com.debug then add_debug_expr actx "after filter-apply" e;
-		let tf,is_real_function = match e.eexpr with
+		let tf,t,is_real_function = match e.eexpr with
 			| TFunction tf ->
-				tf,true
+				tf,e.etype,true
 			| _ ->
 				(* Wrap expression in a function so we don't have to treat it as a special case throughout. *)
 				let e = mk (TReturn (Some e)) t_dynamic e.epos in
 				let tf = { tf_args = []; tf_type = e.etype; tf_expr = e; } in
-				tf,false
+				tf,tfun [] e.etype,false
 		in
-		with_timer "analyzer-from-texpr" (fun () -> AnalyzerTexprTransformer.from_tfunction actx tf e.etype e.epos);
+		with_timer actx "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 "analyzer-to-texpr" (fun () -> AnalyzerTexprTransformer.to_texpr actx) in
+		let e = with_timer actx "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 = with_timer "analyzer-fusion" (fun () -> Fusion.apply actx.com actx.config e) in
+		let e = if actx.config.fusion then with_timer actx "fusion" (fun () -> Fusion.apply actx.com actx.config e) else e in
 		if actx.com.debug then add_debug_expr actx "after fusion" e;
-		let e = with_timer "analyzer-cleanup" (fun () -> Cleanup.apply actx.com e) in
-		if actx.com.debug then add_debug_expr actx "after to-cleanup" e;
+		let e = with_timer actx "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
 		else begin
 			(* Get rid of the wrapping function and its return expressions. *)
 			let rec loop first e = match e.eexpr with
 				| TReturn (Some e) -> e
-				| TFunction tf when first -> loop false tf.tf_expr
+				| TFunction tf when first ->
+					begin match loop false tf.tf_expr with
+						| {eexpr = TBlock _ | TIf _ | TSwitch _ | TTry _} when actx.com.platform = Cpp || actx.com.platform = Hl ->
+							mk (TCall(e,[])) tf.tf_type e.epos
+						| e ->
+							e
+					end
+				| TBlock [e] -> loop first e
 				| TFunction _ -> e
 				| _ -> Type.map_expr (loop first) e
 			in
@@ -1173,23 +1186,27 @@ module Run = struct
 		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 "analyzer-ssa-apply" (fun () -> Ssa.apply actx);
-			if actx.config.const_propagation then with_timer "analyzer-const-propagation" (fun () -> ConstPropagation.apply actx);
-			if actx.config.copy_propagation then with_timer "analyzer-copy-propagation" (fun () -> CopyPropagation.apply actx);
-			if actx.config.code_motion then with_timer "analyzer-code-motion" (fun () -> CodeMotion.apply actx);
-			with_timer "analyzer-local-dce" (fun () -> LocalDce.apply actx);
+			with_timer actx "ssa-apply" (fun () -> Ssa.apply actx);
+			if actx.config.const_propagation then with_timer actx "const-propagation" (fun () -> ConstPropagation.apply actx);
+			if actx.config.copy_propagation then with_timer actx "copy-propagation" (fun () -> CopyPropagation.apply actx);
+			if actx.config.code_motion then with_timer actx "code-motion" (fun () -> CodeMotion.apply actx);
+			with_timer actx "local-dce" (fun () -> LocalDce.apply actx);
 		end;
 		back_again actx is_real_function
 
+	let rec reduce_control_flow ctx e =
+		Type.map_expr (reduce_control_flow ctx) (Optimizer.reduce_control_flow ctx e)
+
 	let run_on_field ctx config c cf = match cf.cf_expr with
-		| Some e when not (is_ignored cf.cf_meta) && not (Codegen.is_removable_field ctx cf) ->
+		| 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
+			(match e.eexpr with TFunction tf -> cf.cf_expr_unoptimized <- Some tf | _ -> ());
 			let actx = create_analyzer_context ctx.Typecore.com config e in
 			let debug() =
 				prerr_endline (Printf.sprintf "While analyzing %s.%s" (s_type_path c.cl_path) cf.cf_name);
 				List.iter (fun (s,e) ->
 					prerr_endline (Printf.sprintf "<%s>" s);
-					prerr_endline (Type.s_expr_pretty true "" (s_type (print_context())) e);
+					prerr_endline (Type.s_expr_pretty true "" false (s_type (print_context())) e);
 					prerr_endline (Printf.sprintf "</%s>" s);
 				) (List.rev actx.debug_exprs);
 				Debug.dot_debug actx c cf;
@@ -1198,13 +1215,13 @@ module Run = struct
 			let e = try
 				run_on_expr actx e
 			with
-			| Error _ | Abort _ as exc ->
+			| Error.Error _ | Abort _ as exc ->
 				raise exc
 			| exc ->
 				debug();
 				raise exc
 			in
-			let e = Cleanup.reduce_control_flow ctx e in
+			let e = reduce_control_flow ctx e in
 			begin match config.debug_kind with
 				| DebugNone -> ()
 				| DebugDot -> Debug.dot_debug actx c cf;

+ 14 - 2
src/optimization/analyzerConfig.ml

@@ -20,6 +20,7 @@
 open Ast
 open Type
 open Common
+open Globals
 
 type debug_kind =
 	| DebugNone
@@ -35,6 +36,9 @@ type t = {
 	fusion : bool;
 	purity_inference : bool;
 	debug_kind : debug_kind;
+	detail_times : bool;
+	user_var_fusion : bool;
+	fusion_debug : bool;
 }
 
 let flag_const_propagation = "const_propagation"
@@ -46,11 +50,13 @@ let flag_purity_inference = "purity_inference"
 let flag_ignore = "ignore"
 let flag_dot_debug = "dot_debug"
 let flag_full_debug = "full_debug"
+let flag_user_var_fusion = "user_var_fusion"
+let flag_fusion_debug = "fusion_debug"
 
 let all_flags =
 	List.fold_left (fun acc flag ->
 		flag :: ("no_" ^ flag) :: acc
-	) [] [flag_const_propagation;flag_copy_propagation;flag_code_motion;flag_local_dce;flag_fusion;flag_purity_inference;flag_ignore;flag_dot_debug]
+	) [] [flag_const_propagation;flag_copy_propagation;flag_code_motion;flag_local_dce;flag_fusion;flag_purity_inference;flag_ignore;flag_dot_debug;flag_user_var_fusion]
 
 let has_analyzer_option meta s =
 	try
@@ -83,9 +89,12 @@ let get_base_config com =
 		copy_propagation = not (Common.raw_defined com "analyzer-no-copy-propagation");
 		code_motion = Common.raw_defined com "analyzer-code-motion";
 		local_dce = not (Common.raw_defined com "analyzer-no-local-dce");
-		fusion = not (Common.raw_defined com "analyzer-no-fusion") && (match com.platform with Flash | Java -> false | _ -> true);
+		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")));
+		fusion_debug = false;
 	}
 
 let update_config_from_meta com config meta =
@@ -106,6 +115,9 @@ let update_config_from_meta com config meta =
 				| EConst (Ident s) when s = flag_purity_inference -> { config with purity_inference = true}
 				| EConst (Ident s) when s = flag_dot_debug -> {config with debug_kind = DebugDot}
 				| EConst (Ident s) when s = flag_full_debug -> {config with debug_kind = DebugFull}
+				| EConst (Ident s) when s = flag_user_var_fusion -> {config with user_var_fusion = true}
+				| EConst (Ident s) when s = "no_" ^ flag_user_var_fusion -> {config with user_var_fusion = false}
+				| EConst (Ident s) when s = flag_fusion_debug -> {config with fusion_debug = true}
 				| _ ->
 					let s = Ast.s_expr e in
 					com.warning (StringError.string_error s all_flags ("Unrecognized analyzer option: " ^ s)) (pos e);

文件差異過大導致無法顯示
+ 579 - 208
src/optimization/analyzerTexpr.ml


+ 125 - 72
src/optimization/analyzerTexprTransformer.ml

@@ -17,6 +17,7 @@
 	Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  *)
 
+open Globals
 open Ast
 open Type
 open Common
@@ -25,6 +26,7 @@ open AnalyzerTypes
 open AnalyzerTypes.BasicBlock
 open AnalyzerTypes.Graph
 open AnalyzerTexpr
+open OptimizerTexpr
 
 (*
 	Transforms an expression to a graph, and a graph back to an expression. This module relies on TexprFilter being
@@ -80,10 +82,18 @@ let rec func ctx bb tf t p =
 		g.g_unreachable
 	in
 	let check_unbound_call v el =
-		if is_unbound_call_that_might_have_side_effects v el then ctx.has_unbound <- true
+		if v.v_name = "$ref" then begin match el with
+			| [{eexpr = TLocal v}] -> v.v_capture <- true
+			| _ -> ()
+		end;
+		if is_unbound_call_that_might_have_side_effects v el then ctx.has_unbound <- true;
 	in
 	let no_void t p =
-		if ExtType.is_void (follow t) then error "Cannot use Void as value" p
+		if ExtType.is_void (follow t) then Error.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 rec value' bb e = match e.eexpr with
 		| TLocal v ->
@@ -99,9 +109,13 @@ let rec func ctx bb tf t p =
 			bb,e
 		| TCall(e1,el) ->
 			call bb e e1 el
+		| TBinop(OpAssignOp op,({eexpr = TArray(e1,e2)} as ea),e3) ->
+			array_assign_op bb op e ea e1 e2 e3
+		| TBinop(OpAssignOp op,({eexpr = TField(e1,fa)} as ef),e2) ->
+			field_assign_op bb op e ef e1 fa e2
 		| TBinop((OpAssign | OpAssignOp _) as op,e1,e2) ->
-			let bb,e2 = value bb e2 in
 			let bb,e1 = value bb e1 in
+			let bb,e2 = value bb e2 in
 			bb,{e with eexpr = TBinop(op,e1,e2)}
 		| TBinop(op,e1,e2) ->
 			let bb,e1,e2 = match ordered_value_list bb [e1;e2] with
@@ -156,24 +170,21 @@ let rec func ctx bb tf t p =
 			close_node g bb;
 			add_cfg_edge bb_func_end bb_next CFGGoto;
 			bb_next,ec
-		| TTypeExpr(TClassDecl {cl_kind = KAbstractImpl a}) when not (Meta.has Meta.RuntimeValue a.a_meta) ->
-			error "Cannot use abstract as value" e.epos
-		| TTypeExpr(TClassDecl c) ->
-			List.iter (fun cf -> if not (Meta.has Meta.MaybeUsed cf.cf_meta) then cf.cf_meta <- (Meta.MaybeUsed,[],cf.cf_pos) :: cf.cf_meta;) c.cl_ordered_statics;
-			bb,e
+		(*| TTypeExpr(TClassDecl {cl_kind = KAbstractImpl a}) when not (Meta.has Meta.RuntimeValue a.a_meta) ->
+			error "Cannot use abstract as value" e.epos*)
 		| TConst _ | TTypeExpr _ ->
 			bb,e
 		| TThrow _ | TReturn _ | TBreak | TContinue ->
 			let bb = block_element bb e in
 			bb,mk (TConst TNull) t_dynamic e.epos
 		| TVar _ | TFor _ | TWhile _ ->
-			error "Cannot use this expression as value" e.epos
+			Error.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;
 		bb,e
 	and ordered_value_list bb el =
-		let might_be_affected,collect_modified_locals = Optimizer.create_affection_checker() in
+		let might_be_affected,collect_modified_locals = create_affection_checker() in
 		let rec can_be_optimized e = match e.eexpr with
 			| TBinop _ | TArray _ | TCall _ -> true
 			| TParenthesis e1 -> can_be_optimized e1
@@ -181,9 +192,9 @@ let rec func ctx bb tf t p =
 		in
 		let _,el = List.fold_left (fun (had_side_effect,acc) e ->
 			if had_side_effect then
-				(true,(might_be_affected e || Optimizer.has_side_effect e,can_be_optimized e,e) :: acc)
+				(true,(might_be_affected e || has_side_effect e,can_be_optimized e,e) :: acc)
 			else begin
-				let had_side_effect = Optimizer.has_side_effect e in
+				let had_side_effect = has_side_effect e in
 				if had_side_effect then collect_modified_locals e;
 				let opt = can_be_optimized e in
 				(had_side_effect || opt,(false,opt,e) :: acc)
@@ -194,17 +205,40 @@ let rec func ctx bb tf t p =
 			bb,(value :: acc)
 		) (bb,[]) el in
 		bb,List.rev values
-	and bind_to_temp bb sequential e =
+	and bind_to_temp ?(v=None) bb sequential 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
+			| FEnum _ -> true
+			| FDynamic ("cca" | "__Index" | "__s") -> true (* This is quite retarded, but we have to deal with this somehow... *)
+			| _ -> match follow e.etype,follow e1.etype with
+				| TFun _,TInst _ -> false
+				| TFun _,_ -> true (* We don't know what's going on here, don't create a temp var (see #5082). *)
+				| _ -> false
+		in
 		let rec loop fl e = match e.eexpr with
-			| TField(e1,fa) when (match extract_field fa with Some {cf_kind = Method MethNormal} -> true | _ -> false) ->
+			| TField(e1,fa) when is_probably_not_affected e e1 fa ->
 				loop ((fun e' -> {e with eexpr = TField(e',fa)}) :: fl) e1
+			| TField(e1,fa) ->
+				let fa = match fa with
+					| FInstance(c,tl,({cf_kind = Method _ } as cf)) -> FClosure(Some(c,tl),cf)
+					| _ -> fa
+				in
+				fl,{e with eexpr = TField(e1,fa)}
 			| _ ->
 				fl,e
 		in
 		let fl,e = loop [] e in
-		let v = alloc_var ctx.temp_var_name e.etype e.epos in
+		let rec loop e = match e.eexpr with
+			| TLocal v -> v.v_name
+			| TArray(e1,_) | TField(e1,_) | TParenthesis e1 | TCast(e1,None) | TMeta(_,e1) -> loop e1
+			| _ -> match ctx.name_stack with
+				| s :: _ -> s
+				| [] -> ctx.temp_var_name
+		in
+		let v = match v with Some v -> v | None -> alloc_var (loop e) e.etype e.epos in
 		begin match ctx.com.platform with
-			| Cpp when sequential && not (Common.defined ctx.com Define.Cppia) -> ()
+			| Globals.Cpp when sequential && not (Common.defined ctx.com Define.Cppia) -> ()
 			| _ -> v.v_meta <- [Meta.CompilerGenerated,[],e.epos];
 		end;
 		let bb = declare_var_and_assign bb v e e.epos in
@@ -241,13 +275,16 @@ let rec func ctx bb tf t p =
 			end;
 			mk (TBinop(OpAssign,ev,e)) ev.etype ev.epos
 		in
-		begin try
+		let close = push_name v.v_name in
+		let bb = try
 			block_element_plus bb (map_values assign e) (fun e -> mk (TVar(v,Some e)) ctx.com.basic.tvoid ev.epos)
 		with Exit ->
 			let bb,e = value bb e in
 			add_texpr bb (mk (TVar(v,Some e)) ctx.com.basic.tvoid ev.epos);
 			bb
-		end
+		in
+		close();
+		bb
 	and block_element_plus bb (e,efinal) f =
 		let bb = block_element bb e in
 		let bb = match efinal with
@@ -259,23 +296,42 @@ let rec func ctx bb tf t p =
 		let e,efinal = map_values f e in
 		block_element_plus bb (e,efinal) f
 	and call bb e e1 el =
-		begin match e1.eexpr with
-			| TConst TSuper when ctx.com.platform = Java || ctx.com.platform = Cs ->
-				bb,e
+		let bb = ref bb in
+		let check e t = match e.eexpr with
+			| TLocal v when is_ref_type t ->
+				v.v_capture <- true;
+				e
 			| _ ->
-				let check e t = match e.eexpr with
-					| TLocal v when is_ref_type t ->
-						v.v_capture <- true;
-						e
-					| _ ->
-						e
-				in
-				let el = Codegen.UnificationCallback.check_call check el e1.etype in
-					let bb,el = ordered_value_list bb (e1 :: el) in
-					match el with
-						| e1 :: el -> bb,{e with eexpr = TCall(e1,el)}
-						| _ -> assert false
-		end
+				if is_asvar_type t then begin
+					let v = alloc_var "tmp" t e.epos in
+					let bb',e = bind_to_temp ~v:(Some v) !bb false e in
+					bb := bb';
+					e
+				end else
+					e
+		in
+		let el = Codegen.UnificationCallback.check_call check el e1.etype in
+		let bb,el = ordered_value_list !bb (e1 :: el) in
+		match el with
+			| e1 :: el -> bb,{e with eexpr = TCall(e1,el)}
+			| _ -> assert false
+	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 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 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 = bind_to_temp bb false 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 eop = {e with eexpr = TBinop(op,e3,e2)} in
+		add_texpr bb {e with eexpr = TBinop(OpAssign,ef,eop)};
+		bb,ef
 	and block_element bb e = match e.eexpr with
 		(* variables *)
 		| TVar(v,None) ->
@@ -287,13 +343,16 @@ let rec func ctx bb tf t p =
 			let assign e =
 				mk (TBinop(OpAssign,e1,e)) e.etype e.epos
 			in
-			begin try
+			let close = push_name v.v_name in
+			let bb = try
 				block_element_value bb e2 assign
 			with Exit ->
 				let bb,e2 = value bb e2 in
 				add_texpr bb {e with eexpr = TBinop(OpAssign,e1,e2)};
 				bb
-			end
+			in
+			close();
+			bb
 		(* branching *)
 		| TMeta((Meta.MergeBlock,_,_),{eexpr = TBlock el}) ->
 			block_el bb el
@@ -349,7 +408,7 @@ let rec func ctx bb tf t p =
 				bb_next
 			end
 		| TSwitch(e1,cases,edef) ->
-			let is_exhaustive = edef <> None || Optimizer.is_exhaustive e1 in
+			let is_exhaustive = edef <> None || is_exhaustive e1 in
 			let bb,e1 = bind_to_temp bb false e1 in
 			add_texpr bb (wrap_meta ":cond-branch" e1);
 			let reachable = ref [] in
@@ -421,37 +480,25 @@ let rec func ctx bb tf t p =
 			let close = begin_try bb_exc in
 			let bb_try_next = block bb_try e1 in
 			close();
-			let bb_next = if bb_exc.bb_incoming = [] then
-				let bb_next = if bb_try_next == g.g_unreachable then
-					g.g_unreachable
-				else begin
-					let bb_next = create_node BKNormal bb.bb_type bb.bb_pos in
-					add_cfg_edge bb_try_next bb_next CFGGoto;
-					close_node g bb_try_next;
-					bb_next
-				end in
-				set_syntax_edge bb (SESubBlock(bb_try,bb_next));
-				bb_next
-			else begin
-				let is_reachable = ref (not (bb_try_next == g.g_unreachable)) in
-				let catches = List.map (fun (v,e) ->
-					let bb_catch = create_node (BKCatch v) e.etype e.epos in
-					add_cfg_edge bb_exc bb_catch CFGGoto;
-					let bb_catch_next = block bb_catch e in
-					is_reachable := !is_reachable || (not (bb_catch_next == g.g_unreachable));
-					v,bb_catch,bb_catch_next
-				) catches in
-				let bb_next = if !is_reachable then create_node BKNormal bb.bb_type bb.bb_pos else g.g_unreachable in
-				let catches = List.map (fun (v,bb_catch,bb_catch_next) ->
-					if bb_catch_next != g.g_unreachable then add_cfg_edge bb_catch_next bb_next CFGGoto;
-					close_node g bb_catch_next;
-					v,bb_catch
-				) catches in
-				set_syntax_edge bb (SETry(bb_try,bb_exc,catches,bb_next,e.epos));
-				if bb_try_next != g.g_unreachable then add_cfg_edge bb_try_next bb_next CFGGoto;
-				close_node g bb_try_next;
-				bb_next
-			end in
+			(* We always want to keep catch-blocks, so let's add a pseudo CFG edge if it's unreachable. *)
+			if bb_exc.bb_incoming = [] then add_cfg_edge bb_try_next bb_exc CFGMaybeThrow;
+			let is_reachable = ref (not (bb_try_next == g.g_unreachable)) in
+			let catches = List.map (fun (v,e) ->
+				let bb_catch = create_node (BKCatch v) e.etype e.epos in
+				add_cfg_edge bb_exc bb_catch CFGGoto;
+				let bb_catch_next = block bb_catch e in
+				is_reachable := !is_reachable || (not (bb_catch_next == g.g_unreachable));
+				v,bb_catch,bb_catch_next
+			) catches in
+			let bb_next = if !is_reachable then create_node BKNormal bb.bb_type bb.bb_pos else g.g_unreachable in
+			let catches = List.map (fun (v,bb_catch,bb_catch_next) ->
+				if bb_catch_next != g.g_unreachable then add_cfg_edge bb_catch_next bb_next CFGGoto;
+				close_node g bb_catch_next;
+				v,bb_catch
+			) catches in
+			set_syntax_edge bb (SETry(bb_try,bb_exc,catches,bb_next,e.epos));
+			if bb_try_next != g.g_unreachable then add_cfg_edge bb_try_next bb_next CFGGoto;
+			close_node g bb_try_next;
             close_node g bb_exc;
             close_node g bb;
 			bb_next
@@ -511,12 +558,18 @@ let rec func ctx bb tf t p =
 			let b,e1 = value bb e1 in
 			add_texpr bb {e with eexpr = TCast(e1,Some mt)};
 			bb
-		| TBinop((OpAssign | OpAssignOp _) as op,({eexpr = TArray(e1,e2)} as ea),e3) ->
+		| TBinop(OpAssignOp op,({eexpr = TArray(e1,e2)} as ea),e3) ->
+			let bb,_ = array_assign_op bb op e ea e1 e2 e3 in
+			bb
+		| TBinop(OpAssignOp op,({eexpr = TField(e1,fa)} as ef),e2) ->
+			let bb,_ = field_assign_op bb op e ef e1 fa e2 in
+			bb
+		| TBinop(OpAssign,({eexpr = TArray(e1,e2)} as ea),e3) ->
 			let bb,e1,e2,e3 = match ordered_value_list bb [e1;e2;e3] with
 				| bb,[e1;e2;e3] -> bb,e1,e2,e3
 				| _ -> assert false
 			in
-			add_texpr bb {e with eexpr = TBinop(op,{ea with eexpr = TArray(e1,e2)},e3)};
+			add_texpr bb {e with eexpr = TBinop(OpAssign,{ea with eexpr = TArray(e1,e2)},e3)};
 			bb
 		| TBinop((OpAssign | OpAssignOp _ as op),e1,e2) ->
 			let bb,e1 = value bb e1 in
@@ -618,7 +671,7 @@ let rec block_to_texpr_el ctx bb =
 					| 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
-					| _ -> error (Printf.sprintf "Invalid node exit: %s" (s_expr_pretty e1)) bb.bb_pos
+					| _ -> abort (Printf.sprintf "Invalid node exit: %s" (s_expr_pretty e1)) bb.bb_pos
 				in
 				bb_next,(mk e1_def t p) :: el
 			| [],_ ->
@@ -661,7 +714,7 @@ and func ctx i =
 					false
 			in
 			begin match e1.eexpr,e2.eexpr with
-				| TLocal v1,TLocal v2 when v1 == v2 && is_valid_assign_op op ->
+				| TLocal v1,TLocal v2 when v1 == v2 && not v1.v_capture && is_valid_assign_op op ->
 					begin match op,e3.eexpr with
 						| OpAdd,TConst (TInt i32) when Int32.to_int i32 = 1 && target_handles_assign_ops ctx.com -> {e with eexpr = TUnop(Increment,Prefix,e1)}
 						| OpSub,TConst (TInt i32) when Int32.to_int i32 = 1 && target_handles_assign_ops ctx.com -> {e with eexpr = TUnop(Decrement,Prefix,e1)}

+ 4 - 2
src/optimization/analyzerTypes.ml

@@ -17,6 +17,7 @@
 	Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  *)
 
+open Globals
 open Ast
 open Type
 open Common
@@ -111,7 +112,7 @@ module BasicBlock = struct
 		| CFGGoto -> "CFGGoto"
 		| CFGFunction -> "CFGFunction"
 		| CFGMaybeThrow -> "CFGMaybeThrow"
-		| CFGCondBranch e -> "CFGCondBranch " ^ (s_expr_pretty false "" (s_type (print_context())) e)
+		| CFGCondBranch e -> "CFGCondBranch " ^ (s_expr_pretty false "" false (s_type (print_context())) e)
 		| CFGCondElse -> "CFGCondElse"
 
 	let has_flag edge flag =
@@ -159,7 +160,7 @@ module BasicBlock = struct
 		bb
 
 	let in_scope bb bb' = match bb'.bb_scopes with
-		| [] -> error (Printf.sprintf "Scope-less block (kind: %s)" (s_block_kind bb'.bb_kind)) bb'.bb_pos
+		| [] -> abort (Printf.sprintf "Scope-less block (kind: %s)" (s_block_kind bb'.bb_kind)) bb'.bb_pos
 		| scope :: _ -> List.mem scope bb.bb_scopes
 end
 
@@ -522,4 +523,5 @@ type analyzer_context = {
 	mutable loop_counter : int;
 	mutable loop_stack : int list;
 	mutable debug_exprs : (string * texpr) list;
+	mutable name_stack : string list;
 }

+ 97 - 47
src/optimization/dce.ml

@@ -20,6 +20,7 @@
 open Ast
 open Common
 open Type
+open Globals
 
 type dce = {
 	com : context;
@@ -27,6 +28,7 @@ type dce = {
 	std_dirs : string list;
 	debug : bool;
 	follow_expr : dce -> texpr -> unit;
+	dependent_types : (string list * string,module_type list) Hashtbl.t;
 	mutable curclass : tclass;
 	mutable added_fields : (tclass * tclass_field * bool) list;
 	mutable marked_fields : tclass_field list;
@@ -54,6 +56,7 @@ let keep_whole_class dce c =
 	|| super_forces_keep c
 	|| (match c with
 		| { cl_path = ([],("Math"|"Array"))} when dce.com.platform = Js -> false
+		| { cl_path = ([],("Math"|"Array"|"String"))} when dce.com.platform = Lua -> false
 		| { cl_extern = true }
 		| { cl_path = ["flash";"_Boot"],"RealBoot" } -> true
 		| { cl_path = [],"String" }
@@ -175,8 +178,8 @@ and mark_t dce p t =
 			List.iter (mark_t dce p) pl
 		| TAbstract(a,pl) when Meta.has Meta.MultiType a.a_meta ->
 			begin try
-				mark_t dce p (snd (Codegen.AbstractCast.find_multitype_specialization dce.com a pl p))
-			with Typecore.Error _ ->
+				mark_t dce p (snd (Typecore.AbstractCast.find_multitype_specialization dce.com a pl p))
+			with Error.Error _ ->
 				()
 			end
 		| TAbstract(a,pl) ->
@@ -203,6 +206,13 @@ let mark_mt dce mt = match mt with
 
 (* find all dependent fields by checking implementing/subclassing types *)
 let rec mark_dependent_fields dce csup n stat =
+	let dependent = try
+		Hashtbl.find dce.dependent_types csup.cl_path
+	with Not_found ->
+		let cl = List.filter (fun mt -> match mt with TClassDecl c -> is_parent csup c | _ -> false) dce.com.types in
+		Hashtbl.add dce.dependent_types csup.cl_path cl;
+		cl
+	in
 	List.iter (fun mt -> match mt with
 		| TClassDecl c when is_parent csup c ->
 			let rec loop c =
@@ -222,7 +232,7 @@ let rec mark_dependent_fields dce csup n stat =
 			in
 			loop c
 		| _ -> ()
-	) dce.com.types
+	) dependent
 
 (* expr and field evaluation *)
 
@@ -341,6 +351,70 @@ and is_const_string e = match e.eexpr with
 	| TConst(TString(_)) -> true
 	| _ -> false
 
+and expr_field dce e fa is_call_expr =
+	let do_default = fun () ->
+		let n = field_name fa in
+			(match fa with
+			| FAnon cf ->
+				if Meta.has Meta.Optional cf.cf_meta then begin
+					check_and_add_feature dce "anon_optional_read";
+					check_and_add_feature dce ("anon_optional_read." ^ n);
+				end else begin
+					check_and_add_feature dce "anon_read";
+					check_and_add_feature dce ("anon_read." ^ n);
+				end
+			| FDynamic _ ->
+				check_and_add_feature dce "dynamic_read";
+				check_and_add_feature dce ("dynamic_read." ^ n);
+			| _ -> ());
+			begin match follow e.etype with
+				| TInst(c,_) ->
+					mark_class dce c;
+					field dce c n false;
+				| TAnon a ->
+					(match !(a.a_status) with
+					| Statics c ->
+						mark_class dce c;
+						field dce c n true;
+					| _ -> ())
+
+
+				| _ -> ()
+			end
+	in
+	let mark_instance_field_access c cf =
+		if (not is_call_expr && dce.com.platform = Python) then begin
+			if c.cl_path = ([], "Array") then begin
+				check_and_add_feature dce "closure_Array";
+				check_and_add_feature dce ("python.internal.ArrayImpl." ^ cf.cf_name);
+				check_and_add_feature dce ("python.internal.ArrayImpl")
+			end
+			else if c.cl_path = ([], "String") then begin
+				check_and_add_feature dce "closure_String";
+				check_and_add_feature dce ("python.internal.StringImpl." ^ cf.cf_name);
+				check_and_add_feature dce ("python.internal.StringImpl")
+			end
+		end;
+	in
+	begin match fa with
+		| FStatic(c,cf) ->
+			mark_class dce c;
+			mark_field dce c cf true;
+		| FInstance(c,_,cf) ->
+			(*mark_instance_field_access c cf;*)
+			mark_class dce c;
+			mark_field dce c cf false
+		| FClosure (Some(c, _), cf) ->
+		 	mark_instance_field_access c cf;
+			do_default()
+		| FClosure _ ->
+			do_default()
+		| _ ->
+			do_default()
+	end;
+	expr dce e;
+
+
 and expr dce e =
 	mark_t dce e.epos e.etype;
 	match e.eexpr with
@@ -475,50 +549,25 @@ and expr dce e =
 		check_and_add_feature dce "binop_>>>";
 		expr dce e1;
 		expr dce e2;
+	| TCall(({ eexpr = TField(ef, fa) } as e2), el ) ->
+		mark_t dce e2.epos e2.etype;
+		expr_field dce ef fa true;
+		List.iter (expr dce) el;
 	| TField(e,fa) ->
-		begin match fa with
-			| FStatic(c,cf) ->
-				mark_class dce c;
-				mark_field dce c cf true;
-			| FInstance(c,_,cf) ->
-				mark_class dce c;
-				mark_field dce c cf false;
-			| _ ->
-
-				let n = field_name fa in
-				(match fa with
-				| FAnon cf ->
-					if Meta.has Meta.Optional cf.cf_meta then begin
-						check_and_add_feature dce "anon_optional_read";
-						check_and_add_feature dce ("anon_optional_read." ^ n);
-					end else begin
-						check_and_add_feature dce "anon_read";
-						check_and_add_feature dce ("anon_read." ^ n);
-					end
-				| FDynamic _ ->
-					check_and_add_feature dce "dynamic_read";
-					check_and_add_feature dce ("dynamic_read." ^ n);
-				| _ -> ());
-				begin match follow e.etype with
-					| TInst(c,_) ->
-						mark_class dce c;
-						field dce c n false;
-					| TAnon a ->
-						(match !(a.a_status) with
-						| Statics c ->
-							mark_class dce c;
-							field dce c n true;
-						| _ -> ())
-
-
-					| _ -> ()
-				end;
-		end;
-		expr dce e;
+		expr_field dce e fa false;
 	| TThrow e ->
 		check_and_add_feature dce "has_throw";
-		to_string dce e.etype;
-		expr dce e
+		expr dce e;
+		(*
+			TODO: Simon, save me! \o
+			This is a hack needed to keep toString field of the actual exception objects
+			that are thrown, but are wrapped into HaxeError before DCE comes into play.
+		*)
+		let e = (match e.eexpr with
+			| TNew({cl_path=(["js";"_Boot"],"HaxeError")}, _, [eoriginal]) -> eoriginal
+			| _ -> e
+		) in
+		to_string dce e.etype
 	| _ ->
 		Type.iter (expr dce) e
 
@@ -560,7 +609,8 @@ let run com main full =
 	let dce = {
 		com = com;
 		full = full;
-		std_dirs = if full then [] else List.map Common.unique_full_path com.std_path;
+		dependent_types = Hashtbl.create 0;
+		std_dirs = if full then [] else List.map Path.unique_full_path com.std_path;
 		debug = Common.defined com Define.DceDebug;
 		added_fields = [];
 		follow_expr = expr;
@@ -599,7 +649,7 @@ let run com main full =
 			begin match c.cl_init with
 				| Some e when keep_class || Meta.has Meta.KeepInit c.cl_meta ->
 					(* create a fake field to deal with our internal logic (issue #3286) *)
-					let cf = mk_field "__init__" e.etype e.epos in
+					let cf = mk_field "__init__" e.etype e.epos null_pos in
 					cf.cf_expr <- Some e;
 					loop true cf
 				| _ ->
@@ -752,4 +802,4 @@ let run com main full =
 		| x :: l -> x :: remove_meta m l
 	in
 	List.iter (fun cf -> cf.cf_meta <- remove_meta Meta.Used cf.cf_meta) dce.marked_fields;
-	List.iter (fun cf -> cf.cf_meta <- remove_meta Meta.MaybeUsed cf.cf_meta) dce.marked_maybe_fields
+	List.iter (fun cf -> cf.cf_meta <- remove_meta Meta.MaybeUsed cf.cf_meta) dce.marked_maybe_fields

+ 69 - 14
src/optimization/filters.ml

@@ -21,6 +21,8 @@ open Ast
 open Common
 open Type
 open Typecore
+open Error
+open Globals
 
 (* PASS 1 begin *)
 
@@ -79,7 +81,7 @@ let rec wrap_js_exceptions com e =
 				let ewrap = Codegen.fcall eterr "wrap" [eerr] t_dynamic e.epos in
 				{ e with eexpr = TThrow ewrap }
 			| _ ->
-				let ewrap = { eerr with eexpr = TNew (cerr,[],[eerr]) } in
+				let ewrap = { eerr with eexpr = TNew (cerr,[],[eerr]); etype = TInst (cerr,[]) } in
 				{ e with eexpr = TThrow ewrap }
 			)
 		| _ ->
@@ -117,6 +119,8 @@ let check_local_vars_init e =
 		| TVar (v,eo) ->
 			begin
 				match eo with
+				| None when Meta.has Meta.InlineConstructorVariable v.v_meta ->
+					()
 				| None ->
 					declared := v.v_id :: !declared;
 					vars := PMap.add v.v_id false !vars
@@ -690,12 +694,12 @@ let remove_extern_fields ctx t = match t with
 	| TClassDecl c ->
 		if not (Common.defined ctx.com Define.DocGen) then begin
 			c.cl_ordered_fields <- List.filter (fun f ->
-				let b = Codegen.is_removable_field ctx f in
+				let b = is_removable_field ctx 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 = Codegen.is_removable_field ctx f in
+				let b = is_removable_field ctx f in
 				if b then c.cl_statics <- PMap.remove f.cf_name c.cl_statics;
 				not b
 			) c.cl_ordered_statics;
@@ -788,7 +792,7 @@ let add_rtti ctx t =
 	in
 	match t with
 	| TClassDecl c when has_rtti c && not (PMap.mem "__rtti" c.cl_statics) ->
-		let f = mk_field "__rtti" ctx.t.tstring c.cl_pos in
+		let f = mk_field "__rtti" ctx.t.tstring c.cl_pos null_pos in
 		let str = Genxml.gen_type_string ctx.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;
@@ -856,7 +860,7 @@ let add_field_inits ctx t =
 					tf_type = ctx.com.basic.tvoid;
 					tf_expr = mk (TBlock el) ctx.com.basic.tvoid c.cl_pos;
 				}) ct c.cl_pos in
-				let ctor = mk_field "new" ct c.cl_pos in
+				let ctor = mk_field "new" ct c.cl_pos null_pos in
 				ctor.cf_kind <- Method MethNormal;
 				{ ctor with cf_expr = Some ce }
 			| Some cf ->
@@ -893,7 +897,7 @@ let add_meta_field ctx t = match t with
 		| None -> ()
 		| Some e ->
 			add_feature ctx.com "has_metadata";
-			let f = mk_field "__meta__" t_dynamic c.cl_pos in
+			let f = mk_field "__meta__" t_dynamic c.cl_pos null_pos in
 			f.cf_expr <- Some e;
 			let can_deal_with_interface_metadata () = match ctx.com.platform with
 				| Flash when Common.defined ctx.com Define.As3 -> false
@@ -903,8 +907,8 @@ let add_meta_field ctx t = match t with
 			if c.cl_interface && not (can_deal_with_interface_metadata()) then begin
 				(* borrowed from gencommon, but I did wash my hands afterwards *)
 				let path = fst c.cl_path,snd c.cl_path ^ "_HxMeta" in
-				let ncls = mk_class c.cl_module path c.cl_pos in
-				let cf = mk_field "__meta__" e.etype e.epos in
+				let ncls = mk_class c.cl_module path c.cl_pos null_pos in
+				let cf = mk_field "__meta__" e.etype e.epos null_pos in
 				cf.cf_expr <- Some e;
 				ncls.cl_statics <- PMap.add "__meta__" cf ncls.cl_statics;
 				ncls.cl_ordered_statics <- cf :: ncls.cl_ordered_statics;
@@ -917,6 +921,55 @@ let add_meta_field ctx t = match t with
 	| _ ->
 		()
 
+(*
+	C# events have special semantics:
+	if we have an @:event var field, there should also be add_<name> and remove_<name> methods,
+	this filter checks for their existence and also adds some metadata for analyzer and C# generator
+*)
+let check_cs_events com t = match t with
+	| TClassDecl cl when not cl.cl_extern ->
+		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 f.cf_public then 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;
+
+				(* type for both add and remove methods *)
+				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
+
+					(* check method signature *)
+					begin
+						try
+							type_eq EqStrict m.cf_type tmeth
+						with Unify_error el ->
+							List.iter (fun e -> com.error (unify_error_msg (print_context()) e) m.cf_pos) el
+					end;
+
+					(*
+						add @:pure(false) to prevent purity inference, because empty add/remove methods
+						have special meaning here and they are always impure
+					*)
+					m.cf_meta <- (Meta.Pure,[EConst(Ident "false"),f.cf_pos],f.cf_pos) :: (Meta.Custom ":cs_event_impl",[],f.cf_pos) :: m.cf_meta;
+
+					(* add @:keep to event methods if the event is kept *)
+					if Meta.has Meta.Keep f.cf_meta && not (Meta.has Meta.Keep m.cf_meta) then
+						m.cf_meta <- (Meta.Keep,[],f.cf_pos) :: m.cf_meta;
+				in
+				process_event_method ("add_" ^ f.cf_name);
+				process_event_method ("remove_" ^ f.cf_name)
+			| _ ->
+				()
+		in
+		List.iter (check cl.cl_fields) cl.cl_ordered_fields;
+		List.iter (check cl.cl_statics) cl.cl_ordered_statics
+	| _ ->
+		()
+
 (* Removes interfaces tagged with @:remove metadata *)
 let check_remove_metadata ctx t = match t with
 	| TClassDecl c ->
@@ -978,10 +1031,10 @@ let run_expression_filters ctx filters t =
 		ctx.curclass <- c;
 		let rec process_field f =
 			(match f.cf_expr with
-			| Some e when not (Codegen.is_removable_field ctx f) ->
-				Codegen.AbstractCast.cast_stack := f :: !Codegen.AbstractCast.cast_stack;
+			| Some e when not (is_removable_field ctx f) ->
+				AbstractCast.cast_stack := f :: !AbstractCast.cast_stack;
 				f.cf_expr <- Some (run e);
-				Codegen.AbstractCast.cast_stack := List.tl !Codegen.AbstractCast.cast_stack;
+				AbstractCast.cast_stack := List.tl !AbstractCast.cast_stack;
 			| _ -> ());
 			List.iter process_field f.cf_overloads
 		in
@@ -1025,18 +1078,20 @@ let iter_expressions fl mt =
 		()
 
 let run com tctx main =
-	if not (Common.defined com Define.NoDeprecationWarnings) then
-		Codegen.DeprecationCheck.run com;
 	let new_types = List.filter (fun t -> not (is_cached t)) com.types in
 	(* PASS 1: general expression filters *)
 	let filters = [
-		Codegen.AbstractCast.handle_abstract_casts tctx;
+		AbstractCast.handle_abstract_casts tctx;
 		Optimizer.inline_constructors tctx;
 		check_local_vars_init;
 		Optimizer.reduce_expression tctx;
 		captured_vars com;
 	] in
 	List.iter (run_expression_filters tctx filters) new_types;
+	(* PASS 1.5: pre-analyzer type filters *)
+	List.iter (fun t ->
+		if com.platform = Cs then check_cs_events tctx.com t;
+	) new_types;
 	if com.platform <> Cross then Analyzer.Run.run_on_types tctx new_types;
 	let filters = [
 		Optimizer.sanitize com;

+ 33 - 256
src/optimization/optimizer.ml

@@ -21,40 +21,13 @@ open Ast
 open Type
 open Common
 open Typecore
+open OptimizerTexpr
+open Error
+open Globals
 
 (* ---------------------------------------------------------------------- *)
 (* API OPTIMIZATIONS *)
 
-let field_call_has_side_effect f e1 fa el =
-	begin match extract_field fa with
-		| Some cf when Meta.has Meta.Pure cf.cf_meta -> ()
-		| _ -> raise Exit
-	end;
-	f e1;
-	List.iter f el
-
-(* tells if an expression causes side effects. This does not account for potential null accesses (fields/arrays/ops) *)
-let has_side_effect e =
-	let rec loop e =
-		match e.eexpr with
-		| TConst _ | TLocal _ | TTypeExpr _ | TFunction _ -> ()
-		| TCall ({ eexpr = TField(_,FStatic({ cl_path = ([],"Std") },{ cf_name = "string" })) },args) -> Type.iter loop e
-		| TCall({eexpr = TField(e1,fa)},el) -> field_call_has_side_effect loop e1 fa el
-		| TNew _ | TCall _ | TBinop ((OpAssignOp _ | OpAssign),_,_) | TUnop ((Increment|Decrement),_,_) -> raise Exit
-		| TReturn _ | TBreak | TContinue | TThrow _ | TCast (_,Some _) -> raise Exit
-		| TArray _ | TEnumParameter _ | TCast (_,None) | TBinop _ | TUnop _ | TParenthesis _ | TMeta _ | TWhile _ | TFor _
-		| TField _ | TIf _ | TTry _ | TSwitch _ | TArrayDecl _ | TBlock _ | TObjectDecl _ | TVar _ -> Type.iter loop e
-	in
-	try
-		loop e; false
-	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 mk_untyped_call name p params =
 	{
 		eexpr = TCall({ eexpr = TLocal(alloc_unbound_var name t_dynamic p); etype = t_dynamic; epos = p }, params);
@@ -231,39 +204,6 @@ let api_inline ctx c field params p = match c.cl_path, field, params with
 	| _ ->
 		api_inline2 ctx.com c field params p
 
-let rec is_affected_type t = match follow t with
-	| TAbstract({a_path = [],("Int" | "Float" | "Bool")},_) -> true
-	| TAbstract({a_path = ["haxe"],("Int64" | "Int32")},_) -> true
-	| TAbstract(a,tl) -> is_affected_type (Abstract.get_underlying_type a tl)
-	| TDynamic _ -> true (* sadly *)
-	| _ -> false
-
-let create_affection_checker () =
-	let modified_locals = Hashtbl.create 0 in
-	let rec might_be_affected e =
-		let rec loop e = match e.eexpr with
-			| TConst _ | TFunction _ | TTypeExpr _ -> ()
-			| TLocal v when Hashtbl.mem modified_locals v.v_id -> raise Exit
-			| TField _ when is_affected_type e.etype -> raise Exit
-			| _ -> Type.iter loop e
-		in
-		try
-			loop e;
-			false
-		with Exit ->
-			true
-	in
-	let rec collect_modified_locals e = match e.eexpr with
-		| TUnop((Increment | Decrement),_,{eexpr = TLocal v}) when is_affected_type v.v_type ->
-			Hashtbl.add modified_locals v.v_id true
-		| TBinop((OpAssign | OpAssignOp _),{eexpr = TLocal v},e2) when is_affected_type v.v_type ->
-			collect_modified_locals e2;
-			Hashtbl.add modified_locals v.v_id true
-		| _ ->
-			Type.iter collect_modified_locals e
-	in
-	might_be_affected,collect_modified_locals
-
 (* ---------------------------------------------------------------------- *)
 (* INLINING *)
 
@@ -400,6 +340,7 @@ let rec type_inline ctx cf f ethis params tret config p ?(self_calling_closure=f
 			had_side_effect := true;
 			l.i_force_temp <- true;
 		end;
+		if l.i_abstract_this then l.i_subst.v_extra <- Some ([],Some e);
 		l, e
 	) (ethis :: loop params f.tf_args true) ((vthis,None) :: f.tf_args) in
 	List.iter (fun (l,e) ->
@@ -432,7 +373,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);
 			(* never inline a function which contain a delayed macro because its bound
 				to its variables and not the calling method *)
-			if v.v_name = "__dollar__delay_call" then cancel_inlining := true;
+			if v.v_name = "$__delayed_call__" then cancel_inlining := true;
 			let e = { e with eexpr = TLocal l.i_subst } in
 			if l.i_abstract_this then mk (TCast(e,None)) v.v_type e.epos else e
 		| TConst TThis ->
@@ -668,6 +609,7 @@ let rec type_inline ctx cf f ethis params tret config p ?(self_calling_closure=f
 			| _ -> e
 		in
 		let e = List.fold_left inline_meta e cf.cf_meta in
+		let e = Display.Diagnostics.secure_generated_code ctx e in
 		(* we need to replace type-parameters that were used in the expression *)
 		if not has_params then
 			Some e
@@ -943,8 +885,8 @@ let standard_precedence op =
 
 let rec need_parent e =
 	match e.eexpr with
-	| TConst _ | TLocal _ | TArray _ | TField _ | TEnumParameter _ | TParenthesis _ | TMeta _ | TCall _ | TNew _ | TTypeExpr _ | TObjectDecl _ | TArrayDecl _ -> false
-	| TCast (e,None) -> need_parent e
+	| TConst _ | TLocal _ | TArray _ | TField _ | TEnumParameter _ | TParenthesis _ | TCall _ | TNew _ | TTypeExpr _ | TObjectDecl _ | TArrayDecl _ -> false
+	| TCast (e,None) | TMeta(_,e) -> need_parent e
 	| TCast _ | TThrow _ | TReturn _ | TTry _ | TSwitch _ | TFor _ | TIf _ | TWhile _ | TBinop _ | TContinue | TBreak
 	| TBlock _ | TVar _ | TFunction _ | TUnop _ -> true
 
@@ -999,7 +941,7 @@ let sanitize_expr com e =
 			match ee.eexpr with
 			| TBinop (op2,_,_) -> if left then not (swap op2 op) else swap op op2
 			| TIf _ -> if left then not (swap (OpAssignOp OpAssign) op) else swap op (OpAssignOp OpAssign)
-			| TCast (e,None) -> loop e left
+			| TCast (e,None) | TMeta (_,e) -> loop e left
 			| _ -> false
 		in
 		let e1 = if loop e1 true then parent e1 else e1 in
@@ -1009,7 +951,7 @@ let sanitize_expr com e =
 		let rec loop ee =
 			match ee.eexpr with
 			| TBinop _ | TIf _ | TUnop _ -> parent e1
-			| TCast (e,None) -> loop e
+			| TCast (e,None) | TMeta (_, e) -> loop e
 			| _ -> e1
 		in
 		{ e with eexpr = TUnop (op,mode,loop e1)}
@@ -1090,181 +1032,6 @@ let rec sanitize com e =
 (* ---------------------------------------------------------------------- *)
 (* REDUCE *)
 
-let optimize_binop e op e1 e2 =
-	let is_float t =
-		match follow t with
-		| TAbstract({ a_path = [],"Float" },_) -> true
-		| _ -> false
-	in
-	let is_numeric t =
-		match follow t with
-		| TAbstract({ a_path = [],("Float"|"Int") },_) -> true
-		| _ -> false
-	in
-	let check_float op f1 f2 =
-		let f = op f1 f2 in
-		let fstr = float_repres f in
-		if (match classify_float f with FP_nan | FP_infinite -> false | _ -> float_of_string fstr = f) then { e with eexpr = TConst (TFloat fstr) } else e
-	in
-	(match e1.eexpr, e2.eexpr with
-	| TConst (TInt 0l) , _ when op = OpAdd && is_numeric e2.etype -> e2
-	| TConst (TInt 1l) , _ when op = OpMult -> e2
-	| TConst (TFloat v) , _ when op = OpAdd && float_of_string v = 0. && is_float e2.etype -> e2
-	| TConst (TFloat v) , _ when op = OpMult && float_of_string v = 1. && is_float e2.etype -> e2
-	| _ , TConst (TInt 0l) when (match op with OpAdd -> is_numeric e1.etype | OpSub | OpShr | OpShl -> true | _ -> false) -> e1 (* bits operations might cause overflow *)
-	| _ , TConst (TInt 1l) when op = OpMult -> e1
-	| _ , TConst (TFloat v) when (match op with OpAdd | OpSub -> float_of_string v = 0. && is_float e1.etype | _ -> false) -> e1 (* bits operations might cause overflow *)
-	| _ , TConst (TFloat v) when op = OpMult && float_of_string v = 1. && is_float e1.etype -> e1
-	| TConst TNull, TConst TNull ->
-		(match op with
-		| OpEq -> { e with eexpr = TConst (TBool true) }
-		| OpNotEq -> { e with eexpr = TConst (TBool false) }
-		| _ -> e)
-	| TFunction _, TConst TNull ->
-		(match op with
-		| OpEq -> { e with eexpr = TConst (TBool false) }
-		| OpNotEq -> { e with eexpr = TConst (TBool true) }
-		| _ -> e)
-	| TConst TNull, TFunction _ ->
-		(match op with
-		| OpEq -> { e with eexpr = TConst (TBool false) }
-		| OpNotEq -> { e with eexpr = TConst (TBool true) }
-		| _ -> e)
-	| TConst (TInt a), TConst (TInt b) ->
-		let opt f = try { e with eexpr = TConst (TInt (f a b)) } with Exit -> e in
-		let check_overflow f =
-			opt (fun a b ->
-				let v = f (Int64.of_int32 a) (Int64.of_int32 b) in
-				let iv = Int64.to_int32 v in
-				if Int64.compare (Int64.of_int32 iv) v <> 0 then raise Exit;
-				iv
-			)
-		in
-		let ebool t =
-			{ e with eexpr = TConst (TBool (t (Int32.compare a b) 0)) }
-		in
-		(match op with
-		| OpAdd -> check_overflow Int64.add
-		| OpSub -> check_overflow Int64.sub
-		| OpMult -> check_overflow Int64.mul
-		| OpDiv -> check_float ( /. ) (Int32.to_float a) (Int32.to_float b)
-		| 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))
-		| OpEq -> ebool (=)
-		| OpNotEq -> ebool (<>)
-		| OpGt -> ebool (>)
-		| OpGte -> ebool (>=)
-		| OpLt -> ebool (<)
-		| OpLte -> ebool (<=)
-		| _ -> e)
-	| TConst ((TFloat _ | TInt _) as ca), TConst ((TFloat _ | TInt _) as cb) ->
-		let fa = (match ca with
-			| TFloat a -> float_of_string a
-			| TInt a -> Int32.to_float a
-			| _ -> assert false
-		) in
-		let fb = (match cb with
-			| TFloat b -> float_of_string b
-			| TInt b -> Int32.to_float b
-			| _ -> assert false
-		) in
-		let fop op = check_float op fa fb in
-		let ebool t =
-			{ e with eexpr = TConst (TBool (t (compare fa fb) 0)) }
-		in
-		(match op with
-		| OpAdd -> fop (+.)
-		| OpDiv -> fop (/.)
-		| OpSub -> fop (-.)
-		| OpMult -> fop ( *. )
-		| OpEq -> ebool (=)
-		| OpNotEq -> ebool (<>)
-		| OpGt -> ebool (>)
-		| OpGte -> ebool (>=)
-		| OpLt -> ebool (<)
-		| OpLte -> ebool (<=)
-		| _ -> e)
-	| TConst (TString ""),TConst (TString s) | TConst (TString s),TConst (TString "") when op = OpAdd ->
-		{e with eexpr = TConst (TString s)}
-	| TConst (TBool a), TConst (TBool b) ->
-		let ebool f =
-			{ e with eexpr = TConst (TBool (f a b)) }
-		in
-		(match op with
-		| OpEq -> ebool (=)
-		| OpNotEq -> ebool (<>)
-		| OpBoolAnd -> ebool (&&)
-		| OpBoolOr -> ebool (||)
-		| _ -> e)
-	| TConst a, TConst b when op = OpEq || op = OpNotEq ->
-		let ebool b =
-			{ e with eexpr = TConst (TBool (if op = OpEq then b else not b)) }
-		in
-		(match a, b with
-		| TInt a, TFloat b | TFloat b, TInt a -> ebool (Int32.to_float a = float_of_string b)
-		| _ -> ebool (a = b))
-	| TConst (TBool a), _ ->
-		(match op with
-		| OpBoolAnd -> if a then e2 else { e with eexpr = TConst (TBool false) }
-		| OpBoolOr -> if a then { e with eexpr = TConst (TBool true) } else e2
-		| _ -> e)
-	| _ , TConst (TBool a) ->
-		(match op with
-		| OpBoolAnd when a  -> e1
-		| OpBoolOr when not a -> e1
-		| _ -> e)
-	| TField (_,FEnum (e1,f1)), TField (_,FEnum (e2,f2)) when e1 == e2 ->
-		(match op with
-		| OpEq -> { e with eexpr = TConst (TBool (f1 == f2)) }
-		| OpNotEq -> { e with eexpr = TConst (TBool (f1 != f2)) }
-		| _ -> e)
-	| _, TCall ({ eexpr = TField (_,FEnum _) },_) | TCall ({ eexpr = TField (_,FEnum _) },_), _ ->
-		(match op with
-		| OpAssign -> e
-		| _ ->
-			error "You cannot directly compare enums with arguments. Use either 'switch' or 'Type.enumEq'" e.epos)
-	| _ ->
-		e)
-
-let optimize_unop e op flag esub =
-	let is_int t = match follow t with
-		| TAbstract({a_path = [],"Int"},_) -> true
-		| _ -> false
-	in
-	match op, esub.eexpr with
-		| Not, (TConst (TBool f) | TParenthesis({eexpr = TConst (TBool f)})) -> { e with eexpr = TConst (TBool (not f)) }
-		| Not, (TBinop(op,e1,e2) | TParenthesis({eexpr = TBinop(op,e1,e2)})) ->
-			begin
-				let is_int = is_int e1.etype && is_int e2.etype in
-				try
-					let op = match is_int, op with
-						| true, OpGt -> OpLte
-						| true, OpGte -> OpLt
-						| true, OpLt -> OpGte
-						| true, OpLte -> OpGt
-						| _, OpEq -> OpNotEq
-						| _, OpNotEq -> OpEq
-						| _ -> raise Exit
-					in
-					{e with eexpr = TBinop(op,e1,e2)}
-				with Exit ->
-					e
-			end
-		| Neg, TConst (TInt i) -> { e with eexpr = TConst (TInt (Int32.neg i)) }
-		| NegBits, TConst (TInt i) -> { e with eexpr = TConst (TInt (Int32.lognot i)) }
-		| Neg, TConst (TFloat f) ->
-			let v = 0. -. float_of_string f in
-			let vstr = float_repres v in
-			if float_of_string vstr = v then
-				{ e with eexpr = TConst (TFloat vstr) }
-			else
-				e
-		| _ -> e
-
 let reduce_control_flow ctx 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)
@@ -1305,7 +1072,7 @@ let rec reduce_loop ctx e =
 		| None -> reduce_expr ctx e
 		| Some e -> reduce_loop ctx e)
 	| TCall ({ eexpr = TFunction func } as ef,el) ->
-		let cf = mk_field "" ef.etype e.epos in
+		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 | _ -> assert false) 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
@@ -1437,6 +1204,7 @@ let inline_constructors ctx e =
 	let add_field_var v s t =
 		let ii = IntMap.find v.v_id !vars in
 		let v' = alloc_var (Printf.sprintf "%s_%s" v.v_name s) t v.v_pos in
+		v'.v_meta <- (Meta.InlineConstructorVariable,[],v.v_pos) :: v'.v_meta;
 		ii.ii_fields <- PMap.add s v' ii.ii_fields;
 		v'
 	in
@@ -1462,8 +1230,8 @@ let inline_constructors ctx e =
 						let ev = mk (TLocal v) v.v_type e.epos in
 						let el_init = List.fold_left (fun acc cf -> match cf.cf_kind,cf.cf_expr with
 							| Var _,Some e ->
-								let ef = mk (TField(ev,FInstance(c,tl,cf))) e.etype e.epos in
-								let e = mk (TBinop(OpAssign,ef,e)) e.etype e.epos in
+								let ef = mk (TField(ev,FInstance(c,tl,cf))) cf.cf_type e.epos in
+								let e = mk (TBinop(OpAssign,ef,e)) cf.cf_type e.epos in
 								e :: acc
 							| _ -> acc
 						) el_init c.cl_ordered_fields in
@@ -1510,7 +1278,16 @@ let inline_constructors ctx e =
 			find_locals e2
 		| TField({eexpr = TLocal v},fa) when v.v_id < 0 ->
 			begin match extract_field fa with
-			| Some {cf_kind = Var _} -> ()
+			| Some ({cf_kind = Var _} as cf) ->
+				(* Arrays are not supposed to have public var fields, besides "length" (which we handle when inlining),
+				   however, its inlined methods may generate access to private implementation fields (such as internal
+				   native array), in this case we have to cancel inlining.
+				*)
+				if cf.cf_name <> "length" then
+					begin match (IntMap.find v.v_id !vars).ii_kind with
+					| IKArray _ -> cancel v e.epos
+					| _ -> ()
+					end
 			| _ -> cancel v e.epos
 			end
 		| TArray({eexpr = TLocal v},{eexpr = TConst (TInt i)}) when v.v_id < 0 ->
@@ -1545,7 +1322,7 @@ let inline_constructors ctx e =
 		 try
 			let v = get_field_var v name in
 			let e1 = mk (TLocal v) t p in
-			{e with eexpr = TBinop(OpAssign,e1,e2)}
+			mk (TBinop(OpAssign,e1,e2)) e1.etype p
 		with Not_found ->
 			let v = add_field_var v name t in
 			mk (TVar(v,Some e2)) ctx.t.tvoid e.epos
@@ -1672,7 +1449,7 @@ let optimize_completion_expr e =
 			let el = List.fold_left (fun acc e ->
 				typing_side_effect := false;
 				let e = loop e in
-				if !typing_side_effect then begin told := true; e :: acc end else acc
+				if !typing_side_effect || Display.is_display_position (pos e) then begin told := true; e :: acc end else acc
 			) [] el in
 			old();
 			typing_side_effect := !told;
@@ -1704,9 +1481,9 @@ let optimize_completion_expr e =
 			map e
 		| ESwitch (e,cases,def) ->
 			let e = loop e in
-			let cases = List.map (fun (el,eg,eo) -> match eo with
+			let cases = List.map (fun (el,eg,eo,p) -> match eo with
 				| None ->
-					el,eg,eo
+					el,eg,eo,p
 				| Some e ->
 					let el = List.map loop el in
 					let old = save() in
@@ -1722,22 +1499,22 @@ let optimize_completion_expr e =
 					) el;
 					let e = loop e in
 					old();
-					el, eg, Some e
+					el, eg, Some e, p
 			) cases in
 			let def = match def with
 				| None -> None
-				| Some None -> Some None
-				| Some (Some e) -> Some (Some (loop e))
+				| Some (None,p) -> Some (None,p)
+				| Some (Some e,p) -> Some (Some (loop e),p)
 			in
 			(ESwitch (e,cases,def),p)
 		| ETry (et,cl) ->
 			let et = loop et in
-			let cl = List.map (fun ((n,pn),(t,pt),e) ->
+			let cl = List.map (fun ((n,pn),(t,pt),e,p) ->
 				let old = save() in
 				decl n (Some t) None;
 				let e = loop e in
 				old();
-				(n,pn), (t,pt), e
+				(n,pn), (t,pt), e, p
 			) cl in
 			(ETry (et,cl),p)
 		| EDisplay (s,call) ->

+ 306 - 0
src/optimization/optimizerTexpr.ml

@@ -0,0 +1,306 @@
+open Ast
+open Type
+open Error
+open Globals
+
+(*
+	PurityState represents whether or not something has a side-effect. Unless otherwise stated
+	by using `@:pure` (equivalent to `@:pure(true)`) or `@:pure(false)`, fields are originally
+	supposed to be "maybe pure". Once all types and fields are known, this is refined by
+	AnalyzerTexpr.Purity.
+
+	There's a special case for fields that override a parent class field or implement an
+	interface field: If the overridden/implemented field is explicitly marked as pure,
+	the type loader marks the overriding/implementing as "expected pure". If during purity
+	inference this assumption does not hold, an error is shown.
+*)
+module PurityState = struct
+	type t =
+		| Pure
+		| Impure
+		| MaybePure
+		| ExpectPure of pos
+
+	let get_purity_from_meta meta =
+		try
+			begin match Meta.get Meta.Pure meta with
+			| (_,[EConst(Ident s),p],_) ->
+				begin match s with
+				| "true" -> Pure
+				| "false" -> Impure
+				| "expect" -> ExpectPure p
+				| _ -> error ("Unsupported purity value " ^ s ^ ", expected true or false") p
+				end
+			| (_,[],_) ->
+				Pure
+			| (_,_,p) ->
+				error "Unsupported purity value" p
+			end
+		with Not_found ->
+			MaybePure
+
+	let get_purity c cf = match get_purity_from_meta cf.cf_meta with
+		| Pure -> Pure
+		| Impure -> Impure
+		| ExpectPure p -> ExpectPure p
+		| _ -> get_purity_from_meta c.cl_meta
+
+	let is_pure c cf = get_purity c cf = Pure
+
+	let is_pure_field_access fa = match fa with
+		| FInstance(c,_,cf) | FClosure(Some(c,_),cf) | FStatic(c,cf) -> is_pure c cf
+		| FAnon cf | FClosure(None,cf) -> (get_purity_from_meta cf.cf_meta = Pure)
+		| FEnum _ -> true
+		| FDynamic _ -> false
+
+	let to_string = function
+		| Pure -> "pure"
+		| Impure -> "impure"
+		| MaybePure -> "maybe"
+		| ExpectPure _ -> "expect"
+end
+
+(* tells if an expression causes side effects. This does not account for potential null accesses (fields/arrays/ops) *)
+let has_side_effect e =
+	let rec loop e =
+		match e.eexpr with
+		| TConst _ | TLocal _ | TTypeExpr _ | TFunction _ -> ()
+		| TCall({eexpr = TField(e1,fa)},el) when PurityState.is_pure_field_access fa -> loop e1; List.iter loop el
+		| TNew(c,_,el) when (match c.cl_constructor with Some cf when PurityState.is_pure c cf -> true | _ -> false) -> List.iter loop el
+		| TNew _ | TCall _ | TBinop ((OpAssignOp _ | OpAssign),_,_) | TUnop ((Increment|Decrement),_,_) -> raise Exit
+		| TReturn _ | TBreak | TContinue | TThrow _ | TCast (_,Some _) -> raise Exit
+		| TArray _ | TEnumParameter _ | TCast (_,None) | TBinop _ | TUnop _ | TParenthesis _ | TMeta _ | TWhile _ | TFor _
+		| TField _ | TIf _ | TTry _ | TSwitch _ | TArrayDecl _ | TBlock _ | TObjectDecl _ | TVar _ -> Type.iter loop e
+	in
+	try
+		loop e; false
+	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
+	| FDynamic _ ->
+		false
+	| FAnon {cf_kind = Var {v_write = AccNo}} when (match e.eexpr with TLocal v when is_unbound v -> true | _ -> false) -> true
+	| FInstance (c,_,cf) | FStatic (c,cf) | FClosure (Some(c,_),cf) ->
+		begin match cf.cf_kind with
+			| Method MethDynamic -> false
+			| Method _ -> true
+			| Var {v_write = AccNever} when not c.cl_interface -> true
+			| _ -> false
+		end
+	| FAnon cf | FClosure(None,cf) ->
+		begin match cf.cf_kind with
+			| Method MethDynamic -> false
+			| Method _ -> true
+			| _ -> false
+		end
+
+let create_affection_checker () =
+	let modified_locals = Hashtbl.create 0 in
+	let rec might_be_affected e =
+		let rec loop e = match e.eexpr with
+			| TConst _ | TFunction _ | TTypeExpr _ -> ()
+			| TLocal v when Hashtbl.mem modified_locals v.v_id -> raise Exit
+			| TField(e1,fa) when not (is_read_only_field_access e1 fa) -> raise Exit
+			| TCall _ | TNew _ -> raise Exit
+			| _ -> Type.iter loop e
+		in
+		try
+			loop e;
+			false
+		with Exit ->
+			true
+	in
+	let rec collect_modified_locals e = match e.eexpr with
+		| TUnop((Increment | Decrement),_,{eexpr = TLocal v}) ->
+			Hashtbl.add modified_locals v.v_id true
+		| TBinop((OpAssign | OpAssignOp _),{eexpr = TLocal v},e2) ->
+			collect_modified_locals e2;
+			Hashtbl.add modified_locals v.v_id true
+		| _ ->
+			Type.iter collect_modified_locals e
+	in
+	might_be_affected,collect_modified_locals
+
+let optimize_binop e op e1 e2 =
+	let is_float t =
+		match follow t with
+		| TAbstract({ a_path = [],"Float" },_) -> true
+		| _ -> false
+	in
+	let is_numeric t =
+		match follow t with
+		| TAbstract({ a_path = [],("Float"|"Int") },_) -> true
+		| _ -> false
+	in
+	let check_float op f1 f2 =
+		let f = op f1 f2 in
+		let fstr = Common.float_repres f in
+		if (match classify_float f with FP_nan | FP_infinite -> false | _ -> float_of_string fstr = f) then { e with eexpr = TConst (TFloat fstr) } else e
+	in
+	(match e1.eexpr, e2.eexpr with
+	| TConst (TInt 0l) , _ when op = OpAdd && is_numeric e2.etype -> e2
+	| TConst (TInt 1l) , _ when op = OpMult -> e2
+	| TConst (TFloat v) , _ when op = OpAdd && float_of_string v = 0. && is_float e2.etype -> e2
+	| TConst (TFloat v) , _ when op = OpMult && float_of_string v = 1. && is_float e2.etype -> e2
+	| _ , TConst (TInt 0l) when (match op with OpAdd -> is_numeric e1.etype | OpSub | OpShr | OpShl -> true | _ -> false) -> e1 (* bits operations might cause overflow *)
+	| _ , TConst (TInt 1l) when op = OpMult -> e1
+	| _ , TConst (TFloat v) when (match op with OpAdd | OpSub -> float_of_string v = 0. && is_float e1.etype | _ -> false) -> e1 (* bits operations might cause overflow *)
+	| _ , TConst (TFloat v) when op = OpMult && float_of_string v = 1. && is_float e1.etype -> e1
+	| TConst TNull, TConst TNull ->
+		(match op with
+		| OpEq -> { e with eexpr = TConst (TBool true) }
+		| OpNotEq -> { e with eexpr = TConst (TBool false) }
+		| _ -> e)
+	| TFunction _, TConst TNull ->
+		(match op with
+		| OpEq -> { e with eexpr = TConst (TBool false) }
+		| OpNotEq -> { e with eexpr = TConst (TBool true) }
+		| _ -> e)
+	| TConst TNull, TFunction _ ->
+		(match op with
+		| OpEq -> { e with eexpr = TConst (TBool false) }
+		| OpNotEq -> { e with eexpr = TConst (TBool true) }
+		| _ -> e)
+	| TConst (TInt a), TConst (TInt b) ->
+		let opt f = try { e with eexpr = TConst (TInt (f a b)) } with Exit -> e in
+		let check_overflow f =
+			opt (fun a b ->
+				let v = f (Int64.of_int32 a) (Int64.of_int32 b) in
+				let iv = Int64.to_int32 v in
+				if Int64.compare (Int64.of_int32 iv) v <> 0 then raise Exit;
+				iv
+			)
+		in
+		let ebool t =
+			{ e with eexpr = TConst (TBool (t (Int32.compare a b) 0)) }
+		in
+		(match op with
+		| OpAdd -> check_overflow Int64.add
+		| OpSub -> check_overflow Int64.sub
+		| OpMult -> check_overflow Int64.mul
+		| OpDiv -> check_float ( /. ) (Int32.to_float a) (Int32.to_float b)
+		| 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))
+		| OpEq -> ebool (=)
+		| OpNotEq -> ebool (<>)
+		| OpGt -> ebool (>)
+		| OpGte -> ebool (>=)
+		| OpLt -> ebool (<)
+		| OpLte -> ebool (<=)
+		| _ -> e)
+	| TConst ((TFloat _ | TInt _) as ca), TConst ((TFloat _ | TInt _) as cb) ->
+		let fa = (match ca with
+			| TFloat a -> float_of_string a
+			| TInt a -> Int32.to_float a
+			| _ -> assert false
+		) in
+		let fb = (match cb with
+			| TFloat b -> float_of_string b
+			| TInt b -> Int32.to_float b
+			| _ -> assert false
+		) in
+		let fop op = check_float op fa fb in
+		let ebool t =
+			{ e with eexpr = TConst (TBool (t (compare fa fb) 0)) }
+		in
+		(match op with
+		| OpAdd -> fop (+.)
+		| OpDiv -> fop (/.)
+		| OpSub -> fop (-.)
+		| OpMult -> fop ( *. )
+		| OpEq -> ebool (=)
+		| OpNotEq -> ebool (<>)
+		| OpGt -> ebool (>)
+		| OpGte -> ebool (>=)
+		| OpLt -> ebool (<)
+		| OpLte -> ebool (<=)
+		| _ -> e)
+	| TConst (TString ""),TConst (TString s) | TConst (TString s),TConst (TString "") when op = OpAdd ->
+		{e with eexpr = TConst (TString s)}
+	| TConst (TBool a), TConst (TBool b) ->
+		let ebool f =
+			{ e with eexpr = TConst (TBool (f a b)) }
+		in
+		(match op with
+		| OpEq -> ebool (=)
+		| OpNotEq -> ebool (<>)
+		| OpBoolAnd -> ebool (&&)
+		| OpBoolOr -> ebool (||)
+		| _ -> e)
+	| TConst a, TConst b when op = OpEq || op = OpNotEq ->
+		let ebool b =
+			{ e with eexpr = TConst (TBool (if op = OpEq then b else not b)) }
+		in
+		(match a, b with
+		| TInt a, TFloat b | TFloat b, TInt a -> ebool (Int32.to_float a = float_of_string b)
+		| _ -> ebool (a = b))
+	| TConst (TBool a), _ ->
+		(match op with
+		| OpBoolAnd -> if a then e2 else { e with eexpr = TConst (TBool false) }
+		| OpBoolOr -> if a then { e with eexpr = TConst (TBool true) } else e2
+		| _ -> e)
+	| _ , TConst (TBool a) ->
+		(match op with
+		| OpBoolAnd when a  -> e1
+		| OpBoolOr when not a -> e1
+		| _ -> e)
+	| TField (_,FEnum (e1,f1)), TField (_,FEnum (e2,f2)) when e1 == e2 ->
+		(match op with
+		| OpEq -> { e with eexpr = TConst (TBool (f1 == f2)) }
+		| OpNotEq -> { e with eexpr = TConst (TBool (f1 != f2)) }
+		| _ -> e)
+	| _, TCall ({ eexpr = TField (_,FEnum _) },_) | TCall ({ eexpr = TField (_,FEnum _) },_), _ ->
+		(match op with
+		| OpAssign -> e
+		| _ ->
+			error "You cannot directly compare enums with arguments. Use either 'switch' or 'Type.enumEq'" e.epos)
+	| _ ->
+		e)
+
+let optimize_unop e op flag esub =
+	let is_int t = match follow t with
+		| TAbstract({a_path = [],"Int"},_) -> true
+		| _ -> false
+	in
+	match op, esub.eexpr with
+		| Not, (TConst (TBool f) | TParenthesis({eexpr = TConst (TBool f)})) -> { e with eexpr = TConst (TBool (not f)) }
+		| Not, (TBinop(op,e1,e2) | TParenthesis({eexpr = TBinop(op,e1,e2)})) ->
+			begin
+				let is_int = is_int e1.etype && is_int e2.etype in
+				try
+					let op = match is_int, op with
+						| true, OpGt -> OpLte
+						| true, OpGte -> OpLt
+						| true, OpLt -> OpGte
+						| true, OpLte -> OpGt
+						| _, OpEq -> OpNotEq
+						| _, OpNotEq -> OpEq
+						| _ -> raise Exit
+					in
+					{e with eexpr = TBinop(op,e1,e2)}
+				with Exit ->
+					e
+			end
+		| Neg, TConst (TInt i) -> { e with eexpr = TConst (TInt (Int32.neg i)) }
+		| NegBits, TConst (TInt i) -> { e with eexpr = TConst (TInt (Int32.lognot i)) }
+		| Neg, TConst (TFloat f) ->
+			let v = 0. -. float_of_string f in
+			let vstr = Common.float_repres v in
+			if float_of_string vstr = v then
+				{ e with eexpr = TConst (TFloat vstr) }
+			else
+				e
+		| _ -> e

+ 148 - 0
src/path.ml

@@ -0,0 +1,148 @@
+let get_path_parts f =
+	(*
+		this function is quite weird: it tries to determine whether the given
+		argument is a .hx file path with slashes or a dotted module path and
+		based on that it returns path "parts", which are basically a list of
+		either folders or packages (which are folders too) appended by the module name
+
+		TODO: i started doubting my sanity while writing this comment, let's somehow
+		refactor this stuff so it doesn't mix up file and module paths and doesn't introduce
+		the weird "path part" entity.
+	*)
+	let l = String.length f in
+	if l > 3 && (String.sub f (l-3) 3) = ".hx" then
+		let f = String.sub f 0 (l-3) in (* strip the .hx *)
+		ExtString.String.nsplit (String.concat "/" (ExtString.String.nsplit f "\\")) "/" (* TODO: wouldn't it be faster to Str.split here? *)
+	else
+		ExtString.String.nsplit f "."
+
+let parse_path f =
+	let cl = get_path_parts f in
+	let error msg =
+		let msg = "Could not process argument " ^ f ^ "\n" ^ msg in
+		failwith msg
+	in
+	let invalid_char x =
+		for i = 1 to String.length x - 1 do
+			match x.[i] with
+			| 'A'..'Z' | 'a'..'z' | '0'..'9' | '_' -> ()
+			| c -> error ("invalid character: " ^ (String.make 1 c))
+		done
+	in
+	let rec loop = function
+		| [] ->
+			error "empty part"
+		| [x] ->
+			invalid_char x;
+			[],x
+		| x :: l ->
+			if String.length x = 0 then
+				error "empty part"
+			else if x.[0] < 'a' || x.[0] > 'z' then
+				error "Package name must start with a lower case character";
+			invalid_char x;
+			let path,name = loop l in
+			x :: path,name
+	in
+	loop cl
+
+let starts_uppercase x =
+	x.[0] = '_' || (x.[0] >= 'A' && x.[0] <= 'Z')
+
+let check_uppercase x =
+	if String.length x = 0 then
+		failwith "empty part"
+	else if not (starts_uppercase x) then
+		failwith "Class name must start with uppercase character"
+
+let parse_type_path s =
+	let pack,name = parse_path s in
+	check_uppercase name;
+	pack,name
+
+let path_regex = Str.regexp "[/\\]+"
+let normalize_path path =
+	let rec normalize acc m =
+		match m with
+		| [] ->
+			List.rev acc
+		| Str.Text "." :: Str.Delim _ :: tl when acc = [] ->
+			normalize [] tl
+		| Str.Text ".." :: Str.Delim _ :: tl ->
+			(match acc with
+			| [] -> raise Exit
+			| _ :: acc -> normalize acc tl)
+		| Str.Text t :: Str.Delim _ :: tl ->
+			normalize (t :: acc) tl
+		| Str.Delim _ :: tl ->
+			normalize ("" :: acc) tl
+		| Str.Text t :: [] ->
+			List.rev (t :: acc)
+		| Str.Text _ :: Str.Text  _ :: _ ->
+			assert false
+	in
+	String.concat "/" (normalize [] (Str.full_split path_regex path))
+
+let path_sep = if Globals.is_windows then "\\" else "/"
+
+(** Returns absolute path. Doesn't fix path case on Windows. *)
+let get_full_path f = try Extc.get_full_path f with _ -> f
+
+(** Returns absolute path (on Windows ensures proper case with drive letter upper-cased)
+    Use for returning positions from IDE support functions *)
+let get_real_path =
+	if Globals.is_windows then
+		(fun p -> try Extc.get_real_path p with _ -> p)
+	else
+		get_full_path
+
+(** Returns absolute path guaranteed to be the same for different letter case.
+    Use where equality comparison is required, lowercases the path on Windows *)
+let unique_full_path =
+	if Globals.is_windows then
+		(fun f -> String.lowercase (get_full_path f))
+	else
+		get_full_path
+
+let add_trailing_slash p =
+	let l = String.length p in
+	if l = 0 then
+		"./"
+	else match p.[l-1] with
+		| '\\' | '/' -> p
+		| _ -> p ^ "/"
+
+let rec remove_trailing_slash p =
+	let l = String.length p in
+	if l = 0 then
+		"./"
+	else match p.[l-1] with
+		| '\\' | '/' -> remove_trailing_slash (String.sub p 0 (l - 1))
+		| _ -> p
+
+open Globals
+
+let find_directories target recursive paths =
+	let target_dirs = List.map platform_name platforms in
+	let rec loop acc dir =
+		try
+			let entries = Sys.readdir dir in
+			Array.fold_left (fun acc file ->
+				match file with
+					| "." | ".." ->
+						acc
+					| _ when Sys.is_directory (dir ^ file) && file.[0] >= 'a' && file.[0] <= 'z' ->
+						if List.mem file target_dirs && file <> target then
+							acc
+						else begin
+							let full = (dir ^ file) in
+							if recursive then loop (full :: acc) (full ^ "/")
+							else full :: acc
+						end
+					| _ ->
+						acc
+			) acc entries;
+		with Sys_error _ ->
+			acc
+	in
+	List.fold_left (fun acc dir -> loop acc dir) [] paths

+ 634 - 0
src/server.ml

@@ -0,0 +1,634 @@
+open Printf
+open Globals
+open Ast
+open Common
+open Common.DisplayMode
+open Type
+open DisplayOutput
+
+exception Dirty of module_def
+
+let measure_times = ref false
+let prompt = ref false
+let start_time = ref (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 : string list;
+	mutable has_next : bool;
+	mutable has_error : bool;
+}
+
+let report_times print =
+	let tot = ref 0. in
+	Hashtbl.iter (fun _ t -> tot := !tot +. t.total) Common.htimers;
+	if !tot > 0. then begin
+		let buckets = Hashtbl.create 0 in
+		let add id time calls =
+			try
+				let time',calls' = Hashtbl.find buckets id in
+				Hashtbl.replace buckets id (time' +. time,calls' + calls)
+			with Not_found ->
+				Hashtbl.add buckets id (time,calls)
+		in
+		Hashtbl.iter (fun _ t ->
+			let rec loop acc ids = match ids with
+				| id :: ids ->
+					add (List.rev (id :: acc)) t.total t.calls;
+					loop (id :: acc) ids
+				| [] ->
+					()
+			in
+			loop [] t.id
+		) Common.htimers;
+		let max_name = ref 0 in
+		let max_calls = ref 0 in
+		let timers = Hashtbl.fold (fun id t acc ->
+			let name,indent = match List.rev id with
+				| [] -> assert false
+				| name :: l -> name,(String.make (List.length l * 2) ' ')
+			in
+			let name,info = try
+				let i = String.rindex name '.' in
+				String.sub name (i + 1) (String.length name - i - 1),String.sub name 0 i
+			with Not_found ->
+				name,""
+			in
+			let name = indent ^ name in
+			if String.length name > !max_name then max_name := String.length name;
+			if snd t > !max_calls then max_calls := snd t;
+			(id,name,info,t) :: acc
+		) buckets [] in
+		let max_calls = String.length (string_of_int !max_calls) in
+		print (Printf.sprintf "%-*s | %7s |   %% | %*s | info" !max_name "name" "time(s)" max_calls "#");
+		let sep = String.make (!max_name + max_calls + 21) '-' in
+		print sep;
+		let timers = List.sort (fun (id1,_,_,_) (id2,_,_,_) -> compare id1 id2) timers in
+		let print_timer id name info (time,calls) =
+			print (Printf.sprintf "%-*s | %7.3f | %3.0f | %*i | %s" !max_name name time (time *. 100. /. !tot) max_calls calls info)
+		in
+		List.iter (fun (id,name,info,t) -> print_timer id name info t) timers;
+		print sep;
+		print_timer ["total"] "total" "" (!tot,0)
+	end
+
+let default_flush ctx =
+	List.iter prerr_endline (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 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 (String.length str)
+
+let rec wait_loop process_params verbose accept =
+	Sys.catch_break false;
+	let has_parse_error = ref false in
+	let cs = CompilationServer.create () in
+	let sign_string com =
+		let sign = get_signature com in
+		let	sign_id =
+			try
+				CompilationServer.get_sign cs sign;
+			with Not_found ->
+				let i = CompilationServer.add_sign cs sign in
+				print_endline (Printf.sprintf "Found context %s:\n%s" i (dump_context com));
+				i
+		in
+		Printf.sprintf "%2s,%3s: " sign_id (short_platform_name com.platform)
+	in
+	MacroContext.macro_enable_cache := true;
+	let current_stdin = ref None in
+	Typeload.parse_hook := (fun com2 file p ->
+		let ffile = Path.unique_full_path file in
+		let is_display_file = ffile = (!Parser.resume_display).pfile in
+
+		match is_display_file, !current_stdin with
+		| true, Some stdin when Common.defined com2 Define.DisplayStdin ->
+			Typeload.parse_file_from_string com2 file p stdin
+		| _ ->
+			let sign = get_signature com2 in
+			let ftime = file_time ffile in
+			let fkey = (ffile,sign) in
+			try
+				let time, data = CompilationServer.find_file cs fkey in
+				if time <> ftime then raise Not_found;
+				data
+			with Not_found ->
+				has_parse_error := false;
+				let data = Typeload.parse_file com2 file p in
+				let info,is_unusual = if !has_parse_error then "not cached, has parse error",true
+					else if is_display_file then "not cached, is display file",true
+					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 com2.display.dms_display then raise Not_found;
+						let ident = Hashtbl.find Parser.special_identifier_files ffile in
+						Printf.sprintf "not cached, using \"%s\" define" ident,true
+					with Not_found ->
+						CompilationServer.cache_file cs fkey (ftime,data);
+						"cached",false
+				end in
+				if verbose && is_unusual then print_endline (Printf.sprintf "%sparsed %s (%s)" (sign_string com2) ffile info);
+				data
+	);
+	let check_module_shadowing com paths m =
+		List.iter (fun (path,_) ->
+			let file = (path ^ (snd m.m_path)) ^ ".hx" in
+			if Sys.file_exists file then begin
+				let time = file_time file in
+				if time > m.m_extra.m_time then begin
+					if verbose then print_endline (Printf.sprintf "%smodule path might have changed: %s\n\twas: %2.0f %s\n\tnow: %2.0f %s"
+						(sign_string com) (s_type_path m.m_path) m.m_extra.m_time m.m_extra.m_file time file);
+					raise Not_found
+				end
+			end
+		) paths
+	in
+	let delays = ref [] in
+	let changed_directories = Hashtbl.create 0 in
+	let arguments = Hashtbl.create 0 in
+	let stat dir =
+		(Unix.stat (Path.remove_trailing_slash dir)).Unix.st_mtime
+	in
+	let get_changed_directories (ctx : Typecore.typer) =
+		let t = Common.timer ["server";"module cache";"changed dirs"] in
+		let com = ctx.Typecore.com in
+		let sign = get_signature com in
+		let dirs = try
+			(* First, check if we already have determined changed directories for current compilation. *)
+			Hashtbl.find changed_directories sign
+		with Not_found ->
+			let dirs = try
+				(* Next, get all directories from the cache and filter the ones that haven't changed. *)
+				let all_dirs = CompilationServer.find_directories cs sign in
+				List.fold_left (fun acc (dir,time) ->
+					try
+						let time' = stat dir in
+						if !time < time' then begin
+							time := time';
+							let sub_dirs = Path.find_directories (platform_name com.platform) false [dir] in
+							List.iter (fun dir ->
+								if not (CompilationServer.has_directory cs sign dir) then begin
+									let time = stat dir in
+									if verbose then print_endline (Printf.sprintf "%sadded directory %s" (sign_string com) dir);
+									CompilationServer.add_directory cs sign (dir,ref time)
+								end;
+							) sub_dirs;
+							(dir,time') :: acc
+						end else
+							acc
+					with Unix.Unix_error _ ->
+						CompilationServer.remove_directory cs sign dir;
+						if verbose then print_endline (Printf.sprintf "%sremoved directory %s" (sign_string com) dir);
+						acc
+				) [] all_dirs
+			with Not_found ->
+				(* There were no directories in the cache, so this must be a new context. Let's add
+				   an empty list to make sure no crazy recursion happens. *)
+				CompilationServer.add_directories cs sign [];
+				(* Register the delay that is going to populate the cache dirs. *)
+				delays := (fun () ->
+					let dirs = ref [] in
+					let add_dir path =
+						try
+							let time = stat path in
+							dirs := (path,ref time) :: !dirs
+						with Unix.Unix_error _ ->
+							()
+					in
+					List.iter add_dir com.class_path;
+					List.iter add_dir (Path.find_directories (platform_name com.platform) true com.class_path);
+					if verbose then print_endline (Printf.sprintf "%sfound %i directories" (sign_string com) (List.length !dirs));
+					CompilationServer.add_directories cs sign !dirs
+				) :: !delays;
+				(* Returning [] should be fine here because it's a new context, so we won't do any
+				   shadowing checks anyway. *)
+				[]
+			in
+			Hashtbl.add changed_directories sign dirs;
+			dirs
+		in
+		t();
+		dirs
+	in
+	let compilation_step = ref 0 in
+	let compilation_mark = ref 0 in
+	let mark_loop = ref 0 in
+	Typeload.type_module_hook := (fun (ctx:Typecore.typer) mpath p ->
+		let t = Common.timer ["server";"module cache"] in
+		let com2 = ctx.Typecore.com in
+		let sign = get_signature com2 in
+		let content_changed m file =
+			let ffile = Path.unique_full_path file in
+			let fkey = (ffile,sign) in
+			try
+				let _, old_data = CompilationServer.find_file cs fkey in
+				(* We must use the module path here because the file path is absolute and would cause
+				   positions in the parsed declarations to differ. *)
+				let new_data = Typeload.parse_module ctx m.m_path p in
+				snd old_data <> snd new_data
+			with Not_found ->
+				true
+		in
+		incr mark_loop;
+		let mark = !mark_loop in
+		let start_mark = !compilation_mark in
+		let rec check m =
+			let check_module_path () =
+				let directories = get_changed_directories ctx in
+				match m.m_extra.m_kind with
+				| MFake | MSub | 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 com2 directories m; true with Not_found -> false) in
+					if has_file then begin
+						if verbose then print_endline ("A file is masking the library file " ^ s_type_path m.m_path);
+						raise Not_found;
+					end;
+					let rec loop = function
+						| [] ->
+							if verbose then print_endline ("No library file was found for " ^ s_type_path m.m_path);
+							raise Not_found (* no extern registration *)
+						| load :: l ->
+							match load m.m_path p with
+							| None -> loop l
+							| Some (file,_) ->
+								if Path.unique_full_path file <> m.m_extra.m_file then begin
+									if verbose then print_endline ("Library file was changed for " ^ s_type_path m.m_path);
+									raise Not_found;
+								end
+					in
+					loop com2.load_extern_type
+				| MCode -> check_module_shadowing com2 directories m
+				| MMacro when ctx.Typecore.in_macro -> check_module_shadowing com2 directories m
+				| MMacro ->
+					let _, mctx = MacroContext.get_macro_context ctx p in
+					check_module_shadowing mctx.Typecore.com (get_changed_directories mctx) m
+			in
+			let has_policy policy = List.mem policy m.m_extra.m_check_policy in
+			let check_file () =
+				if file_time m.m_extra.m_file <> m.m_extra.m_time then begin
+					if has_policy CheckFileContentModification && not (content_changed m m.m_extra.m_file) then begin
+						if verbose then print_endline (Printf.sprintf "%s%s changed time not but content, reusing" (sign_string com2) m.m_extra.m_file)
+					end else begin
+						if verbose then print_endline (Printf.sprintf "%s%s not cached (%s)" (sign_string com2) (s_type_path m.m_path) (if m.m_extra.m_time = -1. then "macro-in-macro" else "modified"));
+						if m.m_extra.m_kind = MFake then Hashtbl.remove Typecore.fake_modules m.m_extra.m_file;
+						raise Not_found;
+					end
+				end
+			in
+			let check_dependencies () =
+				PMap.iter (fun _ m2 -> match check m2 with
+					| None -> ()
+					| Some m -> raise (Dirty m)
+				) m.m_extra.m_deps;
+			in
+			begin match m.m_extra.m_dirty with
+			| Some m ->
+				Some m
+			| None ->
+				if m.m_extra.m_mark = mark then
+					None
+				else try
+					if m.m_extra.m_mark <= start_mark then begin
+						if not (has_policy NoCheckShadowing) then check_module_path();
+						if not (has_policy NoCheckFileTimeModification) then check_file();
+					end;
+					m.m_extra.m_mark <- mark;
+					if not (has_policy NoCheckDependencies) then check_dependencies();
+					None
+				with
+				| Not_found ->
+					m.m_extra.m_dirty <- Some m;
+					Some m
+				| Dirty m' ->
+					m.m_extra.m_dirty <- Some m';
+					Some m'
+				end
+		in
+		let rec add_modules tabs m0 m =
+			if m.m_extra.m_added < !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 com2.resources) m.m_extra.m_binded_res;
+				| _ ->
+					(*if verbose then print_endline (Printf.sprintf "%s%sreusing %s" (sign_string com2) tabs (s_type_path m.m_path));*)
+					m.m_extra.m_added <- !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
+						| _ -> ()
+					) m.m_types;
+					if m.m_extra.m_kind <> MSub then Typeload.add_module ctx m p;
+					PMap.iter (Hashtbl.replace com2.resources) m.m_extra.m_binded_res;
+					if ctx.Typecore.in_macro || com2.display.dms_full_typing then
+						PMap.iter (fun _ m2 -> add_modules (tabs ^ "  ") m0 m2) m.m_extra.m_deps;
+					List.iter (MacroContext.call_init_macro ctx) m.m_extra.m_macro_calls
+				)
+			end
+		in
+		try
+			let m = CompilationServer.find_module cs (mpath,sign) in
+			let tcheck = Common.timer ["server";"module cache";"check"] in
+			begin match check m with
+			| None -> ()
+			| Some m' ->
+				if verbose then print_endline (Printf.sprintf "%sskipping %s%s" (sign_string com2) (s_type_path m.m_path) (if m == m' then "" else Printf.sprintf "(%s)" (s_type_path m'.m_path)));
+				tcheck();
+				raise Not_found;
+			end;
+			tcheck();
+			let tadd = Common.timer ["server";"module cache";"add modules"] in
+			add_modules "" m m;
+			tadd();
+			t();
+			Some m
+		with Not_found ->
+			t();
+			None
+	);
+	let run_count = ref 0 in
+	while true do
+		let read, write, close = accept() in
+		let rec cache_context com =
+			let cache_module m =
+				CompilationServer.cache_module cs (m.m_path,m.m_extra.m_sign) m;
+				(*if verbose then print_endline (Printf.sprintf "%scached %s" (sign_string com) (s_type_path m.m_path));*)
+			in
+			if com.display.dms_full_typing then begin
+				List.iter cache_module com.modules;
+				if verbose then print_endline ("Cached " ^ string_of_int (List.length com.modules) ^ " modules");
+			end;
+			match com.get_macros() with
+			| None -> ()
+			| Some com -> cache_context com
+		in
+		let create params =
+			let ctx = create_context params in
+			ctx.flush <- (fun() ->
+				incr compilation_step;
+				compilation_mark := !mark_loop;
+				List.iter (fun s -> write (s ^ "\n"); if verbose then print_endline ("> " ^ s)) (List.rev ctx.messages);
+				if ctx.has_error then write "\x02\n" else cache_context ctx.com;
+			);
+			ctx.setup <- (fun() ->
+				let sign = get_signature ctx.com in
+				if verbose then begin
+					let defines = PMap.foldi (fun k v acc -> (k ^ "=" ^ v) :: acc) ctx.com.defines [] in
+					print_endline ("Defines " ^ (String.concat "," (List.sort compare defines)));
+					print_endline ("Using signature " ^ Digest.to_hex sign);
+					print_endline ("Display position: " ^ (Printer.s_pos !Parser.resume_display));
+				end;
+				Parser.display_error := (fun e p -> has_parse_error := true; ctx.com.error (Parser.error_msg e) p);
+				if ctx.com.display.dms_display then begin
+					let file = (!Parser.resume_display).pfile in
+					let fkey = (file,sign) in
+					(* force parsing again : if the completion point have been changed *)
+					CompilationServer.remove_file cs fkey;
+					CompilationServer.taint_modules cs file;
+				end;
+				try
+					if (Hashtbl.find arguments sign) <> ctx.com.class_path then begin
+						if verbose then print_endline (Printf.sprintf "%sclass paths changed, resetting directories" (sign_string ctx.com));
+						Hashtbl.replace arguments sign ctx.com.class_path;
+						CompilationServer.clear_directories cs sign;
+					end;
+				with Not_found ->
+					Hashtbl.add arguments sign ctx.com.class_path;
+					()
+			);
+			ctx.com.print <- (fun str -> write ("\x01" ^ String.concat "\x01" (ExtString.String.nsplit str "\n") ^ "\n"));
+			ctx
+		in
+		(try
+			let s = read() in
+			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
+			if verbose then print_endline ("Processing Arguments [" ^ String.concat "," data ^ "]");
+			(try
+				Hashtbl.clear changed_directories;
+				Common.display_default := DMNone;
+				Parser.resume_display := null_pos;
+				Typeload.return_partial_type := false;
+				measure_times := false;
+				close_times();
+				stats.s_files_parsed := 0;
+				stats.s_classes_built := 0;
+				stats.s_methods_typed := 0;
+				stats.s_macros_called := 0;
+				Hashtbl.clear Common.htimers;
+				let _ = Common.timer ["other"] in
+				incr compilation_step;
+				compilation_mark := !mark_loop;
+				start_time := get_time();
+				process_params create data;
+				close_times();
+				if !measure_times then report_times (fun s -> write (s ^ "\n"))
+			with
+			| Completion str ->
+				if verbose then print_endline ("Completion Response =\n" ^ str);
+				write str
+			| Arg.Bad msg ->
+				prerr_endline ("Error: " ^ msg);
+			);
+			let fl = !delays in
+			delays := [];
+			List.iter (fun f -> f()) fl;
+			if verbose then begin
+				print_endline (Printf.sprintf "Stats = %d files, %d classes, %d methods, %d macros" !(stats.s_files_parsed) !(stats.s_classes_built) !(stats.s_methods_typed) !(stats.s_macros_called));
+				print_endline (Printf.sprintf "Time spent : %.3fs" (get_time() -. t0));
+			end;
+		with Unix.Unix_error _ ->
+			if verbose then print_endline "Connection Aborted"
+		| e ->
+			let estr = Printexc.to_string e in
+			if verbose then print_endline ("Uncaught Error : " ^ estr);
+			(try write estr with _ -> ());
+			if is_debug_run() then print_endline (Printexc.get_backtrace());
+		);
+		close();
+		current_stdin := None;
+		(* prevent too much fragmentation by doing some compactions every X run *)
+		incr run_count;
+		if !run_count mod 10 = 0 then begin
+			let t0 = get_time() in
+			Gc.compact();
+			if verbose then begin
+				let stat = Gc.quick_stat() in
+				let size = (float_of_int stat.Gc.heap_words) *. 4. in
+				print_endline (Printf.sprintf "Compacted memory %.3fs %.1fMB" (get_time() -. t0) (size /. (1024. *. 1024.)));
+			end
+		end else Gc.minor();
+	done
+
+and init_wait_stdio() =
+	set_binary_mode_in stdin true;
+	set_binary_mode_out stderr true;
+
+	let chin = IO.input_channel stdin in
+	let cherr = IO.output_channel stderr in
+
+	let berr = Buffer.create 0 in
+	let read = fun () ->
+		let len = IO.read_i32 chin in
+		IO.really_nread chin len
+	in
+	let write = Buffer.add_string berr in
+	let close = fun() ->
+		IO.write_i32 cherr (Buffer.length berr);
+		IO.nwrite cherr (Buffer.contents berr);
+		IO.flush cherr
+	in
+	fun() ->
+		Buffer.clear berr;
+		read, write, close
+
+and init_wait_socket verbose 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));
+	if verbose then print_endline ("Waiting on " ^ host ^ ":" ^ string_of_int port);
+	Unix.listen sock 10;
+	let bufsize = 1024 in
+	let tmp = String.create bufsize in
+	let accept() = (
+		let sin, _ = Unix.accept sock in
+		Unix.set_nonblock sin;
+		if verbose then print_endline "Client connected";
+		let b = Buffer.create 0 in
+		let rec read_loop count =
+			try
+				let r = Unix.recv sin tmp 0 bufsize [] in
+				if r = 0 then
+					failwith "Incomplete request"
+				else begin
+					if verbose then Printf.printf "Reading %d bytes\n" r;
+					Buffer.add_substring b tmp 0 r;
+					if tmp.[r-1] = '\000' then
+						Buffer.sub b 0 (Buffer.length b - 1)
+					else
+						read_loop 0
+				end
+			with Unix.Unix_error((Unix.EWOULDBLOCK|Unix.EAGAIN),_,_) ->
+				if count = 100 then
+					failwith "Aborting inactive connection"
+				else begin
+					if verbose then print_endline "Waiting for data...";
+					ignore(Unix.select [] [] [] 0.05); (* wait a bit *)
+					read_loop (count + 1);
+				end
+		in
+		let read = fun() -> (let s = read_loop 0 in Unix.clear_nonblock sin; s) in
+		let write = ssend sin in
+		let close() = Unix.close sin in
+		read, write, close
+	) in
+	accept
+
+and 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 args = ("--cwd " ^ Unix.getcwd()) :: args in
+	ssend sock (String.concat "" (List.map (fun a -> a ^ "\n") args) ^ "\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 = String.create 1024 in
+	let rec loop() =
+		let b = Unix.recv sock tmp 0 1024 [] in
+		Buffer.add_substring buf tmp 0 b;
+		if b > 0 then begin
+			if String.get tmp (b - 1) = '\n' then begin
+				process();
+				Buffer.reset buf;
+			end;
+			loop();
+		end
+	in
+	loop();
+	process();
+	if !has_error then exit 1

+ 124 - 233
src/syntax/ast.ml

@@ -17,189 +17,7 @@
 	Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  *)
 
-type pos = {
-	pfile : string;
-	pmin : int;
-	pmax : int;
-}
-
-module IntMap = Map.Make(struct type t = int let compare a b = a - b end)
-module StringMap = Map.Make(struct type t = string let compare = String.compare end)
-
-module Meta = struct
-	type strict_meta =
-		| Abi
-		| Abstract
-		| Access
-		| Accessor
-		| Allow
-		| Analyzer
-		| Annotation
-		| ArrayAccess
-		| Ast
-		| AstSource
-		| AutoBuild
-		| Bind
-		| Bitmap
-		| BridgeProperties
-		| Build
-		| BuildXml
-		| Callable
-		| Class
-		| ClassCode
-		| Commutative
-		| CompilerGenerated
-		| Const
-		| CoreApi
-		| CoreType
-		| CppFileCode
-		| CppInclude
-		| CppNamespaceCode
-		| CsNative
-		| Dce
-		| Debug
-		| Decl
-		| DefParam
-		| Delegate
-		| Depend
-		| Deprecated
-		| DirectlyUsed
-		| DynamicObject
-		| Eager
-		| Enum
-		| EnumConstructorParam
-		| Event
-		| Exhaustive
-		| Expose
-		| Extern
-		| FakeEnum
-		| File
-		| FileXml
-		| Final
-		| Fixed
-		| FlatEnum
-		| Font
-		| Forward
-		| ForwardStatics
-		| From
-		| FunctionCode
-		| FunctionTailCode
-		| Generic
-		| GenericBuild
-		| GenericInstance
-		| Getter
-		| Hack
-		| HasUntyped
-		| HaxeGeneric
-		| HeaderClassCode
-		| HeaderCode
-		| HeaderInclude
-		| HeaderNamespaceCode
-		| HxGen
-		| IfFeature
-		| Impl
-		| PythonImport
-		| ImplicitCast
-		| Include
-		| InitPackage
-		| Internal
-		| IsVar
-		| JavaCanonical
-		| JavaNative
-		| JsRequire
-		| Keep
-		| KeepInit
-		| KeepSub
-		| LibType
-		| LuaRequire
-		| Meta
-		| Macro
-		| MaybeUsed
-		| MergeBlock
-		| MultiType
-		| Native
-		| NativeChildren
-		| NativeGen
-		| NativeGeneric
-		| NativeProperty
-		| NativeStaticExtension
-		| NoCompletion
-		| NoDebug
-		| NoDoc
-		| NoExpr
-		| NoImportGlobal
-		| NonVirtual
-		| NoPackageRestrict
-		| NoPrivateAccess
-		| NoStack
-		| NotNull
-		| NoUsing
-		| Ns
-		| Objc
-		| Op
-		| Optional
-		| Overload
-		| PhpConstants
-		| PhpGlobal
-		| PrivateAccess
-		| Property
-		| Protected
-		| Public
-		| PublicFields
-		| Pure
-		| QuotedField
-		| ReadOnly
-		| RealPath
-		| Remove
-		| Require
-		| RequiresAssign
-		| Resolve
-		| ReplaceReflection
-		| Rtti
-		| Runtime
-		| RuntimeValue
-		| Scalar
-		| SelfCall
-		| Setter
-		| SkipCtor
-		| SkipReflection
-		| Sound
-		| SourceFile
-		| StackOnly
-		| StoredTypedExpr
-		| Strict
-		| Struct
-		| StructAccess
-		| StructInit
-		| SuppressWarnings
-		| SwitchVariable
-		| This
-		| Throws
-		| To
-		| ToString
-		| Transient
-		| TemplatedCall
-		| ValueUsed
-		| Volatile
-		| Unbound
-		| UnifyMinDynamic
-		| Unreflective
-		| Unsafe
-		| Usage
-		| Used
-		| Value
-		| Void
-		| Last
-		(* do not put any custom metadata after Last *)
-		| Dollar of string
-		| Custom of string
-
-	let has m ml = List.exists (fun (m2,_,_) -> m = m2) ml
-	let get m ml = List.find (fun (m2,_,_) -> m = m2) ml
-
-	let to_string_ref = ref (fun _ -> assert false)
-	let to_string (m : strict_meta) : string = !to_string_ref m
-end
+open Globals
 
 type keyword =
 	| Function
@@ -355,7 +173,7 @@ and expr_def =
 	| EBinop of binop * expr * expr
 	| EField of expr * string
 	| EParenthesis of expr
-	| EObjectDecl of (string * expr) list
+	| EObjectDecl of (placed_name * expr) list
 	| EArrayDecl of expr list
 	| ECall of expr * expr list
 	| ENew of placed_type_path * expr list
@@ -367,8 +185,8 @@ and expr_def =
 	| EIn of expr * expr
 	| EIf of expr * expr * expr option
 	| EWhile of expr * expr * while_flag
-	| ESwitch of expr * (expr list * expr option * expr option) list * expr option option
-	| ETry of expr * (placed_name * type_hint * expr) list
+	| ESwitch of expr * (expr list * expr option * expr option * pos) list * (expr option * pos) option
+	| ETry of expr * (placed_name * type_hint * expr * pos) list
 	| EReturn of expr option
 	| EBreak
 	| EContinue
@@ -407,7 +225,7 @@ and access =
 and class_field_kind =
 	| FVar of type_hint option * expr option
 	| FFun of func
-	| FProp of string * string * type_hint option * expr option
+	| FProp of placed_name * placed_name * type_hint option * expr option
 
 and class_field = {
 	cff_name : placed_name;
@@ -474,8 +292,6 @@ type type_decl = type_def * pos
 
 type package = string list * type_decl list
 
-exception Error of string * pos
-
 let is_lower_ident i =
 	let rec loop p =
 		match String.unsafe_get i p with
@@ -497,8 +313,6 @@ let is_prefix = function
 
 let base_class_name = snd
 
-let null_pos = { pfile = "?"; pmin = -1; pmax = -1 }
-
 let punion p p2 =
 	{
 		pfile = p.pfile;
@@ -514,8 +328,6 @@ let rec punion_el el = match el with
 	| (_,p) :: el ->
 		punion p (punion_el el)
 
-let s_type_path (p,s) = match p with [] -> s | _ -> String.concat "." p ^ "." ^ s
-
 let parse_path s =
 	match List.rev (ExtString.String.nsplit s ".") with
 	| [] -> failwith "Invalid empty path"
@@ -716,58 +528,132 @@ let map_expr loop (e,p) =
 		| TPExpr e -> TPExpr (loop e)
 	and cfield f =
 		{ f with cff_kind = (match f.cff_kind with
-			| FVar (t,e) -> FVar (opt type_hint t, opt loop e)
+			| FVar (t,e) ->
+				let t = opt type_hint t in
+				let e = opt loop e in
+				FVar (t,e)
 			| FFun f -> FFun (func f)
-			| FProp (get,set,t,e) -> FProp (get,set,opt type_hint t,opt loop e))
+			| FProp (get,set,t,e) ->
+				let t = opt type_hint t in
+				let e = opt loop e in
+				FProp (get,set,t,e))
 		}
 	and type_hint (t,p) = (match t with
 		| CTPath t -> CTPath { t with tparams = List.map tparam t.tparams }
-		| CTFunction (cl,c) -> CTFunction (List.map type_hint cl, type_hint c)
+		| CTFunction (cl,c) ->
+			let cl = List.map type_hint cl in
+			let c = type_hint c in
+			CTFunction (cl,c)
 		| CTAnonymous fl -> CTAnonymous (List.map cfield fl)
 		| CTParent t -> CTParent (type_hint t)
-		| CTExtend (tl,fl) -> CTExtend (List.map tpath tl, List.map cfield fl)
+		| CTExtend (tl,fl) ->
+			let tl = List.map tpath tl in
+			let fl = List.map cfield fl in
+			CTExtend (tl,fl)
 		| CTOptional t -> CTOptional (type_hint t)),p
 	and tparamdecl t =
-		{ tp_name = t.tp_name; tp_constraints = List.map type_hint t.tp_constraints; tp_params = List.map tparamdecl t.tp_params; tp_meta = t.tp_meta }
+		let constraints = List.map type_hint t.tp_constraints 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 }
 	and func f =
+		let params = List.map tparamdecl f.f_params in
+		let args = List.map (fun (n,o,m,t,e) ->
+			let t = opt type_hint t in
+			let e = opt loop e in
+			n,o,m,t,e
+		) f.f_args in
+		let t = opt type_hint f.f_type in
+		let e = opt loop f.f_expr in
 		{
-			f_params = List.map tparamdecl f.f_params;
-			f_args = List.map (fun (n,o,m,t,e) -> n,o,m,opt type_hint t,opt loop e) f.f_args;
-			f_type = opt type_hint f.f_type;
-			f_expr = opt loop f.f_expr;
+			f_params = params;
+			f_args = args;
+			f_type = t;
+			f_expr = e;
 		}
 	and tpath (t,p) = { t with tparams = List.map tparam t.tparams },p
 	in
 	let e = (match e with
 	| EConst _ -> e
-	| EArray (e1,e2) -> EArray (loop e1, loop e2)
-	| EBinop (op,e1,e2) -> EBinop (op,loop e1, loop e2)
+	| EArray (e1,e2) ->
+		let e1 = loop e1 in
+		let e2 = loop e2 in
+		EArray (e1,e2)
+	| EBinop (op,e1,e2) ->
+		let e1 = loop e1 in
+		let e2 = loop e2 in
+		EBinop (op,e1,e2)
 	| EField (e,f) -> EField (loop e, f)
 	| EParenthesis e -> EParenthesis (loop e)
-	| EObjectDecl fl -> EObjectDecl (List.map (fun (f,e) -> f,loop e) fl)
+	| EObjectDecl fl -> EObjectDecl (List.map (fun ((f,p),e) -> (f,p),loop e) fl)
 	| EArrayDecl el -> EArrayDecl (List.map loop el)
-	| ECall (e,el) -> ECall (loop e, List.map loop el)
-	| ENew (t,el) -> ENew (tpath t,List.map loop el)
+	| ECall (e,el) ->
+		let e = loop e in
+		let el = List.map loop el in
+		ECall (e,el)
+	| ENew (t,el) ->
+		let t = tpath t in
+		let el = List.map loop el in
+		ENew (t,el)
 	| EUnop (op,f,e) -> EUnop (op,f,loop e)
-	| EVars vl -> EVars (List.map (fun (n,t,eo) -> n,opt type_hint t,opt loop eo) vl)
+	| EVars vl ->
+		EVars (List.map (fun (n,t,eo) ->
+			let t = opt type_hint t in
+			let eo = opt loop eo in
+			n,t,eo
+		) vl)
 	| EFunction (n,f) -> EFunction (n,func f)
 	| EBlock el -> EBlock (List.map loop el)
-	| EFor (e1,e2) -> EFor (loop e1, loop e2)
-	| EIn (e1,e2) -> EIn (loop e1, loop e2)
-	| EIf (e,e1,e2) -> EIf (loop e, loop e1, opt loop e2)
-	| EWhile (econd,e,f) -> EWhile (loop econd, loop e, f)
-	| ESwitch (e,cases,def) -> ESwitch (loop e, List.map (fun (el,eg,e) -> List.map loop el, opt loop eg, opt loop e) cases, opt (opt loop) def)
-	| ETry (e,catches) -> ETry (loop e, List.map (fun (n,t,e) -> n,type_hint t,loop e) catches)
+	| EFor (e1,e2) ->
+		let e1 = loop e1 in
+		let e2 = loop e2 in
+		EFor (e1,e2)
+	| EIn (e1,e2) ->
+		let e1 = loop e1 in
+		let e2 = loop e2 in
+		EIn (e1,e2)
+	| EIf (e,e1,e2) ->
+		let e = loop e in
+		let e1 = loop e1 in
+		let e2 = opt loop e2 in
+		EIf (e,e1,e2)
+	| EWhile (econd,e,f) ->
+		let econd = loop econd in
+		let e = loop e in
+		EWhile (econd,e,f)
+	| ESwitch (e,cases,def) ->
+		let e = loop e in
+		let cases = List.map (fun (el,eg,e,p) ->
+			let el = List.map loop el in
+			let eg = opt loop eg in
+			let e = opt loop e in
+			el,eg,e,p
+		) cases in
+		let def = opt (fun (eo,p) -> opt loop eo,p) def in
+		ESwitch (e, cases, def)
+	| ETry (e,catches) ->
+		let e = loop e in
+		let catches = List.map (fun (n,t,e,p) -> n,type_hint t,loop e,p) catches in
+		ETry (e,catches)
 	| EReturn e -> EReturn (opt loop e)
 	| EBreak -> EBreak
 	| EContinue -> EContinue
 	| EUntyped e -> EUntyped (loop e)
 	| EThrow e -> EThrow (loop e)
-	| ECast (e,t) -> ECast (loop e,opt type_hint t)
+	| ECast (e,t) ->
+		let e = loop e in
+		let t = opt type_hint t in
+		ECast (e,t)
 	| EDisplay (e,f) -> EDisplay (loop e,f)
 	| EDisplayNew t -> EDisplayNew (tpath t)
-	| ETernary (e1,e2,e3) -> ETernary (loop e1,loop e2,loop e3)
-	| ECheckType (e,t) -> ECheckType (loop e, type_hint t)
+	| ETernary (e1,e2,e3) ->
+		let e1 = loop e1 in
+		let e2 = loop e2 in
+		let e3 = loop e3 in
+		ETernary (e1,e2,e3)
+	| ECheckType (e,t) ->
+		let e = loop e in
+		let t = type_hint t in
+		ECheckType (e,t)
 	| EMeta (m,e) -> EMeta(m, loop e)
 	) in
 	(e,p)
@@ -786,15 +672,15 @@ let iter_expr loop (e,p) =
 	| EObjectDecl fl -> List.iter (fun (_,e) -> loop e) fl;
 	| ETry(e1,catches) ->
 		loop e1;
-		List.iter (fun (_,_,e) -> loop e) catches
+		List.iter (fun (_,_,e,_) -> loop e) catches
 	| ESwitch(e1,cases,def) ->
 		loop e1;
-		List.iter (fun (el,eg,e) ->
+		List.iter (fun (el,eg,e,_) ->
 			exprs el;
 			opt eg;
 			opt e;
 		) cases;
-		(match def with None -> () | Some e -> opt e);
+		(match def with None -> () | Some (e,_) -> opt e);
 	| EFunction(_,f) ->
 		List.iter (fun (_,_,_,_,eo) -> opt eo) f.f_args;
 		opt f.f_expr
@@ -808,7 +694,7 @@ let s_expr e =
 		| 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
 		| EParenthesis e -> "(" ^ (s_expr_inner tabs e) ^ ")"
-		| EObjectDecl fl -> "{ " ^ (String.concat ", " (List.map (fun (n,e) -> n ^ " : " ^ (s_expr_inner tabs e)) fl)) ^ " }"
+		| EObjectDecl fl -> "{ " ^ (String.concat ", " (List.map (fun ((n,_),e) -> n ^ " : " ^ (s_expr_inner tabs e)) fl)) ^ " }"
 		| EArrayDecl el -> "[" ^ s_expr_list tabs el ", " ^ "]"
 		| ECall (e,el) -> s_expr_inner tabs e ^ "(" ^ s_expr_list tabs el ", " ^ ")"
 		| ENew (t,el) -> "new " ^ s_complex_type_path tabs t ^ "(" ^ s_expr_list tabs el ", " ^ ")"
@@ -826,7 +712,7 @@ let s_expr e =
 		| EWhile (econd,e,NormalWhile) -> "while (" ^ s_expr_inner tabs econd ^ ") " ^ s_expr_inner tabs e
 		| EWhile (econd,e,DoWhile) -> "do " ^ s_expr_inner tabs e ^ " while (" ^ s_expr_inner tabs econd ^ ")"
 		| ESwitch (e,cases,def) -> "switch " ^ s_expr_inner tabs e ^ " {\n\t" ^ tabs ^ String.concat ("\n\t" ^ tabs) (List.map (s_case tabs) cases) ^
-			(match def with None -> "" | Some def -> "\n\t" ^ tabs ^ "default:" ^
+			(match def with None -> "" | Some (def,_) -> "\n\t" ^ tabs ^ "default:" ^
 			(match def with None -> "" | Some def -> s_expr_omit_block tabs def)) ^ "\n" ^ tabs ^ "}"
 		| ETry (e,catches) -> "try " ^ s_expr_inner tabs e ^ String.concat "" (List.map (s_catch tabs) catches)
 		| EReturn e -> "return" ^ s_opt_expr tabs e " "
@@ -839,17 +725,15 @@ let s_expr e =
 		| ETernary (e1,e2,e3) -> s_expr_inner tabs e1 ^ " ? " ^ s_expr_inner tabs e2 ^ " : " ^ s_expr_inner tabs e3
 		| 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,_) -> Printf.sprintf "#DISPLAY(%s)" (s_expr_inner tabs e1)
+		| EDisplay (e1,iscall) -> Printf.sprintf "#DISPLAY(%s, %b)" (s_expr_inner tabs e1) iscall
 		| 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,_) =
-		(String.concat "." t.tpackage) ^ if List.length t.tpackage > 0 then "." else "" ^
-		t.tname ^
-		match t.tsub with
-		| Some s -> "." ^ s
-		| None -> "" ^
-		s_type_param_or_consts tabs t.tparams
+		Printf.sprintf "%s%s%s"
+			(s_type_path (t.tpackage,t.tname))
+			(Option.map_default (fun s -> "." ^ s) "" t.tsub)
+			(s_type_param_or_consts tabs t.tparams)
 	and s_type_param_or_consts tabs pl =
 		if List.length pl > 0
 		then "<" ^ (String.concat "," (List.map (s_type_param_or_const tabs) pl)) ^ ">"
@@ -874,7 +758,7 @@ let s_expr e =
 		if List.length f.cff_access > 0 then String.concat " " (List.map s_access f.cff_access) else "" ^
 		match f.cff_kind with
 		| FVar (t,e) -> "var " ^ (fst f.cff_name) ^ s_opt_type_hint tabs t " : " ^ s_opt_expr tabs e " = "
-		| FProp (get,set,t,e) -> "var " ^ (fst f.cff_name) ^ "(" ^ get ^ "," ^ set ^ ")" ^ s_opt_type_hint tabs t " : " ^ s_opt_expr tabs e " = "
+		| FProp ((get,_),(set,_),t,e) -> "var " ^ (fst f.cff_name) ^ "(" ^ get ^ "," ^ set ^ ")" ^ s_opt_type_hint tabs t " : " ^ s_opt_expr tabs e " = "
 		| FFun func -> "function " ^ (fst f.cff_name) ^ s_func tabs func
 	and s_metadata tabs (s,e,_) =
 		"@" ^ Meta.to_string s ^ if List.length e > 0 then "(" ^ s_expr_list tabs e ", " ^ ")" else ""
@@ -900,11 +784,11 @@ let s_expr e =
 		if o then "?" else "" ^ n ^ s_opt_type_hint tabs t ":" ^ s_opt_expr tabs e " = "
 	and s_var tabs ((n,_),t,e) =
 		n ^ (s_opt_type_hint tabs t ":") ^ s_opt_expr tabs e " = "
-	and s_case tabs (el,e1,e2) =
+	and s_case tabs (el,e1,e2,_) =
 		"case " ^ s_expr_list tabs el ", " ^
 		(match e1 with None -> ":" | Some e -> " if (" ^ s_expr_inner tabs e ^ "):") ^
 		(match e2 with None -> "" | Some e -> s_expr_omit_block tabs e)
-	and s_catch tabs ((n,_),(t,_),e) =
+	and s_catch tabs ((n,_),(t,_),e,_) =
 		" catch(" ^ n ^ ":" ^ s_complex_type tabs t ^ ") " ^ s_expr_inner tabs e
 	and s_block tabs el opn nl cls =
 		 opn ^ "\n\t" ^ tabs ^ (s_expr_list (tabs ^ "\t") el (";\n\t" ^ tabs)) ^ ";" ^ nl ^ tabs ^ cls
@@ -918,7 +802,7 @@ let s_expr e =
 let get_value_meta meta =
 	try
 		begin match Meta.get Meta.Value meta with
-			| (_,[EObjectDecl values,_],_) -> List.fold_left (fun acc (s,e) -> PMap.add s e acc) PMap.empty values
+			| (_,[EObjectDecl values,_],_) -> List.fold_left (fun acc ((s,_),e) -> PMap.add s e acc) PMap.empty values
 			| _ -> raise Not_found
 		end
 	with Not_found ->
@@ -975,4 +859,11 @@ module Expr = struct
 	let ensure_block e = match fst e with
 		| EBlock _ -> e
 		| _ -> (EBlock [e],pos e)
+
+	let field_assoc name fl =
+		let rec loop fl = match fl with
+			| ((name',_),e) :: fl -> if name' = name then e else loop fl
+			| [] -> raise Not_found
+		in
+		loop fl
 end

+ 17 - 3
src/syntax/lexer.mll

@@ -20,6 +20,7 @@
 {
 open Lexing
 open Ast
+open Globals
 
 type error_msg =
 	| Invalid_character of char
@@ -49,6 +50,8 @@ type lexer_file = {
 	mutable llines : (int * int) list;
 	mutable lalines : (int * int) array;
 	mutable lstrings : int list;
+	mutable llast : int;
+	mutable llastindex : int;
 }
 
 let make_file file =
@@ -59,6 +62,8 @@ let make_file file =
 		llines = [0,1];
 		lalines = [|0,1|];
 		lstrings = [];
+		llast = max_int;
+		llastindex = 0;
 	}
 
 
@@ -134,18 +139,27 @@ let find_line p f =
 	if f.lmaxline <> f.lline then begin
 		f.lmaxline <- f.lline;
 		f.lalines <- Array.of_list (List.rev f.llines);
+		f.llast <- max_int;
+		f.llastindex <- 0;
 	end;
 	let rec loop min max =
 		let med = (min + max) lsr 1 in
 		let lp, line = Array.unsafe_get f.lalines med in
-		if med = min then
+		if med = min then begin
+			f.llast <- p;
+			f.llastindex <- med;
 			line, p - lp
-		else if lp > p then
+		end else if lp > p then
 			loop min med
 		else
 			loop med max
 	in
-	loop 0 (Array.length f.lalines)
+	if p >= f.llast then begin
+		let lp, line = Array.unsafe_get f.lalines f.llastindex in
+		let lp2 = if f.llastindex = Array.length f.lalines - 1 then max_int else fst(Array.unsafe_get f.lalines (f.llastindex + 1)) in
+		if p >= lp && p < lp2 then line, p - lp else loop 0 (Array.length f.lalines)
+	end else
+		loop 0 (Array.length f.lalines)
 
 (* resolve a position within a non-haxe file by counting newlines *)
 let resolve_pos file =

+ 206 - 112
src/syntax/parser.ml

@@ -18,6 +18,7 @@
  *)
 
 open Ast
+open Globals
 
 type error_msg =
 	| Unexpected of token
@@ -44,6 +45,8 @@ let error_msg = function
 let error m p = raise (Error (m,p))
 let display_error : (error_msg -> pos -> unit) ref = ref (fun _ _ -> assert false)
 
+let special_identifier_files = Hashtbl.create 0
+
 let quoted_ident_prefix = "@$__hx__"
 
 let quote_ident s =
@@ -88,12 +91,15 @@ let type_path sl in_import = match sl with
 	| n :: l when n.[0] >= 'A' && n.[0] <= 'Z' -> raise (TypePath (List.rev l,Some (n,false),in_import));
 	| _ -> raise (TypePath (List.rev sl,None,in_import))
 
+let is_resuming_file file =
+	Path.unique_full_path file = !resume_display.pfile
+
 let is_resuming p =
 	let p2 = !resume_display in
-	p.pmax = p2.pmin && Common.unique_full_path p.pfile = p2.pfile
+	p.pmax = p2.pmin && is_resuming_file p.pfile
 
 let set_resume p =
-	resume_display := { p with pfile = Common.unique_full_path p.pfile }
+	resume_display := { p with pfile = Path.unique_full_path p.pfile }
 
 let is_dollar_ident e = match fst e with
 	| EConst (Ident n) when n.[0] = '$' ->
@@ -137,9 +143,14 @@ let rec make_binop op e ((v,p2) as e2) =
 		EBinop (op,e,e2) , punion (pos e) (pos e2)
 
 let rec make_unop op ((v,p2) as e) p1 =
+	let neg s =
+		if s.[0] = '-' then String.sub s 1 (String.length s - 1) else "-" ^ s
+	in
 	match v with
 	| 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
+	| 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
 	| _ -> EUnop (op,Prefix,e), punion p1 p2
 
 let rec make_meta name params ((v,p2) as e) p1 =
@@ -149,9 +160,9 @@ let rec make_meta name params ((v,p2) as e) p1 =
 	| ETernary (e1,e2,e3) -> ETernary (make_meta name params e1 p1 , e2, e3), punion p1 p2
 	| _ -> EMeta((name,params,p1),e),punion p1 p2
 
-let make_is e (t,_) p =
-	let e_is = EField((EConst(Ident "Std"),p),"is"),p in
-	let e2 = expr_of_type_path (t.tpackage,t.tname) p in
+let make_is e (t,p_t) p p_is =
+	let e_is = EField((EConst(Ident "Std"),null_pos),"is"),p_is in
+	let e2 = expr_of_type_path (t.tpackage,t.tname) p_t in
 	ECall(e_is,[e;e2]),p
 
 let reify in_macro =
@@ -223,7 +234,7 @@ let reify in_macro =
 		(EConst (Ident (if o then "true" else "false")),p)
 	in
 	let to_obj fields p =
-		(EObjectDecl fields,p)
+		(EObjectDecl (List.map (fun (s,e) -> (s,null_pos),e) fields),p)
 	in
 	let rec to_tparam t p =
 		let n, v = (match t with
@@ -313,7 +324,7 @@ let reify in_macro =
 			let n, vl = (match k with
 				| FVar (ct,e) -> "FVar", [to_opt to_type_hint ct p;to_opt to_expr e p]
 				| FFun f -> "FFun", [to_fun f p]
-				| FProp (get,set,t,e) -> "FProp", [to_string get p; to_string set p; to_opt to_type_hint t p; to_opt to_expr e p]
+				| FProp (get,set,t,e) -> "FProp", [to_placed_name get; to_placed_name set; to_opt to_type_hint t p; to_opt to_expr e p]
 			) in
 			mk_enum "FieldType" n vl p
 		in
@@ -330,7 +341,7 @@ let reify in_macro =
 	and to_meta m p =
 		to_array (fun (m,el,p) _ ->
 			let fields = [
-				"name", to_string (fst (Common.MetaInfo.to_string m)) p;
+				"name", to_string (Meta.to_string m) p;
 				"params", to_expr_array el p;
 				"pos", to_pos p;
 			] in
@@ -345,7 +356,7 @@ let reify in_macro =
 		let pmin = (EConst (Int (string_of_int p.pmin)),p) in
 		let pmax = (EConst (Int (string_of_int p.pmax)),p) in
 		if in_macro then
-			(EUntyped (ECall ((EConst (Ident "__dollar__mk_pos"),p),[file;pmin;pmax]),p),p)
+			(EUntyped (ECall ((EConst (Ident "$__mk_pos__"),p),[file;pmin;pmax]),p),p)
 		else
 			to_obj [("file",file);("min",pmin);("max",pmax)] p
 	and to_expr_array a p = match a with
@@ -359,7 +370,7 @@ let reify in_macro =
 		in
 		let loop e = to_expr e (snd e) in
 		match fst e with
-		| EConst (Ident n) when n.[0] = '$' && String.length n > 1 ->
+		| EConst (Ident n) when n.[0] = '$' && String.length n > 1 && n <> "$__mk_pos__" ->
 			to_string n p
 		| EConst c ->
 			expr "EConst" [to_const c p]
@@ -372,7 +383,7 @@ let reify in_macro =
 		| EParenthesis e ->
 			expr "EParenthesis" [loop e]
 		| EObjectDecl fl ->
-			expr "EObjectDecl" [to_array (fun (f,e) -> to_obj [("field",to_string f p);("expr",loop e)]) fl p]
+			expr "EObjectDecl" [to_array (fun ((f,_),e) -> to_obj [("field",to_string f p);("expr",loop e)]) fl p]
 		| EArrayDecl el ->
 			expr "EArrayDecl" [to_expr_array el p]
 		| ECall (e,el) ->
@@ -424,12 +435,12 @@ let reify in_macro =
 		| EWhile (e1,e2,flag) ->
 			expr "EWhile" [loop e1;loop e2;to_bool (flag = NormalWhile) p]
 		| ESwitch (e1,cases,def) ->
-			let scase (el,eg,e) p =
+			let scase (el,eg,e,_) p =
 				to_obj [("values",to_expr_array el p);"guard",to_opt to_expr eg p;"expr",to_opt to_expr e p] p
 			in
-			expr "ESwitch" [loop e1;to_array scase cases p;to_opt (to_opt to_expr) def p]
+			expr "ESwitch" [loop e1;to_array scase cases p;to_opt (fun (e,_) -> to_opt to_expr e) def p]
 		| ETry (e1,catches) ->
-			let scatch ((n,_),t,e) p =
+			let scatch ((n,_),t,e,_) p =
 				to_obj [("name",to_string n p);("type",to_ctype t p);("expr",loop e)] p
 			in
 			expr "ETry" [loop e1;to_array scatch catches p]
@@ -480,7 +491,7 @@ let reify in_macro =
 				cur_pos := old;
 				e
 			| _ ->
-				expr "EMeta" [to_obj [("name",to_string (fst (Common.MetaInfo.to_string m)) p);("params",to_expr_array ml p);("pos",to_pos p)] p;loop e1]
+				expr "EMeta" [to_obj [("name",to_string (Meta.to_string m) p);("params",to_expr_array ml p);("pos",to_pos p)] p;loop e1]
 	and to_tparam_decl p t =
 		to_obj [
 			"name", to_placed_name t.tp_name;
@@ -494,7 +505,12 @@ let reify in_macro =
 			List.iter (function
 				| HExtern | HPrivate -> ()
 				| HInterface -> interf := true;
-				| HExtends t -> ext := Some (to_tpath t p)
+				| HExtends t -> ext := (match !ext with
+					| None -> Some (to_tpath t p)
+					| Some _ -> begin
+						impl := (to_tpath t p) :: !impl;
+						!ext
+					  end)
 				| HImplements i-> impl := (to_tpath i p) :: !impl
 			) d.d_flags;
 			to_obj [
@@ -511,6 +527,10 @@ let reify in_macro =
 	in
 	(fun e -> to_expr e (snd e)), to_ctype, to_type_def
 
+let next_token s = match Stream.peek s with
+	| Some tk -> tk
+	| _ -> last_token s
+
 let popt f = parser
 	| [< v = f >] -> Some v
 	| [< >] -> None
@@ -547,10 +567,10 @@ let lower_ident_or_macro = parser
 	| [< '(Kwd Extern,_) >] -> "extern"
 
 let property_ident = parser
-	| [< i, _ = ident >] -> i
-	| [< '(Kwd Dynamic,_) >] -> "dynamic"
-	| [< '(Kwd Default,_) >] -> "default"
-	| [< '(Kwd Null,_) >] -> "null"
+	| [< i,p = ident >] -> i,p
+	| [< '(Kwd Dynamic,p) >] -> "dynamic",p
+	| [< '(Kwd Default,p) >] -> "default",p
+	| [< '(Kwd Null,p) >] -> "null",p
 
 let get_doc s =
 	(* do the peek first to make sure we fetch the doc *)
@@ -578,6 +598,16 @@ let semicolon s =
 			let pos = snd (last_token s) in
 			if do_resume() then pos else error Missing_semicolon pos
 
+let encloses_resume p =
+	p.pmin <= !resume_display.pmin && p.pmax >= !resume_display.pmax
+
+let would_skip_resume p1 s =
+	match Stream.npeek 1 s with
+	| [ (_,p2) ] ->
+		is_resuming_file p2.pfile && encloses_resume (punion p1 p2)
+	| _ ->
+		false
+
 let rec	parse_file s =
 	last_doc := None;
 	match s with parser
@@ -656,7 +686,7 @@ and parse_type_decl s =
 and parse_class doc meta cflags need_name s =
 	let opt_name = if need_name then type_name else (fun s -> match popt type_name s with None -> "",null_pos | Some n -> n) in
 	match s with parser
-	| [< n , p1 = parse_class_flags; name = opt_name; tl = parse_constraint_params; hl = psep Comma parse_class_herit; '(BrOpen,_); fl, p2 = parse_class_fields (not need_name) p1 >] ->
+	| [< n , p1 = parse_class_flags; name = opt_name; tl = parse_constraint_params; hl = plist parse_class_herit; '(BrOpen,_); fl, p2 = parse_class_fields (not need_name) p1 >] ->
 		(EClass {
 			d_name = name;
 			d_doc = doc;
@@ -699,7 +729,7 @@ and parse_import s p1 =
 	in
 	let p2, path, mode = (match s with parser
 		| [< '(Const (Ident name),p) >] -> loop [name,p]
-		| [< >] -> serror()
+		| [< >] -> if would_skip_resume p1 s then p1, [], INormal else serror()
 	) in
 	(EImport (path,mode),punion p1 p2)
 
@@ -722,7 +752,7 @@ and parse_using s p1 =
 	in
 	let p2, path = (match s with parser
 		| [< '(Const (Ident name),p) >] -> loop [name,p]
-		| [< >] -> serror()
+		| [< >] -> if would_skip_resume p1 s then p1, [] else serror()
 	) in
 	(EUsing path,punion p1 p2)
 
@@ -837,19 +867,24 @@ and parse_meta_params pname s = match s with parser
 	| [< >] -> []
 
 and parse_meta_entry = parser
-	[< '(At,_); name,p = meta_name; params = parse_meta_params p; s >] -> (name,params,p)
+	[< '(At,p1); s >] ->
+		match s with parser
+		| [< name,p = meta_name p1; params = parse_meta_params p; s >] -> (name,params,p)
+		| [< >] ->
+			if is_resuming p1 then (Meta.Last,[],p1) else serror()
 
 and parse_meta = parser
 	| [< entry = parse_meta_entry; s >] ->
 		entry :: parse_meta s
 	| [< >] -> []
 
-and meta_name = parser
-	| [< '(Const (Ident i),p) >] -> (Meta.Custom i), p
-	| [< '(Kwd k,p) >] -> (Meta.Custom (s_keyword k)),p
-	| [< '(DblDot,_); s >] -> match s with parser
-		| [< '(Const (Ident i),p) >] -> (Common.MetaInfo.parse i), p
-		| [< '(Kwd k,p) >] -> (Common.MetaInfo.parse (s_keyword k)),p
+and meta_name p1 = parser
+	| [< '(Const (Ident i),p) when p.pmin = p1.pmax >] -> (Meta.Custom i), p
+	| [< '(Kwd k,p) when p.pmin = p1.pmax >] -> (Meta.Custom (s_keyword k)),p
+	| [< '(DblDot,p) when p.pmin = p1.pmax; s >] -> match s with parser
+		| [< '(Const (Ident i),p1) when p1.pmin = p.pmax >] -> (Meta.parse i),punion p p1
+		| [< '(Kwd k,p1) when p1.pmin = p.pmax >] -> (Meta.parse (s_keyword k)),punion p p1
+		| [< >] -> if is_resuming p then Meta.Last,p else raise Stream.Failure
 
 and parse_enum_flags = parser
 	| [< '(Kwd Enum,p) >] -> [] , p
@@ -858,11 +893,15 @@ and parse_class_flags = parser
 	| [< '(Kwd Class,p) >] -> [] , p
 	| [< '(Kwd Interface,p) >] -> [HInterface] , p
 
+and parse_complex_type_at p = parser
+	| [< t = parse_complex_type >] -> t
+	| [< >] -> if is_resuming p then CTPath { tpackage = []; tname = ""; tparams = []; tsub = None },p else serror()
+
 and parse_type_hint = parser
-	| [< '(DblDot,_); t = parse_complex_type >] -> t
+	| [< '(DblDot,p1); t = parse_complex_type_at p1 >] -> t
 
 and parse_type_hint_with_pos s = match s with parser
-	| [< '(DblDot,p1); t = parse_complex_type >] -> t
+	| [< '(DblDot,p1); t = parse_complex_type_at p1 >] -> t
 
 and parse_type_opt = parser
 	| [< t = parse_type_hint >] -> Some t
@@ -961,10 +1000,11 @@ and parse_complex_type_next (t : type_hint) = parser
 and parse_type_anonymous opt = parser
 	| [< '(Question,_) when not opt; s >] -> parse_type_anonymous true s
 	| [< name, p1 = ident; t = parse_type_hint_with_pos; s >] ->
-		let next p2 acc =
+		let p2 = pos (last_token s) in
+		let next acc =
 			{
 				cff_name = name,p1;
-				cff_meta = if opt then [Meta.Optional,[],p1] else [];
+				cff_meta = if opt then [Meta.Optional,[],null_pos] else [];
 				cff_access = [];
 				cff_doc = None;
 				cff_kind = FVar (Some t,None);
@@ -972,11 +1012,11 @@ and parse_type_anonymous opt = parser
 			} :: acc
 		in
 		match s with parser
-		| [< '(BrClose,p2) >] -> next p2 [],p2
+		| [< '(BrClose,p2) >] -> next [],p2
 		| [< '(Comma,p2) >] ->
 			(match s with parser
-			| [< '(BrClose,p2) >] -> next p2 [],p2
-			| [< l,p2 = parse_type_anonymous false >] -> next p2 l,punion p1 p2
+			| [< '(BrClose,p2) >] -> next [],p2
+			| [< l,p2 = parse_type_anonymous false >] -> next l,punion p1 p2
 			| [< >] -> serror());
 		| [< >] -> serror()
 
@@ -1019,14 +1059,14 @@ and parse_class_field s =
 				let t = popt parse_type_hint_with_pos s in
 				let e , p2 = (match s with parser
 				| [< '(Binop OpAssign,_); e = toplevel_expr; p2 = semicolon >] -> Some e , p2
-				| [< '(Semicolon,p2) >] -> None , p2
+				| [< p2 = semicolon >] -> None , p2
 				| [< >] -> serror()
 				) in
 				name, punion p1 p2, FProp (i1,i2,t, e)
 			| [< t = popt parse_type_hint_with_pos; s >] ->
 				let e , p2 = (match s with parser
 				| [< '(Binop OpAssign,_); e = toplevel_expr; p2 = semicolon >] -> Some e , p2
-				| [< '(Semicolon,p2) >] -> None , p2
+				| [< p2 = semicolon >] -> None , p2
 				| [< >] -> serror()
 				) in
 				name, punion p1 p2, FVar (t,e))
@@ -1035,7 +1075,7 @@ and parse_class_field s =
 				| [< e = toplevel_expr; s >] ->
 					(try ignore(semicolon s) with Error (Missing_semicolon,p) -> !display_error Missing_semicolon p);
 					Some e, pos e
-				| [< '(Semicolon,p) >] -> None, p
+				| [< p = semicolon >] -> None, p
 				| [< >] -> serror()
 			) in
 			let f = {
@@ -1109,13 +1149,17 @@ and parse_constraint_param = parser
 			tp_meta = meta;
 		}
 
+and parse_type_path_or_resume p1 s = match s with parser
+	| [< t = parse_type_path >] -> t
+	| [< >] -> if would_skip_resume p1 s then { tpackage = []; tname = ""; tparams = []; tsub = None },null_pos else raise Stream.Failure
+
 and parse_class_herit = parser
-	| [< '(Kwd Extends,_); t = parse_type_path >] -> HExtends t
-	| [< '(Kwd Implements,_); t = parse_type_path >] -> HImplements t
+	| [< '(Kwd Extends,p1); t = parse_type_path_or_resume p1 >] -> HExtends t
+	| [< '(Kwd Implements,p1); t = parse_type_path_or_resume p1 >] -> HImplements t
 
 and block1 = parser
-	| [< name,p = dollar_ident; s >] -> block2 name (Ident name) p s
-	| [< '(Const (String name),p); s >] -> block2 (quote_ident name) (String name) p s
+	| [< name,p = dollar_ident; s >] -> block2 (name,p) (Ident name) p s
+	| [< '(Const (String name),p); s >] -> block2 (quote_ident name,p) (String name) p s
 	| [< b = block [] >] -> EBlock b
 
 and block2 name ident p s =
@@ -1133,20 +1177,23 @@ and block2 name ident p s =
 				EBlock (block [e] s)
 
 and block acc s =
+	fst (block_with_pos acc null_pos s)
+
+and block_with_pos acc p s =
 	try
 		(* because of inner recursion, we can't put Display handling in errors below *)
 		let e = try parse_block_elt s with Display e -> display (EBlock (List.rev (e :: acc)),snd e) in
-		block (e :: acc) s
+		block_with_pos (e :: acc) (pos e) s
 	with
 		| Stream.Failure ->
-			List.rev acc
+			List.rev acc,p
 		| Stream.Error _ ->
-			let tk , pos = (match Stream.peek s with None -> last_token s | Some t -> t) in
+			let tk , pos = next_token s in
 			(!display_error) (Unexpected tk) pos;
-			block acc s
+			block_with_pos acc pos s
 		| Error (e,p) ->
 			(!display_error) e p;
-			block acc s
+			block_with_pos acc p s
 
 and parse_block_elt = parser
 	| [< '(Kwd Var,p1); vl = parse_var_decls p1; p2 = semicolon >] ->
@@ -1157,8 +1204,8 @@ and parse_block_elt = parser
 and parse_obj_decl = parser
 	| [< '(Comma,_); s >] ->
 		(match s with parser
-		| [< name, _ = ident; '(DblDot,_); e = expr; l = parse_obj_decl >] -> (name,e) :: l
-		| [< '(Const (String name),_); '(DblDot,_); e = expr; l = parse_obj_decl >] -> (quote_ident name,e) :: l
+		| [< name,p = ident; '(DblDot,_); e = expr; l = parse_obj_decl >] -> ((name,p),e) :: l
+		| [< '(Const (String name),p); '(DblDot,_); e = expr; l = parse_obj_decl >] -> ((quote_ident name,p),e) :: l
 		| [< >] -> [])
 	| [< >] -> []
 
@@ -1243,10 +1290,15 @@ and parse_function p1 inl = parser
 
 and expr = parser
 	| [< (name,params,p) = parse_meta_entry; s >] ->
-		(try
+		begin try
 			make_meta name params (secure_expr s) p
-		with Display e ->
-			display (make_meta name params e p))
+		with
+		| Display e ->
+			display (make_meta name params e p)
+		| Stream.Failure | Stream.Error _ when Path.unique_full_path p.pfile = (!resume_display).pfile ->
+			let e = EConst (Ident "null"),p in
+			display (make_meta name params e p)
+		end
 	| [< '(BrOpen,p1); s >] ->
 		if is_resuming p1 then display (EDisplay ((EObjectDecl [],p1),false),p1);
 		(match s with parser
@@ -1258,10 +1310,7 @@ and expr = parser
 				| [< '(BrClose,p2) >] -> p2
 				| [< >] ->
 					(* Ignore missing } if we are resuming and "guess" the last position. *)
-					if do_resume() then begin match Stream.peek s with
-						| Some (_,p) -> p
-						| None -> pos (last_token s)
-					end else serror()
+					if do_resume() then pos (next_token s) else serror()
 			in
 			let e = (b,punion p1 p2) in
 			(match b with
@@ -1283,8 +1332,8 @@ and expr = parser
 			| [< t,pt = parse_type_hint_with_pos; '(PClose,p2); s >] ->
 				let ep = EParenthesis (ECheckType(e,(t,pt)),punion p1 p2), punion p1 p2 in
 				expr_next (ECast (ep,None),punion p1 (pos ep)) s
-			| [< '(Const (Ident "is"),_); t = parse_type_path; '(PClose,p2); >] ->
-				let e_is = make_is e t (punion p1 p2) in
+			| [< '(Const (Ident "is"),p_is); t = parse_type_path; '(PClose,p2); >] ->
+				let e_is = make_is e t (punion p1 p2) p_is in
 				expr_next (ECast (e_is,None),punion p1 (pos e_is)) s
 			| [< '(PClose,p2); s >] ->
 				let ep = expr_next (EParenthesis(e),punion pp p2) s in
@@ -1292,27 +1341,23 @@ and expr = parser
 			| [< >] -> serror())
 		| [< e = secure_expr >] -> expr_next (ECast (e,None),punion p1 (pos e)) s)
 	| [< '(Kwd Throw,p); e = expr >] -> (EThrow e,p)
-	| [< '(Kwd New,p1); t = parse_type_path; '(POpen,p); s >] ->
-		if is_resuming p then display (EDisplayNew t,punion p1 p);
-		(match s with parser
-		| [< al = psep Comma expr; '(PClose,p2); s >] -> expr_next (ENew (t,al),punion p1 p2) s
-		| [< >] -> serror())
+	| [< '(Kwd New,p1); t = parse_type_path; s >] ->
+		begin match s with parser
+		| [< '(POpen,po); e = parse_call_params (fun el p2 -> (ENew(t,el)),punion p1 p2) po >] -> expr_next e s
+		| [< >] ->
+			if do_resume() then (ENew(t,[]),punion p1 (pos t))
+			else serror()
+		end
 	| [< '(POpen,p1); e = expr; s >] -> (match s with parser
 		| [< '(PClose,p2); s >] -> expr_next (EParenthesis e, punion p1 p2) s
 		| [< t,pt = parse_type_hint_with_pos; '(PClose,p2); s >] -> expr_next (EParenthesis (ECheckType(e,(t,pt)),punion p1 p2), punion p1 p2) s
-		| [< '(Const (Ident "is"),_); t = parse_type_path; '(PClose,p2); >] -> expr_next (make_is e t (punion p1 p2)) s
+		| [< '(Const (Ident "is"),p_is); t = parse_type_path; '(PClose,p2); >] -> expr_next (make_is e t (punion p1 p2) p_is) s
 		| [< >] -> serror())
 	| [< '(BkOpen,p1); l = parse_array_decl; '(BkClose,p2); s >] -> expr_next (EArrayDecl l, punion p1 p2) s
 	| [< '(Kwd Function,p1); e = parse_function p1 false; >] -> e
 	| [< '(Unop op,p1) when is_prefix op; e = expr >] -> make_unop op e p1
 	| [< '(Binop OpSub,p1); e = expr >] ->
-		let neg s =
-			if s.[0] = '-' then String.sub s 1 (String.length s - 1) else "-" ^ s
-		in
-		(match make_unop Neg e p1 with
-		| EUnop (Neg,Prefix,(EConst (Int i),pc)),p -> EConst (Int (neg i)),p
-		| EUnop (Neg,Prefix,(EConst (Float j),pc)),p -> EConst (Float (neg j)),p
-		| e -> e)
+		make_unop Neg e p1
 	(*/* removed unary + : this cause too much syntax errors go unnoticed, such as "a + + 1" (missing 'b')
 						without adding anything to the language
 	| [< '(Binop OpAdd,p1); s >] ->
@@ -1350,7 +1395,7 @@ and expr = parser
 			Display e -> display (EWhile (cond,e,NormalWhile),punion p1 (pos e)))
 	| [< '(Kwd Do,p1); e = expr; '(Kwd While,_); '(POpen,_); cond = expr; '(PClose,_); s >] -> (EWhile (cond,e,DoWhile),punion p1 (pos e))
 	| [< '(Kwd Switch,p1); e = expr; '(BrOpen,_); cases , def = parse_switch_cases e []; '(BrClose,p2); s >] -> (ESwitch (e,cases,def),punion p1 p2)
-	| [< '(Kwd Try,p1); e = expr; cl = plist (parse_catch e); >] -> (ETry (e,cl),p1)
+	| [< '(Kwd Try,p1); e = 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
 	| [< '(Kwd Untyped,p1); e = expr >] -> (EUntyped e,punion p1 (pos e))
 	| [< '(Dollar v,p); s >] -> expr_next (EConst (Ident ("$"^v)),p) s
@@ -1358,7 +1403,7 @@ and expr = parser
 and expr_next e1 = parser
 	| [< '(BrOpen,p1) when is_dollar_ident e1; eparam = expr; '(BrClose,p2); s >] ->
 		(match fst e1 with
-		| EConst(Ident n) -> expr_next (EMeta((Common.MetaInfo.from_string n,[],snd e1),eparam), punion p1 p2) s
+		| EConst(Ident n) -> expr_next (EMeta((Meta.from_string n,[],snd e1),eparam), punion p1 p2) s
 		| _ -> assert false)
 	| [< '(Dot,p); s >] ->
 		if is_resuming p then display (EDisplay (e1,false),p);
@@ -1376,14 +1421,7 @@ and expr_next e1 = parser
 			match e1 with
 			| (EConst (Int v),p2) when p2.pmax = p.pmin -> expr_next (EConst (Float (v ^ ".")),punion p p2) s
 			| _ -> serror())
-	| [< '(POpen,p1); s >] ->
-		if is_resuming p1 then display (EDisplay (e1,true),p1);
-		(match s with parser
-		| [< '(Binop OpOr,p2) when do_resume() >] ->
-			set_resume p1;
-			display (EDisplay (e1,true),p1) (* help for debug display mode *)
-		| [< params = parse_call_params e1; '(PClose,p2); s >] -> expr_next (ECall (e1,params) , punion (pos e1) p2) s
-		| [< >] -> serror())
+	| [< '(POpen,p1); e = parse_call_params (fun el p2 -> (ECall(e1,el)),punion (pos e1) p2) p1; s >] -> expr_next e s
 	| [< '(BkOpen,_); e2 = expr; '(BkClose,p2); s >] ->
 		expr_next (EArray (e1,e2), punion (pos e1) p2) s
 	| [< '(Binop OpGt,p1); s >] ->
@@ -1421,10 +1459,10 @@ and parse_guard = parser
 
 and parse_switch_cases eswitch cases = parser
 	| [< '(Kwd Default,p1); '(DblDot,_); s >] ->
-		let b = (try block [] s with Display e -> display (ESwitch (eswitch,cases,Some (Some e)),punion (pos eswitch) (pos e))) in
+		let b,p2 = (try block_with_pos [] p1 s with Display e -> display (ESwitch (eswitch,cases,Some (Some e,punion p1 (pos e))),punion (pos eswitch) (pos e))) in
 		let b = match b with
-			| [] -> None
-			| _ -> Some ((EBlock b,p1))
+			| [] -> None,p1
+			| _ -> let p = punion p1 p2 in Some ((EBlock b,p)),p
 		in
 		let l , def = parse_switch_cases eswitch cases s in
 		(match def with None -> () | Some _ -> error Duplicate_default p1);
@@ -1433,12 +1471,14 @@ and parse_switch_cases eswitch cases = parser
 		(match el with
 		| [] -> error (Custom "case without a pattern is not allowed") p1
 		| _ ->
-			let b = (try block [] s with Display e -> display (ESwitch (eswitch,List.rev ((el,eg,Some e) :: cases),None),punion (pos eswitch) (pos e))) in
-			let b = match b with
-				| [] -> None
-				| _ -> Some ((EBlock b,p1))
+			let b,p2 = (try block_with_pos [] p1 s with Display e -> display (ESwitch (eswitch,List.rev ((el,eg,Some e,punion p1 (pos e)) :: cases),None),punion (pos eswitch) (pos e))) in
+			let b,p = match b with
+				| [] ->
+					let p2 = match eg with Some e -> pos e | None -> match List.rev el with (_,p) :: _ -> p | [] -> p1 in
+					None,punion p1 p2
+				| _ -> let p = punion p1 p2 in Some ((EBlock b,p)),p
 			in
-			parse_switch_cases eswitch ((el,eg,b) :: cases) s
+			parse_switch_cases eswitch ((el,eg,b,p) :: cases) s
 		)
 	| [< >] ->
 		List.rev cases , None
@@ -1448,30 +1488,47 @@ and parse_catch etry = parser
 		match s with parser
 		| [< t,pt = parse_type_hint_with_pos; '(PClose,_); s >] ->
 			(try
-				((name,pn),(t,pt),secure_expr s)
+				let e = secure_expr s in
+				((name,pn),(t,pt),e,punion p (pos e)),(pos e)
 			with
-				Display e -> display (ETry (etry,[(name,pn),(t,pt),e]),punion (pos etry) (pos e)))
+				Display e -> display (ETry (etry,[(name,pn),(t,pt),e,(pos e)]),punion (pos etry) (pos e)))
 		| [< '(_,p) >] -> error Missing_type p
 
-and parse_call_params ec s =
-	let e = (try
+and parse_catches etry catches pmax = parser
+	| [< (catch,pmax) = parse_catch etry; s >] -> parse_catches etry (catch :: catches) pmax s
+	| [< >] -> List.rev catches,pmax
+
+and parse_call_params f p1 s =
+	let make_display_call el p2 =
+		let e = f el p2 in
+		display (EDisplay(e,true),pos e)
+	in
+	if is_resuming p1 then make_display_call [] p1;
+	let rec parse_next_param acc p1 =
+		let e = try
+			expr s
+		with
+		| Stream.Error _ | Stream.Failure as exc ->
+			let p2 = pos (next_token s) in
+			if encloses_resume (punion p1 p2) then make_display_call (List.rev acc) p2
+			else raise exc
+		| Display e ->
+			display (f (List.rev (e :: acc)) (pos e))
+		in
 		match s with parser
-		| [< e = expr >] -> Some e
-		| [< >] -> None
-	with Display e ->
-		display (ECall (ec,[e]),punion (pos ec) (pos e))
-	) in
-	let rec loop acc =
-		try
-			match s with parser
-			| [< '(Comma,_); e = expr >] -> loop (e::acc)
-			| [< >] -> List.rev acc
-		with Display e ->
-			display (ECall (ec,List.rev (e::acc)),punion (pos ec) (pos e))
+		| [< '(PClose,p2) >] -> f (List.rev (e :: acc)) p2
+		| [< '(Comma,p2) >] -> parse_next_param (e :: acc) p2
+		| [< '(Semicolon,p2) >] -> if encloses_resume (punion p1 p2) then make_display_call (List.rev acc) p2 else serror()
+		| [< >] ->
+			let p2 = pos (next_token s) in
+			if encloses_resume (punion p1 p2) then make_display_call (List.rev (e :: acc)) p2 else serror()
 	in
-	match e with
-	| None -> []
-	| Some e -> loop [e]
+	match s with parser
+	| [< '(PClose,p2) >] -> f [] p2
+	| [< '(Binop OpOr,p2) when do_resume() >] ->
+		set_resume p1;
+		make_display_call [] p2
+	| [< >] -> parse_next_param [] p1
 
 and parse_macro_cond allow_op s =
 	match s with parser
@@ -1492,6 +1549,7 @@ and parse_macro_cond allow_op s =
 		tk, make_unop op e p
 
 and parse_macro_ident allow_op t p s =
+	if t = "display" then Hashtbl.replace special_identifier_files (Path.unique_full_path p.pfile) t;
 	let e = (EConst (Ident t),p) in
 	if not allow_op then
 		None, e
@@ -1617,7 +1675,7 @@ let parse ctx code =
 			| _ -> error Unimplemented (snd tk))
 		| Sharp "line" ->
 			let line = (match next_token() with
-				| (Const (Int s),_) -> int_of_string s
+				| (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;
@@ -1628,7 +1686,7 @@ let parse ctx code =
 	and enter_macro p =
 		let tk, e = parse_macro_cond false sraw in
 		let tk = (match tk with None -> Lexer.token code | Some tk -> tk) in
-		if is_true (eval ctx e) || (match fst e with EConst (Ident "macro") when Common.unique_full_path p.pfile = (!resume_display).pfile -> true | _ -> false) then begin
+		if is_true (eval ctx e) || (match fst e with EConst (Ident "macro") when Path.unique_full_path p.pfile = (!resume_display).pfile -> true | _ -> false) then begin
 			mstack := p :: !mstack;
 			tk
 		end else
@@ -1677,3 +1735,39 @@ let parse ctx code =
 			Lexer.restore old;
 			cache := old_cache;
 			raise e
+
+let parse_string 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 old_display = !resume_display in
+	let old_de = !display_error in
+	let restore() =
+		(match old_file with
+		| None -> ()
+		| Some f -> Hashtbl.replace Lexer.all_files p.pfile f);
+		if not inlined then resume_display := old_display;
+		Lexer.restore old;
+		display_error := old_de
+	in
+	Lexer.init p.pfile true;
+	display_error := (fun e p -> raise (Error (e,p)));
+	if not inlined then resume_display := null_pos;
+	let pack, decls = try
+		parse com (Lexing.from_string s)
+	with Error (e,pe) ->
+		restore();
+		error (error_msg e) (if inlined then pe else p)
+	| Lexer.Error (e,pe) ->
+		restore();
+		error (Lexer.error_msg e) (if inlined then pe else p)
+	in
+	restore();
+	pack,decls
+
+let parse_expr_string com s p error inl =
+	let head = "class X{static function main() " in
+	let head = (if p.pmin > String.length head then head ^ String.make (p.pmin - String.length head) ' ' else head) in
+	let rec loop e = let e = Ast.map_expr loop e in (fst e,p) in
+	match parse_string com (head ^ s ^ ";}") p error inl with
+	| _,[EClass { d_data = [{ cff_name = "main",null_pos; cff_kind = FFun { f_expr = Some e } }]},_] -> if inl then e else loop e
+	| _ -> raise Exit

+ 67 - 0
src/typing/abstract.ml

@@ -0,0 +1,67 @@
+open Meta
+open Type
+open Error
+
+let find_to ab pl b =
+	if follow b == t_dynamic then
+		List.find (fun (t,_) -> follow t == t_dynamic) ab.a_to_field
+	else if List.exists (unify_to ab pl ~allow_transitive_cast:false b) ab.a_to then
+		raise Not_found (* legacy compatibility *)
+	else
+		List.find (unify_to_field ab pl b) ab.a_to_field
+
+let find_from ab pl a b =
+	if follow a == t_dynamic then
+		List.find (fun (t,_) -> follow t == t_dynamic) ab.a_from_field
+	else if List.exists (unify_from ab pl a ~allow_transitive_cast:false b) ab.a_from then
+		raise Not_found (* legacy compatibility *)
+	else
+		List.find (unify_from_field ab pl a b) ab.a_from_field
+
+let underlying_type_stack = ref []
+
+let rec get_underlying_type a pl =
+	let maybe_recurse t =
+		underlying_type_stack := (TAbstract(a,pl)) :: !underlying_type_stack;
+		let rec loop t = match t with
+			| TMono r ->
+				(match !r with
+				| Some t -> loop t
+				| _ -> t)
+			| TLazy f ->
+				loop (!f())
+			| TType({t_path=([],"Null")} as tn,[t1]) ->
+				TType(tn,[loop t1])
+			| TType (t,tl) ->
+				loop (apply_params t.t_params tl t.t_type)
+			| TAbstract(a,tl) when not (Meta.has Meta.CoreType a.a_meta) ->
+				if List.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))) in
+					underlying_type_stack := [];
+					error ("Abstract chain detected: " ^ s) a.a_pos
+				end;
+				get_underlying_type a tl
+			| _ ->
+				t
+		in
+		let t = loop t in
+		underlying_type_stack := List.tl !underlying_type_stack;
+		t
+	in
+	try
+		if not (Meta.has Meta.MultiType a.a_meta) then raise Not_found;
+		let m = mk_mono() in
+		let _ = find_to a pl m in
+		maybe_recurse (follow m)
+	with Not_found ->
+		if Meta.has Meta.CoreType a.a_meta then
+			t_dynamic
+		else
+			maybe_recurse (apply_params a.a_params pl a.a_this)
+
+let rec follow_with_abstracts t = match follow t with
+	| TAbstract(a,tl) when not (Meta.has Meta.CoreType a.a_meta) ->
+		follow_with_abstracts (get_underlying_type a tl)
+	| t ->
+		t

+ 94 - 0
src/typing/error.ml

@@ -0,0 +1,94 @@
+open Globals
+open Type
+
+type call_error =
+	| Not_enough_arguments of (string * bool * t) list
+	| Too_many_arguments
+	| Could_not_unify of error_msg
+	| Cannot_skip_non_nullable of string
+
+and error_msg =
+	| Module_not_found of path
+	| Type_not_found of path * string
+	| Unify of unify_error list
+	| Custom of string
+	| Unknown_ident of string
+	| Stack of error_msg * error_msg
+	| Call_error of call_error
+	| No_constructor of module_type
+
+exception Fatal_error of string * Globals.pos
+exception Error of error_msg * Globals.pos
+
+let string_source t = match follow t with
+	| TInst(c,_) -> List.map (fun cf -> cf.cf_name) c.cl_ordered_fields
+	| TAnon a -> PMap.fold (fun cf acc -> cf.cf_name :: acc) a.a_fields []
+	| TAbstract({a_impl = Some c},_) -> List.map (fun cf -> cf.cf_name) c.cl_ordered_statics
+	| _ -> []
+
+let short_type ctx t =
+	let tstr = s_type ctx t in
+	if String.length tstr > 150 then String.sub tstr 0 147 ^ "..." else tstr
+
+let unify_error_msg ctx = function
+	| Cannot_unify (t1,t2) ->
+		s_type ctx t1 ^ " should be " ^ s_type ctx t2
+	| Invalid_field_type s ->
+		"Invalid type for field " ^ s ^ " :"
+	| Has_no_field (t,n) ->
+		StringError.string_error n (string_source t) (short_type ctx t ^ " has no field " ^ n)
+	| Has_no_runtime_field (t,n) ->
+		s_type ctx t ^ "." ^ n ^ " is not accessible at runtime"
+	| Has_extra_field (t,n) ->
+		short_type ctx t ^ " has extra field " ^ n
+	| Invalid_kind (f,a,b) ->
+		(match a, b with
+		| Var va, Var vb ->
+			let name, stra, strb = if va.v_read = vb.v_read then
+				"setter", s_access false va.v_write, s_access false vb.v_write
+			else if va.v_write = vb.v_write then
+				"getter", s_access true va.v_read, s_access true vb.v_read
+			else
+				"access", "(" ^ s_access true va.v_read ^ "," ^ s_access false va.v_write ^ ")", "(" ^ s_access true vb.v_read ^ "," ^ s_access false vb.v_write ^ ")"
+			in
+			"Inconsistent " ^ name ^ " for field " ^ f ^ " : " ^ stra ^ " should be " ^ strb
+		| _ ->
+			"Field " ^ f ^ " is " ^ s_kind a ^ " but should be " ^ s_kind b)
+	| Invalid_visibility n ->
+		"The field " ^ n ^ " is not public"
+	| Not_matching_optional n ->
+		"Optional attribute of parameter " ^ n ^ " differs"
+	| Cant_force_optional ->
+		"Optional parameters can't be forced"
+	| Invariant_parameter _ ->
+		"Type parameters are invariant"
+	| Constraint_failure name ->
+		"Constraint check failure for " ^ name
+	| Missing_overload (cf, t) ->
+		cf.cf_name ^ " has no overload for " ^ s_type ctx t
+	| Unify_custom msg ->
+		msg
+
+let rec error_msg = function
+	| Module_not_found m -> "Type not found : " ^ s_type_path m
+	| Type_not_found (m,t) -> "Module " ^ s_type_path m ^ " does not define type " ^ t
+	| Unify l ->
+		let ctx = print_context() in
+		String.concat "\n" (List.map (unify_error_msg ctx) 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")
+
+and s_call_error = 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
+
+let error msg p = raise (Error (Custom msg,p))
+
+let raise_error err p = raise (Error(err,p))

+ 60 - 47
src/typing/matcher.ml

@@ -17,16 +17,18 @@
 	Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  *)
 
+open Globals
 open Ast
 open Type
 open Common
+open Error
 
 exception Internal_match_failure
 
 let s_type = s_type (print_context())
-let s_expr_pretty = s_expr_pretty false "" s_type
+let s_expr_pretty = s_expr_pretty false "" false s_type
 
-let fake_tuple_type = TInst(mk_class null_module ([],"-Tuple") null_pos, [])
+let fake_tuple_type = TInst(mk_class null_module ([],"-Tuple") null_pos null_pos, [])
 
 let tuple_type tl =
 	tfun tl fake_tuple_type
@@ -187,7 +189,8 @@ module Pattern = struct
 		let check_expr e =
 			let rec loop e = match e.eexpr with
 				| TField(_,FEnum(en,ef)) ->
-					(match follow ef.ef_type with TFun _ -> raise Exit | _ -> ());
+					(* Let the unification afterwards fail so we don't recover. *)
+					(* (match follow ef.ef_type with TFun _ -> raise Exit | _ -> ()); *)
 					PatConstructor(ConEnum(en,ef),[])
 				| TField(_,FStatic(c,({cf_kind = Var {v_write = AccNever}} as cf))) ->
 					PatConstructor(ConStatic(c,cf),[])
@@ -212,6 +215,7 @@ module Pattern = struct
 					unify_type_pattern ctx mt t e.epos;
 					PatConstructor(ConTypeExpr mt,[])
 				| _ ->
+					let pat = check_expr e in
 					begin try
 						Type.unify e.etype t
 					with (Unify_error l) ->
@@ -221,36 +225,40 @@ module Pattern = struct
 							| _ -> raise_or_display ctx l p
 						end
 					end;
-					check_expr e
+					pat
 		in
 		let handle_ident s p =
 			let save =
-				let old = ctx.in_call_args,ctx.locals in
-				ctx.in_call_args <- true;
-				ctx.locals <- PMap.empty;
+				let old = ctx.locals in
+				ctx.locals <- (try PMap.add "this" (PMap.find "this" old) PMap.empty with Not_found -> PMap.empty);
 				(fun () ->
-					ctx.in_call_args <- fst old;
-					ctx.locals <- snd old;
+					ctx.locals <- old;
 				)
 			in
 			try
 				let pat = try_typing (EConst (Ident s),p) in
 				save();
 				pat
-			with _ -> try
-				let mt = module_type_of_type t in
-				let e_mt = Typer.type_module_type ctx mt None p in
-				let e = type_field_access ctx ~resume:true e_mt s in
-				let pat = check_expr e in
-				save();
-				pat
-			with _ ->
+			with
+			| Exit | Bad_pattern _ ->
+				begin try
+					let mt = module_type_of_type t in
+					let e_mt = Typer.type_module_type ctx mt None p in
+					let e = type_field_access ctx ~resume:true e_mt s in
+					let pat = check_expr e in
+					save();
+					pat
+				with _ ->
+					save();
+					if not (is_lower_ident s) && (match s.[0] with '`' | '_' -> false | _ -> true) then begin
+						display_error ctx "Capture variables must be lower-case" p;
+					end;
+					let v = add_local s p in
+					PatVariable v
+				end
+			| exc ->
 				save();
-				if not (is_lower_ident s) && (match s.[0] with '`' | '_' -> false | _ -> true) then begin
-					display_error ctx "Capture variables must be lower-case" p;
-				end;
-				let v = add_local s p in
-				PatVariable v
+				raise exc
 		in
 		let rec loop e = match fst e with
 			| EParenthesis e1 | ECast(e1,None) ->
@@ -375,7 +383,7 @@ module Pattern = struct
 				let patterns,fields = List.fold_left (fun (patterns,fields) (cf,t) ->
 					try
 						if pctx.in_reification && cf.cf_name = "pos" then raise Not_found;
-						let e1 = List.assoc cf.cf_name fl in
+						let e1 = Expr.field_assoc cf.cf_name fl in
 						make pctx t e1 :: patterns,cf.cf_name :: fields
 					with Not_found ->
 						if is_matchable cf then
@@ -383,7 +391,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 error (Printf.sprintf "%s has no field %s" (s_type t) s) (pos e)) fl;
 				PatConstructor(ConFields fields,patterns)
 			| EBinop(OpOr,e1,e2) ->
 				let pctx1 = {pctx with current_locals = PMap.empty} in
@@ -405,9 +413,10 @@ module Pattern = struct
 				v.v_name <- "tmp";
 				let pat = make pctx e1.etype e2 in
 				PatExtractor(v,e1,pat)
-			| EDisplay(e,call) ->
+			| EDisplay(e,iscall) ->
 				let pat = loop e in
-				let _ = Typer.handle_display ctx e call (WithType t) in
+				let _ = if iscall then Typer.handle_signature_display ctx e (WithType t)
+				else Typer.handle_display ctx e (WithType t) in
 				pat
 			| _ ->
 				fail()
@@ -434,7 +443,7 @@ module Case = struct
 		case_pos : pos;
 	}
 
-	let make ctx t el eg eo with_type =
+	let make ctx t el eg eo_ast with_type p =
 		let rec collapse_case el = match el with
 			| e :: [] ->
 				e
@@ -461,7 +470,7 @@ module Case = struct
 			| None -> None
 			| Some e -> Some (type_expr ctx e Value)
 		in
-		let eo = match eo,with_type with
+		let eo = match eo_ast,with_type with
 			| None,WithType t ->
 				unify ctx ctx.t.tvoid t (pos e);
 				None
@@ -469,7 +478,7 @@ module Case = struct
 				None
 			| Some e,WithType t ->
 				let e = type_expr ctx e (WithType (map t)) in
-				let e = Codegen.AbstractCast.cast_or_unify ctx (map t) e e.epos in
+				let e = AbstractCast.cast_or_unify ctx (map t) e e.epos in
 				Some e
 			| Some e,_ ->
 				let e = type_expr ctx e with_type in
@@ -478,10 +487,15 @@ module Case = struct
 		ctx.ret <- old_ret;
 		List.iter (fun (v,t) -> v.v_type <- t) old_types;
 		save();
+		if ctx.is_display_file && Display.is_display_position p then begin match eo,eo_ast with
+			| Some e,Some e_ast -> ignore(Typer.display_expr ctx e_ast e with_type p)
+			| None,None -> ignore(Typer.display_expr ctx (EBlock [],p) (mk (TBlock []) ctx.t.tvoid p) with_type p)
+			| _ -> assert false
+		end;
 		{
 			case_guard = eg;
 			case_expr = eo;
-			case_pos = pos e;
+			case_pos = p;
 		},[],pat
 end
 
@@ -512,7 +526,7 @@ module Decision_tree = struct
 
 	let s_case_expr tabs case = match case.case_expr with
 		| None -> ""
-		| Some e -> Type.s_expr_pretty false tabs s_type e
+		| Some e -> Type.s_expr_pretty false tabs false s_type e
 
 	let rec to_string tabs dt = match dt.dt_t with
 		| Leaf case ->
@@ -523,7 +537,7 @@ module Decision_tree = struct
 			in
 			let s_cases = String.concat "" (List.map s_case cases) in
 			let s_default = to_string (tabs ^ "\t") dt in
-			Printf.sprintf "switch (%s) {%s\n%s\tdefault: %s\n%s}" (Type.s_expr_pretty false tabs s_type e) s_cases tabs s_default tabs
+			Printf.sprintf "switch (%s) {%s\n%s\tdefault: %s\n%s}" (Type.s_expr_pretty false tabs false s_type e) s_cases tabs s_default tabs
 		| Bind(bl,dt) ->
 			(String.concat "" (List.map (fun (v,_,e) -> if v.v_name = "_" then "" else Printf.sprintf "%s<%i> = %s; " v.v_name v.v_id (s_expr_pretty e)) bl)) ^
 			to_string tabs dt
@@ -740,7 +754,7 @@ module Useless = struct
 	let check_case com 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 pattern is unused" case.case_pos
+			| False -> com.warning "This case is unused" case.case_pos
 			| Pos p -> com.warning "This pattern is unused" p
 			| True -> ()
 
@@ -882,8 +896,8 @@ module Compile = struct
 	let s_case (case,bindings,patterns) =
 		let s_bindings = String.concat ", " (List.map (fun (v,_,e) -> Printf.sprintf "%s<%i> = %s" v.v_name v.v_id (s_expr_pretty e)) bindings) in
 		let s_patterns = String.concat " " (List.map Pattern.to_string patterns) in
-		let s_expr = match case.case_expr with None -> "" | Some e -> Type.s_expr_pretty false "\t\t" s_type e in
-		let s_guard = match case.case_guard with None -> "" | Some e -> Type.s_expr_pretty false "\t\t" s_type e in
+		let s_expr = match case.case_expr with None -> "" | Some e -> Type.s_expr_pretty false "\t\t" false s_type e in
+		let s_guard = match case.case_guard with None -> "" | Some e -> Type.s_expr_pretty false "\t\t" false s_type e in
 		Printf.sprintf "\n\t\tbindings: %s\n\t\tpatterns: %s\n\t\tguard: %s\n\t\texpr: %s" s_bindings s_patterns s_guard s_expr
 
 	let s_cases cases =
@@ -1033,6 +1047,7 @@ module Compile = struct
 					v,ex_bindings
 				with Not_found ->
 					let v = alloc_var "_hx_tmp" e1.etype e1.epos in
+					v.v_meta <- (Meta.Custom ":extractorVariable",[],v.v_pos) :: v.v_meta;
 					v,(v,e1.epos,e1) :: ex_bindings
 				in
 				let ev = mk (TLocal v) v.v_type e1.epos in
@@ -1059,7 +1074,6 @@ module Compile = struct
 				(e :: subjects,vars)
 			| _ ->
 				let v = gen_local ctx e.etype e.epos in
-				v.v_meta <- (Meta.SwitchVariable,[],e.epos) :: v.v_meta;
 				let ev = mk (TLocal v) e.etype e.epos in
 				(ev :: subjects,(v,e.epos,e) :: vars)
 		) ([],[]) subjects in
@@ -1236,7 +1250,7 @@ module TexprConverter = struct
 		let p = dt.dt_pos in
 		let c_type = match follow (Typeload.load_instance ctx ({ tpackage = ["std"]; tname="Type"; tparams=[]; tsub = None},null_pos) true p) with TInst(c,_) -> c | t -> assert false in
 		let mk_index_call e =
-			if ctx.com.display <> DMNone then
+			if not ctx.in_macro && 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
@@ -1245,7 +1259,7 @@ module TexprConverter = struct
 				make_static_call ctx c_type cf (fun t -> t) [e] com.basic.tint e.epos
 		in
 		let mk_name_call e =
-			if ctx.com.display <> DMNone then
+			if not ctx.in_macro && 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
@@ -1267,7 +1281,7 @@ module TexprConverter = struct
 					with Not_exhaustive -> match with_type,finiteness with
 						| NoValue,Infinite -> None
 						| _,CompileTimeFinite when unmatched = [] -> None
-						| _ when ctx.com.display <> DMNone -> None
+						| _ when ctx.com.display.DisplayMode.dms_error_policy = DisplayMode.EPIgnore -> None
 						| _ -> report_not_exhaustive e_subject unmatched
 				in
 				let cases = ExtList.List.filter_map (fun (con,_,dt) -> match unify_constructor ctx params e_subject.etype con with
@@ -1325,7 +1339,7 @@ module TexprConverter = struct
 				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)) e_then.etype (punion e_then.epos e_else.epos)
+					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)
 				end
@@ -1336,11 +1350,11 @@ module TexprConverter = struct
 					(fun () ->
 						let e_else = loop false params dt2 in
 						let e_op = mk (TBinop(OpEq,e,e_null)) ctx.t.tbool e.epos in
-						mk (TIf(e_op,e_then,Some e_else)) e_then.etype (punion e_then.epos e_else.epos)
+						mk (TIf(e_op,e_then,Some e_else)) t_switch (punion e_then.epos e_else.epos)
 					)
 				with Not_exhaustive ->
 					if toplevel then (fun () -> loop false params dt2)
-					else if ctx.com.display <> DMNone then (fun () -> mk (TConst TNull) (mk_mono()) dt2.dt_pos)
+					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 e [ConConst TNull,dt.dt_pos]
 				in
 				f()
@@ -1362,7 +1376,6 @@ module Match = struct
 	open Typecore
 
 	let match_expr ctx e cases def with_type p =
-		(* if p.pfile <> "src/Main.hx" then raise Exit; *)
 		let match_debug = Meta.has (Meta.Custom ":matchDebug") ctx.curfield.cf_meta in
 		let rec loop e = match fst e with
 			| EArrayDecl el when (match el with [(EFor _ | EWhile _),_] -> false | _ -> true) ->
@@ -1379,14 +1392,14 @@ module Match = struct
 		let subjects = List.rev subjects in
 		let cases = match def with
 			| None -> cases
-			| Some eo -> cases @ [[EConst (Ident "_"),(match eo with None -> p | Some e -> pos e)],None,eo]
+			| Some (eo,p) -> cases @ [[EConst (Ident "_"),p],None,eo,p]
 		in
 		let tmono,with_type = match with_type with
 			| WithType t -> (match follow t with TMono _ -> Some t,Value | _ -> None,with_type)
 			| _ -> None,with_type
 		in
-		let cases = List.map (fun (el,eg,eo) ->
-			let case,bindings,pat = Case.make ctx t el eg eo with_type in
+		let cases = List.map (fun (el,eg,eo,p) ->
+			let case,bindings,pat = Case.make ctx t el eg eo with_type p in
 			case,bindings,[pat]
 		) cases in
 		let infer_switch_type () =
@@ -1422,7 +1435,7 @@ module Match = struct
 			print_endline (s_expr_pretty e);
 			print_endline "TEXPR END";
 		end;
-		e
+		{e with epos = p}
 end
 ;;
 Typecore.match_expr_ref := Match.match_expr

+ 261 - 0
src/typing/overloads.ml

@@ -0,0 +1,261 @@
+open Type
+
+let same_overload_args ?(get_vmtype) t1 t2 f1 f2 =
+	let get_vmtype = match get_vmtype with
+		| None -> (fun f -> f)
+		| Some f -> f
+	in
+	if List.length f1.cf_params <> List.length f2.cf_params then
+		false
+	else
+	let rec follow_skip_null t = match t with
+		| TMono r ->
+			(match !r with
+			| Some t -> follow_skip_null t
+			| _ -> t)
+		| TLazy f ->
+			follow_skip_null (!f())
+		| TType ({ t_path = [],"Null" } as t, [p]) ->
+			TType(t,[follow p])
+		| TType (t,tl) ->
+			follow_skip_null (apply_params t.t_params tl t.t_type)
+		| _ -> t
+	in
+	let same_arg t1 t2 =
+		let t1 = get_vmtype (follow_skip_null t1) in
+		let t2 = get_vmtype (follow_skip_null t2) in
+		match t1, t2 with
+			| TType _, TType _ -> type_iseq t1 t2
+			| TType _, _
+			| _, TType _ -> false
+			| _ -> type_iseq t1 t2
+	in
+
+	match follow (apply_params f1.cf_params (List.map (fun (_,t) -> t) f2.cf_params) t1), follow t2 with
+		| TFun(a1,_), TFun(a2,_) ->
+			(try
+				List.for_all2 (fun (_,_,t1) (_,_,t2) ->
+				same_arg t1 t2) a1 a2
+			with | Invalid_argument("List.for_all2") ->
+				false)
+		| _ -> assert false
+
+
+(** retrieves all overloads from class c and field i, as (Type.t * tclass_field) list *)
+let rec get_overloads c i =
+	let ret = try
+			let f = PMap.find i c.cl_fields in
+			match f.cf_kind with
+				| Var _ ->
+					(* @:libType may generate classes that have a variable field in a superclass of an overloaded method *)
+					[]
+				| Method _ ->
+					(f.cf_type, f) :: (List.map (fun f -> f.cf_type, f) f.cf_overloads)
+		with | Not_found -> []
+	in
+	let rsup = match c.cl_super with
+	| None when c.cl_interface ->
+			let ifaces = List.concat (List.map (fun (c,tl) ->
+				List.map (fun (t,f) -> apply_params c.cl_params tl t, f) (get_overloads c i)
+			) c.cl_implements) in
+			ret @ ifaces
+	| None -> ret
+	| Some (c,tl) ->
+			ret @ ( List.map (fun (t,f) -> apply_params c.cl_params tl t, f) (get_overloads c i) )
+	in
+	ret @ (List.filter (fun (t,f) -> not (List.exists (fun (t2,f2) -> same_overload_args t t2 f f2) ret)) rsup)
+
+(** Overload resolution **)
+module Resolution =
+struct
+	let rec simplify_t t = match t with
+		| TAbstract(a,_) when Meta.has Meta.CoreType a.a_meta ->
+			t
+		| TInst _ | TEnum _ ->
+			t
+		| TAbstract(a,tl) -> simplify_t (Abstract.get_underlying_type a tl)
+		| TType(({ t_path = [],"Null" } as t), [t2]) -> (match simplify_t t2 with
+			| (TAbstract(a,_) as t2) when Meta.has Meta.CoreType a.a_meta ->
+				TType(t, [simplify_t t2])
+			| (TEnum _ as t2) ->
+				TType(t, [simplify_t t2])
+			| t2 -> t2)
+		| TType(t, tl) ->
+			simplify_t (apply_params t.t_params tl t.t_type)
+		| TMono r -> (match !r with
+			| Some t -> simplify_t t
+			| None -> t_dynamic)
+		| TAnon _ -> t_dynamic
+		| TDynamic _ -> t
+		| TLazy f -> simplify_t (!f())
+		| TFun _ -> t
+
+	(* rate type parameters *)
+	let rate_tp tlfun tlarg =
+		let acc = ref 0 in
+		List.iter2 (fun f a -> if not (type_iseq f a) then incr acc) tlfun tlarg;
+		!acc
+
+	(**
+		The rate function returns an ( int * int ) type.
+		The smaller the int, the best rated the caller argument is in comparison with the callee.
+
+		The first int refers to how many "conversions" would be necessary to convert from the callee to the caller type, and
+		the second refers to the type parameters.
+	**)
+	let rec rate_conv cacc tfun targ =
+		match simplify_t tfun, simplify_t targ with
+		| TInst({ cl_interface = true } as cf, tlf), TInst(ca, tla) ->
+			(* breadth-first *)
+			let stack = ref [0,ca,tla] in
+			let cur = ref (0, ca,tla) in
+			let rec loop () =
+				match !stack with
+				| [] -> (let acc, ca, tla = !cur in match ca.cl_super with
+					| None -> raise Not_found
+					| Some (sup,tls) ->
+						cur := (acc+1,sup,List.map (apply_params ca.cl_params tla) tls);
+						stack := [!cur];
+						loop())
+				| (acc,ca,tla) :: _ when ca == cf ->
+					acc,tla
+				| (acc,ca,tla) :: s ->
+					stack := s @ List.map (fun (c,tl) -> (acc+1,c,List.map (apply_params ca.cl_params tla) tl)) ca.cl_implements;
+					loop()
+			in
+			let acc, tla = loop() in
+			(cacc + acc, rate_tp tlf tla)
+		| TInst(cf,tlf), TInst(ca,tla) ->
+			let rec loop acc ca tla =
+				if cf == ca then
+					acc, tla
+				else match ca.cl_super with
+				| None -> raise Not_found
+				| Some(sup,stl) ->
+					loop (acc+1) sup (List.map (apply_params ca.cl_params tla) stl)
+			in
+			let acc, tla = loop 0 ca tla in
+			(cacc + acc, rate_tp tlf tla)
+		| TEnum(ef,tlf), TEnum(ea, tla) ->
+			if ef != ea then raise Not_found;
+			(cacc, rate_tp tlf tla)
+		| TDynamic _, TDynamic _ ->
+			(cacc, 0)
+		| TDynamic _, _ ->
+			(max_int, 0) (* a function with dynamic will always be worst of all *)
+		| TAbstract(a, _), TDynamic _ when Meta.has Meta.CoreType a.a_meta ->
+			(cacc + 2, 0) (* a dynamic to a basic type will have an "unboxing" penalty *)
+		| _, TDynamic _ ->
+			(cacc + 1, 0)
+		| TAbstract(af,tlf), TAbstract(aa,tla) ->
+			(if af == aa then
+				(cacc, rate_tp tlf tla)
+			else
+				let ret = ref None in
+				if List.exists (fun t -> try
+					ret := Some (rate_conv (cacc+1) (apply_params af.a_params tlf t) targ);
+					true
+				with | Not_found ->
+					false
+				) af.a_from then
+					Option.get !ret
+			else
+				if List.exists (fun t -> try
+					ret := Some (rate_conv (cacc+1) tfun (apply_params aa.a_params tla t));
+					true
+				with | Not_found ->
+					false
+				) aa.a_to then
+					Option.get !ret
+			else
+				raise Not_found)
+		| TType({ t_path = [], "Null" }, [tf]), TType({ t_path = [], "Null" }, [ta]) ->
+			rate_conv (cacc+0) tf ta
+		| TType({ t_path = [], "Null" }, [tf]), ta ->
+			rate_conv (cacc+1) tf ta
+		| tf, TType({ t_path = [], "Null" }, [ta]) ->
+			rate_conv (cacc+1) tf ta
+		| TFun _, TFun _ -> (* unify will make sure they are compatible *)
+			cacc,0
+		| tfun,targ ->
+			raise Not_found
+
+	let is_best arg1 arg2 =
+		(List.for_all2 (fun v1 v2 ->
+			v1 <= v2)
+		arg1 arg2) && (List.exists2 (fun v1 v2 ->
+			v1 < v2)
+		arg1 arg2)
+
+	let rec rm_duplicates acc ret = match ret with
+		| [] -> acc
+		| ( el, t, _ ) :: ret when List.exists (fun (_,t2,_) -> type_iseq t t2) acc ->
+			rm_duplicates acc ret
+		| r :: ret ->
+			rm_duplicates (r :: acc) ret
+
+	let s_options rated =
+		String.concat ",\n" (List.map (fun ((elist,t,_),rate) ->
+			"( " ^ (String.concat "," (List.map (fun(e,_) -> s_expr (s_type (print_context())) e) elist)) ^ " ) => " ^
+			"( " ^ (String.concat "," (List.map (fun (i,i2) -> string_of_int i ^ ":" ^ string_of_int i2) rate)) ^ " ) => " ^ (s_type (print_context()) t)
+		) rated)
+
+	let count_optionals elist =
+		List.fold_left (fun acc (_,is_optional) -> if is_optional then acc + 1 else acc) 0 elist
+
+	let rec fewer_optionals acc compatible = match acc, compatible with
+		| _, [] -> acc
+		| [], c :: comp -> fewer_optionals [c] comp
+		| (elist_acc, _, _) :: _, ((elist, _, _) as cur) :: comp ->
+			let acc_opt = count_optionals elist_acc in
+			let comp_opt = count_optionals elist in
+			if acc_opt = comp_opt then
+				fewer_optionals (cur :: acc) comp
+			else if acc_opt < comp_opt then
+				fewer_optionals acc comp
+			else
+				fewer_optionals [cur] comp
+
+	let reduce_compatible compatible = match fewer_optionals [] (rm_duplicates [] compatible) with
+		| [] -> []
+		| [v] -> [v]
+		| compatible ->
+			(* convert compatible into ( rate * compatible_type ) list *)
+			let rec mk_rate acc elist args = match elist, args with
+				| [], [] -> acc
+				| (_,true) :: elist, _ :: args -> mk_rate acc elist args
+				| (e,false) :: elist, (n,o,t) :: args ->
+					(* if the argument is an implicit cast, we need to start with a penalty *)
+					(* The penalty should be higher than any other implicit cast - other than Dynamic *)
+					(* since Dynamic has a penalty of max_int, we'll impose max_int - 1 to it *)
+					(match e.eexpr with
+						| TMeta( (Meta.ImplicitCast,_,_), _) ->
+							mk_rate ((max_int - 1, 0) :: acc) elist args
+						| _ ->
+							mk_rate (rate_conv 0 t e.etype :: acc) elist args)
+				| _ -> assert false
+			in
+
+			let rated = ref [] in
+			List.iter (function
+				| (elist,TFun(args,ret),d) -> (try
+					rated := ( (elist,TFun(args,ret),d), mk_rate [] elist args ) :: !rated
+					with | Not_found -> ())
+				| _ -> assert false
+			) 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
+			in
+
+			let r = loop [] !rated in
+			List.map fst r
+end

+ 293 - 113
src/typing/type.ml

@@ -18,6 +18,7 @@
  *)
 
 open Ast
+open Globals
 
 type path = string list * string
 
@@ -45,6 +46,12 @@ and method_kind =
 	| MethDynamic
 	| MethMacro
 
+type module_check_policy =
+	| NoCheckFileTimeModification
+	| CheckFileContentModification
+	| NoCheckDependencies
+	| NoCheckShadowing
+
 type t =
 	| TMono of t option ref
 	| TEnum of tenum * tparams
@@ -141,7 +148,7 @@ and tfield_access =
 and texpr = {
 	eexpr : texpr_expr;
 	etype : t;
-	epos : Ast.pos;
+	epos : pos;
 }
 
 and tclass_field = {
@@ -149,18 +156,19 @@ and tclass_field = {
 	mutable cf_type : t;
 	mutable cf_public : bool;
 	cf_pos : pos;
+	cf_name_pos : pos;
 	mutable cf_doc : Ast.documentation;
 	mutable cf_meta : metadata;
 	mutable cf_kind : field_kind;
 	mutable cf_params : type_params;
 	mutable cf_expr : texpr option;
+	mutable cf_expr_unoptimized : tfunc option;
 	mutable cf_overloads : tclass_field list;
 }
 
 and tclass_kind =
 	| KNormal
 	| KTypeParameter of t list
-	| KExtension of tclass * tparams
 	| KExpr of Ast.expr
 	| KGeneric
 	| KGenericInstance of tclass * tparams
@@ -173,7 +181,8 @@ and metadata = Ast.metadata
 and tinfos = {
 	mt_path : path;
 	mt_module : module_def;
-	mt_pos : Ast.pos;
+	mt_pos : pos;
+	mt_name_pos : pos;
 	mt_private : bool;
 	mt_doc : Ast.documentation;
 	mutable mt_meta : metadata;
@@ -183,7 +192,8 @@ and tinfos = {
 and tclass = {
 	mutable cl_path : path;
 	mutable cl_module : module_def;
-	mutable cl_pos : Ast.pos;
+	mutable cl_pos : pos;
+	mutable cl_name_pos : pos;
 	mutable cl_private : bool;
 	mutable cl_doc : Ast.documentation;
 	mutable cl_meta : metadata;
@@ -211,7 +221,8 @@ and tclass = {
 and tenum_field = {
 	ef_name : string;
 	ef_type : t;
-	ef_pos : Ast.pos;
+	ef_pos : pos;
+	ef_name_pos : pos;
 	ef_doc : Ast.documentation;
 	ef_index : int;
 	ef_params : type_params;
@@ -221,7 +232,8 @@ and tenum_field = {
 and tenum = {
 	mutable e_path : path;
 	e_module : module_def;
-	e_pos : Ast.pos;
+	e_pos : pos;
+	e_name_pos : pos;
 	e_private : bool;
 	e_doc : Ast.documentation;
 	mutable e_meta : metadata;
@@ -236,7 +248,8 @@ and tenum = {
 and tdef = {
 	t_path : path;
 	t_module : module_def;
-	t_pos : Ast.pos;
+	t_pos : pos;
+	t_name_pos : pos;
 	t_private : bool;
 	t_doc : Ast.documentation;
 	mutable t_meta : metadata;
@@ -248,7 +261,8 @@ and tdef = {
 and tabstract = {
 	mutable a_path : path;
 	a_module : module_def;
-	a_pos : Ast.pos;
+	a_pos : pos;
+	a_name_pos : pos;
 	a_private : bool;
 	a_doc : Ast.documentation;
 	mutable a_meta : metadata;
@@ -282,8 +296,9 @@ and module_def = {
 and module_def_extra = {
 	m_file : string;
 	m_sign : string;
+	mutable m_check_policy : module_check_policy list;
 	mutable m_time : float;
-	mutable m_dirty : bool;
+	mutable m_dirty : module_def option;
 	mutable m_added : int;
 	mutable m_mark : int;
 	mutable m_deps : (int,module_def) PMap.t;
@@ -301,6 +316,7 @@ and module_kind =
 	| MFake
 	| MSub
 	| MExtern
+	| MImport
 
 and build_state =
 	| Built
@@ -340,15 +356,25 @@ let mk_mono() = TMono (ref None)
 
 let rec t_dynamic = TDynamic t_dynamic
 
+let not_opened = ref Closed
+let mk_anon fl = TAnon { a_fields = fl; a_status = not_opened; }
+
+(* We use this for display purposes because otherwise we never see the Dynamic type that
+   is defined in StdTypes.hx. This is set each time a typer is created, but this is fine
+   because Dynamic is the same in all contexts. If this ever changes we'll have to review
+   how we handle this. *)
+let t_dynamic_def = ref t_dynamic
+
 let tfun pl r = TFun (List.map (fun t -> "",false,t) pl,r)
 
 let fun_args l = List.map (fun (a,c,t) -> a, c <> None, t) l
 
-let mk_class m path pos =
+let mk_class m path pos name_pos =
 	{
 		cl_path = path;
 		cl_module = m;
 		cl_pos = pos;
+		cl_name_pos = name_pos;
 		cl_doc = None;
 		cl_meta = [];
 		cl_private = false;
@@ -371,11 +397,11 @@ let mk_class m path pos =
 		cl_restore = (fun() -> ());
 	}
 
-let module_extra file sign time kind =
+let module_extra file sign time kind policy =
 	{
 		m_file = file;
 		m_sign = sign;
-		m_dirty = false;
+		m_dirty = None;
 		m_added = 0;
 		m_mark = 0;
 		m_time = time;
@@ -386,18 +412,21 @@ let module_extra file sign time kind =
 		m_macro_calls = [];
 		m_if_feature = [];
 		m_features = Hashtbl.create 0;
+		m_check_policy = policy;
 	}
 
 
-let mk_field name t p = {
+let mk_field name t p name_pos = {
 	cf_name = name;
 	cf_type = t;
 	cf_pos = p;
+	cf_name_pos = name_pos;
 	cf_doc = None;
 	cf_meta = [];
 	cf_public = true;
 	cf_kind = Var { v_read = AccNormal; v_write = AccNormal };
 	cf_expr = None;
+	cf_expr_unoptimized = None;
 	cf_params = [];
 	cf_overloads = [];
 }
@@ -406,20 +435,21 @@ let null_module = {
 		m_id = alloc_mid();
 		m_path = [] , "";
 		m_types = [];
-		m_extra = module_extra "" "" 0. MFake;
+		m_extra = module_extra "" "" 0. MFake [];
 	}
 
 let null_class =
-	let c = mk_class null_module ([],"") Ast.null_pos in
+	let c = mk_class null_module ([],"") null_pos null_pos in
 	c.cl_private <- true;
 	c
 
-let null_field = mk_field "" t_dynamic Ast.null_pos
+let null_field = mk_field "" t_dynamic null_pos null_pos
 
 let null_abstract = {
 	a_path = ([],"");
 	a_module = null_module;
 	a_pos = null_pos;
+	a_name_pos = null_pos;
 	a_private = true;
 	a_doc = None;
 	a_meta = [];
@@ -874,15 +904,15 @@ let rec s_type ctx t =
 		| None -> Printf.sprintf "Unknown<%d>" (try List.assq t (!ctx) with Not_found -> let n = List.length !ctx in ctx := (t,n) :: !ctx; n)
 		| Some t -> s_type ctx t)
 	| TEnum (e,tl) ->
-		Ast.s_type_path e.e_path ^ s_type_params ctx tl
+		s_type_path e.e_path ^ s_type_params ctx tl
 	| TInst (c,tl) ->
 		(match c.cl_kind with
 		| KExpr e -> Ast.s_expr e
-		| _ -> Ast.s_type_path c.cl_path ^ s_type_params ctx tl)
+		| _ -> s_type_path c.cl_path ^ s_type_params ctx tl)
 	| TType (t,tl) ->
-		Ast.s_type_path t.t_path ^ s_type_params ctx tl
+		s_type_path t.t_path ^ s_type_params ctx tl
 	| TAbstract (a,tl) ->
-		Ast.s_type_path a.a_path ^ s_type_params ctx tl
+		s_type_path a.a_path ^ s_type_params ctx tl
 	| TFun ([],t) ->
 		"Void -> " ^ s_fun ctx t false
 	| TFun (l,t) ->
@@ -1052,10 +1082,11 @@ let rec s_expr s_type e =
 	) in
 	sprintf "(%s : %s)" str (s_type e.etype)
 
-let rec s_expr_pretty print_var_ids tabs s_type e =
+let rec s_expr_pretty print_var_ids tabs top_level s_type e =
 	let sprintf = Printf.sprintf in
-	let loop = s_expr_pretty print_var_ids tabs s_type in
-	let slist f l = String.concat "," (List.map f l) in
+	let loop = s_expr_pretty print_var_ids tabs false s_type in
+	let slist c f l = String.concat c (List.map f l) in
+	let clist f l = slist ", " f l in
 	let local v = if print_var_ids then sprintf "%s<%i>" v.v_name v.v_id else v.v_name in
 	match e.eexpr with
 	| TConst c -> s_const c
@@ -1066,38 +1097,40 @@ let rec s_expr_pretty print_var_ids tabs s_type e =
 	| TField (e1,s) -> sprintf "%s.%s" (loop e1) (field_name s)
 	| TTypeExpr mt -> (s_type_path (t_path mt))
 	| TParenthesis e1 -> sprintf "(%s)" (loop e1)
-	| TObjectDecl fl -> sprintf "{%s}" (slist (fun (f,e) -> sprintf "%s : %s" f (loop e)) fl)
-	| TArrayDecl el -> sprintf "[%s]" (slist loop el)
-	| TCall (e1,el) -> sprintf "%s(%s)" (loop e1) (slist loop el)
+	| TObjectDecl fl -> sprintf "{%s}" (clist (fun (f,e) -> sprintf "%s : %s" f (loop e)) fl)
+	| TArrayDecl el -> sprintf "[%s]" (clist loop el)
+	| TCall (e1,el) -> sprintf "%s(%s)" (loop e1) (clist loop el)
 	| TNew (c,pl,el) ->
-		sprintf "new %s(%s)" (s_type_path c.cl_path) (slist loop el)
+		sprintf "new %s(%s)" (s_type_path c.cl_path) (clist loop el)
 	| TUnop (op,f,e) ->
 		(match f with
 		| Prefix -> sprintf "%s %s" (s_unop op) (loop e)
 		| Postfix -> sprintf "%s %s" (loop e) (s_unop op))
 	| TFunction f ->
-		let args = slist (fun (v,o) -> sprintf "%s:%s%s" (local v) (s_type v.v_type) (match o with None -> "" | Some c -> " = " ^ s_const c)) f.tf_args in
-		sprintf "function(%s) = %s" args (loop f.tf_expr)
+		let args = clist (fun (v,o) -> sprintf "%s:%s%s" (local v) (s_type v.v_type) (match o with None -> "" | Some c -> " = " ^ s_const c)) f.tf_args in
+		sprintf "%s(%s) %s" (if top_level then "" else "function") args (loop f.tf_expr)
 	| TVar (v,eo) ->
 		sprintf "var %s" (sprintf "%s%s" (local v) (match eo with None -> "" | Some e -> " = " ^ loop e))
 	| TBlock el ->
 		let ntabs = tabs ^ "\t" in
-		let s = sprintf "{\n%s" (String.concat "" (List.map (fun e -> sprintf "%s%s;\n" ntabs (s_expr_pretty print_var_ids ntabs s_type e)) el)) in
-		s ^ tabs ^ "}"
+		let s = sprintf "{\n%s" (String.concat "" (List.map (fun e -> sprintf "%s%s;\n" ntabs (s_expr_pretty print_var_ids ntabs top_level s_type e)) el)) in
+		(match el with
+			| [] -> "{}"
+			| _ ->  s ^ tabs ^ "}")
 	| TFor (v,econd,e) ->
 		sprintf "for (%s in %s) %s" (local v) (loop econd) (loop e)
 	| TIf (e,e1,e2) ->
-		sprintf "if (%s)%s%s" (loop e) (loop e1) (match e2 with None -> "" | Some e -> " else " ^ loop e)
+		sprintf "if (%s) %s%s" (loop e) (loop e1) (match e2 with None -> "" | Some e -> " else " ^ loop e)
 	| TWhile (econd,e,flag) ->
 		(match flag with
 		| NormalWhile -> sprintf "while (%s) %s" (loop econd) (loop e)
 		| DoWhile -> sprintf "do (%s) while(%s)" (loop e) (loop econd))
 	| TSwitch (e,cases,def) ->
 		let ntabs = tabs ^ "\t" in
-		let s = sprintf "switch (%s) {\n%s%s" (loop e) (slist (fun (cl,e) -> sprintf "%scase %s: %s\n" ntabs (slist loop cl) (s_expr_pretty print_var_ids ntabs s_type e)) cases) (match def with None -> "" | Some e -> ntabs ^ "default: " ^ (s_expr_pretty print_var_ids ntabs s_type e) ^ "\n") in
+		let s = sprintf "switch (%s) {\n%s%s" (loop e) (slist "" (fun (cl,e) -> sprintf "%scase %s: %s;\n" ntabs (clist loop cl) (s_expr_pretty print_var_ids ntabs top_level s_type e)) cases) (match def with None -> "" | Some e -> ntabs ^ "default: " ^ (s_expr_pretty print_var_ids ntabs top_level s_type e) ^ "\n") in
 		s ^ tabs ^ "}"
 	| TTry (e,cl) ->
-		sprintf "try %s%s" (loop e) (slist (fun (v,e) -> sprintf "catch( %s : %s ) %s" (local v) (s_type v.v_type) (loop e)) cl)
+		sprintf "try %s%s" (loop e) (clist (fun (v,e) -> sprintf " catch (%s:%s) %s" (local v) (s_type v.v_type) (loop e)) cl)
 	| TReturn None ->
 		"return"
 	| TReturn (Some e) ->
@@ -1209,8 +1242,6 @@ let s_class_kind = function
 		"KNormal"
 	| KTypeParameter tl ->
 		Printf.sprintf "KTypeParameter [%s]" (s_types tl)
-	| KExtension(c,tl) ->
-		Printf.sprintf "KExtension %s<%s>" (s_type_path c.cl_path) (s_types tl)
 	| KExpr _ ->
 		"KExpr"
 	| KGeneric ->
@@ -1229,9 +1260,15 @@ module Printer = struct
 	let s_type =
 		s_type (print_context())
 
+	let s_pair s1 s2 =
+		Printf.sprintf "(%s,%s)" s1 s2
+
 	let s_record_field name value =
 		Printf.sprintf "%s = %s;" name value
 
+	let s_pos p =
+		Printf.sprintf "%s: %i-%i" p.pfile p.pmin p.pmax
+
 	let s_record_fields tabs fields =
 		let sl = List.map (fun (name,value) -> s_record_field name value) fields in
 		Printf.sprintf "{\n%s\t%s\n%s}" tabs (String.concat ("\n\t" ^ tabs) sl) tabs
@@ -1243,10 +1280,13 @@ module Printer = struct
 		| None -> "None"
 		| Some v -> f v
 
+	let s_pmap fk fv pm =
+		"{" ^ (String.concat ", " (PMap.foldi (fun k v acc -> (Printf.sprintf "%s = %s" (fk k) (fv v)) :: acc) pm [])) ^ "}"
+
 	let s_doc = s_opt (fun s -> s)
 
 	let s_metadata_entry (s,el,_) =
-		Printf.sprintf "@%s%s" (Meta.to_string s) (match el with [] -> "" | el -> String.concat ", " (List.map Ast.s_expr el))
+		Printf.sprintf "@%s%s" (Meta.to_string s) (match el with [] -> "" | el -> "(" ^ (String.concat ", " (List.map Ast.s_expr el)) ^ ")")
 
 	let s_metadata metadata =
 		s_list " " s_metadata_entry metadata
@@ -1262,22 +1302,26 @@ module Printer = struct
 	let s_type_params tl =
 		s_list ", " s_type_param tl
 
-	let s_tclass_field cf =
-		s_record_fields "\t" [
+	let s_tclass_field tabs cf =
+		s_record_fields tabs [
 			"cf_name",cf.cf_name;
 			"cf_doc",s_doc cf.cf_doc;
 			"cf_type",s_type_kind (follow cf.cf_type);
 			"cf_public",string_of_bool cf.cf_public;
+			"cf_pos",s_pos cf.cf_pos;
+			"cf_name_pos",s_pos cf.cf_name_pos;
 			"cf_meta",s_metadata cf.cf_meta;
 			"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;
 		]
 
-	let s_tclass c =
-		s_record_fields "" [
+	let s_tclass tabs c =
+		s_record_fields tabs [
 			"cl_path",s_type_path c.cl_path;
 			"cl_module",s_type_path c.cl_module.m_path;
+			"cl_pos",s_pos c.cl_pos;
+			"cl_name_pos",s_pos c.cl_name_pos;
 			"cl_private",string_of_bool c.cl_private;
 			"cl_doc",s_doc c.cl_doc;
 			"cl_meta",s_metadata c.cl_meta;
@@ -1291,15 +1335,17 @@ module Printer = struct
 			"cl_array_access",s_opt s_type c.cl_array_access;
 			"cl_overrides",s_list "," (fun cf -> cf.cf_name) c.cl_overrides;
 			"cl_init",s_opt (s_expr_ast true "" s_type) c.cl_init;
-			"cl_constructor",s_opt s_tclass_field c.cl_constructor;
-			"cl_ordered_fields",s_list "\n\t" s_tclass_field c.cl_ordered_fields;
-			"cl_ordered_statics",s_list "\n\t" s_tclass_field c.cl_ordered_statics;
+			"cl_constructor",s_opt (s_tclass_field (tabs ^ "\t")) c.cl_constructor;
+			"cl_ordered_fields",s_list "\n\t" (s_tclass_field (tabs ^ "\t")) c.cl_ordered_fields;
+			"cl_ordered_statics",s_list "\n\t" (s_tclass_field (tabs ^ "\t")) c.cl_ordered_statics;
 		]
 
 	let s_tdef tabs t =
 		s_record_fields tabs [
 			"t_path",s_type_path t.t_path;
 			"t_module",s_type_path t.t_module.m_path;
+			"t_pos",s_pos t.t_pos;
+			"t_name_pos",s_pos t.t_name_pos;
 			"t_private",string_of_bool t.t_private;
 			"t_doc",s_doc t.t_doc;
 			"t_meta",s_metadata t.t_meta;
@@ -1307,34 +1353,40 @@ module Printer = struct
 			"t_type",s_type_kind t.t_type
 		]
 
-	let s_tenum_field ef =
-		s_record_fields "\t" [
+	let s_tenum_field tabs ef =
+		s_record_fields tabs [
 			"ef_name",ef.ef_name;
 			"ef_doc",s_doc ef.ef_doc;
+			"ef_pos",s_pos ef.ef_pos;
+			"ef_name_pos",s_pos ef.ef_name_pos;
 			"ef_type",s_type_kind ef.ef_type;
 			"ef_index",string_of_int ef.ef_index;
 			"ef_params",s_type_params ef.ef_params;
 			"ef_meta",s_metadata ef.ef_meta
 		]
 
-	let s_tenum en =
-		s_record_fields "" [
+	let s_tenum tabs en =
+		s_record_fields tabs [
 			"e_path",s_type_path en.e_path;
 			"e_module",s_type_path en.e_module.m_path;
+			"e_pos",s_pos en.e_pos;
+			"e_name_pos",s_pos en.e_name_pos;
 			"e_private",string_of_bool en.e_private;
 			"d_doc",s_doc en.e_doc;
 			"e_meta",s_metadata en.e_meta;
 			"e_params",s_type_params en.e_params;
 			"e_type",s_tdef "\t" en.e_type;
 			"e_extern",string_of_bool en.e_extern;
-			"e_constrs",s_list "\n\t" s_tenum_field (PMap.fold (fun ef acc -> ef :: acc) en.e_constrs []);
+			"e_constrs",s_list "\n\t" (s_tenum_field (tabs ^ "\t")) (PMap.fold (fun ef acc -> ef :: acc) en.e_constrs []);
 			"e_names",String.concat ", " en.e_names
 		]
 
-	let s_tabstract a =
-		s_record_fields "" [
+	let s_tabstract tabs a =
+		s_record_fields tabs [
 			"a_path",s_type_path a.a_path;
 			"a_modules",s_type_path a.a_module.m_path;
+			"a_pos",s_pos a.a_pos;
+			"a_name_pos",s_pos a.a_name_pos;
 			"a_private",string_of_bool a.a_private;
 			"a_doc",s_doc a.a_doc;
 			"a_meta",s_metadata a.a_meta;
@@ -1363,6 +1415,65 @@ module Printer = struct
 			"v_extra",s_opt s_tvar_extra v.v_extra;
 			"v_meta",s_metadata v.v_meta;
 		]
+
+	let s_module_kind = function
+		| MCode -> "MCode"
+		| MMacro -> "MMacro"
+		| MFake -> "MFake"
+		| MSub -> "MSub"
+		| MExtern -> "MExtern"
+		| MImport -> "MImport"
+
+	let s_module_def_extra tabs me =
+		s_record_fields tabs [
+			"m_file",me.m_file;
+			"m_sign",me.m_sign;
+			"m_time",string_of_float me.m_time;
+			"m_dirty",s_opt (fun m -> s_type_path m.m_path) me.m_dirty;
+			"m_added",string_of_int me.m_added;
+			"m_mark",string_of_int me.m_mark;
+			"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;
+			"m_binded_res",""; (* TODO *)
+			"m_macro_calls",String.concat ", " me.m_macro_calls;
+			"m_if_feature",""; (* TODO *)
+			"m_features",""; (* TODO *)
+		]
+
+	let s_module_def m =
+		s_record_fields "" [
+			"m_id",string_of_int m.m_id;
+			"m_path",s_type_path m.m_path;
+			"m_extra",s_module_def_extra "\t" m.m_extra
+		]
+
+	let s_type_path tp =
+		s_record_fields "" [
+			"tpackage",s_list "." (fun s -> s) tp.tpackage;
+			"tname",tp.tname;
+			"tparams","";
+			"tsub",s_opt (fun s -> s) tp.tsub;
+		]
+
+	let s_class_flag = function
+		| HInterface -> "HInterface"
+		| HExtern -> "HExtern"
+		| HPrivate -> "HPrivate"
+		| HExtends tp -> "HExtends " ^ (s_type_path (fst tp))
+		| HImplements tp -> "HImplements " ^ (s_type_path (fst tp))
+
+	let s_placed f (x,p) =
+		s_pair (f x) (s_pos p)
+
+	let s_class_field cff =
+		s_record_fields "" [
+			"cff_name",s_placed (fun s -> s) cff.cff_name;
+			"cff_doc",s_opt (fun s -> s) cff.cff_doc;
+			"cff_pos",s_pos cff.cff_pos;
+			"cff_meta",s_metadata cff.cff_meta;
+			"cff_access",s_list ", " Ast.s_access cff.cff_access;
+		]
 end
 
 (* ======= Unification ======= *)
@@ -2083,63 +2194,6 @@ and unify_with_access t1 f2 =
 	(* read/write *)
 	| _ -> with_variance (type_eq EqBothDynamic) t1 f2.cf_type
 
-module Abstract = struct
-	open Ast
-
-	let find_to ab pl b =
-		if follow b == t_dynamic then
-			List.find (fun (t,_) -> follow t == t_dynamic) ab.a_to_field
-		else if List.exists (unify_to ab pl ~allow_transitive_cast:false b) ab.a_to then
-			raise Not_found (* legacy compatibility *)
-		else
-			List.find (unify_to_field ab pl b) ab.a_to_field
-
-	let find_from ab pl a b =
-		if follow a == t_dynamic then
-			List.find (fun (t,_) -> follow t == t_dynamic) ab.a_from_field
-		else if List.exists (unify_from ab pl a ~allow_transitive_cast:false b) ab.a_from then
-			raise Not_found (* legacy compatibility *)
-		else
-			List.find (unify_from_field ab pl a b) ab.a_from_field
-
-	let underlying_type_stack = ref []
-
-	let rec get_underlying_type a pl =
-		let maybe_recurse t =
-			underlying_type_stack := (TAbstract(a,pl)) :: !underlying_type_stack;
-			let t = match follow t with
-				| TAbstract(a,tl) when not (Meta.has Meta.CoreType a.a_meta) ->
-					if List.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))) in
-						underlying_type_stack := [];
-						raise (Error("Abstract chain detected: " ^ s,a.a_pos))
-					end;
-					get_underlying_type a tl
-				| _ ->
-					t
-			in
-			underlying_type_stack := List.tl !underlying_type_stack;
-			t
-		in
-		try
-			if not (Meta.has Meta.MultiType a.a_meta) then raise Not_found;
-			let m = mk_mono() in
-			let _ = find_to a pl m in
-			maybe_recurse (follow m)
-		with Not_found ->
-			if Meta.has Meta.CoreType a.a_meta then
-				t_dynamic
-			else
-				maybe_recurse (apply_params a.a_params pl a.a_this)
-
-	let rec follow_with_abstracts t = match follow t with
-		| TAbstract(a,tl) when not (Meta.has Meta.CoreType a.a_meta) ->
-			follow_with_abstracts (get_underlying_type a tl)
-		| t ->
-			t
-end
-
 (* ======= Mapping and iterating ======= *)
 
 let iter f e =
@@ -2345,6 +2399,16 @@ let map_expr_type f ft fv e =
 	| TMeta (m,e1) ->
 		{e with eexpr = TMeta(m, f e1); etype = ft e.etype }
 
+let resolve_typedef t =
+	match t with
+	| TClassDecl _ | TEnumDecl _ | TAbstractDecl _ -> t
+	| TTypeDecl td ->
+		match follow td.t_type with
+		| TEnum (e,_) -> TEnumDecl e
+		| TInst (c,_) -> TClassDecl c
+		| TAbstract (a,_) -> TAbstractDecl a
+		| _ -> t
+
 module TExprToExpr = struct
 	let tpath p mp pl =
 		if snd mp = snd p then
@@ -2443,7 +2507,7 @@ module TExprToExpr = struct
 		| TField (e,f) -> EField (convert_expr e, field_name f)
 		| TTypeExpr t -> fst (mk_path (full_type_path t) e.epos)
 		| TParenthesis e -> EParenthesis (convert_expr e)
-		| TObjectDecl fl -> EObjectDecl (List.map (fun (f,e) -> f, convert_expr e) fl)
+		| TObjectDecl fl -> EObjectDecl (List.map (fun (f,e) -> (f,null_pos), convert_expr e) fl)
 		| TArrayDecl el -> EArrayDecl (List.map convert_expr el)
 		| TCall (e,el) -> ECall (convert_expr e,List.map convert_expr el)
 		| TNew (c,pl,el) -> ENew ((match (try convert_type (TInst (c,pl)) with Exit -> convert_type (TInst (c,[]))) with CTPath p -> p,null_pos | _ -> assert false),List.map convert_expr el)
@@ -2461,14 +2525,21 @@ module TExprToExpr = struct
 		| TWhile (e1,e2,flag) -> EWhile (convert_expr e1, convert_expr e2, flag)
 		| TSwitch (e,cases,def) ->
 			let cases = List.map (fun (vl,e) ->
-				List.map convert_expr vl,None,(match e.eexpr with TBlock [] -> None | _ -> Some (convert_expr e))
+				List.map convert_expr vl,None,(match e.eexpr with TBlock [] -> None | _ -> Some (convert_expr e)),e.epos
 			) cases in
-			let def = match eopt def with None -> None | Some (EBlock [],_) -> Some None | e -> Some e in
+			let def = match eopt def with None -> None | Some (EBlock [],_) -> Some (None,null_pos) | Some e -> Some (Some e,pos e) in
 			ESwitch (convert_expr e,cases,def)
 		| TEnumParameter _ ->
 			(* these are considered complex, so the AST is handled in TMeta(Meta.Ast) *)
 			assert false
-		| TTry (e,catches) -> ETry (convert_expr e,List.map (fun (v,e) -> (v.v_name,v.v_pos), (try convert_type v.v_type,null_pos with Exit -> assert false), convert_expr e) catches)
+		| TTry (e,catches) ->
+			let e1 = convert_expr e in
+			let catches = List.map (fun (v,e) ->
+				let ct = try convert_type v.v_type,null_pos with Exit -> assert false in
+				let e = convert_expr e in
+				(v.v_name,v.v_pos),ct,e,(pos e)
+			) catches in
+			ETry (e1,catches)
 		| TReturn e -> EReturn (eopt e)
 		| TBreak -> EBreak
 		| TContinue -> EContinue
@@ -2578,6 +2649,115 @@ module Texpr = struct
 	let rec skip e = match e.eexpr with
 		| TParenthesis e1 | TMeta(_,e1) | TBlock [e1] | TCast(e1,None) -> skip e1
 		| _ -> e
+
+	let foldmap_list f acc el =
+		let rec loop acc el acc2 = (match el with
+			| [] -> acc,(List.rev acc2)
+			| e1 :: el ->
+				let acc,e1 = f acc e1 in
+				loop acc el (e1 :: acc2))
+		in loop acc el []
+
+	let foldmap_opt f acc eo = match eo with
+		| Some(e) -> let acc,e = f acc e in acc,Some(e)
+		| None    -> acc,eo
+
+	let foldmap_pairs f acc pairs =
+		let acc,pairs = List.fold_left
+			(fun (acc,el) (v,e) -> let acc,e = f acc e in (acc,(v,e) :: el))
+			(acc,[])
+			pairs
+		in acc,(List.rev pairs)
+
+	let foldmap f acc e =
+		begin match e.eexpr with
+		| TConst _
+		| TLocal _
+		| TBreak
+		| TContinue
+		| TTypeExpr _ ->
+			acc,e
+		| TArray (e1,e2) ->
+			let acc,e1 = f acc e1 in
+			let acc,e2 = f acc e2 in
+			acc,{ e with eexpr = TArray (e1, e2) }
+		| TBinop (op,e1,e2) ->
+			let acc,e1 = f acc e1 in
+			let acc,e2 = f acc e2 in
+			acc,{ e with eexpr = TBinop (op,e1,e2) }
+		| TFor (v,e1,e2) ->
+			let acc,e1 = f acc e1 in
+			let acc,e2 = f acc e2 in
+			acc,{ e with eexpr = TFor (v,e1,e2) }
+		| TWhile (e1,e2,flag) ->
+			let acc,e1 = f acc e1 in
+			let acc,e2 = f acc e2 in
+			acc,{ e with eexpr = TWhile (e1,e2,flag) }
+		| TThrow e1 ->
+			let acc,e1 = f acc e1 in
+			acc,{ e with eexpr = TThrow (e1) }
+		| TEnumParameter (e1,ef,i) ->
+			let acc,e1 = f acc e1 in
+			acc,{ e with eexpr = TEnumParameter(e1,ef,i) }
+		| TField (e1,v) ->
+			let acc,e1 = f acc e1 in
+			acc,{ e with eexpr = TField (e1,v) }
+		| TParenthesis e1 ->
+			let acc,e1 = f acc e1 in
+			acc,{ e with eexpr = TParenthesis (e1) }
+		| TUnop (op,pre,e1) ->
+			let acc,e1 = f acc e1 in
+			acc,{ e with eexpr = TUnop (op,pre,e1) }
+		| TArrayDecl el ->
+			let acc,el = foldmap_list f acc el in
+			acc,{ e with eexpr = TArrayDecl el }
+		| TNew (t,pl,el) ->
+			let acc,el = foldmap_list f acc el in
+			acc,{ e with eexpr = TNew (t,pl,el) }
+		| TBlock el ->
+			let acc,el = foldmap_list f acc el in
+			acc,{ e with eexpr = TBlock (el) }
+		| TObjectDecl el ->
+			let acc,el = foldmap_pairs f acc el in
+			acc,{ e with eexpr = TObjectDecl el }
+		| TCall (e1,el) ->
+			let acc,e1 = f acc e1 in
+			let acc,el = foldmap_list f acc el in
+			acc,{ e with eexpr = TCall (e1,el) }
+		| TVar (v,eo) ->
+			let acc,eo = foldmap_opt f acc eo in
+			acc,{ e with eexpr = TVar (v, eo) }
+		| TFunction fu ->
+			let acc,e1 = f acc fu.tf_expr in
+			acc,{ e with eexpr = TFunction { fu with tf_expr = e1 } }
+		| TIf (ec,e1,eo) ->
+			let acc,ec = f acc ec in
+			let acc,e1 = f acc e1 in
+			let acc,eo = foldmap_opt f acc eo in
+			acc,{ e with eexpr = TIf (ec,e1,eo)}
+		| TSwitch (e1,cases,def) ->
+			let acc,e1 = f acc e1 in
+			let acc,cases = List.fold_left (fun (acc,cases) (el,e2) ->
+				let acc,el = foldmap_list f acc el in
+				let acc,e2 = f acc e2 in
+				acc,((el,e2) :: cases)
+			) (acc,[]) cases in
+			let acc,def = foldmap_opt f acc def in
+			acc,{ e with eexpr = TSwitch (e1, cases, def) }
+		| TTry (e1,catches) ->
+			let acc,e1 = f acc e1 in
+			let acc,catches = foldmap_pairs f acc catches in
+			acc,{ e with eexpr = TTry (e1, catches) }
+		| TReturn eo ->
+			let acc,eo = foldmap_opt f acc eo in
+			acc,{ e with eexpr = TReturn eo }
+		| TCast (e1,t) ->
+			let acc,e1 = f acc e1 in
+			acc,{ e with eexpr = TCast (e1,t) }
+		| TMeta (m,e1) ->
+			let acc,e1 = f acc e1 in
+			acc,{ e with eexpr = TMeta(m,e1)}
+		end
 end
 
 module ExtType = struct
@@ -2636,4 +2816,4 @@ module StringError = struct
 	let string_error s sl msg =
 		try string_error_raise s sl msg
 		with Not_found -> msg
-end
+end

+ 335 - 119
src/typing/typecore.ml

@@ -17,8 +17,11 @@
 	Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  *)
 
+open Globals
+open Ast
 open Common
 open Type
+open Error
 
 type with_type =
 	| NoValue
@@ -26,9 +29,9 @@ type with_type =
 	| WithType of t
 
 type type_patch = {
-	mutable tp_type : Ast.complex_type option;
+	mutable tp_type : complex_type option;
 	mutable tp_remove : bool;
-	mutable tp_meta : Ast.metadata;
+	mutable tp_meta : metadata;
 }
 
 type current_fun =
@@ -59,7 +62,7 @@ type typer_module = {
 	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 : Ast.import list;
+	mutable module_imports : import list;
 }
 
 type typer_globals = {
@@ -73,17 +76,21 @@ type typer_globals = {
 	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 * Ast.metadata_entry * (bool * bool * bool)) list;
-	mutable get_build_infos : unit -> (module_type * t list * Ast.class_field list) option;
+	mutable global_metadata : (string list * metadata_entry * (bool * bool * bool)) list;
+	mutable module_check_policies : (string list * module_check_policy list * bool) list;
+	mutable get_build_infos : unit -> (module_type * t list * class_field list) option;
 	delayed_macros : (unit -> unit) DynArray.t;
-	mutable global_using : (tclass * Ast.pos) list;
+	mutable global_using : (tclass * pos) list;
 	(* api *)
-	do_inherit : typer -> Type.tclass -> Ast.pos -> (bool * Ast.placed_type_path) -> bool;
+	do_inherit : typer -> Type.tclass -> pos -> (bool * placed_type_path) -> bool;
 	do_create : Common.context -> typer;
-	do_macro : typer -> macro_mode -> path -> string -> Ast.expr list -> Ast.pos -> Ast.expr option;
+	do_macro : typer -> macro_mode -> path -> string -> expr list -> pos -> expr option;
 	do_load_module : typer -> path -> pos -> module_def;
 	do_optimize : typer -> texpr -> texpr;
 	do_build_instance : typer -> module_type -> pos -> ((string * t) 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);
 }
 
 and typer = {
@@ -94,7 +101,7 @@ and typer = {
 	mutable meta : metadata;
 	mutable this_stack : texpr list;
 	mutable with_type_stack : with_type list;
-	mutable call_argument_stack : Ast.expr list list;
+	mutable call_argument_stack : expr list list;
 	(* variable *)
 	mutable pass : typer_pass;
 	(* per-module *)
@@ -120,108 +127,21 @@ and typer = {
 	(* events *)
 	mutable on_error : typer -> string -> pos -> unit;
 }
-
-type call_error =
-	| Not_enough_arguments of (string * bool * t) list
-	| Too_many_arguments
-	| Could_not_unify of error_msg
-	| Cannot_skip_non_nullable of string
-
-and error_msg =
-	| Module_not_found of path
-	| Type_not_found of path * string
-	| Unify of unify_error list
-	| Custom of string
-	| Unknown_ident of string
-	| Stack of error_msg * error_msg
-	| Call_error of call_error
-
-exception Fatal_error of string * Ast.pos
-
 exception Forbid_package of (string * path * pos) * pos list * string
 
-exception Error of error_msg * pos
-
-exception WithTypeError of unify_error list * pos
+exception WithTypeError of error_msg * pos
 
 let make_call_ref : (typer -> texpr -> texpr list -> t -> pos -> texpr) ref = ref (fun _ _ _ _ _ -> assert false)
-let type_expr_ref : (typer -> Ast.expr -> with_type -> texpr) ref = ref (fun _ _ _ -> assert false)
+let type_expr_ref : (typer -> expr -> with_type -> texpr) ref = ref (fun _ _ _ -> assert false)
 let type_module_type_ref : (typer -> module_type -> t list option -> pos -> texpr) ref = ref (fun _ _ _ _ -> assert false)
 let unify_min_ref : (typer -> texpr list -> t) ref = ref (fun _ _ -> assert false)
-let match_expr_ref : (typer -> Ast.expr -> (Ast.expr list * Ast.expr option * Ast.expr option) list -> Ast.expr option option -> with_type -> Ast.pos -> texpr) ref = ref (fun _ _ _ _ _ _ -> assert false)
-let get_pattern_locals_ref : (typer -> Ast.expr -> Type.t -> (string, tvar * pos) PMap.t) ref = ref (fun _ _ _ -> assert false)
-let get_constructor_ref : (typer -> tclass -> t list -> Ast.pos -> (t * tclass_field)) ref = ref (fun _ _ _ _ -> assert false)
-let cast_or_unify_ref : (typer -> t -> texpr -> Ast.pos -> texpr) ref = ref (fun _ _ _ _ -> assert false)
+let match_expr_ref : (typer -> expr -> (expr list * expr option * expr option * pos) list -> (expr option * pos) option -> with_type -> pos -> texpr) ref = ref (fun _ _ _ _ _ _ -> assert false)
+let get_pattern_locals_ref : (typer -> expr -> Type.t -> (string, tvar * pos) PMap.t) ref = ref (fun _ _ _ -> assert false)
+let get_constructor_ref : (typer -> tclass -> t list -> pos -> (t * tclass_field)) ref = ref (fun _ _ _ _ -> assert false)
+let cast_or_unify_ref : (typer -> t -> texpr -> pos -> texpr) ref = ref (fun _ _ _ _ -> assert false)
 let find_array_access_raise_ref : (typer -> tabstract -> tparams -> texpr -> texpr option -> pos -> (tclass_field * t * t * texpr * texpr option)) ref = ref (fun _ _ _ _ _ _ -> assert false)
 let analyzer_run_on_expr_ref : (Common.context -> texpr -> texpr) ref = ref (fun _ _ -> assert false)
-
-let string_source t = match follow t with
-	| TInst(c,_) -> List.map (fun cf -> cf.cf_name) c.cl_ordered_fields
-	| TAnon a -> PMap.fold (fun cf acc -> cf.cf_name :: acc) a.a_fields []
-	| TAbstract({a_impl = Some c},_) -> List.map (fun cf -> cf.cf_name) c.cl_ordered_statics
-	| _ -> []
-
-let short_type ctx t =
-	let tstr = s_type ctx t in
-	if String.length tstr > 150 then String.sub tstr 0 147 ^ "..." else tstr
-
-let unify_error_msg ctx = function
-	| Cannot_unify (t1,t2) ->
-		s_type ctx t1 ^ " should be " ^ s_type ctx t2
-	| Invalid_field_type s ->
-		"Invalid type for field " ^ s ^ " :"
-	| Has_no_field (t,n) ->
-		StringError.string_error n (string_source t) (short_type ctx t ^ " has no field " ^ n)
-	| Has_no_runtime_field (t,n) ->
-		s_type ctx t ^ "." ^ n ^ " is not accessible at runtime"
-	| Has_extra_field (t,n) ->
-		short_type ctx t ^ " has extra field " ^ n
-	| Invalid_kind (f,a,b) ->
-		(match a, b with
-		| Var va, Var vb ->
-			let name, stra, strb = if va.v_read = vb.v_read then
-				"setter", s_access false va.v_write, s_access false vb.v_write
-			else if va.v_write = vb.v_write then
-				"getter", s_access true va.v_read, s_access true vb.v_read
-			else
-				"access", "(" ^ s_access true va.v_read ^ "," ^ s_access false va.v_write ^ ")", "(" ^ s_access true vb.v_read ^ "," ^ s_access false vb.v_write ^ ")"
-			in
-			"Inconsistent " ^ name ^ " for field " ^ f ^ " : " ^ stra ^ " should be " ^ strb
-		| _ ->
-			"Field " ^ f ^ " is " ^ s_kind a ^ " but should be " ^ s_kind b)
-	| Invalid_visibility n ->
-		"The field " ^ n ^ " is not public"
-	| Not_matching_optional n ->
-		"Optional attribute of parameter " ^ n ^ " differs"
-	| Cant_force_optional ->
-		"Optional parameters can't be forced"
-	| Invariant_parameter _ ->
-		"Type parameters are invariant"
-	| Constraint_failure name ->
-		"Constraint check failure for " ^ name
-	| Missing_overload (cf, t) ->
-		cf.cf_name ^ " has no overload for " ^ s_type ctx t
-	| Unify_custom msg ->
-		msg
-
-let rec error_msg = function
-	| Module_not_found m -> "Type not found : " ^ Ast.s_type_path m
-	| Type_not_found (m,t) -> "Module " ^ Ast.s_type_path m ^ " does not define type " ^ t
-	| Unify l ->
-		let ctx = print_context() in
-		String.concat "\n" (List.map (unify_error_msg ctx) 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
-
-and s_call_error = 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
+let merge_core_doc_ref : (typer -> tclass -> unit) ref = ref (fun _ _ -> assert false)
 
 let pass_name = function
 	| PBuildModule -> "build-module"
@@ -231,11 +151,9 @@ let pass_name = function
 	| PForce -> "force"
 	| PFinal -> "final"
 
-let display_error ctx msg p = match ctx.com.display with
-	| DMDiagnostics -> add_diagnostics_message ctx.com msg p DiagnosticsSeverity.Error
-	| _ -> ctx.on_error ctx msg p
-
-let error msg p = raise (Error (Custom msg,p))
+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.DiagnosticsSeverity.Error
 
 let make_call ctx e el t p = (!make_call_ref) ctx e el t p
 
@@ -261,11 +179,11 @@ 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(l,p))
+	else if ctx.in_call_args then raise (WithTypeError(Unify l,p))
 	else display_error ctx (error_msg (Unify l)) p
 
 let raise_or_display_message ctx msg p =
-	if ctx.in_call_args then raise (WithTypeError ([Unify_custom msg],p))
+	if ctx.in_call_args then raise (WithTypeError (Custom msg,p))
 	else display_error ctx msg p
 
 let unify ctx t1 t2 p =
@@ -292,6 +210,11 @@ let add_local ctx n t p =
 	ctx.locals <- PMap.add n v ctx.locals;
 	v
 
+let add_unbound_local ctx n t p =
+	let v = add_local ctx n t p in
+	v.v_meta <- (Meta.Unbound,[],null_pos) :: v.v_meta;
+	v
+
 let gen_local_prefix = "`"
 
 let gen_local ctx t p =
@@ -308,9 +231,6 @@ let gen_local ctx t p =
 let is_gen_local v =
 	String.unsafe_get v.v_name 0 = String.unsafe_get gen_local_prefix 0
 
-let not_opened = ref Closed
-let mk_anon fl = TAnon { a_fields = fl; a_status = not_opened; }
-
 let delay ctx p f =
 	let rec loop = function
 		| [] -> [p,[f]]
@@ -365,13 +285,13 @@ let exc_protect ctx f (where:string) =
 
 let fake_modules = Hashtbl.create 0
 let create_fake_module ctx file =
-	let file = Common.unique_full_path file in
+	let file = Path.unique_full_path file in
 	let mdep = (try Hashtbl.find fake_modules file with Not_found ->
 		let mdep = {
 			m_id = alloc_mid();
 			m_path = (["$DEP"],file);
 			m_types = [];
-			m_extra = module_extra file (Common.get_signature ctx.com) (file_time file) MFake;
+			m_extra = module_extra file (Common.get_signature ctx.com) (file_time file) MFake [];
 		} in
 		Hashtbl.add fake_modules file mdep;
 		mdep
@@ -381,12 +301,308 @@ let create_fake_module ctx file =
 
 let push_this ctx e = match e.eexpr with
 	| TConst ((TInt _ | TFloat _ | TString _ | TBool _) as ct) ->
-		(Ast.EConst (tconst_to_const ct),e.epos),fun () -> ()
+		(EConst (tconst_to_const ct),e.epos),fun () -> ()
 	| _ ->
 		ctx.this_stack <- e :: ctx.this_stack;
-		let er = Ast.EMeta((Ast.Meta.This,[],e.epos), (Ast.EConst(Ast.Ident "this"),e.epos)),e.epos in
+		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 =
+	Meta.has Meta.Extern f.cf_meta || Meta.has Meta.Generic f.cf_meta
+	|| (match f.cf_kind with
+		| Var {v_read = AccRequire (s,_)} -> true
+		| Method MethMacro -> not ctx.in_macro
+		| _ -> false)
+
+(* -------------------------------------------------------------------------- *)
+(* ABSTRACT CASTS *)
+
+module AbstractCast = struct
+
+	let cast_stack = ref []
+
+	let make_static_call ctx c cf a pl args t p =
+		if cf.cf_kind = Method MethMacro then begin
+			match args with
+				| [e] ->
+					let e,f = push_this ctx e in
+					ctx.with_type_stack <- (WithType t) :: ctx.with_type_stack;
+					let e = match ctx.g.do_macro ctx MExpr c.cl_path cf.cf_name [e] p with
+						| Some e -> type_expr ctx e Value
+						| None ->  type_expr ctx (EConst (Ident "null"),p) Value
+					in
+					ctx.with_type_stack <- List.tl ctx.with_type_stack;
+					f();
+					e
+				| _ -> assert false
+		end else
+			make_static_call ctx c cf (apply_params a.a_params pl) args t p
+
+	let do_check_cast ctx tleft eright p =
+		let recurse cf f =
+			if cf == ctx.curfield || List.mem cf !cast_stack then error "Recursive implicit cast" p;
+			cast_stack := cf :: !cast_stack;
+			let r = f() in
+			cast_stack := List.tl !cast_stack;
+			r
+		in
+		let find a tl f =
+			let tcf,cf = f() in
+			if (Meta.has Meta.MultiType a.a_meta) then
+				mk_cast eright tleft p
+			else match a.a_impl with
+				| Some c -> recurse cf (fun () ->
+					let ret = make_static_call ctx c cf a tl [eright] tleft p in
+					{ ret with eexpr = TMeta( (Meta.ImplicitCast,[],ret.epos), ret) }
+				)
+				| None -> assert false
+		in
+		if type_iseq tleft eright.etype then
+			eright
+		else begin
+			let rec loop tleft tright = match follow tleft,follow tright with
+			| TAbstract(a1,tl1),TAbstract(a2,tl2) ->
+				begin try find a2 tl2 (fun () -> Abstract.find_to a2 tl2 tleft)
+				with Not_found -> try find a1 tl1 (fun () -> Abstract.find_from a1 tl1 eright.etype tleft)
+				with Not_found -> raise Not_found
+				end
+			| TAbstract(a,tl),_ ->
+				begin try find a tl (fun () -> Abstract.find_from a tl eright.etype tleft)
+				with Not_found ->
+					let rec loop2 tcl = match tcl with
+						| tc :: tcl ->
+							if not (type_iseq tc tleft) then loop (apply_params a.a_params tl tc) tright
+							else loop2 tcl
+						| [] -> raise Not_found
+					in
+					loop2 a.a_from
+				end
+			| _,TAbstract(a,tl) ->
+				begin try find a tl (fun () -> Abstract.find_to a tl tleft)
+				with Not_found ->
+					let rec loop2 tcl = match tcl with
+						| tc :: tcl ->
+							if not (type_iseq tc tright) then loop tleft (apply_params a.a_params tl tc)
+							else loop2 tcl
+						| [] -> raise Not_found
+					in
+					loop2 a.a_to
+				end
+			| _ ->
+				raise Not_found
+			in
+			loop tleft eright.etype
+		end
+
+	let cast_or_unify_raise ctx tleft eright p =
+		try
+			(* can't do that anymore because this might miss macro calls (#4315) *)
+			(* if ctx.com.display <> DMNone then raise Not_found; *)
+			do_check_cast ctx tleft eright p
+		with Not_found ->
+			unify_raise ctx eright.etype tleft p;
+			eright
+
+	let cast_or_unify ctx tleft eright p =
+		try
+			cast_or_unify_raise ctx tleft eright 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 rec loop cfl = match cfl with
+			| [] -> raise Not_found
+			| cf :: cfl ->
+				let monos = List.map (fun _ -> mk_mono()) 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
+				match follow (map cf.cf_type) with
+				| TFun([(_,_,tab);(_,_,ta1);(_,_,ta2)],r) as tf when is_set ->
+					begin try
+						Type.unify tab 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 _,_) ->
+						loop cfl
+					end
+				| TFun([(_,_,tab);(_,_,ta1)],r) as tf when not is_set ->
+					begin try
+						Type.unify tab ta;
+						let e1 = cast_or_unify_raise ctx ta1 e1 p in
+						check_constraints();
+						cf,tf,r,e1,None
+					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
+		with Not_found -> match e2o with
+			| None ->
+				error (Printf.sprintf "No @:arrayAccess function accepts argument of %s" (s_type (print_context()) e1.etype)) p
+			| Some e2 ->
+				error (Printf.sprintf "No @:arrayAccess function accepts arguments of %s and %s" (s_type (print_context()) e1.etype) (s_type (print_context()) e2.etype)) p
+
+	let find_multitype_specialization com a pl p =
+		let m = mk_mono() in
+		let tl = match Meta.get Meta.MultiType a.a_meta with
+			| _,[],_ -> pl
+			| _,el,_ ->
+				let relevant = Hashtbl.create 0 in
+				List.iter (fun e ->
+					let rec loop f e = match fst e with
+						| EConst(Ident s) ->
+							Hashtbl.replace relevant s f
+						| EMeta((Meta.Custom ":followWithAbstracts",_,_),e1) ->
+							loop Abstract.follow_with_abstracts e1;
+						| _ ->
+							error "Type parameter expected" (pos e)
+					in
+					loop (fun t -> t) e
+				) el;
+				let tl = List.map2 (fun (n,_) t ->
+					try
+						(Hashtbl.find relevant n) t
+					with Not_found ->
+						if not (has_mono t) then t
+						else t_dynamic
+				) a.a_params pl in
+				if com.platform = Globals.Js && a.a_path = ([],"Map") then begin match tl with
+					| t1 :: _ ->
+						let rec loop stack t =
+							if List.exists (fun t2 -> fast_eq t t2) stack then
+								t
+							else begin
+								let stack = t :: stack in
+								match follow t with
+								| TAbstract ({ a_path = [],"Class" },_) ->
+									error (Printf.sprintf "Cannot use %s as key type to Map because Class<T> is not comparable" (s_type (print_context()) t1)) p;
+								| TEnum(en,tl) ->
+									PMap.iter (fun _ ef -> ignore(loop stack ef.ef_type)) en.e_constrs;
+									Type.map (loop stack) t
+								| t ->
+									Type.map (loop stack) t
+							end
+						in
+						ignore(loop [] t1)
+					| _ -> assert false
+				end;
+				tl
+		in
+		let _,cf =
+			try
+				Abstract.find_to a tl m
+			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
+				else
+					error ("Abstract " ^ (s_type_path a.a_path) ^ " has no @:to function that accepts " ^ st) p;
+		in
+		cf, follow m
+
+	let handle_abstract_casts ctx e =
+		let rec loop ctx e = match e.eexpr with
+			| TNew({cl_kind = KAbstractImpl a} as c,pl,el) ->
+				if not (Meta.has Meta.MultiType a.a_meta) then begin
+					(* This must have been a @:generic expansion with a { new } constraint (issue #4364). In this case
+					   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
+				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
+					let e = make_static_call ctx c cf a pl ((mk (TConst TNull) (TAbstract(a,pl)) e.epos) :: el) m e.epos in
+					{e with etype = m}
+				end
+			| TCall({eexpr = TField(_,FStatic({cl_path=[],"Std"},{cf_name = "string"}))},[e1]) when (match follow e1.etype with TAbstract({a_impl = Some _},_) -> true | _ -> false) ->
+				begin match follow e1.etype with
+					| 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
+						with Not_found ->
+							e
+						end
+					| _ ->
+						assert false
+				end
+			| TCall(e1, el) ->
+				begin try
+					let rec find_abstract e t = match follow t,e.eexpr with
+						| TAbstract(a,pl),_ when Meta.has Meta.MultiType a.a_meta -> a,pl,e
+						| _,TCast(e1,None) -> find_abstract e1 e1.etype
+						| _,TLocal {v_extra = Some(_,Some e')} ->
+							begin match follow e'.etype with
+							| TAbstract(a,pl) when Meta.has Meta.MultiType a.a_meta -> a,pl,mk (TCast(e,None)) e'.etype e.epos
+							| _ -> raise Not_found
+							end
+						| _ -> raise Not_found
+					in
+					let rec find_field e1 =
+						match e1.eexpr with
+						| TCast(e2,None) ->
+							{e1 with eexpr = TCast(find_field e2,None)}
+						| TField(e2,fa) ->
+							let a,pl,e2 = find_abstract e2 e2.etype in
+							let m = Abstract.get_underlying_type a pl in
+							let fname = field_name fa in
+							let el = List.map (loop ctx) el in
+							begin try
+								let fa = quick_field m fname in
+								let get_fun_type t = match follow t with
+									| TFun(_,tr) as tf -> tf,tr
+									| _ -> raise Not_found
+								in
+								let tf,tr = match fa with
+									| FStatic(_,cf) -> get_fun_type cf.cf_type
+									| FInstance(c,tl,cf) -> get_fun_type (apply_params c.cl_params tl cf.cf_type)
+									| FAnon cf -> get_fun_type cf.cf_type
+									| _ -> raise Not_found
+								in
+								let ef = mk (TField({e2 with etype = m},fa)) tf e2.epos in
+								let ecall = make_call ctx ef el tr e.epos in
+								if not (type_iseq ecall.etype e.etype) then
+									mk (TCast(ecall,None)) e.etype e.epos
+								else
+									ecall
+							with Not_found ->
+								(* quick_field raises Not_found if m is an abstract, we have to replicate the 'using' call here *)
+								match follow m with
+								| TAbstract({a_impl = Some c} as a,pl) ->
+									let cf = PMap.find fname c.cl_statics in
+									make_static_call ctx c cf a pl (e2 :: el) e.etype e.epos
+								| _ -> raise Not_found
+							end
+						| _ ->
+							raise Not_found
+					in
+					find_field e1
+				with Not_found ->
+					Type.map_expr (loop ctx) e
+				end
+			| _ ->
+				Type.map_expr (loop ctx) e
+		in
+		loop ctx e
+end
+
 (* -------------- debug functions to activate when debugging typer passes ------------------------------- *)
 (*/*
 
@@ -404,11 +620,11 @@ let debug ctx str =
 	if Common.raw_defined ctx.com "cdebug" then prerr_endline (context_ident ctx ^ !delay_tabs ^ str)
 
 let init_class_done ctx =
-	debug ctx ("init_class_done " ^ Ast.s_type_path ctx.curclass.cl_path);
+	debug ctx ("init_class_done " ^ s_type_path ctx.curclass.cl_path);
 	init_class_done ctx
 
 let ctx_pos ctx =
-	let inf = Ast.s_type_path ctx.m.curmod.m_path in
+	let inf = s_type_path ctx.m.curmod.m_path in
 	let inf = (match snd ctx.curclass.cl_path with "" -> inf | n when n = snd ctx.m.curmod.m_path -> inf | n -> inf ^ "." ^ n) in
 	let inf = (match ctx.curfield.cf_name with "" -> inf | n -> inf ^ ":" ^ n) in
 	inf

文件差異過大導致無法顯示
+ 217 - 227
src/typing/typeload.ml


文件差異過大導致無法顯示
+ 166 - 349
src/typing/typer.ml


+ 37 - 0
std/Any.hx

@@ -0,0 +1,37 @@
+/*
+ * Copyright (C)2005-2016 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.
+ */
+
+/**
+	`Any` is a type that is compatible with any other in both ways.
+
+	This means that a value of any type can be assigned to `Any`, and
+	vice-versa, a value of `Any` type can be assigned to any other type.
+
+	It's a more type-safe alternative to `Dynamic`, because it doesn't
+	support field access or operators and it's bound to monomorphs. So,
+	to work with the actual value, it needs to be explicitly promoted
+	to another type.
+**/
+abstract Any(Dynamic) {
+	@:noCompletion @:extern @:to inline function __promote<T>():T return this;
+	@:noCompletion @:extern @:from inline static function __cast<T>(value:T):Any return cast value;
+}

+ 5 - 5
std/Array.hx

@@ -23,8 +23,8 @@
 	An Array is a storage for values. You can access it using indexes or
 	with its API.
 
-	@see http://haxe.org/manual/std-Array.html
-	@see http://haxe.org/manual/lf-array-comprehension.html
+	@see https://haxe.org/manual/std-Array.html
+	@see https://haxe.org/manual/lf-array-comprehension.html
 **/
 extern class Array<T> {
 
@@ -124,11 +124,11 @@ extern class Array<T> {
 		`this` Array.
 
 		If `pos` or `end` are negative, their offsets are calculated from the
-		end	of `this` Array by `this.length + pos` and `this.length + end`
+		end of `this` Array by `this.length + pos` and `this.length + end`
 		respectively. If this yields a negative value, 0 is used instead.
 
-		If `pos` exceeds `this.length` or if `end` exceeds or equals `pos`,
-		the result is `[]`.
+		If `pos` exceeds `this.length` or if `end` is less than or equals
+		`pos`, the result is `[]`.
 	**/
 	function slice( pos : Int, ?end : Int ) : Array<T>;
 

+ 1 - 1
std/Class.hx

@@ -24,7 +24,7 @@
 
 	See `Type` for the Haxe Reflection API.
 
-	@see http://haxe.org/manual/types-class-instance.html
+	@see https://haxe.org/manual/types-class-instance.html
 **/
 @:coreType @:runtimeValue abstract Class<T> {
 }

+ 3 - 3
std/Date.hx

@@ -51,8 +51,8 @@ extern class Date
 	function new(year : Int, month : Int, day : Int, hour : Int, min : Int, sec : Int ) : Void;
 
 	/**
-		Returns the timestamp of the date. It might only have a per-second
-		precision depending on the platforms.
+		Returns the timestamp (in milliseconds) of the date. It might
+		only have a per-second precision depending on the platforms.
 	**/
 	function getTime() : Float;
 
@@ -87,7 +87,7 @@ extern class Date
 	function getDate() : Int;
 
 	/**
-		Returns the day of the week of `this` Date (0-6 range).
+		Returns the day of the week of `this` Date (0-6 range) where `0` is Sunday.
 	**/
 	function getDay() : Int;
 

+ 14 - 0
std/DateTools.hx

@@ -112,6 +112,20 @@ class DateTools {
 		support in Flash and JS for day and months names (due to lack of proper
 		internationalization API). On Haxe/Neko/Windows, some formats are not
 		supported.
+
+		```haxe
+		var t = DateTools.format(Date.now(), "%Y-%m-%d_%H:%M:%S"); 
+		// 2016-07-08_14:44:05
+
+		var t = DateTools.format(Date.now(), "%r"); 
+		// 02:44:05 PM
+
+		var t = DateTools.format(Date.now(), "%T"); 
+		// 14:44:05
+
+		var t = DateTools.format(Date.now(), "%F"); 
+		// 2016-07-08
+		```
 	**/
 	public static function format( d : Date, f : String ) : String {
 		#if (neko && !(macro || interp))

+ 1 - 1
std/EReg.hx

@@ -34,7 +34,7 @@
 	its methods.
 
 	A detailed explanation of the supported operations is available at
-	<http://haxe.org/manual/std-regex.html>
+	<https://haxe.org/manual/std-regex.html>
 **/
 class EReg {
 

+ 1 - 1
std/Enum.hx

@@ -27,7 +27,7 @@
 
 	See `Type` for the Haxe Reflection API.
 
-	@see http://haxe.org/manual/types-enum-instance.html
+	@see https://haxe.org/manual/types-enum-instance.html
 **/
 @:coreType @:runtimeValue abstract Enum<T> {
 }

+ 1 - 1
std/EnumValue.hx

@@ -24,7 +24,7 @@
 	An abstract type that represents any enum value.
 	See `Type` for the Haxe Reflection API.
 
-	@see http://haxe.org/manual/types-enum-instance.html
+	@see https://haxe.org/manual/types-enum-instance.html
 **/
 @:coreType abstract EnumValue {
 }

+ 1 - 1
std/IntIterator.hx

@@ -31,7 +31,7 @@
 	in a for-loop. Subsequent uses of the same instance will then have no
 	effect.
 
-	@see http://haxe.org/manual/lf-iterators.html
+	@see https://haxe.org/manual/lf-iterators.html
 **/
 class IntIterator {
 

+ 1 - 1
std/Lambda.hx

@@ -32,7 +32,7 @@
 	If the first argument to any of the methods is null, the result is
 	unspecified.
 
-	@see http://haxe.org/manual/std-Lambda.html
+	@see https://haxe.org/manual/std-Lambda.html
 **/
 class Lambda {
 

+ 1 - 1
std/List.hx

@@ -25,7 +25,7 @@
 	that are chained together. It is optimized so that adding or removing an
 	element does not imply copying the whole list content every time.
 
-	@see http://haxe.org/manual/std-List.html
+	@see https://haxe.org/manual/std-List.html
 **/
 class List<T> {
 

+ 1 - 1
std/Map.hx

@@ -42,7 +42,7 @@ import haxe.Constraints.IMap;
 
 	Map is an abstract type, it is not available at runtime.
 
-	@see http://haxe.org/manual/std-Map.html
+	@see https://haxe.org/manual/std-Map.html
 **/
 @:multiType(@:followWithAbstracts K)
 abstract Map<K,V>(IMap<K,V> ) {

+ 6 - 1
std/Math.hx

@@ -22,9 +22,10 @@
 /**
 	This class defines mathematical functions and constants.
 
-	@see http://haxe.org/manual/std-math.html
+	@see https://haxe.org/manual/std-math.html
 **/
 #if cpp @:include("hxMath.h") #end
+@:pure
 extern class Math
 {
 	/**
@@ -205,6 +206,8 @@ extern class Math
 	/**
 		Rounds `v` to the nearest integer value.
 
+		Ties are rounded up, so that `0.5` becomes `1` and `-0.5` becomes `0`.
+
 		If `v` is outside of the signed `Int32` range, or is `NaN`, `NEGATIVE_INFINITY`
 		or `POSITIVE_INFINITY`, the result is unspecified.
 	**/
@@ -252,6 +255,8 @@ extern class Math
 	/**
 		Rounds `v` to the nearest integer value, as a Float.
 
+		Ties are rounded up, so that `0.5` becomes `1` and `-0.5` becomes `0`.
+
 		If `v` is is `NaN`, `NEGATIVE_INFINITY` or `POSITIVE_INFINITY`,
 		the result is unspecified.
 	**/

+ 1 - 1
std/Reflect.hx

@@ -23,7 +23,7 @@
 	The Reflect API is a way to manipulate values dynamically through an
 	abstract interface in an untyped manner. Use with care.
 
-	@see http://haxe.org/manual/std-reflection.html
+	@see https://haxe.org/manual/std-reflection.html
 **/
 extern class Reflect {
 

+ 17 - 16
std/StdTypes.hx

@@ -24,7 +24,7 @@
 /**
 	The standard `Void` type. Only `null` values can be of the type `Void`.
 
-	@see http://haxe.org/manual/types-void.html
+	@see https://haxe.org/manual/types-void.html
 **/
 @:coreType abstract Void { }
 
@@ -34,11 +34,11 @@
 	On static targets, `null` cannot be assigned to Float. If this is necessary,
 	`Null<Float>` can be used instead.
 
-	`Std.int` converts a `Float` to an `Int`, rounded towards 0.  
+	`Std.int` converts a `Float` to an `Int`, rounded towards 0.
 	`Std.parseFloat` converts a `String` to a `Float`.
 
-	@see http://haxe.org/manual/types-basic-types.html
-	@see http://haxe.org/manual/types-nullability.html
+	@see https://haxe.org/manual/types-basic-types.html
+	@see https://haxe.org/manual/types-nullability.html
 **/
 @:coreType @:notNull @:runtimeValue abstract Float { }
 
@@ -48,12 +48,12 @@
 	On static targets, `null` cannot be assigned to `Int`. If this is necessary,
 	`Null<Int>` can be used instead.
 
-	`Std.int` converts a `Float` to an `Int`, rounded towards 0.  
+	`Std.int` converts a `Float` to an `Int`, rounded towards 0.
 	`Std.parseInt` converts a `String` to an `Int`.
 
-	@see http://haxe.org/manual/types-basic-types.html
-	@see http://haxe.org/manual/std-math-integer-math.html
-	@see http://haxe.org/manual/types-nullability.html
+	@see https://haxe.org/manual/types-basic-types.html
+	@see https://haxe.org/manual/std-math-integer-math.html
+	@see https://haxe.org/manual/types-nullability.html
 **/
 @:coreType @:notNull @:runtimeValue abstract Int to Float { }
 
@@ -70,7 +70,7 @@
 	generator to distinguish between base values that can be `null` and others that
 	can't.
 
-	@see http://haxe.org/manual/types-nullability.html
+	@see https://haxe.org/manual/types-nullability.html
 **/
 typedef Null<T> = T
 
@@ -80,8 +80,8 @@ typedef Null<T> = T
 	On static targets, `null` cannot be assigned to `Bool`. If this is necessary,
 	`Null<Bool>` can be used instead.
 
-	@see http://haxe.org/manual/types-bool.html
-	@see http://haxe.org/manual/types-nullability.html
+	@see https://haxe.org/manual/types-bool.html
+	@see https://haxe.org/manual/types-nullability.html
 **/
 @:coreType @:notNull @:runtimeValue abstract Bool {
 }
@@ -90,9 +90,10 @@ typedef Null<T> = T
 	`Dynamic` is a special type which is compatible with all other types.
 
 	Use of `Dynamic` should be minimized as it prevents several compiler
-	checks and optimizations.
+	checks and optimizations. See `Any` type for a safer alternative for
+	representing values of any type.
 
-	@see http://haxe.org/manual/types-dynamic.html
+	@see https://haxe.org/manual/types-dynamic.html
 **/
 @:coreType @:runtimeValue abstract Dynamic<T> {
 }
@@ -104,7 +105,7 @@ typedef Null<T> = T
 	and can then be used e.g. in `for`-loops. This makes it easy to implement
 	custom iterators.
 
-	@see http://haxe.org/manual/lf-iterators.html
+	@see https://haxe.org/manual/lf-iterators.html
 **/
 typedef Iterator<T> = {
 
@@ -135,7 +136,7 @@ typedef Iterator<T> = {
 	An `Iterable` is a data structure which has an `iterator()` method.
 	See `Lambda` for generic functions on iterable structures.
 
-	@see http://haxe.org/manual/lf-iterators.html
+	@see https://haxe.org/manual/lf-iterators.html
 **/
 typedef Iterable<T> = {
 	function iterator() : Iterator<T>;
@@ -149,6 +150,6 @@ typedef Iterable<T> = {
 	array access on classes. However, array access can be implemented for
 	abstract types.
 
-	@see http://haxe.org/manual/types-abstract-array-access.html
+	@see https://haxe.org/manual/types-abstract-array-access.html
 **/
 extern interface ArrayAccess<T> { }

+ 1 - 1
std/String.hx

@@ -30,7 +30,7 @@
 	String can be concatenated by using the `+` operator. If an operand is not a
 	String, it is passed through `Std.string()` first.
 	
-	@see http://haxe.org/manual/std-String.html
+	@see https://haxe.org/manual/std-String.html
 **/
 extern class String {
 

+ 4 - 4
std/StringTools.hx

@@ -21,7 +21,7 @@
  */
 /**
 	This class provides advanced methods on Strings. It is ideally used with
-	`using StringTools` and then acts as an [extension](http://haxe.org/manual/lf-static-extension.html)
+	`using StringTools` and then acts as an [extension](https://haxe.org/manual/lf-static-extension.html)
 	to the `String` class.
 
 	If the first argument to any of the methods is null, the result is
@@ -86,8 +86,8 @@ class StringTools {
 					ret.addChar('('.code);
 				case ['2'.code, '9'.code]:
 					ret.addChar(')'.code);
-				case ['7'.code, 'E'.code]:
-					ret.addChar('-'.code);
+				case ['7'.code, 'E'.code] | ['7'.code, 'e'.code]:
+					ret.addChar('~'.code);
 				case _:
 					ret.addChar('%'.code);
 					ret.addChar(cast c1);
@@ -503,7 +503,7 @@ class StringTools {
 	/**
 		Character codes of the characters that will be escaped by `quoteWinArg(_, true)`.
 	*/
-	public static var winMetaCharacters = [" ".code, "(".code, ")".code, "%".code, "!".code, "^".code, "\"".code, "<".code, ">".code, "&".code, "|".code, "\n".code, "\r".code];
+	public static var winMetaCharacters = [" ".code, "(".code, ")".code, "%".code, "!".code, "^".code, "\"".code, "<".code, ">".code, "&".code, "|".code, "\n".code, "\r".code, ",".code, ";".code];
 
 	/**
 		Returns a String that can be used as a single command line argument

+ 2 - 2
std/Type.hx

@@ -26,8 +26,8 @@
 	This class complements the more lightweight Reflect class, with a focus on
 	class and enum instances.
 
-	@see http://haxe.org/manual/types.html
-	@see http://haxe.org/manual/std-reflection.html
+	@see https://haxe.org/manual/types.html
+	@see https://haxe.org/manual/std-reflection.html
 **/
 extern class Type {
 

+ 2 - 2
std/UInt.hx

@@ -25,7 +25,7 @@
 	The unsigned `Int` type is only defined for Flash and C#. It's currently
 	handled the same as a normal Int.
 
-	@see http://haxe.org/manual/types-basic-types.html
+	@see https://haxe.org/manual/types-basic-types.html
 **/
 @:coreType
 @:notNull
@@ -97,7 +97,7 @@ abstract UInt to Int from Int
 	The unsigned `Int` type is only defined for Flash and C#.
 	Simulate it for other platforms.
 
-	@see http://haxe.org/manual/types-basic-types.html
+	@see https://haxe.org/manual/types-basic-types.html
 **/
 abstract UInt(Int) from Int to Int {
 

+ 2 - 2
std/Xml.hx

@@ -23,7 +23,7 @@
 /**
 	Xml node types.
 
-	@see http://haxe.org/manual/std-Xml.html
+	@see https://haxe.org/manual/std-Xml.html
 **/
 @:enum abstract XmlType(Int) {
 	/**
@@ -59,7 +59,7 @@
 /**
 	Crossplatform Xml API.
 
-	@see http://haxe.org/manual/std-Xml.html
+	@see https://haxe.org/manual/std-Xml.html
 **/
 class Xml {
 	/**

+ 2 - 3
std/hl/types/NativeAbstract.hx → std/cpp/AutoCast.hx

@@ -19,7 +19,6 @@
  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
  * DEALINGS IN THE SOFTWARE.
  */
-package hl.types;
+package cpp;
 
-extern class NativeAbstract<Const> {
-}
+extern class AutoCast { }

+ 4 - 1
std/cpp/Callable.hx

@@ -22,10 +22,13 @@
 package cpp;
 
 
+@:noPackageRestrict @:callable
+typedef CallableData<T> = T;
+
 // The generator intercepts this type and converts it to a cpp.Function<T> on cpp
 @:noPackageRestrict @:callable
 #if cpp extern #end
-abstract Callable<T>(T)
+abstract Callable<T>( CallableData<T> )
 {
    inline public function new(inValue:T) this = inValue;
    public var call(get,never):T;

+ 7 - 0
std/cpp/ConstPointer.hx

@@ -55,6 +55,13 @@ extern class ConstPointer<T>
    public static function fromPointer<T>(inNativePointer:Dynamic) : ConstPointer<T>;
 
    public function reinterpret<Other>():Pointer<Other>;
+
+   inline public function typeCast<Other>():Pointer<Other>
+   {
+      var tmp:haxe.extern.AsVar<Pointer<Other>> = reinterpret();
+      return tmp;
+   }
+
    public function rawCast<Other>():RawPointer<Other>;
 
    public function at(inIndex:Int):Reference<T>;

+ 28 - 0
std/cpp/FILE.hx

@@ -0,0 +1,28 @@
+/*
+ * Copyright (C)2005-2016 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;
+
+@:include("stdio.h")
+@:native(" ::cpp::Pointer<FILE>")
+extern class FILE { }
+

+ 20 - 17
std/cpp/Function.hx

@@ -19,36 +19,39 @@
  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
  * DEALINGS IN THE SOFTWARE.
  */
- package cpp;
+package cpp;
 
-@:coreType @:structAccess @:include("cpp/Pointer.h")
-extern class Function<T,ABI:cpp.abi.Abi>
+typedef FunctionData<T,ABI> = T;
+
+
+@:include("cpp/Pointer.h") @:callable
+extern abstract Function<T, ABI:cpp.abi.Abi>( FunctionData<T,ABI> )
 {
-   public function new(d:Dynamic);
+   inline public function new(inValue:T) this = inValue;
+
+   // Legacy Api
+   public var call(get,never):T;
+   inline function get_call():T return this;
 
-   // Actually a function pointer, but can be called using haxe notation
-	public var call(default,null):T;
 
    @:native("::cpp::Function_obj::getProcAddress")
-   static function nativeGetProcAddress<T,ABI:cpp.abi.Abi>(inModule:String, inFunction:String) : Function<T,ABI>;
-   public static function getProcAddress<T,ABI:cpp.abi.Abi>(inModule:String, inFunction:String) : Function<T,ABI>
+   @:extern static function nativeGetProcAddress<T,ABI:cpp.abi.Abi>(inModule:String, inFunction:String) : AutoCast return null;
+   inline public static function getProcAddress<T,ABI:cpp.abi.Abi>(inModule:String, inFunction:String) : Function<T,ABI>
    {
-      var autoCast = nativeGetProcAddress(inModule, inFunction);
-      return autoCast;
+      return cast nativeGetProcAddress(inModule, inFunction);
    }
 
    @:native("::cpp::Function_obj::fromStaticFunction")
-   static function nativeFromStaticFunction<T>(inStaticFunction:T) : Callable<T>;
+   @:extern static function nativeFromStaticFunction<T>(inStaticFunction:T) : AutoCast return null;
    inline public static function fromStaticFunction<T>(inStaticFunction:T) : Callable<T>
    {
-      var autoCast = nativeFromStaticFunction(inStaticFunction);
-      return autoCast;
+      return cast nativeFromStaticFunction(inStaticFunction);
    }
 
-	public function lt(inOther:Function<T,ABI>):Bool;
-	public function leq(inOther:Function<T,ABI>):Bool;
-	public function gt(inOther:Function<T,ABI>):Bool;
-	public function geq(inOther:Function<T,ABI>):Bool;
+	@:extern public function lt(inOther:Function<T,ABI>):Bool return false;
+	@:extern public function leq(inOther:Function<T,ABI>):Bool return false;
+	@:extern public function gt(inOther:Function<T,ABI>):Bool return false;
+	@:extern public function geq(inOther:Function<T,ABI>):Bool return false;
 }
 
 

+ 5 - 1
std/cpp/Lib.hx

@@ -81,7 +81,11 @@ class Lib {
 		return null;
 	}
 
-	public static function rethrow(inExp:Dynamic) { throw inExp; }
+	@:extern  @:noDebug @:native("HX_STACK_DO_RETHROW")
+	static function do_rethrow(inExp:Dynamic) { throw inExp; }
+
+	@:noDebug #if(!cppia) inline #end
+	public static function rethrow(inExp:Dynamic) { do_rethrow(inExp); }
 
 	public static function stringReference(inBytes:haxe.io.Bytes) : String
 	{

+ 12 - 0
std/cpp/NativeArc.hx

@@ -0,0 +1,12 @@
+package cpp;
+
+extern class NativeArc
+{
+   @:native("(__bridge_transfer id)")
+   public static function _bridgeTransfer<T>(ptr:cpp.RawPointer<cpp.Void> ):cpp.RawPointer<T>;
+
+   public static inline function bridgeTransfer<T>(ptr:cpp.RawPointer<cpp.Void> ):T return cast _bridgeTransfer(ptr);
+}
+
+
+

+ 19 - 1
std/cpp/NativeArray.hx

@@ -23,6 +23,18 @@
 
 extern class NativeArray {
 
+   #if cppia
+   public static inline function create<T>(length:Int):Array<T>
+   {
+      var result = new Array<T>();
+      NativeArray.setSize(result,length);
+      return result;
+   }
+   #else
+   @:native("_hx_create_array_length")
+   public static function create<T>(length:Int):Array<T>;
+   #end
+
 	public static inline function blit<T>( ioDestArray:Array<T>,
 		inDestElement:Int, inSourceArray:Array<T>,
 		inSourceElement:Int, inElementCount:Int ): Void  {
@@ -36,13 +48,19 @@ extern class NativeArray {
    @:nativeStaticExtension
 	public static function reserve<T>( inArray:Array<T>,inElements:Int ) : Void { }
 
+   @:nativeStaticExtension
+	public static function capacity<T>( inArray:Array<T> ) : Int { }
+
+   @:nativeStaticExtension
+	public static function getElementSize<T>( inArray:Array<T> ) : Int { }
+
 	public static inline function address<T>( inArray:Array<T>,inIndex:Int ) : Pointer<T> {
       return Pointer.arrayElem(inArray,inIndex);
    }
 
 	public static inline function setData<T>( inArray:Array<T>,inData:Pointer<T>,inElementCount:Int ) : Void {
       untyped inArray.setData(inData.raw,inElementCount);
-   }
+      }
 	public static inline function setUnmanagedData<T>( inArray:Array<T>,inData:ConstPointer<T>,inElementCount:Int ) : Void {
       untyped inArray.setUnmanagedData(inData.raw,inElementCount);
    }

+ 2 - 13
std/cpp/Object.hx

@@ -21,17 +21,6 @@
  */
  package cpp;
 
-@:native("hx::Object *")
-extern class HxObjectPtr
-{
-   @:native("hx::DynamicPtr")
-   static function fromDynamic(x:Dynamic):Object;
-   @:native("Dynamic")
-   static function toDynamic(x:Object):Dynamic;
-}
+@:noPackageRestrict
+typedef Object = Dynamic;
 
-@:extern
-abstract Object(HxObjectPtr) {
-	@:from public inline static function from(x:Dynamic):Object return HxObjectPtr.fromDynamic(x);
-	@:to public inline static function to(inVal:HxObjectPtr):Dynamic return HxObjectPtr.toDynamic(inVal);
-}

+ 9 - 10
std/cpp/Pointer.hx

@@ -21,6 +21,8 @@
  */
  package cpp;
 
+import haxe.extern.AsVar;
+
 @:coreType
 extern class Pointer<T> extends ConstPointer<T> implements ArrayAccess<T>
 {
@@ -34,33 +36,30 @@ extern class Pointer<T> extends ConstPointer<T> implements ArrayAccess<T>
    public static function fromRaw<T>(ptr:RawPointer<T>) : Pointer<T>;
 
    @:native("::cpp::Pointer_obj::fromHandle")
-   static function nativeFromHandle<T>(inHandle:Dynamic,?inKind:String):Pointer<T>;
+   static function nativeFromHandle<T>(inHandle:Dynamic,?inKind:String):AutoCast;
    inline public static function fromHandle<T>(inHandle:Dynamic,?inKind:String) : Pointer<T>
    {
-     var autoCast = nativeFromHandle(inHandle,inKind);
-     return autoCast;
+     return cast nativeFromHandle(inHandle,inKind);
    }
 
    public static function fromPointer<T>(inNativePointer:Dynamic) : Pointer<T>;
 
-   public static function addressOf<T>(inVariable:T) : Pointer<T>;
+   public static function addressOf<T>(inVariable:cpp.Reference<T>) : Pointer<T>;
 
    public static function endOf<T:{}>(inVariable:T) : Pointer<cpp.Void>;
 
    @:native("::cpp::Pointer_obj::arrayElem")
-   static function nativeArrayElem<T>(array:Array<T>, inElem:Int):Pointer<T>;
+   static function nativeArrayElem<T>(array:Array<T>, inElem:Int):AutoCast;
    inline static function arrayElem<T>(array:Array<T>, inElem:Int):Pointer<T>
    {
-      var autoCast = nativeArrayElem(array,inElem);
-      return autoCast;
+      return cast nativeArrayElem(array,inElem);
    }
 
    @:native("::cpp::Pointer_obj::ofArray")
-   static function nativeOfArray<T>(array:Array<T>):Pointer<T>;
+   static function nativeOfArray<T>(array:Array<T>):AutoCast;
    inline public static function ofArray<T>(array:Array<T>):Pointer<T>
    {
-     var autoCast = nativeOfArray(array);
-     return autoCast;
+     return cast nativeOfArray(array);
    }
 
    inline public function toUnmanagedArray(elementCount:Int) : Array<T>

+ 2 - 0
std/cpp/RawConstPointer.hx

@@ -24,4 +24,6 @@
 @:unreflective
 extern class RawConstPointer<T> implements ArrayAccess<T>
 {
+   @:native("hx::AddressOf")
+   public static function addressOf<T>(t:T) : RawConstPointer<T>;
 }

+ 2 - 0
std/cpp/RawPointer.hx

@@ -24,4 +24,6 @@
 @:unreflective
 extern class RawPointer<T> extends RawConstPointer<T>
 {
+   @:native("hx::AddressOf")
+   public static function addressOf<T>(t:T) : RawPointer<T>;
 }

部分文件因文件數量過多而無法顯示