浏览代码

Merge pull request #5284 from HaxeFoundation/development

3.3.0-rc1
Simon Krajewski 9 年之前
父节点
当前提交
b2398eeef7
共有 100 个文件被更改,包括 31301 次插入13337 次删除
  1. 5 0
      .gitattributes
  2. 19 3
      .gitignore
  3. 132 57
      .travis.yml
  4. 15 0
      .travis/platform.sh
  5. 3 0
      .travis/setenv_lua.sh
  6. 124 0
      .travis/setup_lua.sh
  7. 13 3
      CONTRIBUTING.md
  8. 130 57
      Makefile
  9. 6 5
      Makefile.win
  10. 22 9
      README.md
  11. 0 1667
      analyzer.ml
  12. 35 32
      appveyor.yml
  13. 68 1
      extra/CHANGES.txt
  14. 22 21
      extra/ImportAll.hx
  15. 3 467
      extra/LICENSE.txt
  16. 4 0
      extra/all.hxml
  17. 27 0
      extra/bintray.tpl.json
  18. 二进制
      extra/deploy_key.enc
  19. 7 1
      extra/extract.hxml
  20. 3 0
      extra/extract.patch
  21. 二进制
      extra/haxeci_sec.gpg.enc
  22. 二进制
      extra/haxeci_ssh.enc
  23. 1 1
      extra/haxelib_src
  24. 二进制
      extra/images/Banner.bmp
  25. 二进制
      extra/images/Wizard.bmp
  26. 1 1
      extra/installer.nsi
  27. 5 3
      extra/release-checklist.txt
  28. 16 17
      extra/setup.cpp
  29. 0 5732
      gencpp.ml
  30. 156 2
      haxe.hxproj
  31. 1 1
      libs
  32. 0 1454
      matcher.ml
  33. 422 0
      src/display/display.ml
  34. 188 764
      src/generators/codegen.ml
  35. 71 33
      src/generators/genas3.ml
  36. 259 177
      src/generators/gencommon.ml
  37. 6957 0
      src/generators/gencpp.ml
  38. 147 131
      src/generators/gencs.ml
  39. 6974 0
      src/generators/genhl.ml
  40. 98 71
      src/generators/genjava.ml
  41. 212 126
      src/generators/genjs.ml
  42. 1852 0
      src/generators/genlua.ml
  43. 18 22
      src/generators/genneko.ml
  44. 75 44
      src/generators/genphp.ml
  45. 176 127
      src/generators/genpy.ml
  46. 41 44
      src/generators/genswf.ml
  47. 33 24
      src/generators/genswf9.ml
  48. 19 26
      src/generators/genxml.ml
  49. 104 0
      src/json.ml
  50. 258 246
      src/macro/interp.ml
  51. 333 195
      src/main.ml
  52. 1262 0
      src/optimization/analyzer.ml
  53. 126 0
      src/optimization/analyzerConfig.ml
  54. 725 0
      src/optimization/analyzerTexpr.ml
  55. 684 0
      src/optimization/analyzerTexprTransformer.ml
  56. 525 0
      src/optimization/analyzerTypes.ml
  57. 62 42
      src/optimization/dce.ml
  58. 167 291
      src/optimization/filters.ml
  59. 464 312
      src/optimization/optimizer.ml
  60. 288 82
      src/syntax/ast.ml
  61. 29 26
      src/syntax/lexer.mll
  62. 200 134
      src/syntax/parser.ml
  63. 229 158
      src/typing/common.ml
  64. 1428 0
      src/typing/matcher.ml
  65. 627 103
      src/typing/type.ml
  66. 83 94
      src/typing/typecore.ml
  67. 4187 0
      src/typing/typeload.ml
  68. 366 293
      src/typing/typer.ml
  69. 5 2
      std/Array.hx
  70. 3 1
      std/Class.hx
  71. 3 3
      std/Date.hx
  72. 3 3
      std/DateTools.hx
  73. 4 4
      std/EReg.hx
  74. 3 1
      std/Enum.hx
  75. 3 1
      std/EnumValue.hx
  76. 3 1
      std/IntIterator.hx
  77. 28 2
      std/Lambda.hx
  78. 58 35
      std/List.hx
  79. 5 2
      std/Map.hx
  80. 4 2
      std/Math.hx
  81. 5 3
      std/Reflect.hx
  82. 1 1
      std/Std.hx
  83. 60 25
      std/StdTypes.hx
  84. 20 18
      std/String.hx
  85. 1 1
      std/StringBuf.hx
  86. 197 31
      std/StringTools.hx
  87. 25 9
      std/Sys.hx
  88. 24 20
      std/Type.hx
  89. 19 15
      std/UInt.hx
  90. 74 20
      std/Xml.hx
  91. 22 1
      std/cpp/ArrayBase.hx
  92. 18 0
      std/cpp/AtomicInt.hx
  93. 43 10
      std/cpp/Callable.hx
  94. 22 1
      std/cpp/CastCharStar.hx
  95. 22 1
      std/cpp/Char.hx
  96. 23 2
      std/cpp/ConstCharStar.hx
  97. 50 22
      std/cpp/ConstPointer.hx
  98. 31 0
      std/cpp/EnumBase.hx
  99. 1 1
      std/cpp/FastIterator.hx
  100. 14 0
      std/cpp/Finalizable.hx

+ 5 - 0
.gitattributes

@@ -1,2 +1,7 @@
 # Auto detect text files and perform LF normalization
 * text=auto
+
+.gitattributes export-ignore  
+.gitignore export-ignore
+.travis.yml export-ignore
+appveyor.yml export-ignore

+ 19 - 3
.gitignore

@@ -3,6 +3,7 @@
 *.cmx
 *.cmi
 *.cmxa
+*.cmo
 *.a
 *.exe
 .*.swp
@@ -11,14 +12,19 @@
 /extra/hxclasses
 /extra/*.swf
 /extra/*.swc
+/extra/bintray.json
+/extra/git-archive-all
+/extra/deploy_key
+/extra/*_sec.gpg
+/extra/*_ssh
 
-/version.ml
+/src/version.ml
 /haxe
 /haxelib*
 /haxedoc*
 /lib
 
-/lexer.ml
+/src/syntax/lexer.ml
 /libs/xml-light/xml_lexer.ml
 /libs/xml-light/xml_parser.ml
 /libs/xml-light/xml_parser.mli
@@ -57,6 +63,7 @@
 
 /haxe.sublime*
 build.bat
+/.vscode
 tests/unit/compile.php.hxml
 /extra/*.xml
 tests/optimization/testopt.js
@@ -64,7 +71,6 @@ tests/misc/pythonImport/native_python/__pycache__
 tests/unit/unit.py
 tests/unit/unit.py.res1.txt
 tests/unit/unit.py.res2.bin
-*.cmo
 tests/sys/bin/
 tests/optimization/dump/
 tests/misc/projects/*/*.n
@@ -72,3 +78,13 @@ tests/unit/bin/
 tests/*.n
 tests/misc/projects/Issue3756/cpp/
 tests/misc/projects/Issue4070/cpp/
+
+/*.manifest
+/tests/misc/eventLoop/cpp
+/tests/misc/eventLoop/eventLoop.js
+/tests/misc/eventLoop/eventLoop.n
+/tests/misc/eventLoop/eventLoop.swf
+/tests/misc/eventLoop/dump
+/tests/misc/eventLoop/eventLoop.py
+/tests/misc/eventLoop/php
+*.vscode/

+ 132 - 57
.travis.yml

@@ -1,74 +1,149 @@
-language: cpp
-
-os:
-  - linux
-  - osx
-
 env:
   global:
     # make variables
     - OCAMLC=ocamlc.opt
     - OCAMLOPT=ocamlopt.opt
     - ADD_REVISION=1
-    # SAUCE_USERNAME
+    # SauceLabs
     - secure: SjyKefmjUEXi0IKHGGpcbLAajU0mLHONg8aA8LoY7Q9nAkSN6Aql+fzS38Boq7w1jWn+2FOpr+4jy0l6wVd/bftsF+huFfYpFJmdh8BlKmE0K71zZAral0H1c7YxkuQpPiJCIFGXqtkvev7SWTy0z31u7kuuQeEyW27boXe5cDA=
-    # SAUCE_ACCESS_KEY
     - secure: sUvWUjCyPuWht4seNa4f2VG9DkvXkhZyLZfjJO9TUAHB2JndS16E2j/qrvKEjycyH6w8tU/B9vnjDRvvGrYXxEXcBEwsJVfkorFnRl9uwGCGIYrzjMhssEl3fMYZK7P304f+gAp5ULrDBX2gIaKeSa8lUNRtz2PsZOieE4kMdhk=
-  matrix:
-    - TEST=third-party
-    - TEST=macro
-    - TEST=neko
-    - TEST=js
-    - TEST=php
-    - TEST=cpp
-    - TEST=flash9
-    - TEST=as3
-    - TEST=java
-    - TEST=cs
-    - TEST=python
+    # Bintray
+    # - 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"
+    - DEBEMAIL="[email protected]"
+    # lua specific versions
+    - LUAROCKS=2.3.0
+    - LUA=lua5.2
 
-matrix:
-  # fast_finish: true #https://github.com/travis-ci/travis-ci/issues/1696
+sudo: required
+dist: trusty
+addons: &addons
+  ssh_known_hosts: haxe.org
 
-before_script:
-  - if [ "${TRAVIS_OS_NAME}" = "linux" ]; then
-    mysql -u root -e "CREATE DATABASE haxe_test;";
-    fi
+install_linux: &install_linux
+  # Install neko and haxe dependencies
+  - sudo apt-get install -y
+      ocaml-native-compilers
+      camlp4
+      pkg-config
+      libgc-dev
+      libssl-dev
+      libpcre3-dev
+      zlib1g-dev
+      apache2-dev
+      libmysqlclient-dev
+      libsqlite3-dev
+      libgtk2.0-dev
+  # Install neko
+  - travis_retry git clone https://github.com/HaxeFoundation/neko.git ~/neko
+  - pushd ~/neko
+  - cmake -DSTATIC_DEPS=MbedTLS
+  - make
+  - sudo make install
+  - popd
+  # Setup database
+  - travis_retry sudo apt-get install mysql-server -y
+  - mysql -u root -e "create user travis@localhost identified by '';"
+  - mysql -u root -e "create database haxe_test;"
+  - mysql -u root -e "grant all on haxe_test.* to travis@localhost;"
+  # Build haxe
+  - make package_src -s
+  - make -s
+  - make package_bin -s
+  - export PATH="$PATH:$TRAVIS_BUILD_DIR"
+  - export HAXE_STD_PATH="$TRAVIS_BUILD_DIR/std"
 
-install:
-  # For our forks that do not have mult-os enabled...
-  - if [ -z "${TRAVIS_OS_NAME}" ]; then
-    export TRAVIS_OS_NAME=linux;
-    fi
-  # Install haxe and neko dependencies
-  - if [ "${TRAVIS_OS_NAME}" = "linux" ]; then
-    travis_retry sudo apt-get update -qq;
-    travis_retry sudo apt-get install ocaml-native-compilers zlib1g-dev libgc-dev -qq;
-    fi
-  - if [ "${TRAVIS_OS_NAME}" = "osx" ]; then
-    travis_retry brew update;
-    travis_retry brew install caskroom/cask/brew-cask;
-    travis_retry brew install ocaml camlp4;
-    fi
+install_osx: &install_osx
+  # Install haxe dependencies
+  - travis_retry brew update
+  - travis_retry brew install ocaml camlp4;
   # Install neko
-  - if [ "${TRAVIS_OS_NAME}" = "linux" ]; then
-    travis_retry git clone https://github.com/HaxeFoundation/neko.git ~/neko;
-    cd ~/neko && make os=${TRAVIS_OS_NAME} -s && sudo make install -s;
-    cd $TRAVIS_BUILD_DIR;
-    fi
-  - if [ "${TRAVIS_OS_NAME}" = "osx" ]; then
-    travis_retry brew install neko --HEAD;
-    fi
+  - travis_retry brew install neko --HEAD;
+  # Setup database
+  - travis_retry brew install mysql
+  - mysql.server start
+  - mysql -u root -e "create user if not exists travis@localhost identified by '';"
+  - mysql -u root -e "create database haxe_test;"
+  - mysql -u root -e "grant all on haxe_test.* to travis@localhost;"
+  # Build haxe
+  - make package_src -s
+  - make -s
+  - make package_bin -s
+  - export PATH="$PATH:$TRAVIS_BUILD_DIR"
+  - export HAXE_STD_PATH="$TRAVIS_BUILD_DIR/std"
+
+matrix:
+  include:
+    #########
+    # linux #
+    #########
+    - os: linux
+      env:
+        - TEST=macro,neko,js,php,flash9,as3,java,cs,python,hl,lua
+        - DEPLOY=1
+        - SAUCE=1
+        # haxeci_decrypt (Deploy source package to ppa:haxe/snapshots.)
+        - secure: "Mw3p6bDZuqVQ6u7GrwLQfje5hhIOA4+mdqqLXYHP79UKdhgqb91Dn6IbG9vQ1VXVe64W4YZbQAMBMMRX5kEPDl6JvTVGSBhg00Mi69oO5qrCMcBI6f9FntG72YaVvLf+PA7co+vKrnJzaP2M9pe4SH9Ztbhy0YNxULp7NQ8FLsM="
+        # deploy_key_decrypt (Deploy doc to api.haxe.org.)
+        - secure: "A75uYqU0Xz6plIgSewEs0QQWe472dCMb9kf3j7Hx0DS7dApXgx8++189sw9Sv0wam5KPtbcIM292MucjGCb5zocVj9xCUVgajhEA0QpTuDMBjk/cg3ClWCGjfybaCl2E5LLdUs7Zy4b4oNWtVikOWLWJ4sC1kaarR9p6kv8yYZg="
+      addons:
+        <<: *addons
+        sauce_connect: true
+      before_install:
+        - sudo dpkg --add-architecture i386
+        - sudo apt-get update -y || true
+        - travis_retry sudo apt-get install -y
+            libcurl3:i386
+            libglib2.0-0:i386
+            libx11-6:i386
+            libxext6:i386
+            libxt6:i386
+            libxcursor1:i386
+            libnss3:i386
+            libgtk2.0-0:i386
+        - "export DISPLAY=:99.0"
+        - "sh -e /etc/init.d/xvfb start"
+        - "export AUDIODEV=null"
+      install: *install_linux
+
+    - os: linux
+      env:
+        - TEST=cpp
+      before_install:
+        - sudo apt-get update -y || true
+        - travis_retry sudo apt-get install -y
+            gcc-multilib
+            g++-multilib
+      install: *install_linux
+
+    #######
+    # osx #
+    #######
+    - os: osx
+      env:
+        - TEST=macro,neko,js,php,flash9,as3,java,cs,python,hl,lua
+        - DEPLOY=1
+      install: *install_osx
+
+    - os: osx
+      env:
+        - TEST=cpp
+      install: *install_osx
 
 script:
-  - make -s
-  - make tools -s
-  - sudo make install -s
-  - cd tests/
-  - mkdir ~/haxelib && haxelib setup ~/haxelib
-  - haxe -version
-  - haxe RunCi.hxml
-  - neko RunCi.n
+  - eval `ssh-agent -s` # for deployment to haxe.org
+  - pushd tests
+  -   mkdir ~/haxelib && haxelib setup ~/haxelib
+  -   haxe -version
+  -   haxe RunCi.hxml
+  -   neko RunCi.n
+  - popd
 
 branches:
   except:

+ 15 - 0
.travis/platform.sh

@@ -0,0 +1,15 @@
+if [ -z "${PLATFORM:-}" ]; then
+  PLATFORM=$TRAVIS_OS_NAME;
+fi
+
+if [ "$PLATFORM" == "osx" ]; then
+  PLATFORM="macosx";
+fi
+
+if [ -z "$PLATFORM" ]; then
+  if [ "$(uname)" == "Linux" ]; then
+    PLATFORM="linux";
+  else
+    PLATFORM="macosx";
+  fi;
+fi

+ 3 - 0
.travis/setenv_lua.sh

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

+ 124 - 0
.travis/setup_lua.sh

@@ -0,0 +1,124 @@
+#! /bin/bash
+
+# A script for setting up environment for travis-ci testing.
+# Sets up Lua and Luarocks.
+# LUA must be "lua5.1", "lua5.2", "lua5.3", or "luajit".
+# luajit2.0 - master v2.0
+# luajit2.1 - master v2.1
+
+set -eufo pipefail
+
+LUAJIT_VERSION="2.0.4"
+LUAJIT_BASE="LuaJIT-$LUAJIT_VERSION"
+
+source .travis/platform.sh
+
+LUA_HOME_DIR=$TRAVIS_BUILD_DIR/install/lua
+
+LR_HOME_DIR=$TRAVIS_BUILD_DIR/install/luarocks
+
+mkdir $HOME/.lua
+
+LUAJIT="no"
+
+if [ "$PLATFORM" == "macosx" ]; then
+  if [ "$LUA" == "luajit" ]; then
+    LUAJIT="yes";
+  fi
+  if [ "$LUA" == "luajit2.0" ]; then
+    LUAJIT="yes";
+  fi
+  if [ "$LUA" == "luajit2.1" ]; then
+    LUAJIT="yes";
+  fi;
+elif [ "$(expr substr $LUA 1 6)" == "luajit" ]; then
+  LUAJIT="yes";
+fi
+
+mkdir -p "$LUA_HOME_DIR"
+
+if [ "$LUAJIT" == "yes" ]; then
+
+  if [ "$LUA" == "luajit" ]; then
+    curl --location https://github.com/LuaJIT/LuaJIT/archive/v$LUAJIT_VERSION.tar.gz | tar xz;
+  else
+    git clone https://github.com/LuaJIT/LuaJIT.git $LUAJIT_BASE;
+  fi
+
+  cd $LUAJIT_BASE
+
+  if [ "$LUA" == "luajit2.1" ]; then
+    git checkout v2.1;
+    # force the INSTALL_TNAME to be luajit
+    perl -i -pe 's/INSTALL_TNAME=.+/INSTALL_TNAME= luajit/' Makefile
+  fi
+
+  make && make install PREFIX="$LUA_HOME_DIR"
+
+  ln -s $LUA_HOME_DIR/bin/luajit $HOME/.lua/luajit
+  ln -s $LUA_HOME_DIR/bin/luajit $HOME/.lua/lua;
+
+else
+
+  if [ "$LUA" == "lua5.1" ]; then
+    curl http://www.lua.org/ftp/lua-5.1.5.tar.gz | tar xz
+    cd lua-5.1.5;
+  elif [ "$LUA" == "lua5.2" ]; then
+    curl http://www.lua.org/ftp/lua-5.2.4.tar.gz | tar xz
+    cd lua-5.2.4;
+  elif [ "$LUA" == "lua5.3" ]; then
+    curl http://www.lua.org/ftp/lua-5.3.2.tar.gz | tar xz
+    cd lua-5.3.2;
+  fi
+
+  # Build Lua without backwards compatibility for testing
+  perl -i -pe 's/-DLUA_COMPAT_(ALL|5_2)//' src/Makefile
+  make $PLATFORM
+  make INSTALL_TOP="$LUA_HOME_DIR" install;
+
+  ln -s $LUA_HOME_DIR/bin/lua $HOME/.lua/lua
+  ln -s $LUA_HOME_DIR/bin/luac $HOME/.lua/luac;
+
+fi
+
+ls -l $HOME/.lua
+echo $PATH
+
+cd $TRAVIS_BUILD_DIR
+lua -v
+
+LUAROCKS_BASE=luarocks-$LUAROCKS
+
+curl --location http://luarocks.org/releases/$LUAROCKS_BASE.tar.gz | tar xz
+
+cd $LUAROCKS_BASE
+
+if [ "$LUA" == "luajit" ]; then
+  ./configure --lua-suffix=jit --with-lua-include="$LUA_HOME_DIR/include/luajit-2.0" --prefix="$LR_HOME_DIR";
+elif [ "$LUA" == "luajit2.0" ]; then
+  ./configure --lua-suffix=jit --with-lua-include="$LUA_HOME_DIR/include/luajit-2.0" --prefix="$LR_HOME_DIR";
+elif [ "$LUA" == "luajit2.1" ]; then
+  ./configure --lua-suffix=jit --with-lua-include="$LUA_HOME_DIR/include/luajit-2.1" --prefix="$LR_HOME_DIR";
+else
+  ./configure --with-lua="$LUA_HOME_DIR" --prefix="$LR_HOME_DIR"
+fi
+
+make build && make install
+
+ln -s $LR_HOME_DIR/bin/luarocks $HOME/.lua/luarocks
+
+cd $TRAVIS_BUILD_DIR
+
+luarocks --version
+
+rm -rf $LUAROCKS_BASE
+
+if [ "$LUAJIT" == "yes" ]; then
+  rm -rf $LUAJIT_BASE;
+elif [ "$LUA" == "lua5.1" ]; then
+  rm -rf lua-5.1.5;
+elif [ "$LUA" == "lua5.2" ]; then
+  rm -rf lua-5.2.4;
+elif [ "$LUA" == "lua5.3" ]; then
+  rm -rf lua-5.3.2;
+fi

+ 13 - 3
CONTRIBUTING.md

@@ -1,11 +1,21 @@
-Things to check before/while filing an issue:
+## 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.
 - 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` sub directory.
+- 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.
 
-Other remarks:
+## Is this the right repository to report the issue?
+
+This repository is about the Haxe compiler itself and the Haxe standard library. Here's an overview of repositories that are part of the Haxe ecosystem:
+
+* The haxelib command line tool or lib.haxe.org: <https://github.com/HaxeFoundation/haxelib/issues>
+* Something on try.haxe.org: <https://github.com/clemos/try-haxe/issues>
+* Something under haxe.org/manual: <https://github.com/HaxeFoundation/HaxeManual/issues>
+* Something on api.haxe.org: For content this is probably the right repository. If it's about the representation, try <https://github.com/dpeek/dox/issues> instead.
+* Something else on haxe.org: <https://github.com/HaxeFoundation/haxe.org/issues>
+
+## Other remarks:
 
 - Sometimes people try to be particularly helpful by not only including broken parts in their code, but also "similar" code which is working. More often than not this is more distracting than helpful. If you want to highlight something like this, consider adding the working code commented out.
 - We do not require a classic "What do you see/what do you expect?" form, but in some cases it is hard to figure out where you think the actual problem is otherwise.

+ 130 - 57
Makefile

@@ -10,9 +10,12 @@
 #
 .SUFFIXES : .ml .mli .cmo .cmi .cmx .mll .mly
 
-INSTALL_DIR=/usr
+INSTALL_DIR=$(DESTDIR)/usr
 INSTALL_BIN_DIR=$(INSTALL_DIR)/bin
 INSTALL_LIB_DIR=$(INSTALL_DIR)/lib/haxe
+INSTALL_STD_DIR=$(INSTALL_LIB_DIR)/std
+PACKAGE_OUT_DIR=out
+PACKAGE_SRC_EXTENSION=.tar.gz
 
 OUTPUT=haxe
 EXTENSION=
@@ -20,7 +23,8 @@ OCAMLOPT?=ocamlopt
 OCAMLC?=ocamlc
 LFLAGS=
 
-CFLAGS= -g -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
+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
 
 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 \
@@ -43,20 +47,32 @@ endif
 
 CC_CMD = $(COMPILER) $(CFLAGS) -c $<
 
-CC_PARSER_CMD = $(COMPILER) -pp camlp4o $(CFLAGS) -c parser.ml
+CC_PARSER_CMD = $(COMPILER) -pp camlp4o $(CFLAGS) -c src/syntax/parser.ml
 
 RELDIR=../../..
 
-MODULES=ast type lexer common genxml parser typecore optimizer typeload \
-	codegen gencommon genas3 gencpp genjs genneko genphp \
-	genswf9 genswf genjava gencs genpy interp dce analyzer filters typer matcher version main
+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 \
+	generators/gencpp generators/genjs generators/genneko generators/genphp generators/genswf9 \
+	generators/genswf generators/genjava generators/gencs generators/genpy macro/interp generators/genhl \
+	generators/genlua \
+	optimization/dce optimization/analyzerConfig optimization/analyzerTypes optimization/analyzerTexpr \
+	optimization/analyzerTexprTransformer optimization/analyzer \
+	optimization/filters typing/typer typing/matcher version main
 
 ADD_REVISION?=0
 
 BRANCH=$(shell echo $$APPVEYOR_REPO_NAME | grep -q /haxe && echo $$APPVEYOR_REPO_BRANCH || echo $$TRAVIS_REPO_SLUG | grep -q /haxe && echo $$TRAVIS_BRANCH || git rev-parse --abbrev-ref HEAD)
 COMMIT_SHA=$(shell git rev-parse --short HEAD)
-COMMIT_DATE=$(shell git show -s --format=%ci HEAD | grep -oh ....-..-..)
-PACKAGE_FILE_NAME=haxe_$(COMMIT_DATE)_$(BRANCH)_$(COMMIT_SHA)
+COMMIT_DATE=$(shell \
+	if [ "$$(uname)" = "Darwin" ]; then \
+		date -u -r $$(git show -s --format=%ct HEAD) +%Y%m%d%H%M%S; \
+	else \
+		date -u -d @$$(git show -s --format=%ct HEAD) +%Y%m%d%H%M%S; \
+	fi \
+)
+PACKAGE_FILE_NAME=haxe_$(COMMIT_DATE)_$(COMMIT_SHA)
 
 # using $(CURDIR) on Windows will not work since it might be a Cygwin path
 ifdef SYSTEMROOT
@@ -65,7 +81,7 @@ else
 	export HAXE_STD_PATH=$(CURDIR)/std
 endif
 
-all: libs haxe
+all: libs haxe tools
 
 libs:
 	make -C libs/extlib OCAMLOPT=$(OCAMLOPT) OCAMLC=$(OCAMLC) $(TARGET_FLAG)
@@ -79,30 +95,30 @@ libs:
 	make -C libs/ttflib OCAMLOPT=$(OCAMLOPT) OCAMLC=$(OCAMLC) $(TARGET_FLAG)
 	make -C libs/objsize OCAMLOPT=$(OCAMLOPT) OCAMLC=$(OCAMLC) $(TARGET_FLAG)
 
-haxe: $(MODULES:=.$(MODULE_EXT))
-	$(COMPILER) -o $(OUTPUT) $(NATIVE_LIBS) $(NATIVE_LIB_FLAG) $(LFLAGS) $(LIBS:=.$(LIB_EXT)) $(MODULES:=.$(MODULE_EXT))
+haxe: $(MODULES:%=src/%.$(MODULE_EXT))
+	$(COMPILER) -o $(OUTPUT) $(NATIVE_LIBS) $(NATIVE_LIB_FLAG) $(LFLAGS) $(LIBS:=.$(LIB_EXT)) $(MODULES:%=src/%.$(MODULE_EXT))
 
 haxelib:
-	(cd $(CURDIR)/extra/haxelib_src && $(CURDIR)/$(OUTPUT) haxelib.hxml && nekotools boot bin/haxelib.n)
-	cp extra/haxelib_src/bin/haxelib$(EXTENSION) haxelib$(EXTENSION)
+	(cd $(CURDIR)/extra/haxelib_src && $(CURDIR)/$(OUTPUT) client.hxml && nekotools boot run.n)
+	mv extra/haxelib_src/run$(EXTENSION) haxelib$(EXTENSION)
 
 tools: haxelib
 
 install:
-	-rm -f $(INSTALL_LIB_DIR)
-	-mkdir -p $(INSTALL_LIB_DIR)
-	rm -rf $(INSTALL_LIB_DIR)/std
-	cp -rf std $(INSTALL_LIB_DIR)/std
+	rm -rf $(INSTALL_LIB_DIR)
+	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)
-	-mkdir -p $(INSTALL_LIB_DIR)/lib
 	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)
 	chmod 777 $(INSTALL_LIB_DIR)/lib
-	# cp extra/haxelib_src/haxelib_script.sh $(INSTALL_DIR)/bin/haxelib
-	echo "#!/bin/sh" > $(INSTALL_BIN_DIR)/haxelib
-	echo "exec haxe -cp $(INSTALL_LIB_DIR)/extra/haxelib_src/src --run tools.haxelib.Main \"\$$@\"" >> $(INSTALL_BIN_DIR)/haxelib
 	chmod a+rx $(INSTALL_BIN_DIR)/haxe $(INSTALL_BIN_DIR)/haxelib
 
 # will install native version of the tools instead of script ones
@@ -115,84 +131,138 @@ uninstall:
 
 # Modules
 
-analyzer.$(MODULE_EXT): ast.$(MODULE_EXT) type.$(MODULE_EXT) common.$(MODULE_EXT) codegen.$(MODULE_EXT)
+# display
 
-codegen.$(MODULE_EXT): optimizer.$(MODULE_EXT) typeload.$(MODULE_EXT) typecore.$(MODULE_EXT) type.$(MODULE_EXT) genxml.$(MODULE_EXT) common.$(MODULE_EXT) ast.$(MODULE_EXT)
+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)
 
-common.$(MODULE_EXT): type.$(MODULE_EXT) ast.$(MODULE_EXT) libs/ilib/il.$(LIB_EXT)
+# generators
 
-dce.$(MODULE_EXT): ast.$(MODULE_EXT) common.$(MODULE_EXT) codegen.$(MODULE_EXT) type.$(MODULE_EXT)
+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)
 
-filters.$(MODULE_EXT): ast.$(MODULE_EXT) analyzer.$(MODULE_EXT) common.$(MODULE_EXT) type.$(MODULE_EXT) dce.$(MODULE_EXT) codegen.$(MODULE_EXT) typecore.$(MODULE_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)
 
-genas3.$(MODULE_EXT): type.$(MODULE_EXT) common.$(MODULE_EXT) codegen.$(MODULE_EXT) 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)
 
-gencommon.$(MODULE_EXT): type.$(MODULE_EXT) common.$(MODULE_EXT) codegen.$(MODULE_EXT) 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)
 
-gencpp.$(MODULE_EXT): type.$(MODULE_EXT) lexer.$(MODULE_EXT) common.$(MODULE_EXT) codegen.$(MODULE_EXT) ast.$(MODULE_EXT) gencommon.$(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)
 
-gencs.$(MODULE_EXT): type.$(MODULE_EXT) lexer.$(MODULE_EXT) gencommon.$(MODULE_EXT) common.$(MODULE_EXT) codegen.$(MODULE_EXT) ast.$(MODULE_EXT) libs/ilib/il.$(LIB_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)
 
-genjava.$(MODULE_EXT): type.$(MODULE_EXT) gencommon.$(MODULE_EXT) common.$(MODULE_EXT) codegen.$(MODULE_EXT) 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)
 
-genjs.$(MODULE_EXT): type.$(MODULE_EXT) optimizer.$(MODULE_EXT) lexer.$(MODULE_EXT) common.$(MODULE_EXT) codegen.$(MODULE_EXT) 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)
 
-genneko.$(MODULE_EXT): type.$(MODULE_EXT) lexer.$(MODULE_EXT) common.$(MODULE_EXT) codegen.$(MODULE_EXT) 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)
 
-genphp.$(MODULE_EXT): type.$(MODULE_EXT) lexer.$(MODULE_EXT) common.$(MODULE_EXT) codegen.$(MODULE_EXT) 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)
 
-genpy.$(MODULE_EXT): type.$(MODULE_EXT) lexer.$(MODULE_EXT) common.$(MODULE_EXT) codegen.$(MODULE_EXT) 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)
 
-genswf.$(MODULE_EXT): type.$(MODULE_EXT) genswf9.$(MODULE_EXT) common.$(MODULE_EXT) ast.$(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)
 
-genswf9.$(MODULE_EXT): type.$(MODULE_EXT) lexer.$(MODULE_EXT) common.$(MODULE_EXT) codegen.$(MODULE_EXT) ast.$(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)
 
-genxml.$(MODULE_EXT): type.$(MODULE_EXT) lexer.$(MODULE_EXT) common.$(MODULE_EXT) ast.$(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)
 
-interp.$(MODULE_EXT): typecore.$(MODULE_EXT) type.$(MODULE_EXT) lexer.$(MODULE_EXT) genneko.$(MODULE_EXT) common.$(MODULE_EXT) codegen.$(MODULE_EXT) ast.$(MODULE_EXT) genswf.$(MODULE_EXT) genjava.$(MODULE_EXT) gencs.$(MODULE_EXT) parser.$(MODULE_EXT) libs/ilib/il.$(LIB_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)
 
-matcher.$(MODULE_EXT): optimizer.$(MODULE_EXT) codegen.$(MODULE_EXT) typecore.$(MODULE_EXT) type.$(MODULE_EXT) typer.$(MODULE_EXT) common.$(MODULE_EXT) ast.$(MODULE_EXT)
+# macro
 
-main.$(MODULE_EXT): filters.$(MODULE_EXT) matcher.$(MODULE_EXT) typer.$(MODULE_EXT) typeload.$(MODULE_EXT) typecore.$(MODULE_EXT) type.$(MODULE_EXT) parser.$(MODULE_EXT) optimizer.$(MODULE_EXT) lexer.$(MODULE_EXT) interp.$(MODULE_EXT) genxml.$(MODULE_EXT) genswf.$(MODULE_EXT) genphp.$(MODULE_EXT) genneko.$(MODULE_EXT) genjs.$(MODULE_EXT) gencpp.$(MODULE_EXT) genas3.$(MODULE_EXT) common.$(MODULE_EXT) codegen.$(MODULE_EXT) ast.$(MODULE_EXT) gencommon.$(MODULE_EXT) genjava.$(MODULE_EXT) gencs.$(MODULE_EXT) genpy.$(MODULE_EXT) version.$(MODULE_EXT) libs/ilib/il.$(LIB_EXT)
+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)
 
-optimizer.$(MODULE_EXT): typecore.$(MODULE_EXT) type.$(MODULE_EXT) parser.$(MODULE_EXT) common.$(MODULE_EXT) ast.$(MODULE_EXT)
+# optimization
 
-parser.$(MODULE_EXT): parser.ml lexer.$(MODULE_EXT) common.$(MODULE_EXT) ast.$(MODULE_EXT)
+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/analyzerConfig.$(MODULE_EXT): src/syntax/ast.$(MODULE_EXT) src/typing/type.$(MODULE_EXT) src/typing/common.$(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/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/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/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/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/typing/typecore.$(MODULE_EXT) src/typing/type.$(MODULE_EXT) src/syntax/parser.$(MODULE_EXT) src/typing/common.$(MODULE_EXT) src/syntax/ast.$(MODULE_EXT)
+
+# syntax
+
+src/syntax/ast.$(MODULE_EXT):
+
+src/syntax/lexer.$(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)
 	$(CC_PARSER_CMD)
 
-type.$(MODULE_EXT): ast.$(MODULE_EXT)
+# typing
+
+src/typing/common.$(MODULE_EXT): src/typing/type.$(MODULE_EXT) src/syntax/ast.$(MODULE_EXT) libs/ilib/il.$(LIB_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)
 
-typecore.$(MODULE_EXT): type.$(MODULE_EXT) common.$(MODULE_EXT) ast.$(MODULE_EXT)
+src/typing/type.$(MODULE_EXT): src/syntax/ast.$(MODULE_EXT) src/json.$(MODULE_EXT)
 
-typeload.$(MODULE_EXT): typecore.$(MODULE_EXT) type.$(MODULE_EXT) parser.$(MODULE_EXT) optimizer.$(MODULE_EXT) lexer.$(MODULE_EXT) common.$(MODULE_EXT) ast.$(MODULE_EXT)
+src/typing/typecore.$(MODULE_EXT): src/typing/type.$(MODULE_EXT) src/typing/common.$(MODULE_EXT) src/syntax/ast.$(MODULE_EXT)
 
-typer.$(MODULE_EXT): typeload.$(MODULE_EXT) typecore.$(MODULE_EXT) type.$(MODULE_EXT) parser.$(MODULE_EXT) optimizer.$(MODULE_EXT) lexer.$(MODULE_EXT) interp.$(MODULE_EXT) genneko.$(MODULE_EXT) genjs.$(MODULE_EXT) common.$(MODULE_EXT) codegen.$(MODULE_EXT) ast.$(MODULE_EXT) filters.$(MODULE_EXT) gencommon.$(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)
 
-lexer.$(MODULE_EXT): lexer.ml
+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)
 
-lexer.$(MODULE_EXT): ast.$(MODULE_EXT)
 
-ast.$(MODULE_EXT):
+# main
 
-version.$(MODULE_EXT):
-	$(MAKE) -f Makefile.version_extra -s ADD_REVISION=$(ADD_REVISION) BRANCH=$(BRANCH) COMMIT_SHA=$(COMMIT_SHA) COMMIT_DATE=$(COMMIT_DATE) > version.ml
-	$(COMPILER) $(CFLAGS) -c version.ml
+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/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
 
 # Package
 
+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)
+
 package_bin:
-	mkdir -p out
+	mkdir -p $(PACKAGE_OUT_DIR)
 	rm -rf $(PACKAGE_FILE_NAME) $(PACKAGE_FILE_NAME).tar.gz
 	# Copy the package contents to $(PACKAGE_FILE_NAME)
 	mkdir -p $(PACKAGE_FILE_NAME)
 	cp -r $(OUTPUT) haxelib$(EXTENSION) std extra/LICENSE.txt extra/CONTRIB.txt extra/CHANGES.txt $(PACKAGE_FILE_NAME)
 	# archive
-	tar -zcf out/$(PACKAGE_FILE_NAME).tar.gz $(PACKAGE_FILE_NAME)
+	tar -zcf $(PACKAGE_OUT_DIR)/$(PACKAGE_FILE_NAME)_bin.tar.gz $(PACKAGE_FILE_NAME)
 	rm -r $(PACKAGE_FILE_NAME)
 
+
+install_dox:
+	haxelib git hxparse https://github.com/Simn/hxparse development 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
+
+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/
+
+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"
+
 # Clean
 
-clean: clean_libs clean_haxe clean_tools
+clean: clean_libs clean_haxe clean_tools clean_package
 
 clean_libs:
 	make -C libs/extlib clean
@@ -207,11 +277,14 @@ clean_libs:
 	make -C libs/objsize clean
 
 clean_haxe:
-	rm -f $(MODULES:=.obj) $(MODULES:=.o) $(MODULES:=.cmx) $(MODULES:=.cmi) $(MODULES:=.cmo) lexer.ml $(OUTPUT)
+	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)
 
 clean_tools:
 	rm -f $(OUTPUT) haxelib
 
+clean_package:
+	rm -rf $(PACKAGE_OUT_DIR)
+
 # SUFFIXES
 
 .ml.cmx:

+ 6 - 5
Makefile.win

@@ -2,6 +2,7 @@ include Makefile
 
 OUTPUT=haxe.exe
 EXTENSION=.exe
+PACKAGE_SRC_EXTENSION=.zip
 
 OCAMLOPT=ocamlopt.opt
 
@@ -32,7 +33,7 @@ endif
 
 ifdef FILTER
 CC_CMD=($(OCAMLOPT) $(CFLAGS) -c $< 2>tmp.cmi && $(FILTER)) || ($(FILTER) && exit 1)
-CC_PARSER_CMD=($(OCAMLOPT) -pp camlp4o $(CFLAGS) -c parser.ml 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)
 endif
 
 package_bin:
@@ -41,11 +42,11 @@ package_bin:
 	# Copy the package contents to $(PACKAGE_FILE_NAME)
 	# Using poor man's cp (zip then unzip), because cp in cygwin is quite broken
 	mkdir -p $(PACKAGE_FILE_NAME)
-	7za a -y -tzip -mx0 temp.zip $(OUTPUT) haxelib$(EXTENSION) std > log.txt || type log.txt
-	cd extra && 7za a -y -tzip -mx0 ../temp.zip LICENSE.txt CONTRIB.txt CHANGES.txt > log.txt || type log.txt
-	7za x -y temp.zip -o$(PACKAGE_FILE_NAME) > log.txt || type log.txt
+	7z a -y -tzip -mx0 temp.zip $(OUTPUT) haxelib$(EXTENSION) std > log.txt || type log.txt
+	cd extra && 7z a -y -tzip -mx0 ../temp.zip LICENSE.txt CONTRIB.txt CHANGES.txt > log.txt || type log.txt
+	7z x -y temp.zip -o$(PACKAGE_FILE_NAME) > log.txt || type log.txt
 	rm temp.zip
 	# archive
-	7za a -r -tzip out/$(PACKAGE_FILE_NAME).zip $(PACKAGE_FILE_NAME) > log.txt || type log.txt
+	7z a -r -tzip out/$(PACKAGE_FILE_NAME)_bin.zip $(PACKAGE_FILE_NAME) > log.txt || type log.txt
 	rm -r $(PACKAGE_FILE_NAME)
 	rm log.txt extra/log.txt

+ 22 - 9
README.md

@@ -32,21 +32,22 @@ For more information about Haxe, head to the [offical Haxe website](http://haxe.
 The Haxe project has several licenses, covering different parts of the projects.
 
  * The Haxe compiler is released under the GNU General Public License version 2 or any later version.
- * The Haxe libraries are released under a "two-clause" BSD license.
- * The Neko runtime is licensed under the GNU Lesser General Public License version 2.1 or any later version.
+ * 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).
 
 ## Installing Haxe
 
-The latest stable release is [Haxe 3.2.0-rc2](http://haxe.org/download/version/3.2.0-rc.2/). Pre-built binaries are available for your platform:
+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:
 
- * **[Windows installer](http://haxe.org/download/file/3.2.0-rc.2/haxe-3.2.0-rc2-win.exe)**
- * **[Windows binaries](http://haxe.org/download/file/3.2.0-rc.2/haxe-3.2.0-rc2-win.zip)**
- * **[OSX installer](http://haxe.org/download/file/3.2.0-rc.2/haxe-3.2.0-rc2-osx-installer.pkg)**
- * **[OSX binaries](http://haxe.org/download/file/3.2.0-rc.2/haxe-3.2.0-rc2-osx.tar.gz)**
- * **[Linux 32-bit binaries](http://haxe.org/download/file/3.2.0-rc.2/haxe-3.2.0-rc2-linux32.tar.gz)**
- * **[Linux 64-bit binaries](http://haxe.org/download/file/3.2.0-rc.2/haxe-3.2.0-rc2-linux64.tar.gz)**
+ * **[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)**
 
 Automated development builds are available from [build.haxe.org](http://build.haxe.org).
 
@@ -65,6 +66,7 @@ For information on on using Haxe, consult the [Haxe documentation](http://haxe.o
 
  * [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 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
 
@@ -72,9 +74,13 @@ For information on on using Haxe, consult the [Haxe documentation](http://haxe.o
 
 You can get help and talk with fellow Haxers from around the world via:
 
+ * [Haxe on Stack Overflow](http://stackoverflow.com/questions/tagged/haxe)
  * the [official Haxe Google Group](https://groups.google.com/forum/#!forum/haxelang)
+ * [#Haxe on Twitter](https://twitter.com/hashtag/haxe?src=hash)
  * the [Haxe IRC chatroom](http://unic0rn.github.io/tiramisu/haxe), #haxe on chat.freenode.net
 
+:+1: Get notified of the latest Haxe news, follow us on [Twitter](https://twitter.com/haxelang), [Facebook](https://www.facebook.com/haxe.org) or [Google+](https://plus.google.com/+HaxeOrg)
+
 ## Version compatibility
 
 Haxe   | neko
@@ -82,3 +88,10 @@ Haxe   | neko
 2.*    | 1.*
 3.0.0  | 2.0.0
 3.1.3  | 2.0.0
+3.2.0  | 2.0.0
+3.3.0  | 2.1.0
+
+
+## Contributing
+
+See [CONTRIBUTING.md](CONTRIBUTING.md) for more. Thank you!

+ 0 - 1667
analyzer.ml

@@ -1,1667 +0,0 @@
-open Ast
-open Type
-open Common
-open Typecore
-
-let s_expr = s_expr (s_type (print_context()))
-let s_expr_pretty = s_expr_pretty "" (s_type (print_context()))
-let debug e = print_endline (s_expr e)
-let debug_pretty s e = Printf.printf "%s %s\n" s (s_expr_pretty e)
-
-let flag_no_check = "no_check"
-let flag_check = "check"
-let flag_no_const_propagation = "no_const_propagation"
-let flag_const_propagation = "const_propagation"
-let flag_no_local_dce = "no_local_dce"
-let flag_local_dce = "local_dce"
-let flag_ignore = "ignore"
-let flag_no_simplification = "no_simplification"
-let flag_check_has_effect = "check_has_effect"
-let flag_no_check_has_effect = "no_check_has_effect"
-
-let has_analyzer_option meta s =
-	try
-		let rec loop ml = match ml with
-			| (Meta.Analyzer,el,_) :: ml ->
-				if List.exists (fun (e,p) ->
-					match e with
-						| EConst(Ident s2) when s = s2 -> true
-						| _ -> false
-				) el then
-					true
-				else
-					loop ml
-			| _ :: ml ->
-				loop ml
-			| [] ->
-				false
-		in
-		loop meta
-	with Not_found ->
-		false
-
-let is_ignored meta =
-	try
-		let rec loop ml = match ml with
-			| (Meta.Analyzer,el,_) :: ml ->
-				if List.exists (fun (e,p) ->
-					match e with
-						| EConst(Ident s2) when flag_ignore = s2 -> true
-						| _ -> false
-				) el then
-					true
-				else
-					loop ml
-			| (Meta.HasUntyped,_,_) :: _ ->
-				true
-			| _ :: ml ->
-				loop ml
-			| [] ->
-				false
-		in
-		loop meta
-	with Not_found ->
-		false
-
-let rec get_type_meta t = match t with
-	| TMono r ->
-		begin match !r with
-			| None -> raise Not_found
-			| Some t -> get_type_meta t
-		end
-	| TLazy f ->
-		get_type_meta (!f())
-	| TInst(c,_) ->
-		c.cl_meta
-	| TEnum(en,_) ->
-		en.e_meta
-	| TAbstract(a,_) ->
-		a.a_meta
-	| TType(t,_) ->
-		t.t_meta
-	| TAnon _ | TFun _ | TDynamic _ ->
-		raise Not_found
-
-let type_has_analyzer_option t s =
-	try
-		has_analyzer_option (get_type_meta t) s
-	with Not_found ->
-		false
-
-let is_enum_type t = match follow t with
-	| TEnum(_) -> true
-	| _ -> false
-
-let rec awkward_get_enum_index com e = match e.eexpr with
-	| TArray(e1,{eexpr = TConst(TInt i)}) when com.platform = Js && Int32.to_int i = 1 && is_enum_type e1.etype ->
-		e1
-	| TCall({eexpr = TField(e1, FDynamic "__Index")},[]) when com.platform = Cpp && is_enum_type e1.etype ->
-		e1
-	| TField(e1,FDynamic "index") when com.platform = Neko && is_enum_type e1.etype ->
-		e1
-	| TParenthesis e1 | TCast(e1,None) | TMeta(_,e1) ->
-		awkward_get_enum_index com e1
-	| _ ->
-		raise Not_found
-
-(*
-	This module simplifies the AST by introducing temporary variables for complex expressions in many places.
-	In particular, it ensures that no branching can occur in value-places so that we can later insert SSA PHI
-	nodes without worrying about their placement.
-*)
-module Simplifier = struct
-	let mk_block_context com =
-		let block_el = ref [] in
-		let push e = block_el := e :: !block_el in
-		let assign ev e =
-			let mk_assign e2 = match e2.eexpr with
-				| TBreak | TContinue | TThrow _ | TReturn _ -> e2
-				| _ -> mk (TBinop(OpAssign,ev,e2)) e2.etype e2.epos
-			in
-			let rec loop e = match e.eexpr with
-				| TBlock el ->
-					begin match List.rev el with
-						| e1 :: el ->
-							let el = List.rev ((loop e1) :: el) in
-							{e with eexpr = TBlock el}
-						| _ ->
-							mk_assign e
-					end
-				| TIf(e1,e2,eo) ->
-					let e2 = loop e2 in
-					let eo = match eo with None -> None | Some e3 -> Some (loop e3) in
-					{e with eexpr = TIf(e1,e2,eo)}
-				| TSwitch(e1,cases,edef) ->
-					let cases = List.map (fun (el,e) ->
-						let e = loop e in
-						el,e
-					) cases in
-					let edef = match edef with None -> None | Some edef -> Some (loop edef) in
-					{e with eexpr = TSwitch(e1,cases,edef)}
-				| TTry(e1,catches) ->
-					let e1 = loop e1 in
-					let catches = List.map (fun (v,e) ->
-						let e = loop e in
-						v,e
-					) catches in
-					{e with eexpr = TTry(e1,catches)}
-				| TParenthesis e1 | TMeta(_,e1) ->
-					loop e1 (* this is still weird, have to review *)
-(* 				| TBinop(OpAssign,({eexpr = TLocal _} as e1),e2) ->
-					push e;
-					mk_assign e1 *)
-(* 				| TBinop(OpAssignOp op,({eexpr = TLocal _} as e1),e2) ->
-					push e;
-					mk_assign e1 *)
-				| _ ->
-					mk_assign e
-			in
-			loop e
-		in
-		let declare_temp t eo p =
-			let v = alloc_var "tmp" t in
-			v.v_meta <- [Meta.CompilerGenerated,[],p];
-			let e_v = mk (TLocal v) t p in
-			let declare e_init =
-				let e = mk (TVar (v,e_init)) com.basic.tvoid p in
-				push e;
-			in
-			let e_v = match eo with
-				| None ->
-					declare None;
-					e_v
-				| Some e1 ->
-					begin match e1.eexpr with
-						| TThrow _ | TReturn _ | TBreak | TContinue ->
-							e1
-						| _ ->
-							let rec loop e_v e = match e.eexpr with
-								| TParenthesis e1 ->
-									loop {e_v with eexpr = TParenthesis e_v} e1
-								| TMeta(m,e1) ->
-									loop {e_v with eexpr = TMeta(m,e_v)} e1
-								| _ ->
-									e_v,e
-							in
-							let e_v',e1 = loop e_v e1 in
-							let e1 = assign e_v e1 in
-							begin match e1.eexpr with
-								| TBinop(OpAssign,{eexpr = TLocal v1},e2) when v == v1 ->
-									declare (Some e2)
-								| _ ->
-									declare None;
-									push e1
-							end;
-							e_v'
-					end
-			in
-			e_v
-		in
-		let rec push_block () =
-			let cur = !block_el in
-			block_el := [];
-			fun () ->
-				let added = !block_el in
-				block_el := cur;
-				List.rev added
-		and block f el =
-			let close = push_block() in
-			List.iter (fun e ->
-				push (f e)
-			) el;
-			close()
-		in
-		block,declare_temp,fun () -> !block_el
-
-	let apply com e =
-		let block,declare_temp,close_block = mk_block_context com in
-		let skip_binding ?(allow_tlocal=false) e =
-			let rec loop e =
-				match e.eexpr with
-				| TConst _ | TTypeExpr _ | TFunction _ -> ()
-				| TLocal _ when allow_tlocal -> ()
-				| TParenthesis e1 | TCast(e1,None) -> Type.iter loop e
-				| TField(_,(FStatic(c,cf) | FInstance(c,_,cf))) when has_analyzer_option cf.cf_meta flag_no_simplification || has_analyzer_option c.cl_meta flag_no_simplification -> ()
-				| TField({eexpr = TLocal _},_) when allow_tlocal -> ()
-				| TCall({eexpr = TField(_,(FStatic(c,cf) | FInstance(c,_,cf)))},el) when has_analyzer_option cf.cf_meta flag_no_simplification || has_analyzer_option c.cl_meta flag_no_simplification -> ()
-				| TCall({eexpr =  TLocal { v_name = "__cpp__" } },_) -> ()
-				| TField(_,FEnum _) -> ()
-				| TField(_,FDynamic _) -> ()
-				| _ when (try ignore(awkward_get_enum_index com e); true with Not_found -> false) -> ()
-				| _ -> raise Exit
-			in
-			try
-				loop e;
-				true
-			with Exit ->
-				begin match follow e.etype with
-					| TAbstract({a_path = [],"Void"},_) -> true
-					| TInst ({ cl_path = [],"Array" }, _) when com.platform = Cpp -> true
-					| _ -> false
-				end
-		in
-		let has_unbound = ref false in
-		let rec loop e = match e.eexpr with
-			| TCall({eexpr = TLocal v | TField({eexpr = TLocal v},_)},_) | TField({eexpr = TLocal v},_) | TLocal v when Meta.has Meta.Unbound v.v_meta && v.v_name <> "`trace" ->
-				has_unbound := true;
-				e
-			| TBlock el ->
-				{e with eexpr = TBlock (block loop el)}
-			| TCall({eexpr = TField(_,(FStatic(c,cf) | FInstance(c,_,cf)))},el) when has_analyzer_option cf.cf_meta flag_no_simplification || has_analyzer_option c.cl_meta flag_no_simplification ->
-				e
-			| TField(_,(FStatic(c,cf) | FInstance(c,_,cf))) when has_analyzer_option cf.cf_meta flag_no_simplification || has_analyzer_option c.cl_meta flag_no_simplification ->
-				e
-			| TCall(e1,el) ->
-				let rec is_valid_call_target e = match e.eexpr with
-					| TFunction _ | TField _ | TLocal _ | TConst (TSuper)  ->
-						true
-					| TParenthesis e1 | TCast(e1,None) | TMeta(_,e1) ->
-						is_valid_call_target e1
-					| _ ->
-						false
-				in
-				let e1 = if is_valid_call_target e1 then
-					loop e1
-				else
-					bind e1
-				in
-				let check e t =
-					if type_has_analyzer_option t flag_no_simplification then e
-					else bind e
-				in
-				let el = match e1.eexpr,follow e1.etype with
-					| TConst TSuper,_ when com.platform = Java || com.platform = Cs ->
-						(* they hate you if you mess up the super call *)
-						el
-					| _,TFun _ | TConst TSuper,_ ->
-						Codegen.UnificationCallback.check_call check el e1.etype
-					| _ ->
-						(* too dangerous *)
-						List.map loop el
-				in
-				{e with eexpr = TCall(e1,el)}
-			| TNew(c,tl,el) ->
-				{e with eexpr = TNew(c,tl,ordered_list el)}
-			| TArrayDecl el ->
-				{e with eexpr = TArrayDecl (ordered_list el)}
-			| TObjectDecl fl ->
-				let el = ordered_list (List.map snd fl) in
-				{e with eexpr = TObjectDecl (List.map2 (fun (n,_) e -> n,e) fl el)}
-			| TBinop(OpBoolAnd | OpBoolOr as op,e1,e2) ->
-				let e1 = loop e1 in
-				let e_then = mk (TBlock (block loop [e2])) e2.etype e2.epos in
-				let e_if,e_else = if op = OpBoolOr then
-					mk (TUnop(Not,Prefix,e1)) com.basic.tbool e.epos,mk (TConst (TBool(true))) com.basic.tbool e.epos
-				else
-					e1,mk (TConst (TBool(false))) com.basic.tbool e.epos
-				in
-				loop (mk (TIf(e_if,e_then,Some e_else)) com.basic.tbool e.epos)
-			| TBinop((OpAssign | OpAssignOp _) as op,{eexpr = TArray(e11,e12)},e2) ->
-				let e1 = match ordered_list [e11;e12] with
-					| [e1;e2] ->
-						{e with eexpr = TArray(e1,e2)}
-					| _ ->
-						assert false
-				in
-				let e2 = bind e2 in
-				{e with eexpr = TBinop(op,e1,e2)}
-			| TBinop((OpAssign | OpAssignOp _) as op,e1,e2) ->
-				let e2 = bind ~allow_tlocal:true e2 in
-				let e1 = loop e1 in
-				{e with eexpr = TBinop(op,e1,e2)}
-			| TBinop(op,e1,e2) ->
-				begin match ordered_list [e1;e2] with
-					| [e1;e2] ->
-						{e with eexpr = TBinop(op,e1,e2)}
-					| _ ->
-						assert false
-				end
-			| TArray(e1,e2) ->
-				begin match ordered_list [e1;e2] with
-					| [e1;e2] ->
-						{e with eexpr = TArray(e1,e2)}
-					| _ ->
-						assert false
-				end
-			| TWhile(e1,e2,flag) when (match e1.eexpr with TConst(TBool true) | TParenthesis {eexpr = TConst(TBool true)} -> false | _ -> true) ->
-				let p = e.epos in
-				let e_break = mk TBreak t_dynamic p in
-				let e_not = mk (TUnop(Not,Prefix,Codegen.mk_parent e1)) e1.etype e1.epos in
-				let e_if eo = mk (TIf(e_not,e_break,eo)) com.basic.tvoid p in
-				let rec map_continue e = match e.eexpr with
-					| TContinue ->
-						(e_if (Some e))
-					| TWhile _ | TFor _ ->
-						e
-					| _ ->
-						Type.map_expr map_continue e
-				in
-				let e2 = if flag = NormalWhile then e2 else map_continue e2 in
-				let e_if = e_if None in
-				let e_if = mk (TMeta((Meta.Custom ":whileCond",[],e_if.epos), e_if)) e_if.etype e_if.epos in
-				let e_block = if flag = NormalWhile then Type.concat e_if e2 else Type.concat e2 e_if in
-				let e_true = mk (TConst (TBool true)) com.basic.tbool p in
-				let e = mk (TWhile(Codegen.mk_parent e_true,e_block,NormalWhile)) e.etype p in
-				loop e
-			| TFor(v,e1,e2) ->
-				let e1 = bind e1 in
-				let e2 = loop e2 in
-				{e with eexpr = TFor(v,e1,e2)}
-			| TIf(e1,e2,eo) ->
-				let e1 = bind e1 in
-				let e2 = loop e2 in
-				let eo = match eo with None -> None | Some e -> Some (loop e) in
-				{e with eexpr = TIf(e1,e2,eo)}
-			| TSwitch (e1,cases,eo) ->
-				let e1 = bind e1 in
-				let cases = List.map (fun (el,e) ->
-					let el = List.map loop el in
-					let e = loop e in
-					el,e
-				) cases in
-				let eo = match eo with None -> None | Some e -> Some (loop e) in
-				{e with eexpr = TSwitch(e1,cases,eo)}
-			| TVar(v,Some e1) ->
-				let e1 = match e1.eexpr with
-					| TFunction _ -> loop e1
-					| TArrayDecl [{eexpr = TFunction _}] -> loop e1
-					| _ -> bind ~allow_tlocal:true e1
-				in
-				{e with eexpr = TVar(v,Some e1)}
-			| TUnop((Neg | NegBits | Not) as op,flag,e1) ->
-				let e1 = bind e1 in
-				{e with eexpr = TUnop(op,flag,e1)}
-			| TField(e1,fa) ->
-				let e1 = bind ~allow_tlocal:true e1 in
-				{e with eexpr = TField(e1,fa)}
-			| TReturn (Some ({eexpr = TThrow _ | TReturn _} as e1)) ->
-				loop e1 (* this is a bit hackish *)
-			| TReturn (Some e1) ->
-				let e1 = bind e1 in
-				{e with eexpr = TReturn (Some e1)}
-			| TThrow e1 ->
-				let e1 = bind e1 in
-				{e with eexpr = TThrow e1}
-			| TCast(e1,mto) ->
-				let e1 = bind ~allow_tlocal:true e1 in
-				{e with eexpr = TCast(e1,mto)}
-			| _ ->
-				Type.map_expr loop e
-		and bind ?(allow_tlocal=false) e =
-			let e = loop e in
-			if skip_binding ~allow_tlocal e then
-				e
-			else
-				declare_temp e.etype (Some e) e.epos
-		and ordered_list el =
-			if List.for_all (skip_binding ~allow_tlocal:true) el then
-				List.map loop el
-			else
-				List.map bind el
-		in
-		let e = loop e in
-		!has_unbound,match close_block() with
-			| [] ->
-				e
-			| el ->
-				mk (TBlock (List.rev (e :: el))) e.etype e.epos
-
-	let unapply com e =
-		let var_map = ref IntMap.empty in
-		let rec get_assignment_to v e = match e.eexpr with
-			| TBinop(OpAssign,{eexpr = TLocal v2},e2) when v == v2 -> Some e2
-			| TBlock [e] -> get_assignment_to v e
-			| TIf(e1,e2,Some e3) ->
-				begin match get_assignment_to v e2,get_assignment_to v e3 with
-				| Some e2,Some e3 -> Some ({e with eexpr = TIf(e1,e2,Some e3)})
-				| _ -> None
-				end
-			| _ -> None
-		in
-		let if_or_op e e1 e2 e3 = match e1.eexpr,e3.eexpr with
-			| TUnop(Not,Prefix,e1),TConst (TBool true) -> {e with eexpr = TBinop(OpBoolOr,e1,e2)}
-			| _,TConst (TBool false) -> {e with eexpr = TBinop(OpBoolAnd,e1,e2)}
-			| _ -> {e with eexpr = TIf(e1,e2,Some e3)}
-		in
-		let rec loop e = match e.eexpr with
-			| TBlock el ->
-				let rec loop2 el = match el with
-					| e :: el ->
-						begin match e.eexpr with
-							| TVar(v,Some e1) when Meta.has Meta.CompilerGenerated v.v_meta ->
-								if el = [] then
-									[loop e1]
-								else begin
-									var_map := IntMap.add v.v_id (loop e1) !var_map;
-									loop2 el
-								end
-							| TVar(v,None) when not (com.platform = Php) ->
-								begin match el with
-									| {eexpr = TBinop(OpAssign,{eexpr = TLocal v2},e2)} :: el when v == v2 ->
-										let e = {e with eexpr = TVar(v,Some e2)} in
-										loop2 (e :: el)
-									| ({eexpr = TIf(e1,e2,Some e3)} as e_if) :: el ->
-										let e1 = loop e1 in
-										let e2 = loop e2 in
-										let e3 = loop e3 in
-										begin match get_assignment_to v e2,get_assignment_to v e3 with
-											| Some e2,Some e3 ->
-												let e_if = if_or_op e_if (loop e1) (loop e2) (loop e3) in
-												let e = {e with eexpr = TVar(v,Some e_if)} in
-												loop2 (e :: el)
-											| _ ->
-												let e_if = {e_if with eexpr = TIf(e1,e2,Some e3)} in
-												e :: e_if :: loop2 el
-										end
-									| _ ->
-										let e = loop e in
-										e :: loop2 el
-								end
-							| TReturn (Some e1) when (match follow e1.etype with TAbstract({a_path=[],"Void"},_) -> true | _ -> false) ->
-								[(loop e1);{e with eexpr = TReturn None}]
-							| _ ->
-								let e = loop e in
-								e :: loop2 el
-						end
-					| [] ->
-						[]
-				in
-				let el = loop2 el in
-				{e with eexpr = TBlock el}
-			| TLocal v when Meta.has Meta.CompilerGenerated v.v_meta ->
-				begin try IntMap.find v.v_id !var_map
-				with Not_found -> e end
-			| TWhile(e1,e2,flag) ->
-				let e1 = loop e1 in
-				let e2 = loop e2 in
-				let extract_cond e = match e.eexpr with
-					| TIf({eexpr = TUnop(Not,_,e1)},_,_) -> e1
-					| TBreak -> raise Exit (* can happen due to optimization, not so easy to deal with because there might be other breaks/continues *)
-					| _ -> assert false
-				in
-				let e1,e2,flag = try
-					begin match e2.eexpr with
-						| TBlock el ->
-							begin match el with
-								| {eexpr = TMeta((Meta.Custom ":whileCond",_,_),e1)} :: el ->
-									let e1 = extract_cond e1 in
-									e1,{e2 with eexpr = TBlock el},NormalWhile
-								| _ ->
-									e1,e2,flag
-									(* issue 3844 *)
-(* 									begin match List.rev el with
-										| {eexpr = TMeta((Meta.Custom ":whileCond",_,_),e1)} :: el ->
-											let e1 = extract_cond e1 in
-											e1,{e2 with eexpr = TBlock (List.rev el)},DoWhile
-										| _ ->
-											e1,e2,flag
-									end *)
-							end
-						| _ ->
-							e1,e2,flag
-					end with Exit ->
-						e1,e2,flag
-				in
-				{e with eexpr = TWhile(e1,e2,flag)}
-			| TIf(e1,e2,Some e3) ->
-				let e1 = loop e1 in
-				let e2 = loop e2 in
-				let e3 = loop e3 in
-				if_or_op e e1 e2 e3
-			| _ ->
-				Type.map_expr loop e
-		in
-		loop e
-end
-
-module Ssa = struct
-
-	type var_map = tvar IntMap.t
-
-	type condition =
-		| Equal of tvar * texpr
-		| NotEqual of tvar * texpr
-
-	type node_data = {
-		nd_pos: pos;
-		mutable nd_var_map : var_map;
-		mutable nd_terminates : bool;
-	}
-
-	type join_node = {
-		mutable branches : node_data list;
-	}
-
-	type ssa_context = {
-		com : Common.context;
-		mutable cleanup : (unit -> unit) list;
-		mutable cur_data : node_data;
-		mutable var_conds : (condition list) IntMap.t;
-		mutable loop_stack : (join_node * join_node) list;
-		mutable exception_stack : join_node list;
-		mutable block_depth : int;
-	}
-
-	let s_cond = function
-		| Equal(v,e) -> Printf.sprintf "%s == %s" v.v_name (s_expr_pretty e)
-		| NotEqual(v,e) -> Printf.sprintf "%s != %s" v.v_name (s_expr_pretty e)
-
-	let s_conds conds =
-		String.concat " && " (List.map s_cond conds)
-
-	let mk_loc v p = mk (TLocal v) v.v_type p
-
-	let mk_phi =
-		let v_phi = alloc_var "__ssa_phi__" t_dynamic in
-		(fun vl p ->
-			let e = mk (TCall(mk_loc v_phi p,(List.map (fun (v,p) -> mk_loc v p) vl))) t_dynamic p in
-			e
-		)
-
-	(* TODO: make sure this is conservative *)
-	let can_throw e =
-		let rec loop e = match e.eexpr with
-			| TConst _ | TLocal _ | TTypeExpr _ | TFunction _ | TBlock _ -> ()
-			| TCall _ | TNew _ | TThrow _ | TCast(_,Some _) -> raise Exit
-			| _ -> Type.iter loop e
-		in
-		try
-			loop e; false
-		with Exit ->
-			true
-
-	let mk_join_node() = {
-		branches = []
-	}
-
-	let mk_node_data p = {
-		nd_pos = p;
-		nd_var_map = IntMap.empty;
-		nd_terminates = false;
-	}
-
-	let add_branch join branch p =
-		join.branches <- {branch with nd_pos = p} :: join.branches
-
-	let branch ctx p =
-		let old_map = ctx.cur_data.nd_var_map in
-		let old_term = ctx.cur_data.nd_terminates in
-		ctx.cur_data.nd_terminates <- false;
-		(fun join ->
-			add_branch join ctx.cur_data p;
-			ctx.cur_data.nd_var_map <- old_map;
-			ctx.cur_data.nd_terminates <- old_term;
-		)
-
-	let terminate ctx =
-		ctx.cur_data.nd_terminates <- true
-
-	let set_loop_join ctx join_top join_bottom =
-		ctx.loop_stack <- (join_top,join_bottom) :: ctx.loop_stack;
-		(fun () ->
-			ctx.loop_stack <- List.tl ctx.loop_stack
-		)
-
-	let set_exception_join ctx join =
-		ctx.exception_stack <- join :: ctx.exception_stack;
-		(fun () ->
-			ctx.exception_stack <- List.tl ctx.exception_stack;
-		)
-
-	let create_v_extra v =
-		match v.v_extra with
-		| Some (_,Some _) ->
-			()
-		| Some (tl,None) ->
-			let e_extra = mk (TObjectDecl []) t_dynamic null_pos in
-			v.v_extra <- Some (tl,Some e_extra)
-		| None ->
-			let e_extra = mk (TObjectDecl []) t_dynamic null_pos in
-			v.v_extra <- Some ([],Some e_extra)
-
-	let set_v_extra_value v s e = match v.v_extra with
-		| Some (tl, Some {eexpr = TObjectDecl fl}) ->
-			let rec loop fl = match fl with
-				| (s',_) :: fl when s' = s ->
-					(s,e) :: fl
-				| f1 :: fl ->
-					f1 :: loop fl
-				| [] ->
-					[s,e]
-			in
-			let e_extra = mk (TObjectDecl (loop fl)) t_dynamic null_pos in
-			v.v_extra <- Some (tl, Some e_extra)
-		| _ ->
-			assert false
-
-	let get_origin_var v = match v.v_extra with
-		| Some (_,Some {eexpr = TObjectDecl fl}) ->
-			begin match List.assoc "origin_var" fl with
-				| {eexpr = TLocal v'} -> v'
-				| _ -> raise Not_found
-			end
-		| _ ->
-			raise Not_found
-
-	let set_origin_var v v_origin p =
-		let ev = mk_loc v_origin p in
-		set_v_extra_value v "origin_var" ev
-
-	let get_var_value v = match v.v_extra with
-		| Some (_,Some {eexpr = TObjectDecl fl}) ->
-			List.assoc "var_value" fl
-		| _ ->
-			raise Not_found
-
-	let set_var_value v e =
-		set_v_extra_value v "var_value" e
-
-	let get_var_usage_count v = match v.v_extra with
-		| Some (_,Some {eexpr = TObjectDecl fl}) ->
-			begin try
-				begin match List.assoc "usage_count" fl with
-				| {eexpr = TConst (TInt i32)} -> Int32.to_int i32
-				| _ -> 0
-				end
-			with Not_found ->
-				0
-			end
-		| _ ->
-			raise Not_found
-
-	let set_var_usage_count v i =
-		let e = mk (TConst (TInt (Int32.of_int i))) t_dynamic null_pos in
-		set_v_extra_value v "usage_count" e
-
-	let declare_var ctx v p =
-		let old = v.v_extra in
-		ctx.cleanup <- (fun () ->
-			v.v_extra <- old
-		) :: ctx.cleanup;
-		ctx.cur_data.nd_var_map <- IntMap.add v.v_id v ctx.cur_data.nd_var_map;
-		v.v_meta <- ((Meta.Custom ":blockDepth",[EConst (Int (string_of_int ctx.block_depth)),p],p)) :: v.v_meta;
-		v.v_extra <- None;
-		create_v_extra v;
-		set_origin_var v v p
-
-	let assign_var ctx v e p =
-		if v.v_capture then
-			v
-		else begin
-			let i = match v.v_extra with
-				| Some (l,eo) ->
-					v.v_extra <- Some (("",t_dynamic) :: l,eo);
-					List.length l + 1
-				| _ ->
-					error "Something went wrong" p
-			in
-			let v' = alloc_var (Printf.sprintf "%s<%i>" v.v_name i) v.v_type in
-			create_v_extra v';
-			v'.v_meta <- [(Meta.Custom ":ssa"),[],p];
-			set_origin_var v' v p;
-			ctx.cur_data.nd_var_map <- IntMap.add v.v_id v' ctx.cur_data.nd_var_map;
-			set_var_value v' e;
-			v'
-		end
-
-	let get_var ctx v p =
-		try
-			IntMap.find v.v_id ctx.cur_data.nd_var_map
-		with Not_found ->
-			if not (has_meta Meta.Unbound v.v_meta) then
-				error (Printf.sprintf "Unbound variable %s" v.v_name) p;
-			v
-
-	let close_join_node ctx node p =
-		let terminates = ref true in
-		let branches = List.filter (fun branch ->
-			if branch.nd_terminates then false
-			else begin
-				terminates := false;
-				true
-			end
-		) node.branches in
-		match branches with
-			| [] ->
-				()
-			| branch :: branches ->
-				let vars = ref (IntMap.map (fun v -> [v,branch.nd_pos]) branch.nd_var_map) in
-				let rec handle_branch branch =
-					IntMap.iter (fun i v ->
-						try
-							let vl = IntMap.find i !vars in
-							if not (List.exists (fun (v',_) -> v == v') vl) then
-								vars := IntMap.add i ((v,p) :: vl) !vars
-						with Not_found ->
-							()
-					) branch.nd_var_map;
-				in
-				List.iter handle_branch branches;
-				ctx.cur_data.nd_terminates <- !terminates;
-				IntMap.iter (fun i vl -> match vl with
-					| [v,p] ->
-						ctx.cur_data.nd_var_map <- IntMap.add i v ctx.cur_data.nd_var_map;
-					| (v',_) :: _ ->
-						let v = get_origin_var v' in
-						ignore(assign_var ctx v (mk_phi vl p) p)
-					| _ ->
-						assert false
-				) !vars
-
-	let invert_cond = function
-		| Equal(v,e) -> NotEqual(v,e)
-		| NotEqual(v,e) -> Equal(v,e)
-
-	let invert_conds =
-		List.map invert_cond
-
-	let rec eval_cond ctx e = match e.eexpr with
-		| TBinop(OpNotEq,{eexpr = TLocal v},e1) ->
-			[NotEqual(v,e1)]
-		| TBinop(OpEq,{eexpr = TLocal v},e1) ->
-			[Equal(v,e1)]
-		| TUnop(Not,_,e1) ->
-			invert_conds (eval_cond ctx e1)
-		| TLocal v ->
-			begin try eval_cond ctx (get_var_value v)
-			with Not_found -> [] end
-		| _ ->
-			[]
-
-	let append_cond ctx v cond p =
-		begin try
-			let conds = IntMap.find v.v_id ctx.var_conds in
-			ctx.var_conds <- IntMap.add v.v_id (cond :: conds) ctx.var_conds
-		with Not_found ->
-			ctx.var_conds <- IntMap.add v.v_id [cond] ctx.var_conds
-		end
-
-(* 	let apply_cond ctx = function
-		| Equal({v_extra = Some(_,Some {eexpr = TLocal v})} as v0,e1) ->
-			let v' = assign_var ctx v (mk_loc v0 e1.epos) e1.epos in
-			append_cond ctx v' (Equal(v',e1)) e1.epos
-		| NotEqual({v_extra = Some(_,Some {eexpr = TLocal v})} as v0,e1) ->
-			let v' = assign_var ctx v (mk_loc v0 e1.epos) e1.epos in
-			append_cond ctx v' (NotEqual(v',e1)) e1.epos
-		| _ -> ()
-
-	let apply_not_null_cond ctx v p =
-		apply_cond ctx (NotEqual(v,(mk (TConst TNull) t_dynamic p))) *)
-
-	let apply com e =
-		let rec handle_if ctx f econd eif eelse =
-			let econd = loop ctx econd in
-			(* let cond = eval_cond ctx econd in *)
-			let join = mk_join_node() in
-			let close = branch ctx eif.epos in
-			(* List.iter (apply_cond ctx) cond; *)
-			let eif = loop ctx eif in
-			close join;
-			let eelse = match eelse with
-				| None ->
-					(* let cond = invert_conds cond in *)
-					(* List.iter (apply_cond ctx) cond; *)
-					add_branch join ctx.cur_data e.epos;
-					None
-				| Some e ->
-					let close = branch ctx e.epos in
-					(* let cond = invert_conds cond in *)
-					(* List.iter (apply_cond ctx) cond; *)
-					let eelse = loop ctx e in
-					close join;
-					Some eelse
-			in
-			close_join_node ctx join e.epos;
-			f econd eif eelse
-		and handle_loop_body ctx e =
-			let join_top = mk_join_node() in
-			let join_bottom = mk_join_node() in
-			let unset = set_loop_join ctx join_top join_bottom in
-			let close = branch ctx e.epos in
-			ignore(loop ctx e); (* TODO: I don't know if this is sane. *)
-			close join_top;
-			add_branch join_top ctx.cur_data e.epos;
-			close_join_node ctx join_top e.epos;
-			let ebody = loop ctx e in
-			ctx.cur_data.nd_terminates <- false;
-			unset();
-			close_join_node ctx join_bottom e.epos;
-			ebody
-		and loop ctx e = match e.eexpr with
-			(* var declarations *)
-			| TVar(v,eo) ->
-				declare_var ctx v e.epos;
-				let eo = match eo with
-					| None -> None
-					| Some e ->
-						let e = loop ctx e in
-						set_var_value v e;
-						Some e
-				in
-				{e with eexpr = TVar(v,eo)}
-			| TFunction tf ->
-				let close = branch ctx e.epos in
-				List.iter (fun (v,co) ->
-					declare_var ctx v e.epos;
-(* 					match co with
-						| Some TNull when (match v.v_type with TType({t_path=["haxe"],"PosInfos"},_) -> false | _ -> true) -> ()
-						| _ -> apply_not_null_cond ctx v e.epos *)
-				) tf.tf_args;
-				let e' = loop ctx tf.tf_expr in
-				close (mk_join_node());
-				{e with eexpr = TFunction {tf with tf_expr = e'}}
-			(* var modifications *)
-			| TBinop(OpAssign,({eexpr = TLocal v} as e1),e2) when v.v_name <> "this" ->
-				let e2 = loop ctx e2 in
-				let _ = assign_var ctx v e2 e1.epos in
-				{e with eexpr = TBinop(OpAssign,e1,e2)}
-			| TBinop(OpAssignOp op,({eexpr = TLocal v} as e1),e2) ->
-				let e1 = loop ctx e1 in
-				let e2 = loop ctx e2 in
-				let e_op = mk (TBinop(op,e1,e2)) e.etype e.epos in
-				let _ = assign_var ctx v e_op e1.epos in
-				{e with eexpr = TBinop(OpAssignOp op,e1,e2)}
-			| TUnop((Increment | Decrement as op),flag,({eexpr = TLocal v} as e1)) ->
-				let op = match op with Increment -> OpAdd | Decrement -> OpSub | _ -> assert false in
-				let e_one = mk (TConst (TInt (Int32.of_int 1))) com.basic.tint e.epos in
-				let e1 = loop ctx e1 in
-				let e_op = mk (TBinop(op,e1,e_one)) e.etype e.epos in
-				let _ = assign_var ctx v e_op e1.epos in
-				e
-			(* var user *)
-			| TLocal v ->
-				let v = get_var ctx v e.epos in
-				{e with eexpr = TLocal v}
-			(* control flow *)
-			| TIf(econd,eif,eelse) ->
-				let f econd eif eelse = {e with eexpr = TIf(econd,eif,eelse)} in
-				handle_if ctx f econd eif eelse
-			| TSwitch(e1,cases,edef) ->
-				let e1 = loop ctx e1 in
-				let join = mk_join_node() in
-				let cases = List.map (fun (el,e) ->
-					let close = branch ctx e.epos in
-					let el = List.map (loop ctx) el in
-					let e = loop ctx e in
-					close join;
-					el,e
-				) cases in
-				let edef = match edef with
-					| Some e ->
-						let close = branch ctx e.epos in
-						let e = loop ctx e in
-						close join;
-						Some e
-					| None ->
-						begin match e1.eexpr with
-							| TMeta((Meta.Exhaustive,_,_),_)
-							| TParenthesis({eexpr = TMeta((Meta.Exhaustive,_,_),_)}) ->
-								()
-							| _ ->
-								add_branch join ctx.cur_data e.epos;
-						end;
-						None
-				in
-				close_join_node ctx join e.epos;
-				let e = {e with eexpr = TSwitch(e1,cases,edef)} in
-				e
-			| TWhile(econd,ebody,mode) ->
-				let econd = loop ctx econd in
-				let ebody = handle_loop_body ctx ebody in
-				let e = {e with eexpr = TWhile(econd,ebody,mode)} in
-				e
-			| TFor(v,e1,ebody) ->
-				declare_var ctx v e.epos;
-				(* apply_not_null_cond ctx v e1.epos; *)
-				let v' = IntMap.find v.v_id ctx.cur_data.nd_var_map in
-				let e1 = loop ctx e1 in
-				let ebody = handle_loop_body ctx ebody in
-				let e = {e with eexpr = TFor(v',e1,ebody)} in
-				e
-			| TTry(e1,catches) ->
-				let join_ex = mk_join_node() in
-				let join_bottom = mk_join_node() in
-				let unset = set_exception_join ctx join_ex in
-				let e1 = loop ctx e1 in
-				unset();
-				add_branch join_bottom ctx.cur_data e.epos;
-				close_join_node ctx join_ex e.epos;
-				let catches = List.map (fun (v,e) ->
-					declare_var ctx v e.epos;
-					(* apply_not_null_cond ctx v e.epos; *)
-					let close = branch ctx e.epos in
-					let e = loop ctx e in
-					close join_bottom;
-					v,e
-				) catches in
-				close_join_node ctx join_bottom e.epos;
-				let e = {e with eexpr = TTry(e1,catches)} in
-				e
-			| TBreak ->
-				begin match ctx.loop_stack with
-					| [] -> error "Break outside loop" e.epos
-					| (_,join) :: _ -> add_branch join ctx.cur_data e.epos
-				end;
-				terminate ctx;
-				e
-			| TContinue ->
-				begin match ctx.loop_stack with
-					| [] -> error "Continue outside loop" e.epos
-					| (join,_) :: _ -> add_branch join ctx.cur_data e.epos
-				end;
-				terminate ctx;
-				e
-			| TThrow e1 ->
-				let e1 = loop ctx e1 in
-				begin match ctx.exception_stack with
-					| join :: _ -> add_branch join ctx.cur_data e.epos
-					| _ -> ()
-				end;
-				terminate ctx;
-				{e with eexpr = TThrow e1}
-			| TReturn eo ->
-				let eo = match eo with None -> None | Some e -> Some (loop ctx e) in
-				terminate ctx;
-				{e with eexpr = TReturn eo}
-			| TBlock el ->
-				let rec loop2 el = match el with
-					| [] ->
-						[]
-					| e :: el ->
-						if ctx.cur_data.nd_terminates then begin
-							(* ctx.com.warning (Printf.sprintf "Unreachable code: %s" (s_expr_pretty e)) e.epos; *)
-							[]
-						end else
-							let e = loop ctx e in
-							e :: (loop2 el)
-				in
-				ctx.block_depth <- ctx.block_depth + 1;
-				let el = loop2 el in
-				ctx.block_depth <- ctx.block_depth - 1;
-				{e with eexpr = TBlock(el)}
-			| _ ->
-				begin match ctx.exception_stack with
-					| join :: _ when can_throw e -> add_branch join ctx.cur_data e.epos
-					| _ -> ()
-				end;
-				Type.map_expr (loop ctx) e
-		in
-		let ctx = {
-			com = com;
-			cur_data = mk_node_data e.epos;
-			var_conds = IntMap.empty;
-			loop_stack = [];
-			exception_stack = [];
-			cleanup = [];
-			block_depth = 0;
-		} in
-		let e = loop ctx e in
-		e,ctx
-
-	let unapply com e =
-		let rec loop e = match e.eexpr with
-			| TFor(v,e1,e2) when Meta.has (Meta.Custom ":ssa") v.v_meta ->
-				let v' = get_origin_var v in
-				let e1 = loop e1 in
-				let e2 = loop e2 in
-				{e with eexpr = TFor(v',e1,e2)}
-			| TLocal v when Meta.has (Meta.Custom ":ssa") v.v_meta ->
-				let v' = get_origin_var v in
-				{e with eexpr = TLocal v'}
-			| TBlock el ->
-				let rec filter e = match e.eexpr with
-					| TMeta((Meta.Custom ":ssa",_,_),_) ->
-						false
-					| _ ->
-						true
-				in
-				let el = List.filter filter el in
-				let el = List.map loop el in
-				{e with eexpr = TBlock el}
-			| _ ->
-				Type.map_expr loop e
-		in
-		loop e
-end
-
-module ConstPropagation = struct
-	open Ssa
-
-	let expr_eq e1 e2 = match e1.eexpr,e2.eexpr with
-		| TConst ct1, TConst ct2 ->
-			ct1 = ct2
-		| _ ->
-			false
-
-	let get_block_depth v = try
-		let i = match Meta.get (Meta.Custom ":blockDepth") v.v_meta with
-			| _,[EConst(Int s),_],_ -> int_of_string s
-			| _ -> raise Not_found
-		in
-		i
-		with Not_found ->
-			-1
-
-	let can_be_inlined com v0 e = type_iseq v0.v_type e.etype && match e.eexpr with
-		| TConst ct ->
-			begin match ct with
-				| TThis | TSuper -> false
-				(* Some targets don't like seeing null in certain places and won't even compile. We have to detect `if (x != null)
-				   in order for this to work. *)
-				| TNull when (match com.platform with Php | Cpp -> true | _ -> false) -> false
-				| _ -> true
-			end
-		| TLocal v ->
-			not (Meta.has Meta.CompilerGenerated v.v_meta) &&
-			begin try
-				let v' = Ssa.get_origin_var v in
-				begin match v'.v_extra with
-					| Some ([],_) -> get_block_depth v <= get_block_depth v0
-					| _ -> false
-				end
-			with Not_found ->
-				false
-			end
-		| TEnumParameter _ when not (com.platform = Php) ->
-			Ssa.get_var_usage_count v0 <= 1
-		| _ ->
-			false
-
-	let semi_awkward_enum_value ssa e i = match e.eexpr with
-		| TCall({eexpr = TField(_,FEnum _)},el) -> (try List.nth el i with Failure _ -> raise Not_found)
-		| _ -> raise Not_found
-
-	let rec local ssa force v e =
-		begin try
-			if v.v_capture then raise Not_found;
-			if type_has_analyzer_option v.v_type flag_no_const_propagation then raise Not_found;
-			begin match follow v.v_type with
-				| TDynamic _ -> raise Not_found
-				| _ -> ()
-			end;
-			let e = Ssa.get_var_value v in
-			let old = v.v_extra in
-			v.v_extra <- None;
-			let e = value ssa force e in
-			v.v_extra <- old;
-			Ssa.set_var_value v e;
-			e
-		with Not_found ->
-			e
-		end
-
-	(* force must only be true if the value is not used in the output *)
-	and value ssa force e = match e.eexpr with
-		| TUnop((Increment | Decrement),_,_)
-		| TBinop(OpAssignOp _,_,_)
-		| TBinop(OpAssign,_,_) ->
-			e
-		| TBinop(op,e1,e2) ->
-			let e1 = value ssa force e1 in
-			let e2 = value ssa force e2 in
-			let e = {e with eexpr = TBinop(op,e1,e2)} in
-			let e' = Optimizer.optimize_binop e op e1 e2 in
-			if e == e' then
-				e
-			else
-				value ssa force e'
-		| TUnop(op,flag,e1) ->
-			let e1 = value ssa force e1 in
-			let e = {e with eexpr = TUnop(op,flag,e1)} in
-			let e' = Optimizer.optimize_unop e op flag e1 in
-			if e == e' then
-				e
-			else
-				value ssa force e'
-		| TCall (({eexpr = TLocal {v_name = "__ssa_phi__"}} as ephi),el) ->
-			let el = List.map (value ssa force) el in
-			begin match el with
-				| [] -> assert false
-				| e1 :: el ->
-					if List.for_all (fun e2 -> expr_eq e1 e2) el then
-						value ssa force e1
-					else
-						{e with eexpr = TCall(ephi, e1 :: el)}
-			end
-		| TParenthesis e1 | TMeta(_,e1) ->
-			value ssa force e1
-		| TLocal v ->
-			let e' = local ssa force v e in
-			if force || can_be_inlined ssa.com v e' then
-				e'
-			else
-				e
- 		| TEnumParameter(e1,ef,i) ->
-			let ev = value ssa true e1 in
-			begin try
-				value ssa force (semi_awkward_enum_value ssa ev i)
-			with Not_found ->
-				e
-			end
-		| _ ->
-			e
-
-	(* TODO: the name is quite accurate *)
-	let awkward_get_enum_index ssa e =
-		let e = awkward_get_enum_index ssa.com e in
-		let ev = (value ssa true e) in
-		match ev.eexpr with
-			| TField(_,FEnum(_,ef)) -> TInt (Int32.of_int ef.ef_index)
-			| TCall({eexpr = TField(_,FEnum(_,ef))},_) -> TInt (Int32.of_int ef.ef_index)
-			| _ -> raise Not_found
-
-	let apply ssa e =
-		let rec loop e = match e.eexpr with
-			| TLocal v when not (Meta.has Meta.Unbound v.v_meta) ->
-				set_var_usage_count v (get_var_usage_count v + 1);
-			| _ ->
-				Type.iter loop e
-		in
-		loop e;
-		let had_function = ref false in
-		let rec loop e = match e.eexpr with
-			| TFunction _ when !had_function ->
-				e
-			| TFunction tf ->
-				had_function := true;
-				{e with eexpr = TFunction {tf with tf_expr = loop tf.tf_expr}}
-			| TLocal v ->
-				let e' = local ssa false v e in
-				if can_be_inlined ssa.com v e' then
-					e'
-				else
-					e
-			| TCall({eexpr = TField(_,(FStatic(_,cf) | FInstance(_,_,cf) | FAnon cf))},el) when has_analyzer_option cf.cf_meta flag_no_const_propagation ->
-				e
-			| TCall(e1,el) ->
-				let e1 = loop e1 in
-				let check e t =
-					if type_has_analyzer_option t flag_no_const_propagation then e
-					else loop e
-				in
-				let el = Codegen.UnificationCallback.check_call check el e1.etype in
-				{e with eexpr = TCall(e1,el)}
-(* 			| TField(e1,fa) ->
-				let e1' = loop e1 in
-				let fa = if e1' != e1 then
-					begin try quick_field e1'.etype (field_name fa)
-					with Not_found -> fa end
-				else
-					fa
-				in
-				{e with eexpr = TField(e1',fa)} *)
-			| TUnop((Increment | Decrement),_,_) ->
-				e
-			| TBinop(OpAssignOp op,e1,e2) ->
-				let e2 = loop e2 in
-				{e with eexpr = TBinop(OpAssignOp op,e1,e2)}
-			| TBinop(OpAssign,({eexpr = TLocal _} as e1),e2) ->
-				let e2 = loop e2 in
-				{e with eexpr = TBinop(OpAssign,e1,e2)}
-			| TBinop(op,e1,e2) ->
-				let e1 = loop e1 in
-				let e2 = loop e2 in
-				let e = {e with eexpr = TBinop(op,e1,e2)} in
-				let e' = Optimizer.optimize_binop e op e1 e2 in
-				e'
-			| TUnop(op,flag,e1) ->
-				let e1 = loop e1 in
-				let e = {e with eexpr = TUnop(op,flag,e1)} in
-				let e' = Optimizer.optimize_unop e op flag e1 in
-				e'
-			| TIf(e1,e2,eo) ->
-				let e1 = loop e1 in
-				let e2 = loop e2 in
-				let rec check_const e1 = match e1.eexpr with
-					| TConst (TBool true) ->
-						e2
-					| TConst (TBool false) ->
-						begin match eo with
-							| None ->
-								mk (TConst TNull) t_dynamic e.epos
-							| Some e ->
-								loop e
-						end
-					| TParenthesis e1 ->
-						check_const e1
-					| _ ->
-						let eo = match eo with None -> None | Some e -> Some (loop e) in
-						{e with eexpr = TIf(e1,e2,eo)}
-				in
-				check_const e1
-			| TSwitch(e1,cases,edef) ->
-				let e1 = loop e1 in
-				let rec check_constant e = match e.eexpr with
-					| TConst ct -> ct
-					| TParenthesis e1 | TCast(e1,None) | TMeta(_,e1) -> check_constant e1
-					| _ -> awkward_get_enum_index ssa e
-				in
-				begin try
-					let ct = check_constant e1 in
-					begin try
-						let _,e = List.find (fun (el,_) ->
-							List.exists (fun e -> match e.eexpr with
-								| TConst ct2 -> ct = ct2
-								| _ -> false
-							) el
-						) cases in
-						loop e
-					with Not_found ->
-						begin match edef with None -> raise Not_found | Some e -> loop e end
-					end
-				with Not_found ->
-					let cases = List.map (fun (el,e) -> el,loop e) cases in
-					let edef = match edef with None -> None | Some e -> Some (loop e) in
-					{e with eexpr = TSwitch(e1,cases,edef)}
-				end
-			| _ ->
-				Type.map_expr loop e
-		in
-		loop e
-end
-
-module EffectChecker = struct
-	let run com is_var_expression e =
-		let has_effect e = match e.eexpr with
-			| TVar _ -> true
-			| _ -> Optimizer.has_side_effect e
-		in
-		let e = if is_var_expression then
-			(* var initialization expressions are like assignments, so let's cheat a bit here *)
-			snd (Simplifier.apply com (Codegen.binop OpAssign (mk (TConst TNull) t_dynamic e.epos) e e.etype e.epos))
-		else e
-		in
-		let rec loop e = match e.eexpr with
-			| TBlock el ->
-				List.iter (fun e ->
-					if not (has_effect e) then com.warning "This expression has no effect" e.epos
-				) el
-			| _ ->
-				Type.iter loop e
-			in
-		loop e
-end
-
-module Checker = struct
-	open Ssa
-
-	let apply ssa e =
-		let given_warnings = ref PMap.empty in
-		let add_pos p =
-			given_warnings := PMap.add p true !given_warnings
-		in
-		let resolve_value v =
-			let e' = Ssa.get_var_value v in
-			begin match e'.eexpr with
-				| TLocal v' when v == v' -> e'
-				| _ -> e'
-			end
-		in
-		let rec is_null_expr e = match e.eexpr with
-			| TConst TNull ->
-				true
-			| TLocal v ->
-				(try is_null_expr (resolve_value v) with Not_found -> false)
-			| _ ->
-				false
-		in
-		let can_be_null v =
-			not (has_meta Meta.NotNull v.v_meta)
-			&& try not (List.exists (fun cond -> match cond with
-				| NotEqual(v',e) when v == v' && is_null_expr e -> true
-				| _ -> false
-			) (IntMap.find v.v_id ssa.var_conds)) with Not_found -> true
-		in
-		let return b p =
-			if b then add_pos p;
-			b
-		in
-		let rec can_be_null_expr vstack e =
-			if PMap.mem e.epos !given_warnings then
-				false
-			else match e.eexpr with
-			| TConst TNull ->
-				add_pos e.epos;
-				true
-			| TBinop((OpAssign | OpAssignOp _),_,e1) ->
-				can_be_null_expr vstack e1
-			| TBinop _ | TUnop _ ->
-				false
-			| TConst _ | TTypeExpr _ | TNew _ | TObjectDecl _ | TArrayDecl _ | TEnumParameter _ | TFunction _ | TVar _ ->
-				false
-			| TFor _ | TWhile _ | TIf _ | TSwitch _ | TTry _ | TReturn _ | TBreak | TContinue | TThrow _ ->
-				assert false
-			| TField _ | TBlock _ | TArray _ ->
-				false (* TODO *)
-			| TCall ({eexpr = TLocal {v_name = "__ssa_phi__"}},el) ->
-				List.exists (can_be_null_expr vstack) el
-			| TLocal v ->
-				if List.mem v.v_id vstack then
-					false (* not really, but let's not be a nuisance *)
-				else
-					return (can_be_null v && (try can_be_null_expr (v.v_id :: vstack) (resolve_value v) with Not_found -> true)) e.epos;
-			| TMeta(_,e1) | TParenthesis e1 | TCast(e1,_) ->
-				can_be_null_expr vstack e1
-			| TCall(e1,_) ->
-				begin match follow e1.etype with
-					| TFun(_,r) -> return (is_explicit_null r) e1.epos
-					| _ -> false
-				end
-		in
-		let check_null e p =
-			if can_be_null_expr [] e then begin
-				ssa.com.warning "Possible null exception" p;
-			end
-		in
-		let rec loop e = match e.eexpr with
-			| TField(e1,fa) ->
-				let e1 = loop e1 in
-				check_null e1 e.epos;
-				{e with eexpr = TField(e1,fa)}
-			| TMeta((Meta.Analyzer,[EConst(Ident "testIsNull"),_],_),e1) ->
-				if not (can_be_null_expr [] e) then error "Analyzer did not find a possible null exception" e.epos;
-				e
-			| TMeta((Meta.Analyzer,[EConst(Ident "testIsNotNull"),_],_),e1) ->
-				if (can_be_null_expr [] e) then error "Analyzer found a possible null exception" e.epos;
-				e
-			| _ ->
-				Type.map_expr loop e
-		in
-		loop e;
-end
-
-let rec lrev_iter f el = match el with
-	| e :: el ->
-		lrev_iter f el;
-		f e
-	| [] ->
-		()
-
-let rev_iter f e = match e.eexpr with
-	| TConst _
-	| TLocal _
-	| TBreak
-	| TContinue
-	| TTypeExpr _ ->
-		()
-	| TArray (e1,e2)
-	| TBinop (_,e1,e2)
-	| TFor (_,e1,e2)
-	| TWhile (e1,e2,_) ->
-		f e2;
-		f e1;
-	| TThrow e
-	| TField (e,_)
-	| TEnumParameter (e,_,_)
-	| TParenthesis e
-	| TCast (e,_)
-	| TUnop (_,_,e)
-	| TMeta(_,e) ->
-		f e
-	| TArrayDecl el
-	| TNew (_,_,el)
-	| TBlock el ->
-		lrev_iter f el
-	| TObjectDecl fl ->
-		lrev_iter (fun (_,e) -> f e) fl
-	| TCall (e,el) ->
-		f e;
-		lrev_iter f el
-	| TVar (v,eo) ->
-		(match eo with None -> () | Some e -> f e)
-	| TFunction fu ->
-		f fu.tf_expr
-	| TIf (e,e1,e2) ->
-		(match e2 with None -> () | Some e -> f e);
-		f e1;
-		f e;
-	| TSwitch (e,cases,def) ->
-		(match def with None -> () | Some e -> f e);
-		lrev_iter (fun (el,e2) -> lrev_iter f el; f e2) cases;
-		f e;
-	| TTry (e,catches) ->
-		lrev_iter (fun (_,e) -> f e) catches;
-		f e;
-	| TReturn eo ->
-		(match eo with None -> () | Some e -> f e)
-
-module LocalDce = struct
-	let apply e =
-		let is_used v = Meta.has Meta.Used v.v_meta || type_has_analyzer_option v.v_type flag_no_local_dce || v.v_capture in
-		let is_ref_type t = match t with
-			| TType({t_path = ["cs"],("Ref" | "Out")},_) -> true
-			| _ -> false
-		in
-		let rec use v =
-			if not (Meta.has Meta.Used v.v_meta) then begin
-				v.v_meta <- (Meta.Used,[],Ast.null_pos) :: v.v_meta;
-				try use (Ssa.get_origin_var v) with Not_found -> ()
-			end
-		in
-		let rec has_side_effect e =
-			let rec loop e =
-				match e.eexpr with
-				| TLocal v when Meta.has Meta.CompilerGenerated v.v_meta -> (try loop (Ssa.get_var_value v) with Not_found -> ())
-				| TBinop((OpAssign | OpAssignOp _),{eexpr = TLocal v},e2) when is_used v || has_side_effect e2 || is_ref_type v.v_type -> raise Exit
-				| TVar(v,None) when is_used v -> raise Exit
-				| TVar(v,Some e1) when is_used v || has_side_effect e1 -> raise Exit
-				| TConst _ | TLocal _ | TTypeExpr _ | TFunction _ -> ()
-				| TCall ({ eexpr = TField(_,FStatic({ cl_path = ([],"Std") },{ cf_name = "string" })) },args) -> Type.iter loop e
-				| TCall (({eexpr = TLocal {v_name = "__ssa_phi__"}}),el) -> ()
-				| TCall ({eexpr = TField(_,FEnum _)},_) -> Type.iter loop e
-				| TNew _ | TCall _ | TBinop ((OpAssignOp _ | OpAssign),_,_) | TUnop ((Increment|Decrement),_,_) -> raise Exit
-				| TReturn _ | TBreak | TContinue | TThrow _ | TCast (_,Some _) -> raise Exit
-				| TFor _ -> raise Exit
-				| TArray _ | TEnumParameter _ | TCast (_,None) | TBinop _ | TUnop _ | TParenthesis _ | TMeta _ | TWhile _
-				| TField _ | TIf _ | TTry _ | TSwitch _ | TArrayDecl _ | TBlock _ | TObjectDecl _ | TVar _ -> Type.iter loop e
-			in
-			try
-				loop e;
-				false
-			with Exit ->
-				true
-		in
-		let rec collect e = match e.eexpr with
-			| TLocal v ->
-				use v
-			| TVar(v,_) when not (is_used v) ->
-				(* TODO: this is probably dangerous *)
-				()
-			| _ ->
-				rev_iter collect e
-		in
-		let rec loop need_val e =
-			match e.eexpr with
-			| TLocal v ->
-				use v;
-				e
-			| TBinop(OpAssign,({eexpr = TLocal v} as e1),e2) ->
-				let e2 = loop false e2 in
-				if not (is_used v) && not (is_ref_type v.v_type) then
-					e2
-				else
-					{e with eexpr = TBinop(OpAssign,{e1 with eexpr = TLocal v},e2)}
-			| TVar(v,Some e1) when not (is_used v) ->
-				let e1 = if has_side_effect e1 then loop true e1 else e1 in
-				e1
-			| TWhile(e1,e2,flag) ->
-				collect e2;
-				let e2 = loop false e2 in
-				let e1 = loop false e1 in
-				{e with eexpr = TWhile(e1,e2,flag)}
-			| TFor(v,e1,e2) ->
-				collect e2;
-				let e2 = loop false e2 in
-				let e1 = loop false e1 in
-				{e with eexpr = TFor(v,e1,e2)}
-			| TBlock el ->
-				let rec block el = match el with
-					| e :: el ->
-						let el = block el in
-						if not need_val && not (has_side_effect e) then
-							el
-						else begin
-							let e = loop false e in
-							e :: el
-						end
-					| [] ->
-						[]
-				in
-				{e with eexpr = TBlock (block el)}
-			| TCall(e1, el) ->
-				let el = List.rev_map (loop true) (List.rev el) in
-				let e1 = loop false e1 in
-				{e with eexpr = TCall(e1,el)}
-			| TIf(e1,e2,e3) ->
-				let e3 = match e3 with None -> None | Some e -> Some (loop need_val e) in
-				let e2 = loop need_val e2 in
-				let e1 = loop false e1 in
-				{e with eexpr = TIf(e1,e2,e3)}
-			| TArrayDecl el ->
-				let el = List.rev_map (loop true) (List.rev el) in
-				{e with eexpr = TArrayDecl el}
-			| TObjectDecl fl ->
-				let fl = List.rev_map (fun (s,e) -> s,loop true e) (List.rev fl) in
-				{e with eexpr = TObjectDecl fl}
-			| _ ->
-				Type.map_expr (loop false) e
-		in
-		loop false e
-end
-
-module Config = struct
-
-	type analyzer_config = {
-		analyzer_use : bool;
-		simplifier_apply : bool;
-		ssa_apply : bool;
-		const_propagation : bool;
-		check : bool;
-		check_has_effect : bool;
-		local_dce : bool;
-		ssa_unapply : bool;
-		simplifier_unapply : bool;
-	}
-
-	let get_base_config com =
-		{
-			analyzer_use = true;
-			simplifier_apply = true;
-			ssa_apply = true;
-			const_propagation = not (Common.raw_defined com "analyzer-no-const-propagation");
-			check_has_effect = (Common.raw_defined com "analyzer-check-has-effect");
-			check = not (Common.raw_defined com "analyzer-no-check");
-			local_dce = not (Common.raw_defined com "analyzer-no-local-dce") && not (Common.defined com Define.As3);
-			ssa_unapply = not (Common.raw_defined com "analyzer-no-ssa-unapply");
-			simplifier_unapply = not (Common.raw_defined com "analyzer-no-simplify-unapply");
-		}
-
-	let update_config_from_meta config meta =
-		List.fold_left (fun config meta -> match meta with
-			| (Meta.Analyzer,el,_) ->
-				List.fold_left (fun config e -> match fst e with
-					| EConst (Ident s) when s = flag_no_check -> { config with check = false}
-					| EConst (Ident s) when s = flag_check -> { config with check = true}
-					| EConst (Ident s) when s = flag_no_const_propagation -> { config with const_propagation = false}
-					| EConst (Ident s) when s = flag_const_propagation -> { config with const_propagation = true}
-					| EConst (Ident s) when s = flag_no_local_dce -> { config with local_dce = false}
-					| EConst (Ident s) when s = flag_local_dce -> { config with local_dce = true}
-					| EConst (Ident s) when s = flag_no_check_has_effect -> { config with check_has_effect = false}
-					| EConst (Ident s) when s = flag_check_has_effect -> { config with check_has_effect = true}
-					| _ -> config
-				) config el
-			| _ ->
-				config
-		) config meta
-
-	let get_class_config com c =
-		let config = get_base_config com in
-		update_config_from_meta config c.cl_meta
-
-	let get_field_config com c cf =
-		let config = get_class_config com c in
-		update_config_from_meta config cf.cf_meta
-end
-
-module Run = struct
-
-	open Config
-
-	let run_on_expr com config is_var_expression e =
-		let do_simplify = (not (Common.defined com Define.NoSimplify) ) && match com.platform with
-			| Cpp when Common.defined com Define.Cppia -> false
-			| Cpp | Python -> true
-			| _ -> false
-		in
-		let with_timer s f =
-			let timer = timer s in
-			let r = f() in
-			timer();
-			r
-		in
-		try
-			let has_unbound,e = if do_simplify || config.analyzer_use then
-				with_timer "analyzer-simplify-apply" (fun () -> Simplifier.apply com e)
-			else
-				false,e
-			in
-			let e = if config.analyzer_use && not has_unbound then begin
-					if config.check_has_effect then EffectChecker.run com is_var_expression e;
-					let e,ssa = with_timer "analyzer-ssa-apply" (fun () -> Ssa.apply com e) in
-					let e = if config.const_propagation then with_timer "analyzer-const-propagation" (fun () -> ConstPropagation.apply ssa e) else e in
-					(* let e = if config.check then with_timer "analyzer-checker" (fun () -> Checker.apply ssa e) else e in *)
-					let e = if config.local_dce && config.analyzer_use && not has_unbound && not is_var_expression then with_timer "analyzer-local-dce" (fun () -> LocalDce.apply e) else e in
-					let e = if config.ssa_unapply then with_timer "analyzer-ssa-unapply" (fun () -> Ssa.unapply com e) else e in
-					List.iter (fun f -> f()) ssa.Ssa.cleanup;
-					e
-			end else
-				e
-			in
-			let e = if not do_simplify && not (Common.raw_defined com "analyzer-no-simplify-unapply") then
-				with_timer "analyzer-simplify-unapply" (fun () -> Simplifier.unapply com e)
-			else
-				e
-			in
-			e
-		with Exit ->
-			e
-
-	let run_on_field ctx config cf =
-		match cf.cf_expr with
-		| Some e when not (is_ignored cf.cf_meta) && not (Codegen.is_removable_field ctx cf) ->
-			let config = update_config_from_meta config cf.cf_meta in
-			let is_var_expression = match cf.cf_kind with
-				| Var _ -> true
-				| _ -> false
-			in
-			cf.cf_expr <- Some (run_on_expr ctx.com config is_var_expression e);
-		| _ -> ()
-
-	let run_on_class ctx config c =
-		let config = update_config_from_meta config c.cl_meta in
-		let process_field cf = run_on_field ctx config cf in
-		List.iter process_field c.cl_ordered_fields;
-		List.iter process_field c.cl_ordered_statics;
-		(match c.cl_constructor with
-		| None -> ()
-		| Some f -> process_field f);
-		(match c.cl_init with
-		| None -> ()
-		| Some e ->
-			(* never optimize init expressions (too messy) *)
-			c.cl_init <- Some (run_on_expr ctx.com {config with analyzer_use = false} false e))
-
-	let run_on_type ctx config t =
-		match t with
-		| TClassDecl c when (is_ignored c.cl_meta) -> ()
-		| TClassDecl c -> run_on_class ctx config c
-		| TEnumDecl _ -> ()
-		| TTypeDecl _ -> ()
-		| TAbstractDecl _ -> ()
-
-	let run_on_types ctx types =
-		let com = ctx.com in
-		let config = get_base_config com in
-		List.iter (run_on_type ctx config) types
-
-end

+ 35 - 32
appveyor.yml

@@ -1,60 +1,63 @@
 version: "{build}"
 
-platform:
-    - Win32
-
 environment:
     global:
-        NEKO_ROOT: C:/projects/neko
         HAXELIB_ROOT: C:/projects/haxelib
-        CYG_ARCH: x86
         CYG_ROOT: C:/cygwin
-        CYG_SETUP: C:/cygwin/setup-x86.exe
-        MINGW_ARCH: i686
-        WODI: wodi32
         ADD_REVISION: 1
         OCAMLOPT: ocamlopt.opt
+        MYSQL_PATH: C:\Program Files\MySQL\MySQL Server 5.7
+        MYSQL_USER: root
+        MYSQL_PASSWORD: Password12!
     matrix:
-        - TEST: "neko,cs,java,macro"
+        - TEST: "neko,python,cs,java,macro"
         - TEST: "cpp"
 
+services:
+    - mysql
+
 skip_tags: true
 
+cache:
+    - ocaml-installer.exe -> appveyor.yml
+
 install:
     - 'git submodule update --init --recursive'
-    - cinst 7zip.commandline -y
-    # Install ocaml using wodi
-    - appveyor DownloadFile "http://cygwin.com/setup-%CYG_ARCH%.exe" -FileName "%CYG_ROOT%\setup.exe"
-    - '%CYG_ROOT%/setup.exe -g -q -R "%CYG_ROOT%" -P dos2unix -P diffutils -P cpio -P make -P patch -P mingw64-%MINGW_ARCH%-gcc-core -P mingw64-%MINGW_ARCH%-gcc-g++ > log.txt || type log.txt'
-    - '%CYG_ROOT%/bin/bash -lc "cygcheck -dc cygwin" > log.txt || type log.txt'
-    # The archive is a dropbox hosted version of https://github.com/fdopen/godi-repo/issues/7#issuecomment-98480339
-    - '%CYG_ROOT%/bin/bash -lc "wget -q https://dl.dropboxusercontent.com/u/2661116/wodi/%WODI%.tar.xz -O /tmp/%WODI%.tar.xz" > log.txt || type log.txt'
-    - '%CYG_ROOT%/bin/bash -lc "cd /tmp && rm -rf %WODI% && tar -xf %WODI%.tar.xz && bash %WODI%/install.sh" > log.txt || type log.txt'
-    - '%CYG_ROOT%/bin/bash -lc "godi_add godi-zip" > log.txt || type log.txt'
-    - 'set PATH=%PATH%;%CYG_ROOT%/opt/%WODI%/bin'
+    # 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
+      )
+    - 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'
     # Install neko
-    - cinst make -y
-    - 'git clone --recursive https://github.com/HaxeFoundation/neko.git %NEKO_ROOT%'
-    - 'cd %NEKO_ROOT%'
-    - set PATH=%PATH%;%NEKO_ROOT%/bin
-    - msbuild neko_vc10.sln /verbosity:minimal /logger:"C:\Program Files\AppVeyor\BuildAgent\Appveyor.MSBuildLogger.dll" > log.txt || type log.txt
-    - msbuild libs/libs_vc10.sln /verbosity:minimal /logger:"C:\Program Files\AppVeyor\BuildAgent\Appveyor.MSBuildLogger.dll" > log.txt || type log.txt
-    - copy /y libs\include\gc\gc.dll bin
-    - cd %NEKO_ROOT%/src
-    - neko ../boot/nekoc tools/install.neko
-    - neko tools/install -nolibs > log.txt || type log.txt
+    - choco install neko --prerelease --ignore-dependencies -s 'https://ci.appveyor.com/nuget/neko' -y
+    - RefreshEnv
+    # do not use chocolatey's shim, which is buggy when processing arguments
+    # see https://github.com/chocolatey/shimgen/issues/27
+    - set PATH=C:\ProgramData\chocolatey\lib\neko;%PATH%
     - neko -version
+    # setup python
+    - cmd: mklink C:\Python34-x64\python3.exe C:\Python34-x64\python.exe
+    - set PATH=%PATH%;C:\Python34-x64
 
 build_script:
     - 'cd %APPVEYOR_BUILD_FOLDER%'
-    - 'set PATH=%PATH%;%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"'
-    - '%CYG_ROOT%/bin/bash -lc "cd \"$OLDPWD\" && make -s -f Makefile.win tools"'
+    - 'set PATH=%PATH%;%APPVEYOR_BUILD_FOLDER%'
+    - 'set HAXEPATH=%APPVEYOR_BUILD_FOLDER%'
     - '%CYG_ROOT%/bin/bash -lc "cd \"$OLDPWD\" && make -s -f Makefile.win package_bin"'
     - dir %APPVEYOR_BUILD_FOLDER%\out
     - cd %APPVEYOR_BUILD_FOLDER%/tests/
     - mkdir "%HAXELIB_ROOT%"
     - haxelib setup "%HAXELIB_ROOT%"
+    # setup mysql for testing
+    - set PATH=%MYSQL_PATH%\bin;%PATH%
+    - mysql --user=%MYSQL_USER% --password=%MYSQL_PASSWORD% -e "create user 'travis'@'localhost';"
+    - mysql --user=%MYSQL_USER% --password=%MYSQL_PASSWORD% -e "create database haxe_test;"
+    - mysql --user=%MYSQL_USER% --password=%MYSQL_PASSWORD% -e "grant all on haxe_test.* to 'travis'@'localhost';"
 
 test_script:
     - cd %APPVEYOR_BUILD_FOLDER%/tests/
@@ -63,4 +66,4 @@ test_script:
     - neko RunCi.n
 
 artifacts:
-    - path: out/haxe_*.zip
+    - path: out/*

+ 68 - 1
extra/CHANGES.txt

@@ -1,3 +1,70 @@
+2016-05-27: 3.3.0-RC1
+
+	New features:
+
+	all : support @:resolve on abstracts (#3753)
+	all : support extern abstracts and extern @:enum abstracts (#4862)
+	all : support completion on { if the expected type is a structure (#3907)
+	all : support (expr is Type) with mandatory parentheses (#2976)
+	all : allowed passing package dot-paths on command line (#4227)
+	all : support import.hx modules that define per-directory import/using directives (#1138)
+	all : support parsing of postfix ! operator (can be used by abstract operator overloading and macros) (#4284)
+	all : support parsing of ||= and &&= operators (can be used by abstract operator overloading and macros) (#4427)
+	all : support @:structInit classes (#4526)
+	all : reworked static analyzer and enabled it by default
+	flash : update flash externs to version 21
+	hl : added HL target (interpreter and C output)
+	lua: added lua target
+	js : introduced new jQuery extern (js.jquery.*) for jQuery 1.12.4 / 2.2.4 support. (#4377)
+	js : introduced new SWFObject extern (js.swfobject.SWFObject) for SWFObject 2.3.20130521 (#4451)
+	js : added js.Lib.rethrow (#4551)
+	cs/java : added -D fast_cast as an experimental feature to cleanup casts
+
+	Bugfixes:
+
+	all : properly disallowed assigning methods to structures with read-accessors (#3975)
+	all : fixed a bug related to abstract + Int/Float and implicit casts (#4122)
+	all : disallowed duplicate type parameter names (#4293)
+	all : made UInt's >>> behave like >> (#2736)
+	all : fixed various type loading issues
+	all : fixed code generation problems with `inline` (#1827)
+	php/as3 : support run-time metadata on interfaces (#2042)
+	php : fixed argument passing to closures (#2587)
+	neko/cpp : fixed various sys.Filesystem issues with Windows drive paths (#3266)
+	as3 : fixed problem with covariant return types (#4222)
+	as3 : fixed rare problem with static initialization order (#3563)
+	python : fixed various reflection problems
+
+	General improvements and optimizations:
+
+	all : added support for determining minimal types in Map literals (#4196)
+	all : allowed @:native on abstracts to set the name of the implementation class (#4158)
+	all : allowed creating closures on abstract inline methods (#4165)
+	all : type parameter declarations can now have metadata (#3836)
+	all : optimize Math.ceil/floor on constant arguments (#4223)
+	all : allowed extern classes to have field names being used for both static and instance (#4376)
+	all : added haxe.Constraints.Constructible (#4761)
+	all : rewrote pattern matcher to improve output in many cases (#4940)
+	python : use spaces instead of tabs to indent the output (#4299)
+	cpp : reworked backend to improve overall code output quality and fix various issues
+	swf : added scene-tag to allow creating accessible SWF files
+
+	Standard Library:
+
+	all : added Lambda.flatten and Lambda.flatMap (#3553)
+	all : added haxe.Constraints.Constructible (#4761)
+	sys : proper quoting/escaping (can be opt-out) for Sys.command and sys.io.Process (#3603)
+	js : added position parameter to haxe.macro.Compiler.includeFile
+	js : removed -D embed-js (#4074)
+	js : updated HTML externs
+
+	Macro features and changes:
+
+	macro : added overloads field to ClassField (#3460)
+	macro : added Context.getLocalImports (#3560)
+	macro : added Context.onAfterTyping (#4714)
+	macro : added Context.resolveType
+
 2015-10-11: 3.2.1
 
 	Bugfixes:
@@ -1449,7 +1516,7 @@
 	fixed infinite loop in neko EReg split/replace and epsilon matching
 	added neko native serialization support
 	fixed syntax for multiple constraints in type parameter
-	added recursive type parameters contraints (T : C<T> constraints)
+	added recursive type parameters constraints (T : C<T> constraints)
 	updated Xml handling
 
 2006-04-17: beta 5

+ 22 - 21
extra/ImportAll.hx

@@ -1,26 +1,23 @@
 /*
- * Copyright (c) 2005, The Haxe Project Contributors
- * All rights reserved.
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
+ * Copyright (C)2005-2016 Haxe Foundation
  *
- *   - Redistributions of source code must retain the above copyright
- *     notice, this list of conditions and the following disclaimer.
- *   - Redistributions in binary form must reproduce the above copyright
- *     notice, this list of conditions and the following disclaimer in the
- *     documentation and/or other materials provided with the distribution.
+ * 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:
  *
- * THIS SOFTWARE IS PROVIDED BY THE HAXE PROJECT CONTRIBUTORS "AS IS" AND ANY
- * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL THE HAXE PROJECT CONTRIBUTORS BE LIABLE FOR
- * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
- * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
- * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
- * DAMAGE.
+ * 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.
  */
 import haxe.macro.Context;
 
@@ -49,7 +46,11 @@ class ImportAll {
 		case "cs":
 			if( !Context.defined("cs") ) return;
 		case "python":
-			if( !Context.defined("python") ) return;
+			if ( !Context.defined("python") ) return;
+		case "hl":
+			if( !Context.defined("hl") ) return;
+		case "lua":
+			if( !Context.defined("lua") ) return;
 		case "tools":
 			return;
 		case "build-tool":

+ 3 - 467
extra/LICENSE.txt

@@ -1,12 +1,12 @@
 Haxe Licenses
 -------------
 
-For details about Haxe Licenses, please read http://haxe.org/doc/license
+For details about Haxe Licenses, please read http://haxe.org/foundation/open-source.html
 
-The MIT Licence :
+The Haxe Standard Library MIT License :
 --------------------------
 
-Copyright (C)2005-2012 Haxe Foundation
+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"),
@@ -309,467 +309,3 @@ PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
 POSSIBILITY OF SUCH DAMAGES.
 
 		     END OF TERMS AND CONDITIONS
-
-
-The Neko LGPL License :
------------------------
-
-
-		  GNU LESSER GENERAL PUBLIC LICENSE
-		       Version 2.1, February 1999
-
- Copyright (C) 1991, 1999 Free Software Foundation, Inc.
-     51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
- Everyone is permitted to copy and distribute verbatim copies
- of this license document, but changing it is not allowed.
-
-[This is the first released version of the Lesser GPL.  It also counts
- as the successor of the GNU Library Public License, version 2, hence
- the version number 2.1.]
-
-			    Preamble
-
-  The licenses for most software are designed to take away your
-freedom to share and change it.  By contrast, the GNU General Public
-Licenses are intended to guarantee your freedom to share and change
-free software--to make sure the software is free for all its users.
-
-  This license, the Lesser General Public License, applies to some
-specially designated software packages--typically libraries--of the
-Free Software Foundation and other authors who decide to use it.  You
-can use it too, but we suggest you first think carefully about whether
-this license or the ordinary General Public License is the better
-strategy to use in any particular case, based on the explanations below.
-
-  When we speak of free software, we are referring to freedom of use,
-not price.  Our General Public Licenses are designed to make sure that
-you have the freedom to distribute copies of free software (and charge
-for this service if you wish); that you receive source code or can get
-it if you want it; that you can change the software and use pieces of
-it in new free programs; and that you are informed that you can do
-these things.
-
-  To protect your rights, we need to make restrictions that forbid
-distributors to deny you these rights or to ask you to surrender these
-rights.  These restrictions translate to certain responsibilities for
-you if you distribute copies of the library or if you modify it.
-
-  For example, if you distribute copies of the library, whether gratis
-or for a fee, you must give the recipients all the rights that we gave
-you.  You must make sure that they, too, receive or can get the source
-code.  If you link other code with the library, you must provide
-complete object files to the recipients, so that they can relink them
-with the library after making changes to the library and recompiling
-it.  And you must show them these terms so they know their rights.
-
-  We protect your rights with a two-step method: (1) we copyright the
-library, and (2) we offer you this license, which gives you legal
-permission to copy, distribute and/or modify the library.
-
-  To protect each distributor, we want to make it very clear that
-there is no warranty for the free library.  Also, if the library is
-modified by someone else and passed on, the recipients should know
-that what they have is not the original version, so that the original
-author's reputation will not be affected by problems that might be
-introduced by others.
-
-  Finally, software patents pose a constant threat to the existence of
-any free program.  We wish to make sure that a company cannot
-effectively restrict the users of a free program by obtaining a
-restrictive license from a patent holder.  Therefore, we insist that
-any patent license obtained for a version of the library must be
-consistent with the full freedom of use specified in this license.
-
-  Most GNU software, including some libraries, is covered by the
-ordinary GNU General Public License.  This license, the GNU Lesser
-General Public License, applies to certain designated libraries, and
-is quite different from the ordinary General Public License.  We use
-this license for certain libraries in order to permit linking those
-libraries into non-free programs.
-
-  When a program is linked with a library, whether statically or using
-a shared library, the combination of the two is legally speaking a
-combined work, a derivative of the original library.  The ordinary
-General Public License therefore permits such linking only if the
-entire combination fits its criteria of freedom.  The Lesser General
-Public License permits more lax criteria for linking other code with
-the library.
-
-  We call this license the "Lesser" General Public License because it
-does Less to protect the user's freedom than the ordinary General
-Public License.  It also provides other free software developers Less
-of an advantage over competing non-free programs.  These disadvantages
-are the reason we use the ordinary General Public License for many
-libraries.  However, the Lesser license provides advantages in certain
-special circumstances.
-
-  For example, on rare occasions, there may be a special need to
-encourage the widest possible use of a certain library, so that it becomes
-a de-facto standard.  To achieve this, non-free programs must be
-allowed to use the library.  A more frequent case is that a free
-library does the same job as widely used non-free libraries.  In this
-case, there is little to gain by limiting the free library to free
-software only, so we use the Lesser General Public License.
-
-  In other cases, permission to use a particular library in non-free
-programs enables a greater number of people to use a large body of
-free software.  For example, permission to use the GNU C Library in
-non-free programs enables many more people to use the whole GNU
-operating system, as well as its variant, the GNU/Linux operating
-system.
-
-  Although the Lesser General Public License is Less protective of the
-users' freedom, it does ensure that the user of a program that is
-linked with the Library has the freedom and the wherewithal to run
-that program using a modified version of the Library.
-
-  The precise terms and conditions for copying, distribution and
-modification follow.  Pay close attention to the difference between a
-"work based on the library" and a "work that uses the library".  The
-former contains code derived from the library, whereas the latter must
-be combined with the library in order to run.
-
-		  GNU LESSER GENERAL PUBLIC LICENSE
-   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
-
-  0. This License Agreement applies to any software library or other
-program which contains a notice placed by the copyright holder or
-other authorized party saying it may be distributed under the terms of
-this Lesser General Public License (also called "this License").
-Each licensee is addressed as "you".
-
-  A "library" means a collection of software functions and/or data
-prepared so as to be conveniently linked with application programs
-(which use some of those functions and data) to form executables.
-
-  The "Library", below, refers to any such software library or work
-which has been distributed under these terms.  A "work based on the
-Library" means either the Library or any derivative work under
-copyright law: that is to say, a work containing the Library or a
-portion of it, either verbatim or with modifications and/or translated
-straightforwardly into another language.  (Hereinafter, translation is
-included without limitation in the term "modification".)
-
-  "Source code" for a work means the preferred form of the work for
-making modifications to it.  For a library, complete source code means
-all the source code for all modules it contains, plus any associated
-interface definition files, plus the scripts used to control compilation
-and installation of the library.
-
-  Activities other than copying, distribution and modification are not
-covered by this License; they are outside its scope.  The act of
-running a program using the Library is not restricted, and output from
-such a program is covered only if its contents constitute a work based
-on the Library (independent of the use of the Library in a tool for
-writing it).  Whether that is true depends on what the Library does
-and what the program that uses the Library does.
-
-  1. You may copy and distribute verbatim copies of the Library's
-complete source code as you receive it, in any medium, provided that
-you conspicuously and appropriately publish on each copy an
-appropriate copyright notice and disclaimer of warranty; keep intact
-all the notices that refer to this License and to the absence of any
-warranty; and distribute a copy of this License along with the
-Library.
-
-  You may charge a fee for the physical act of transferring a copy,
-and you may at your option offer warranty protection in exchange for a
-fee.
-
-  2. You may modify your copy or copies of the Library or any portion
-of it, thus forming a work based on the Library, and copy and
-distribute such modifications or work under the terms of Section 1
-above, provided that you also meet all of these conditions:
-
-    a) The modified work must itself be a software library.
-
-    b) You must cause the files modified to carry prominent notices
-    stating that you changed the files and the date of any change.
-
-    c) You must cause the whole of the work to be licensed at no
-    charge to all third parties under the terms of this License.
-
-    d) If a facility in the modified Library refers to a function or a
-    table of data to be supplied by an application program that uses
-    the facility, other than as an argument passed when the facility
-    is invoked, then you must make a good faith effort to ensure that,
-    in the event an application does not supply such function or
-    table, the facility still operates, and performs whatever part of
-    its purpose remains meaningful.
-
-    (For example, a function in a library to compute square roots has
-    a purpose that is entirely well-defined independent of the
-    application.  Therefore, Subsection 2d requires that any
-    application-supplied function or table used by this function must
-    be optional: if the application does not supply it, the square
-    root function must still compute square roots.)
-
-These requirements apply to the modified work as a whole.  If
-identifiable sections of that work are not derived from the Library,
-and can be reasonably considered independent and separate works in
-themselves, then this License, and its terms, do not apply to those
-sections when you distribute them as separate works.  But when you
-distribute the same sections as part of a whole which is a work based
-on the Library, the distribution of the whole must be on the terms of
-this License, whose permissions for other licensees extend to the
-entire whole, and thus to each and every part regardless of who wrote
-it.
-
-Thus, it is not the intent of this section to claim rights or contest
-your rights to work written entirely by you; rather, the intent is to
-exercise the right to control the distribution of derivative or
-collective works based on the Library.
-
-In addition, mere aggregation of another work not based on the Library
-with the Library (or with a work based on the Library) on a volume of
-a storage or distribution medium does not bring the other work under
-the scope of this License.
-
-  3. You may opt to apply the terms of the ordinary GNU General Public
-License instead of this License to a given copy of the Library.  To do
-this, you must alter all the notices that refer to this License, so
-that they refer to the ordinary GNU General Public License, version 2,
-instead of to this License.  (If a newer version than version 2 of the
-ordinary GNU General Public License has appeared, then you can specify
-that version instead if you wish.)  Do not make any other change in
-these notices.
-
-  Once this change is made in a given copy, it is irreversible for
-that copy, so the ordinary GNU General Public License applies to all
-subsequent copies and derivative works made from that copy.
-
-  This option is useful when you wish to copy part of the code of
-the Library into a program that is not a library.
-
-  4. You may copy and distribute the Library (or a portion or
-derivative of it, under Section 2) in object code or executable form
-under the terms of Sections 1 and 2 above provided that you accompany
-it with the complete corresponding machine-readable source code, which
-must be distributed under the terms of Sections 1 and 2 above on a
-medium customarily used for software interchange.
-
-  If distribution of object code is made by offering access to copy
-from a designated place, then offering equivalent access to copy the
-source code from the same place satisfies the requirement to
-distribute the source code, even though third parties are not
-compelled to copy the source along with the object code.
-
-  5. A program that contains no derivative of any portion of the
-Library, but is designed to work with the Library by being compiled or
-linked with it, is called a "work that uses the Library".  Such a
-work, in isolation, is not a derivative work of the Library, and
-therefore falls outside the scope of this License.
-
-  However, linking a "work that uses the Library" with the Library
-creates an executable that is a derivative of the Library (because it
-contains portions of the Library), rather than a "work that uses the
-library".  The executable is therefore covered by this License.
-Section 6 states terms for distribution of such executables.
-
-  When a "work that uses the Library" uses material from a header file
-that is part of the Library, the object code for the work may be a
-derivative work of the Library even though the source code is not.
-Whether this is true is especially significant if the work can be
-linked without the Library, or if the work is itself a library.  The
-threshold for this to be true is not precisely defined by law.
-
-  If such an object file uses only numerical parameters, data
-structure layouts and accessors, and small macros and small inline
-functions (ten lines or less in length), then the use of the object
-file is unrestricted, regardless of whether it is legally a derivative
-work.  (Executables containing this object code plus portions of the
-Library will still fall under Section 6.)
-
-  Otherwise, if the work is a derivative of the Library, you may
-distribute the object code for the work under the terms of Section 6.
-Any executables containing that work also fall under Section 6,
-whether or not they are linked directly with the Library itself.
-
-  6. As an exception to the Sections above, you may also combine or
-link a "work that uses the Library" with the Library to produce a
-work containing portions of the Library, and distribute that work
-under terms of your choice, provided that the terms permit
-modification of the work for the customer's own use and reverse
-engineering for debugging such modifications.
-
-  You must give prominent notice with each copy of the work that the
-Library is used in it and that the Library and its use are covered by
-this License.  You must supply a copy of this License.  If the work
-during execution displays copyright notices, you must include the
-copyright notice for the Library among them, as well as a reference
-directing the user to the copy of this License.  Also, you must do one
-of these things:
-
-    a) Accompany the work with the complete corresponding
-    machine-readable source code for the Library including whatever
-    changes were used in the work (which must be distributed under
-    Sections 1 and 2 above); and, if the work is an executable linked
-    with the Library, with the complete machine-readable "work that
-    uses the Library", as object code and/or source code, so that the
-    user can modify the Library and then relink to produce a modified
-    executable containing the modified Library.  (It is understood
-    that the user who changes the contents of definitions files in the
-    Library will not necessarily be able to recompile the application
-    to use the modified definitions.)
-
-    b) Use a suitable shared library mechanism for linking with the
-    Library.  A suitable mechanism is one that (1) uses at run time a
-    copy of the library already present on the user's computer system,
-    rather than copying library functions into the executable, and (2)
-    will operate properly with a modified version of the library, if
-    the user installs one, as long as the modified version is
-    interface-compatible with the version that the work was made with.
-
-    c) Accompany the work with a written offer, valid for at
-    least three years, to give the same user the materials
-    specified in Subsection 6a, above, for a charge no more
-    than the cost of performing this distribution.
-
-    d) If distribution of the work is made by offering access to copy
-    from a designated place, offer equivalent access to copy the above
-    specified materials from the same place.
-
-    e) Verify that the user has already received a copy of these
-    materials or that you have already sent this user a copy.
-
-  For an executable, the required form of the "work that uses the
-Library" must include any data and utility programs needed for
-reproducing the executable from it.  However, as a special exception,
-the materials to be distributed need not include anything that is
-normally distributed (in either source or binary form) with the major
-components (compiler, kernel, and so on) of the operating system on
-which the executable runs, unless that component itself accompanies
-the executable.
-
-  It may happen that this requirement contradicts the license
-restrictions of other proprietary libraries that do not normally
-accompany the operating system.  Such a contradiction means you cannot
-use both them and the Library together in an executable that you
-distribute.
-
-  7. You may place library facilities that are a work based on the
-Library side-by-side in a single library together with other library
-facilities not covered by this License, and distribute such a combined
-library, provided that the separate distribution of the work based on
-the Library and of the other library facilities is otherwise
-permitted, and provided that you do these two things:
-
-    a) Accompany the combined library with a copy of the same work
-    based on the Library, uncombined with any other library
-    facilities.  This must be distributed under the terms of the
-    Sections above.
-
-    b) Give prominent notice with the combined library of the fact
-    that part of it is a work based on the Library, and explaining
-    where to find the accompanying uncombined form of the same work.
-
-  8. You may not copy, modify, sublicense, link with, or distribute
-the Library except as expressly provided under this License.  Any
-attempt otherwise to copy, modify, sublicense, link with, or
-distribute the Library is void, and will automatically terminate your
-rights under this License.  However, parties who have received copies,
-or rights, from you under this License will not have their licenses
-terminated so long as such parties remain in full compliance.
-
-  9. You are not required to accept this License, since you have not
-signed it.  However, nothing else grants you permission to modify or
-distribute the Library or its derivative works.  These actions are
-prohibited by law if you do not accept this License.  Therefore, by
-modifying or distributing the Library (or any work based on the
-Library), you indicate your acceptance of this License to do so, and
-all its terms and conditions for copying, distributing or modifying
-the Library or works based on it.
-
-  10. Each time you redistribute the Library (or any work based on the
-Library), the recipient automatically receives a license from the
-original licensor to copy, distribute, link with or modify the Library
-subject to these terms and conditions.  You may not impose any further
-restrictions on the recipients' exercise of the rights granted herein.
-You are not responsible for enforcing compliance by third parties with
-this License.
-
-  11. If, as a consequence of a court judgment or allegation of patent
-infringement or for any other reason (not limited to patent issues),
-conditions are imposed on you (whether by court order, agreement or
-otherwise) that contradict the conditions of this License, they do not
-excuse you from the conditions of this License.  If you cannot
-distribute so as to satisfy simultaneously your obligations under this
-License and any other pertinent obligations, then as a consequence you
-may not distribute the Library at all.  For example, if a patent
-license would not permit royalty-free redistribution of the Library by
-all those who receive copies directly or indirectly through you, then
-the only way you could satisfy both it and this License would be to
-refrain entirely from distribution of the Library.
-
-If any portion of this section is held invalid or unenforceable under any
-particular circumstance, the balance of the section is intended to apply,
-and the section as a whole is intended to apply in other circumstances.
-
-It is not the purpose of this section to induce you to infringe any
-patents or other property right claims or to contest validity of any
-such claims; this section has the sole purpose of protecting the
-integrity of the free software distribution system which is
-implemented by public license practices.  Many people have made
-generous contributions to the wide range of software distributed
-through that system in reliance on consistent application of that
-system; it is up to the author/donor to decide if he or she is willing
-to distribute software through any other system and a licensee cannot
-impose that choice.
-
-This section is intended to make thoroughly clear what is believed to
-be a consequence of the rest of this License.
-
-  12. If the distribution and/or use of the Library is restricted in
-certain countries either by patents or by copyrighted interfaces, the
-original copyright holder who places the Library under this License may add
-an explicit geographical distribution limitation excluding those countries,
-so that distribution is permitted only in or among countries not thus
-excluded.  In such case, this License incorporates the limitation as if
-written in the body of this License.
-
-  13. The Free Software Foundation may publish revised and/or new
-versions of the Lesser General Public License from time to time.
-Such new versions will be similar in spirit to the present version,
-but may differ in detail to address new problems or concerns.
-
-Each version is given a distinguishing version number.  If the Library
-specifies a version number of this License which applies to it and
-"any later version", you have the option of following the terms and
-conditions either of that version or of any later version published by
-the Free Software Foundation.  If the Library does not specify a
-license version number, you may choose any version ever published by
-the Free Software Foundation.
-
-  14. If you wish to incorporate parts of the Library into other free
-programs whose distribution conditions are incompatible with these,
-write to the author to ask for permission.  For software which is
-copyrighted by the Free Software Foundation, write to the Free
-Software Foundation; we sometimes make exceptions for this.  Our
-decision will be guided by the two goals of preserving the free status
-of all derivatives of our free software and of promoting the sharing
-and reuse of software generally.
-
-			    NO WARRANTY
-
-  15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
-WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
-EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
-OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
-KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
-IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
-PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
-LIBRARY IS WITH YOU.  SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
-THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
-
-  16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
-WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
-AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
-FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
-CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
-LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
-RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
-FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
-SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
-DAMAGES.
-
-		     END OF TERMS AND CONDITIONS

+ 4 - 0
extra/all.hxml

@@ -44,6 +44,10 @@
 -python all_python
 -xml python.xml
 
+--next
+-hl all_hl
+-xml hl.xml
+
 --next
 
 -xml cross.xml

+ 27 - 0
extra/bintray.tpl.json

@@ -0,0 +1,27 @@
+{
+    "package": {
+        "name": "haxe",
+        "repo": "snapshot",
+        "subject": "::packageSubject::"
+    },
+
+    "version": {
+        "name": "::versionName::",
+        "desc": "::versionDesc::",
+        "released": "::gitDate::",
+        "attributes": [
+            {"name": "repo",   "values" : ["::gitRepo::"], "type": "string"},
+            {"name": "branch", "values" : ["::gitBranch::"], "type": "string"},
+            {"name": "commit", "values" : ["::gitCommit::"], "type": "string"}
+        ],
+        "gpgSign": false
+    },
+
+    "files":
+        [
+            {"includePattern": "out/(.*)_src\\.(.*)", "uploadPattern": "::os::/::gitBranch::/haxe_::versionName::_src.$2"},
+            {"includePattern": "out/(.*)_bin\\.(.*)", "uploadPattern": "::os::/::gitBranch::/haxe_::versionName::_bin.$2"},
+            {"includePattern": "out/(.*)_doc\\.(.*)", "uploadPattern": "::os::/::gitBranch::/haxe_::versionName::_doc.$2"}
+        ],
+    "publish": true
+}

二进制
extra/deploy_key.enc


+ 7 - 1
extra/extract.hxml

@@ -1,6 +1,12 @@
+# To rebuild Flash externs (quoting ncannasse):
+# - Remove haxe/std/flash/DIR where DIR is all directories except _std
+# - Place library.swf (extracted from playerglobal.swc) next to extract.hxml
+# - Run haxe extract.hxml
+# - Restore removed haxe/std/flash/DIR directories
+# - Copy directories from hxclasses/flash to haxe/std/flash, overwriting restored ones
 -debug
 -swf-lib library.swf
 -swf test.swf
 -swf-version 15
 --macro patchTypes("../extra/extract.patch")
---gen-hx-classes
+--gen-hx-classes

+ 3 - 0
extra/extract.patch

@@ -1196,6 +1196,7 @@ flash.system.Worker.state : WorkerState;
 flash.system.MessageChannel.state : MessageChannelState;
 
 enum flash.system.WorkerState;
+enum flash.system.MessageChannelState;
 
 
 // ---  11.6 API
@@ -1219,6 +1220,8 @@ flash.display3D.Context3D.$mipfilter : Context3DMipFilter;
 @:require(flash11_7) flash.system.AuthorizedFeatures.enableHLSPlayback;
 @:require(flash11_7) flash.system.AuthorizedFeaturesLoader.loadAuthorizedFeaturesFromData;
 
+// requires com.adobe.tvsdk.mediacore.MediaPlayer which we don't include
+flash.system.AuthorizedFeatures.$enableMediaPlayerHLSPlayback__player : Dynamic;
 
 @:require(flash11_8) flash.display.DisplayObjectContainer.stopAllMovieClips
 @:require(flash11_8) flash.display3D.Context3D.createRectangleTexture

二进制
extra/haxeci_sec.gpg.enc


二进制
extra/haxeci_ssh.enc


+ 1 - 1
extra/haxelib_src

@@ -1 +1 @@
-Subproject commit 4fefbd17a2d9a98200b621de801018af3896d68a
+Subproject commit f8bb48ba5a59c0dbab7a3373cdc908e9c1678644

二进制
extra/images/Banner.bmp


二进制
extra/images/Wizard.bmp


+ 1 - 1
extra/installer.nsi

@@ -25,7 +25,7 @@
 ; Installer details
 VIAddVersionKey "CompanyName" "Haxe Foundation"
 VIAddVersionKey "ProductName" "Haxe Installer"
-VIAddVersionKey "LegalCopyright" "Haxe Foundation 2005-2013"
+VIAddVersionKey "LegalCopyright" "Haxe Foundation 2005-2016"
 VIAddVersionKey "FileDescription" "Haxe Installer"
 VIAddVersionKey "ProductVersion" "${VERSION}.0"
 VIAddVersionKey "FileVersion" "${VERSION}.0"

+ 5 - 3
extra/release-checklist.txt

@@ -8,6 +8,9 @@
 
 - Make sure CHANGES.txt has a proper date set!
 - Make sure `version` in main.ml has the correct value.
+- Update README.md:
+  - Installing Haxe: update "Latest stable version"
+  - Version compatibility: add/update the Haxe/Neko version
 - Merge development branch into master.
 - Wait for Travis to greenlight master.
 - Tag master as MAJOR.MINOR.PATCH.
@@ -17,15 +20,14 @@
 
 # Making the release
 
-- Push the generated binaries and installers to haxe.org.
 - Copy relevant changelog part to CHANGES.md.
 - Write announcement post.
 - Copy announcement post to RELEASE.md.
 - Update versions.json
+- Push the generated binaries and installers to haxe.org.
 
 # Announcing the release
 
-- Update Github README page.
-- Regenerate and upload API documentation.
+- Regenerate and upload API documentation (check --title and -D version values).
 - Update http://haxe.org/file/CHANGES.txt
 - Post announcement post to haxelang.

+ 16 - 17
extra/setup.cpp

@@ -1,22 +1,21 @@
 /*
- *  Haxe Setup
- *  Copyright (c)2006 Nicolas Cannasse
- *
- *  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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
- */
+	Haxe Setup
+	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.
+*/
 // this is a small program that do basic Haxe setup on Windows
 #include <windows.h>
 

+ 0 - 5732
gencpp.ml

@@ -1,5732 +0,0 @@
-(*
- * Copyright (C)2005-2013 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 Ast
-open Type
-open Common
-
-let unsupported p = error "This expression cannot be generated to Cpp" p
-
-(*
-   Generators do not care about non-core-type abstracts, so let us follow them
-   away by default.
-*)
-let follow = Abstract.follow_with_abstracts
-
-(*
-   Code for generating source files.
-   It manages creating diretories, indents, blocks and only modifying files
-   when the content changes.
-*)
-
-(*
-   A class_path is made from a package (array of strings) and a class name.
-   Join these together, inclding a separator.  eg, "/" for includes : pack1/pack2/Name or "::"
-   for namespace "pack1::pack2::Name"
-*)
-let join_class_path path separator =
-   let result = match fst path, snd path with
-   | [], s -> s
-   | el, s -> String.concat separator el ^ separator ^ s in
-   if (String.contains result '+') then begin
-      let idx = String.index result '+' in
-      (String.sub result 0 idx) ^ (String.sub result (idx+1) ((String.length result) - idx -1 ) )
-   end else
-      result;;
-
-
-(* The internal classes are implemented by the core hxcpp system, so the cpp
-   classes should not be generated *)
-let is_internal_class = function
-   |  ([],"Int") | ([],"Void") |  ([],"String") | ([], "Null") | ([], "Float")
-   |  ([],"Array") | ([], "Class") | ([], "Enum") | ([], "Bool")
-   |  ([], "Dynamic") | ([], "ArrayAccess") | (["cpp"], "FastIterator")
-   |  (["cpp"],"Pointer") | (["cpp"],"ConstPointer")
-   |  (["cpp"],"RawPointer") | (["cpp"],"RawConstPointer")
-   |  (["cpp"],"Function") -> true
-   |  ([],"Math") | (["haxe";"io"], "Unsigned_char__") -> true
-   |  (["cpp"],"Int8") | (["cpp"],"UInt8") | (["cpp"],"Char")
-   |  (["cpp"],"Int16") | (["cpp"],"UInt16")
-   |  (["cpp"],"Int32") | (["cpp"],"UInt32")
-   |  (["cpp"],"Int64") | (["cpp"],"UInt64")
-   |  (["cpp"],"Float32") | (["cpp"],"Float64") -> true
-   | _ -> false;;
-
-let get_include_prefix common_ctx with_slash =
-   try
-     (Common.defined_value common_ctx Define.IncludePrefix) ^ (if with_slash then "/" else "")
-   with
-     Not_found -> ""
-;;
-
-
-let should_prefix_include = function
-   | x when is_internal_class x -> false
-   |  ([],"hxMath") -> true
-   | _ -> false;;
-
-class source_writer common_ctx write_func close_func =
-   object(this)
-   val indent_str = "\t"
-   val mutable indent = ""
-   val mutable indents = []
-   val mutable just_finished_block = false
-   method close = close_func(); ()
-   method write x = write_func x; just_finished_block <- false
-   method indent_one = this#write indent_str
-
-   method push_indent = indents <- indent_str::indents; indent <- String.concat "" indents
-   method pop_indent = match indents with
-                     | h::tail -> indents <- tail; indent <- String.concat "" indents
-                     | [] -> indent <- "/*?*/";
-   method write_i x = this#write (indent ^ x)
-   method get_indent = indent
-   method begin_block = this#write ("{\n"); this#push_indent
-   method end_block = this#pop_indent; this#write_i "}\n"; just_finished_block <- true
-   method end_block_line = this#pop_indent; this#write_i "}"; just_finished_block <- true
-   method terminate_line = this#write (if just_finished_block then "" else ";\n")
-
-
-   method add_include class_path =
-      ( match class_path with
-         | (["@verbatim"],file) -> this#write ("#include \"" ^ file ^ "\"\n");
-         | _ ->
-            let prefix = if should_prefix_include class_path then "" else get_include_prefix common_ctx true in
-            this#write ("#ifndef INCLUDED_" ^ (join_class_path class_path "_") ^ "\n");
-            this#write ("#include <" ^ prefix ^ (join_class_path class_path "/") ^ ".h>\n");
-            this#write ("#endif\n")
-      )
-end;;
-
-let file_source_writer common_ctx filename =
-   let out_file = open_out filename in
-   new source_writer common_ctx (output_string out_file) (fun ()-> close_out out_file);;
-
-
-let read_whole_file chan =
-   Std.input_all chan;;
-
-(* The cached_source_writer will not write to the file if it has not changed,
-   thus allowing the makefile dependencies to work correctly *)
-let cached_source_writer common_ctx filename =
-   try
-      let in_file = open_in filename in
-      let old_contents = read_whole_file in_file in
-      close_in in_file;
-      let buffer = Buffer.create 0 in
-      let add_buf str = Buffer.add_string buffer str in
-      let close = fun () ->
-         let contents = Buffer.contents buffer in
-         if (not (contents=old_contents) ) then begin
-            let out_file = open_out filename in
-            output_string out_file contents;
-            close_out out_file;
-         end;
-      in
-      new source_writer common_ctx (add_buf) (close);
-   with _ ->
-      file_source_writer common_ctx filename;;
-
-let make_class_directories = Common.mkdir_recursive;;
-
-let make_base_directory dir =
-   make_class_directories "" ( ( Str.split_delim (Str.regexp "[\\/]+") dir ) );;
-
-let new_source_file common_ctx base_dir sub_dir extension class_path =
-   let include_prefix = get_include_prefix common_ctx true in
-   let full_dir =
-      if (sub_dir="include") && (include_prefix<>"") then begin
-         let dir = match fst class_path with
-            | [] -> base_dir ^ "/include/" ^ (get_include_prefix common_ctx false)
-            | path -> base_dir ^ "/include/" ^ include_prefix ^ ( String.concat "/" path )
-         in
-         make_base_directory dir;
-         dir
-      end else begin
-         make_class_directories base_dir ( sub_dir :: (fst class_path));
-         base_dir ^ "/" ^ sub_dir ^ "/" ^ ( String.concat "/" (fst class_path) )
-      end
-   in
-   cached_source_writer common_ctx (full_dir ^ "/" ^ ((snd class_path) ^ extension));;
-
-
-
-let source_file_extension common_ctx =
-   try
-     "." ^ (Common.defined_value common_ctx Define.FileExtension)
-   with
-     Not_found -> ".cpp"
-;;
-
-
-let new_cpp_file common_ctx base_dir = new_source_file common_ctx base_dir "src" (source_file_extension common_ctx);;
-
-let new_header_file common_ctx base_dir =
-   new_source_file common_ctx base_dir "include" ".h";;
-
-
-
-(* CPP code generation context *)
-
-type context =
-{
-   mutable ctx_common : Common.context;
-   mutable ctx_output : string -> unit;
-   mutable ctx_dbgout : string -> unit;
-   mutable ctx_writer : source_writer;
-   mutable ctx_calling : bool;
-   mutable ctx_assigning : bool;
-   mutable ctx_return_from_block : bool;
-   mutable ctx_tcall_expand_args : bool;
-   (* This is for returning from the child nodes of TMatch, TSwitch && TTry *)
-   mutable ctx_return_from_internal_node : bool;
-   mutable ctx_debug_level : int;
-   mutable ctx_real_this_ptr : bool;
-   mutable ctx_real_void : bool;
-   mutable ctx_dynamic_this_ptr : bool;
-   mutable ctx_dump_src_pos : unit -> unit;
-   mutable ctx_static_id_curr : int;
-   mutable ctx_static_id_used : int;
-   mutable ctx_static_id_depth : int;
-   mutable ctx_switch_id : int;
-   mutable ctx_class_name : string;
-   mutable ctx_class_super_name : string;
-   mutable ctx_local_function_args : (string,string) Hashtbl.t;
-   mutable ctx_local_return_block_args : (string,string) Hashtbl.t;
-   mutable ctx_class_member_types : (string,string) Hashtbl.t;
-   mutable ctx_file_info : (string,string) PMap.t ref;
-   mutable ctx_for_extern : bool;
-}
-
-let new_context common_ctx writer debug file_info =
-{
-   ctx_common = common_ctx;
-   ctx_writer = writer;
-   ctx_output = (writer#write);
-   ctx_dbgout = if debug>1 then (writer#write) else (fun _ -> ());
-   ctx_calling = false;
-   ctx_assigning = false;
-   ctx_debug_level = debug;
-   ctx_dump_src_pos = (fun() -> ());
-   ctx_return_from_block = false;
-   ctx_tcall_expand_args = false;
-   ctx_return_from_internal_node = false;
-   ctx_real_this_ptr = true;
-   ctx_real_void = false;
-   ctx_dynamic_this_ptr = false;
-   ctx_static_id_curr = 0;
-   ctx_static_id_used = 0;
-   ctx_static_id_depth = 0;
-   ctx_switch_id = 0;
-   ctx_class_name = "";
-   ctx_class_super_name = "";
-   ctx_local_function_args = Hashtbl.create 0;
-   ctx_local_return_block_args = Hashtbl.create 0;
-   ctx_class_member_types =  Hashtbl.create 0;
-   ctx_file_info = file_info;
-   ctx_for_extern = false;
-}
-
-let new_extern_context common_ctx writer debug file_info =
-   let ctx = new_context common_ctx writer debug file_info in
-   ctx.ctx_for_extern <- true;
-   ctx
-;;
-
-
-(* The internal header files are also defined in the hx/Object.h file, so you do
-   #include them separately.  However, Math classes has its
-   own header file (under the hxcpp tree) so these should be included *)
-let include_class_header = function
-   | ([],"@Main") -> false
-   | ([],"Math") -> true
-   | path -> not ( is_internal_class path )
-
-
-let is_cpp_class = function
-   | ("cpp"::_ , _)  -> true
-   | ( [] , "EReg" )  -> true
-   | ( ["haxe"] , "Log" )  -> true
-   | _ -> false;;
-
-let is_scalar typename = match typename with
-   | "int" | "unsigned int" | "signed int"
-   | "char" | "unsigned char"
-   | "short" | "unsigned short"
-   | "float" | "double"
-   | "bool" -> true
-   | _ -> false
-;;
-
-let is_block exp = match exp.eexpr with | TBlock _ -> true | _ -> false ;;
-
-let to_block expression =
-   if is_block expression then expression else (mk_block expression);;
-
-(* todo - is this how it's done? *)
-let hash_keys hash =
-   let key_list = ref [] in
-   Hashtbl.iter (fun key value -> key_list :=  key :: !key_list ) hash;
-   !key_list;;
-
-let pmap_keys pmap =
-   let key_list = ref [] in
-   PMap.iter (fun key _ -> key_list :=  key :: !key_list ) pmap;
-   !key_list;;
-
-let pmap_values pmap =
-   let value_list = ref [] in
-   PMap.iter (fun _ value -> value_list :=  value :: !value_list ) pmap;
-   !value_list;;
-
-
-
-(* The Hashtbl structure seems a little odd - but here is a helper function *)
-let hash_iterate hash visitor =
-   let result = ref [] in
-   Hashtbl.iter (fun key value -> result :=  (visitor key value) :: !result ) hash;
-   !result
-
-(* Convert function names that can't be written in c++ ... *)
-let keyword_remap name =
-   match name with
-   | "int"
-   | "auto" | "char" | "const" | "delete" | "double" | "Float" | "enum"
-   | "extern" | "float" | "friend" | "goto" | "long" | "operator" | "protected"
-   | "register" | "short" | "signed" | "sizeof" | "template" | "typedef"
-   | "union" | "unsigned" | "void" | "volatile" | "or" | "and" | "xor" | "or_eq" | "not"
-   | "and_eq" | "xor_eq" | "typeof" | "stdin" | "stdout" | "stderr" | "system"
-   | "BIG_ENDIAN" | "LITTLE_ENDIAN" | "assert" | "NULL" | "wchar_t" | "EOF"
-   | "bool" | "const_cast" | "dynamic_cast" | "explicit" | "export" | "mutable" | "namespace"
-   | "reinterpret_cast" | "static_cast" | "typeid" | "typename" | "virtual"
-   | "_Complex" | "INFINITY" | "NAN"
-   | "INT_MIN" | "INT_MAX" | "INT8_MIN" | "INT8_MAX" | "UINT8_MAX" | "INT16_MIN"
-   | "INT16_MAX" | "UINT16_MAX" | "INT32_MIN" | "INT32_MAX" | "UINT32_MAX"
-   | "struct" -> "_" ^ name
-   | "asm" -> "_asm_"
-   | x -> x
-;;
-
-let remap_class_path class_path =
-   (List.map keyword_remap (fst class_path)) , (snd class_path)
-;;
-
-let join_class_path_remap path separator =
-   match join_class_path (remap_class_path path) separator with
-   | "Class" -> "hx::Class"
-   | x -> x
-;;
-
-let get_meta_string meta key =
-   let rec loop = function
-      | [] -> ""
-      | (k,[Ast.EConst (Ast.String name),_],_) :: _  when k=key-> name
-      | _ :: l -> loop l
-      in
-   loop meta
-;;
-
-
-
-let get_meta_string_path meta key =
-   let rec loop = function
-      | [] -> ""
-      | (k,[Ast.EConst (Ast.String name),_], pos) :: _  when k=key->
-           (try
-           if (String.sub name 0 2) = "./" then begin
-              let base = if (Filename.is_relative pos.pfile) then
-                 Filename.concat (Sys.getcwd()) pos.pfile
-              else
-                 pos.pfile
-              in
-              Gencommon.normalize (Filename.concat (Filename.dirname base) (String.sub name 2 ((String.length name) -2)  ))
-           end else
-              name
-           with Invalid_argument _ -> name)
-      | _ :: l -> loop l
-      in
-   loop meta
-;;
-
-
-let get_meta_string_full_filename meta key =
-   let rec loop = function
-      | [] -> ""
-      | (k,_, pos) :: _  when k=key->
-           if (Filename.is_relative pos.pfile) then
-              Gencommon.normalize (Filename.concat (Sys.getcwd()) pos.pfile)
-           else
-              pos.pfile
-      | _ :: l -> loop l
-      in
-   loop meta
-;;
-
-let get_meta_string_full_dirname meta key =
-   let name = get_meta_string_full_filename meta key in
-   try
-      Gencommon.normalize (Filename.dirname name)
-   with Invalid_argument _ -> ""
-;;
-
-
-let get_field_access_meta field_access key =
-match field_access with
-   | FInstance(_,_,class_field)
-   | FStatic(_,class_field) -> get_meta_string class_field.cf_meta key
-   | _ -> ""
-;;
-
-let format_code code =
-	String.concat "\n" (ExtString.String.nsplit code "\r\n")
-
-let get_code meta key =
-   let code = get_meta_string meta key in
-   let magic_var = "${GENCPP_SOURCE_DIRECTORY}"  in
-   let code = if ExtString.String.exists code magic_var then begin
-         let source_directory = get_meta_string_full_dirname meta key in
-         let _,code = ExtString.String.replace code magic_var source_directory in
-         code
-      end else
-         code
-      in
-   if (code<>"") then format_code code ^ "\n" else code
-;;
-
-let has_meta_key meta key =
-   List.exists (fun m -> match m with | (k,_,_) when k=key-> true | _ -> false ) meta
-;;
-
-let type_has_meta_key haxe_type key =
-   match follow haxe_type with
-   | TInst (klass,_) -> has_meta_key klass.cl_meta key
-   | TType (type_def,_) -> has_meta_key type_def.t_meta key
-   | TEnum (enum_def,_) -> has_meta_key enum_def.e_meta key
-   | _ -> false
-;;
-
-
-(*
-let dump_meta meta =
-   List.iter (fun m -> match m with | (k,_,_) -> print_endline ((fst (MetaInfo.to_string k)) ^ "=" ^ (get_meta_string meta k) ) | _ -> () ) meta;;
-*)
-
-let get_class_code class_def key = match class_def.cl_kind with
-    | KAbstractImpl abstract_def ->
-       let value = (get_code abstract_def.a_meta key) in
-       value
-    | _ -> get_code class_def.cl_meta key
-;;
-
-
-(* Add include to source code *)
-let add_include writer class_path =
-   writer#add_include class_path;;
-
-
-(* This gets the class include order correct.  In the header files, we forward declare
-   the class types so the header file does not have any undefined variables.
-   In the cpp files, we include all the required header files, providing the actual
-   types for everything.  This way there is no problem with circular class references.
-*)
-let gen_forward_decl writer class_path =
-   begin
-      let output = writer#write in
-      match class_path with
-      | (["@verbatim"],file) -> writer#write ("#include <" ^ file ^ ">\n");
-      | _ ->
-         let name = fst (remap_class_path class_path) in
-         output ("HX_DECLARE_CLASS" ^ (string_of_int (List.length name ) ) ^ "(");
-         List.iter (fun package_part -> output (package_part ^ ",") ) name;
-         output ( (snd class_path) ^ ")\n")
-end;;
-
-let real_interfaces =
-List.filter (function (t,pl) ->
-   match t, pl with
-   | { cl_path = ["cpp";"rtti"],_ },[] -> false
-   | _ -> true
-);;
-
-let rec is_function_expr expr =
-   match expr.eexpr with
-   | TParenthesis expr | TMeta(_,expr) -> is_function_expr expr
-   | TFunction _ -> true
-   | _ -> false;;
-
-let is_var_field field =
-   match field.cf_kind with
-   | Var _ -> true
-   | Method MethDynamic -> true
-   | _ -> false
-;;
-
-let rec has_rtti_interface c interface =
-   List.exists (function (t,pl) ->
-      (snd t.cl_path) = interface && (match fst t.cl_path with | ["cpp";"rtti"] -> true | _ -> false )
-   ) c.cl_implements ||
-      (match c.cl_super with None -> false | Some (c,_) -> has_rtti_interface c interface);;
-
-let has_field_integer_lookup class_def =
-   has_rtti_interface class_def "FieldIntegerLookup";;
-
-let has_field_integer_numeric_lookup class_def =
-   has_rtti_interface class_def "FieldNumericIntegerLookup";;
-
-(* Output required code to place contents in required namespace *)
-let gen_open_namespace output class_path =
-      List.iter (fun namespace -> output ("namespace " ^ namespace ^ "{\n")) (List.map keyword_remap (fst class_path));;
-
-let gen_close_namespace output class_path =
-      List.iter
-         (fun namespace -> output ( "}" ^ " // end namespace " ^ namespace ^"\n"))
-         (fst class_path);;
-
-(* The basic types can have default values and are passesby value *)
-let is_numeric = function
-   | "Int" | "Bool" | "Float" |  "::haxe::io::Unsigned_char__" | "unsigned char" -> true
-   | "::cpp::UInt8" | "::cpp::Int8" | "::cpp::Char"
-   | "::cpp::UInt16" | "::cpp::Int16"
-   | "::cpp::UInt32" | "::cpp::Int32"
-   | "::cpp::UInt64" | "::cpp::Int64"
-   | "::cpp::Float32" | "::cpp::Float64"
-   | "int" | "bool" | "double" | "float" -> true
-   | _ -> false
-
-
-let rec remove_parens expression =
-      match expression.eexpr with
-      | TParenthesis e -> remove_parens e
-      | TMeta(_,e) -> remove_parens e
-      | _ -> expression
-;;
-
-
-(*
-let rec remove_parens_cast expression =
-   match expression.eexpr with
-   | TParenthesis e -> remove_parens_cast e
-   | TMeta(_,e) -> remove_parens_cast e
-   | TCast ( e,None) -> remove_parens_cast e
-   | _ -> expression
-;;
-*)
-let is_interface_type t =
-   match follow t with
-   | TInst (klass,params) -> klass.cl_interface
-   | _ -> false
-;;
-
-
-let is_cpp_function_instance haxe_type =
-   match follow haxe_type with
-   | TInst (klass,params) ->
-      (match klass.cl_path with
-      | ["cpp"] , "Function" -> true
-      | _ -> false )
-   | _ -> false
-   ;;
-
-
-let is_cpp_function_class haxe_type =
-   match follow haxe_type with
-   | TType (klass,params) ->
-      (match klass.t_path with
-      | ["cpp"] , "Function" -> true
-      | _ -> false )
-   | _ -> false
-   ;;
-
-let is_fromStaticFunction_call func =
-   match (remove_parens func).eexpr with
-   | TField (_,FStatic ({cl_path=["cpp"],"Function"},{cf_name="fromStaticFunction"} ) ) -> true
-   | _ -> false
-;;
-
-let is_addressOf_call func =
-   match (remove_parens func).eexpr with
-   | TField (_,FStatic ({cl_path=["cpp"],"Pointer"},{cf_name="addressOf"} ) ) -> true
-   | _ -> false
-;;
-
-let is_lvalue var =
-   match (remove_parens var).eexpr with
-   | TLocal _ -> true
-   | TField (_,FStatic(_,field) ) | TField (_,FInstance(_,_,field) ) -> is_var_field field
-   | _ -> false
-;;
-
-
-
-let is_pointer haxe_type includeRaw =
-   match follow haxe_type with
-   | TInst (klass,params) ->
-      (match klass.cl_path with
-      | ["cpp"] , "Pointer"
-      | ["cpp"] , "ConstPointer"
-      | ["cpp"] , "Function" -> true
-      | ["cpp"] , "RawPointer" when includeRaw -> true
-      | ["cpp"] , "RawConstPointer" when includeRaw -> true
-      | _ -> false )
-   | TType (type_def,params) ->
-      (match type_def.t_path with
-      | ["cpp"] , "Pointer"
-      | ["cpp"] , "ConstPointer"
-      | ["cpp"] , "Function" -> true
-      | ["cpp"] , "RawPointer" when includeRaw -> true
-      | ["cpp"] , "RawConstPointer" when includeRaw -> true
-      | _ -> false )
-   | _ -> false
-   ;;
-
-let is_dynamic_type_param class_kind =
-   match class_kind with
-   | KTypeParameter _ -> true
-   | _ -> false
-;;
-
-(*  Get a string to represent a type.
-   The "suffix" will be nothing or "_obj", depending if we want the name of the
-   pointer class or the pointee (_obj class *)
-let rec class_string klass suffix params remap =
-   let type_string = type_string_remap remap in
-   let join_class_path_remap = if remap then join_class_path_remap else join_class_path in
-   (match klass.cl_path with
-   (* Array class *)
-   |  ([],"Array") when is_dynamic_array_param (List.hd params) ->
-           "cpp::ArrayBase" ^ suffix (* "Dynamic" *)
-   |  ([],"Array") -> (snd klass.cl_path) ^ suffix ^ "< " ^ (String.concat ","
-               (List.map array_element_type params) ) ^ " >"
-   (* FastIterator class *)
-   |  (["cpp"],"FastIterator") -> "::cpp::FastIterator" ^ suffix ^ "< " ^ (String.concat ","
-               (List.map type_string  params) ) ^ " >"
-   |  (["cpp"],"Pointer")
-   |  (["cpp"],"ConstPointer") ->
-        "::cpp::Pointer< " ^ (String.concat "," (List.map type_string params) ) ^ " >"
-   |  (["cpp"],"RawPointer") ->
-        " " ^ (String.concat "," (List.map type_string params) ) ^ " * "
-   |  (["cpp"],"RawConstPointer") ->
-        " const " ^ (String.concat "," (List.map type_string params) ) ^ " * "
-   |  (["cpp"],"Function") ->
-        "::cpp::Function< " ^ (cpp_function_signature_params params) ^ " >"
-   | _ when is_dynamic_type_param klass.cl_kind -> "Dynamic"
-   |  ([],"#Int") -> "/* # */int"
-   |  (["haxe";"io"],"Unsigned_char__") -> "unsigned char"
-   |  ([],"Class") -> "hx::Class"
-   |  ([],"EnumValue") -> "Dynamic"
-   |  ([],"Null") -> (match params with
-         | [t] ->
-            (match follow t with
-            | TAbstract ({ a_path = [],"Int" },_)
-            | TAbstract ({ a_path = [],"Float" },_)
-            | TAbstract ({ a_path = [],"Bool" },_)
-            | TInst ({ cl_path = [],"Int" },_)
-            | TInst ({ cl_path = [],"Float" },_)
-            | TEnum ({ e_path = [],"Bool" },_) -> "Dynamic"
-            | t when type_has_meta_key t Meta.NotNull -> "Dynamic"
-            | _ -> "/*NULL*/" ^ (type_string t) )
-         | _ -> assert false);
-   (* Normal class *)
-   | path when klass.cl_extern && (not (is_internal_class path) )->
-            (join_class_path_remap klass.cl_path "::") ^ suffix
-   | _ -> "::" ^ (join_class_path_remap klass.cl_path "::") ^ suffix
-   )
-and type_string_suff suffix haxe_type remap =
-   let type_string = type_string_remap remap in
-   let join_class_path_remap = if remap then join_class_path_remap else join_class_path in
-   (match haxe_type with
-   | TMono r -> (match !r with None -> "Dynamic" ^ suffix | Some t -> type_string_suff suffix t remap)
-   | TAbstract ({ a_path = ([],"Void") },[]) -> "Void"
-   | TAbstract ({ a_path = ([],"Bool") },[]) -> "bool"
-   | TAbstract ({ a_path = ([],"Float") },[]) -> "Float"
-   | TAbstract ({ a_path = ([],"Int") },[]) -> "int"
-   | TAbstract( { a_path = ([], "EnumValue") }, _  ) -> "Dynamic"
-   | TEnum (enum,params) ->  "::" ^ (join_class_path_remap enum.e_path "::") ^ suffix
-   | TInst (klass,params) ->  (class_string klass suffix params remap)
-   | TType (type_def,params) ->
-      (match type_def.t_path with
-      | [] , "Null" ->
-         (match params with
-         | [t] ->
-            (match follow t with
-            | TAbstract ({ a_path = [],"Int" },_)
-            | TAbstract ({ a_path = [],"Float" },_)
-            | TAbstract ({ a_path = [],"Bool" },_)
-            | TInst ({ cl_path = [],"Int" },_)
-            | TInst ({ cl_path = [],"Float" },_)
-            | TEnum ({ e_path = [],"Bool" },_) -> "Dynamic" ^ suffix
-            | t when type_has_meta_key t Meta.NotNull -> "Dynamic" ^ suffix
-            | _ -> type_string_suff suffix t remap)
-         | _ -> assert false);
-      | [] , "Array" ->
-         (match params with
-         | [t] when (type_string (follow t) ) = "Dynamic" -> "Dynamic"
-         | [t] -> "Array< " ^ (type_string (follow t) ) ^ " >"
-         | _ -> assert false)
-      | ["cpp"] , "FastIterator" ->
-         (match params with
-         | [t] -> "::cpp::FastIterator< " ^ (type_string (follow t) ) ^ " >"
-         | _ -> assert false)
-      | ["cpp"] , "Pointer"
-      | ["cpp"] , "ConstPointer" ->
-         (match params with
-         | [t] -> "::cpp::Pointer< " ^ (type_string (follow t) ) ^ " >"
-         | _ -> assert false)
-      | ["cpp"] , "RawPointer" ->
-         (match params with
-         | [t] -> " " ^ (type_string (follow t) ) ^ " *"
-         | _ -> assert false)
-      | ["cpp"] , "RawConstPointer" ->
-         (match params with
-         | [t] -> "const " ^ (type_string (follow t) ) ^ " *"
-         | _ -> assert false)
-      | ["cpp"] , "Function" ->
-         "::cpp::Function< " ^ (cpp_function_signature_params params ) ^ " >"
-      | _ ->  type_string_suff suffix (apply_params type_def.t_params params type_def.t_type) remap
-      )
-   | TFun (args,haxe_type) -> "Dynamic" ^ suffix
-   | TAnon a -> "Dynamic"
-      (*
-      (match !(a.a_status) with
-      | Statics c -> type_string_suff suffix (TInst (c,List.map snd c.cl_params))
-      | EnumStatics e -> type_string_suff suffix (TEnum (e,List.map snd e.e_params))
-      | _ -> "Dynamic"  ^ suffix )
-      *)
-   | TDynamic haxe_type -> "Dynamic" ^ suffix
-   | TLazy func -> type_string_suff suffix ((!func)()) remap
-   | TAbstract (abs,pl) when abs.a_impl <> None ->
-      type_string_suff suffix (Abstract.get_underlying_type abs pl) remap
-   | TAbstract (abs,pl) ->
-      "::" ^ (join_class_path_remap abs.a_path "::") ^ suffix
-   )
-
-and type_string_remap remap haxe_type =
-   type_string_suff "" haxe_type remap
-
-and type_string haxe_type =
-   type_string_suff "" haxe_type true
-
-and array_element_type haxe_type =
-   match type_string haxe_type with
-   | x when cant_be_null haxe_type -> x
-   | x when is_interface_type (follow haxe_type) -> x
-   | "::String" -> "::String"
-   | _ -> "::Dynamic"
-
-and is_dynamic_array_param haxe_type =
-   if (type_string (follow haxe_type)) = "Dynamic" then true
-   else (match follow haxe_type with
-   | TInst (klass,params) ->
-         (match klass.cl_path with
-         | ([],"Array") | ([],"Class") | (["cpp"],"FastIterator")
-         | (["cpp"],"RawPointer") |(["cpp"],"ConstRawPointer")
-         | (["cpp"],"Pointer") |(["cpp"],"ConstPointer")|(["cpp"],"Function") -> false
-         | _ -> (match klass.cl_kind with KTypeParameter _ -> true | _ -> false)
-         )
-   | _ -> false
-   )
-and cpp_function_signature tfun abi =
-   match follow tfun with
-   | TFun(args,ret) -> (type_string ret) ^ " " ^ abi ^ "( " ^ (gen_tfun_interface_arg_list args) ^ ")"
-   | _ -> "void *"
-
-and cpp_function_signature_params params = match params with
-   | [t; abi] -> (match follow abi with
-       | TInst (klass,_) -> cpp_function_signature t (get_meta_string klass.cl_meta Meta.Abi)
-       | _ -> print_endline (type_string abi);
-           assert false )
-   | _ ->
-      print_endline ("Params:" ^ (String.concat "," (List.map type_string params) ));
-      assert false;
-
-and gen_interface_arg_type_name name opt typ =
-   let type_str = (type_string typ) in
-   (* type_str may have already converted Null<X> to Dynamic because of NotNull tag ... *)
-   (if (opt && (cant_be_null typ) && type_str<>"Dynamic" ) then
-      "hx::Null< " ^ type_str ^ " > "
-   else
-      type_str )
-
-
-   ^ " " ^ (keyword_remap name)
-and gen_tfun_interface_arg_list args =
-   String.concat "," (List.map (fun (name,opt,typ) -> gen_interface_arg_type_name name opt typ) args)
-and cant_be_null haxe_type =
-   is_numeric (type_string haxe_type) || (type_has_meta_key haxe_type Meta.NotNull )
-;;
-
-let is_object type_string =
-   not (is_numeric type_string || type_string="::String");
-;;
-
-
-
-
-
-let is_array haxe_type =
-   match follow haxe_type with
-   | TInst (klass,params) ->
-      (match klass.cl_path with
-      | [] , "Array" -> not (is_dynamic_array_param (List.hd params))
-      | _ -> false )
-   | TType (type_def,params) ->
-      (match type_def.t_path with
-      | [] , "Array" ->  not (is_dynamic_array_param (List.hd params))
-      | _ -> false )
-   | _ -> false
-   ;;
-
-let is_array_or_dyn_array haxe_type =
-   match follow haxe_type with
-   | TInst (klass,params) ->
-      (match klass.cl_path with | [] , "Array" -> true | _ -> false )
-   | TType (type_def,params) ->
-      (match type_def.t_path with | [] , "Array" -> true | _ -> false )
-   | _ -> false
-   ;;
-
-
-
-let is_array_implementer haxe_type =
-   match follow haxe_type with
-   | TInst (klass,params) ->
-      (match klass.cl_array_access with
-      | Some _ -> true
-      | _ -> false )
-   | _ -> false
-   ;;
-
-
-let is_numeric_field field =
-   match field.cf_kind with
-   | Var _ -> is_numeric (type_string field.cf_type)
-   | _ -> false;
-;;
-
-
-
-let is_static_access obj =
-   match (remove_parens obj).eexpr with
-   | TTypeExpr _ -> true
-   | _ -> false
-;;
-
-let is_native_with_space func =
-   match (remove_parens func).eexpr with
-   | TField(obj,field) when is_static_access obj ->
-      String.contains (get_field_access_meta field Meta.Native) ' '
-   | _ -> false
-;;
-
-
-let rec is_cpp_function_member func =
-   match (remove_parens func).eexpr with
-   | TField(obj,field) when is_cpp_function_instance obj.etype -> true
-   | TCall(obj,_) -> is_cpp_function_member obj
-   | _ -> false
-;;
-
-
-
-
-(* Get the type and output it to the stream *)
-let gen_type ctx haxe_type =
-   ctx.ctx_output (type_string haxe_type)
-;;
-
-let member_type ctx field_object member =
-   let name = (if (is_array field_object.etype) then "::Array"
-            else (type_string field_object.etype)) ^ "." ^ member in
-   try ( Hashtbl.find ctx.ctx_class_member_types name )
-   with Not_found -> "?";;
-
-let is_interface obj = is_interface_type obj.etype;;
-
-let should_implement_field x = not (is_extern_field x);;
-
-let is_function_member expression =
-   match (follow expression.etype) with | TFun (_,_) -> true | _ -> false;;
-
-let is_internal_member member =
-   match member with
-   | "__Field" | "__IField" | "__Run" | "__Is" | "__GetClass" | "__GetType" | "__ToString"
-   | "__s" | "__GetPtr" | "__SetField" | "__length" | "__IsArray" | "__SetThis" | "__Internal"
-   | "__EnumParams" | "__Index" | "__Tag" | "__GetFields" | "toString" | "__HasField"
-   | "__GetRealObject"
-         -> true
-   | _ -> false;;
-
-
-let is_extern_class class_def =
-   class_def.cl_extern || (has_meta_key class_def.cl_meta Meta.Extern) ||
-      (match class_def.cl_kind with
-       | KAbstractImpl abstract_def -> (has_meta_key abstract_def.a_meta Meta.Extern)
-       | _ -> false );
-;;
-
-
-let is_native_gen_class class_def =
-   (has_meta_key class_def.cl_meta Meta.NativeGen) ||
-      (match class_def.cl_kind with
-       | KAbstractImpl abstract_def -> (has_meta_key abstract_def.a_meta Meta.NativeGen)
-       | _ -> false );
-;;
-
-
-let is_extern_class_instance obj =
-   match follow obj.etype with
-   | TInst (klass,params) -> klass.cl_extern
-   | _ -> false
-;;
-
-
-let is_struct_access t =
-   match follow t with
-   | TInst (class_def,_) -> (has_meta_key class_def.cl_meta Meta.StructAccess)
-   | _ -> false
-;;
-
-
-
-let rec is_dynamic_accessor name acc field class_def =
- ( ( acc ^ "_" ^ field.cf_name) = name ) &&
-   ( not (List.exists (fun f -> f.cf_name=name) class_def.cl_ordered_fields) )
-   && (match class_def.cl_super with None -> true | Some (parent,_) -> is_dynamic_accessor name acc field parent )
-;;
-
-
-let gen_arg_type_name name default_val arg_type prefix =
-   let remap_name = keyword_remap name in
-   let type_str = (type_string arg_type) in
-   match default_val with
-   | Some TNull  -> (type_str,remap_name)
-   | Some constant when (cant_be_null arg_type) -> ("hx::Null< " ^ type_str ^ " > ",prefix ^ remap_name)
-   | Some constant  -> (type_str,prefix ^ remap_name)
-   | _ -> (type_str,remap_name);;
-
-(* Generate prototype text, including allowing default values to be null *)
-let gen_arg name default_val arg_type prefix =
-   let pair = gen_arg_type_name name default_val arg_type prefix in
-   (fst pair) ^ " " ^ (snd pair);;
-
-let rec gen_arg_list arg_list prefix =
-   String.concat "," (List.map (fun (v,o) -> (gen_arg v.v_name o v.v_type prefix) ) arg_list)
-
-
-let rec gen_tfun_arg_list arg_list =
-   match arg_list with
-   | [] -> ""
-   | [(name,o,arg_type)] -> gen_arg name None arg_type ""
-   | (name,o,arg_type) :: remaining  ->
-      (gen_arg name None arg_type "") ^ "," ^ (gen_tfun_arg_list remaining)
-
-(* Check to see if we are the first object in the parent tree to implement a dynamic interface *)
-let implement_dynamic_here class_def =
-   let implements_dynamic c = match c.cl_dynamic with None -> false | _ -> true  in
-   let rec super_implements_dynamic c = match c.cl_super with
-      | None -> false
-      | Some (csup, _) -> if (implements_dynamic csup) then true else
-            super_implements_dynamic csup;
-   in
-   ( (implements_dynamic class_def) && (not (super_implements_dynamic class_def) ) );;
-
-
-let gen_hash32 seed str =
-   let h = ref (Int32.of_int seed) in
-   let cycle = Int32.of_int 223 in
-   for i = 0 to String.length str - 1 do
-      h := Int32.add (Int32.mul !h cycle) (Int32.of_int (int_of_char (String.unsafe_get str i)));
-   done;
-   !h
-;;
-
-let gen_hash seed str =
-   Printf.sprintf "0x%08lx" (gen_hash32 seed str)
-;;
-
-let gen_string_hash str =
-   let h = gen_hash32 0 str in
-   Printf.sprintf "\"\\x%02lx\",\"\\x%02lx\",\"\\x%02lx\",\"\\x%02lx\""
-       (Int32.shift_right_logical (Int32.shift_left h 24) 24)
-       (Int32.shift_right_logical (Int32.shift_left h 16) 24)
-       (Int32.shift_right_logical (Int32.shift_left h 8) 24)
-       (Int32.shift_right_logical h 24)
-;;
-
-
-
-
-(* Make string printable for c++ code *)
-(* Here we know there are no utf8 characters, so use the L"" notation to avoid conversion *)
-let escape_stringw s l =
-   let b = Buffer.create 0 in
-   Buffer.add_char b 'L';
-   Buffer.add_char b '"';
-   let skip = ref 0 in
-   for i = 0 to String.length s - 1 do
-      if (!skip>0) then begin
-         skip := !skip -1;
-         l := !l-1;
-      end else
-      match Char.code (String.unsafe_get s i) with
-      | c when (c>127) ->
-         let encoded =  ((c land 0x3F) lsl 6) lor ( Char.code ((String.unsafe_get s (i+1))) land 0x7F) in
-         skip := 1;
-         Buffer.add_string b (Printf.sprintf "\\x%X\"L\"" encoded)
-      | c when (c < 32) -> Buffer.add_string b (Printf.sprintf "\\x%X\"L\"" c)
-      | c -> Buffer.add_char b (Char.chr c)
-   done;
-   Buffer.add_char b '"';
-   Buffer.contents b;;
-
-let special_to_hex s =
-   let l = String.length s in
-   let b = Buffer.create 0 in
-   for i = 0 to l - 1 do
-      match Char.code (String.unsafe_get s i) with
-      | c when (c>127) || (c<32) ->
-         Buffer.add_string b (Printf.sprintf "\\x%02x\"\"" c)
-      | c -> Buffer.add_char b (Char.chr c)
-   done;
-   Buffer.contents b;;
-
-let escape_extern s =
-   let l = String.length s in
-   let b = Buffer.create 0 in
-   for i = 0 to l - 1 do
-      match Char.code (String.unsafe_get s i) with
-      | c when (c>127) || (c<32) || (c=34) || (c=92) ->
-         Buffer.add_string b (Printf.sprintf "\\x%02x" c)
-      | c -> Buffer.add_char b (Char.chr c)
-   done;
-   Buffer.contents b;;
-
-
-
-let has_utf8_chars s =
-   let result = ref false in
-   for i = 0 to String.length s - 1 do
-      result := !result || ( Char.code (String.unsafe_get s i) > 127 )
-   done;
-   !result;;
-
-let escape_command s =
-   let b = Buffer.create 0 in
-   String.iter (fun ch -> if (ch=='"' || ch=='\\' ) then Buffer.add_string b "\\";  Buffer.add_char b ch ) s;
-   Buffer.contents b;;
-
-let str s =
-   let rec split s plus =
-      let escaped = Ast.s_escape ~hex:false s in
-      let hexed = (special_to_hex escaped) in
-      if (String.length hexed <= 16000 ) then
-         plus ^ " HX_CSTRING(\"" ^ hexed ^ "\")"
-      else begin
-         let len = String.length s in
-         let half = len lsr 1 in
-         (split (String.sub s 0 half) plus ) ^ (split (String.sub s half (len-half)) "+" )
-      end
-   in
-   let escaped = Ast.s_escape ~hex:false s in
-   let hexed = (special_to_hex escaped) in
-   if (String.length hexed <= 16000 ) then
-      "HX_HCSTRING(\"" ^ hexed ^ "\"," ^ (gen_string_hash s) ^ ")"
-   else
-      "(" ^ (split s "" ) ^ ")"
-;;
-
-let const_char_star s =
-   let escaped = Ast.s_escape ~hex:false s in
-   "\"" ^ special_to_hex escaped ^ "\"";
-;;
-
-
-
-(* When we are in a "real" object, we refer to ourselves as "this", but
-   if we are in a local class that is used to generate return values,
-   we use the fake "__this" pointer.
-   If we are in an "Anon" object, then the "this" refers to the anon object (eg List iterator) *)
-let clear_real_this_ptr ctx dynamic_this =
-   let old_flag = ctx.ctx_real_this_ptr in
-   let old_dynamic = ctx.ctx_dynamic_this_ptr in
-   let old_void = ctx.ctx_real_void in
-   ctx.ctx_real_this_ptr <- false;
-   ctx.ctx_dynamic_this_ptr <- dynamic_this;
-   fun () -> (
-      ctx.ctx_real_this_ptr <- old_flag;
-      ctx.ctx_dynamic_this_ptr <- old_dynamic;
-      ctx.ctx_real_void <- old_void;
-      )
-;;
-
-
-(* Generate temp variable names *)
-let next_anon_function_name ctx =
-   ctx.ctx_static_id_curr <- ctx.ctx_static_id_curr + 1;
-   "_Function_" ^ (string_of_int ctx.ctx_static_id_depth) ^"_"^ (string_of_int ctx.ctx_static_id_curr);;
-
-let use_anon_function_name ctx =
-   ctx.ctx_static_id_used <- ctx.ctx_static_id_used + 1;
-   "_Function_" ^ (string_of_int ctx.ctx_static_id_depth) ^"_"^ (string_of_int ctx.ctx_static_id_used);;
-
-let push_anon_names ctx =
-   let old_used = ctx.ctx_static_id_used in
-   let old_curr = ctx.ctx_static_id_curr in
-   let old_depth = ctx.ctx_static_id_depth in
-   ctx.ctx_static_id_used <- 0;
-   ctx.ctx_static_id_curr <- 0;
-   ctx.ctx_static_id_depth <- ctx.ctx_static_id_depth + 1;
-   ( function () -> (
-      ctx.ctx_static_id_used <- old_used;
-      ctx.ctx_static_id_curr <- old_curr;
-      ctx.ctx_static_id_depth <- old_depth; ) )
-;;
-
-let get_switch_var ctx =
-   ctx.ctx_switch_id <- ctx.ctx_switch_id + 1;
-   "_switch_" ^ (string_of_int ctx.ctx_switch_id)
-
-
-(* If you put on the "-debug" flag, you get extra comments in the source code *)
-let debug_expression expression type_too =
-   "/* " ^ Type.s_expr_kind expression ^ (if (type_too) then " = " ^ (type_string expression.etype) else "") ^ " */";;
-
-(* This is like the Type.iter, but also keeps the "retval" flag up to date *)
-let rec iter_retval f retval e =
-   match e.eexpr with
-   | TConst _
-   | TLocal _
-   | TBreak
-   | TContinue
-   | TTypeExpr _ ->
-      ()
-   | TArray (e1,e2)
-   | TBinop (_,e1,e2) ->
-      f true e1;
-      f true e2;
-   | TWhile (e1,e2,_) ->
-      f true e1;
-      f false e2;
-   | TFor (_,e1,e2) ->
-      f true e1;
-      f false e2;
-   | TThrow e
-   | TField (e,_)
-   | TEnumParameter (e,_,_)
-   | TUnop (_,_,e) ->
-      f true e
-   | TParenthesis e | TMeta(_,e) ->
-      f retval e
-   | TBlock expr_list when retval ->
-      let rec return_last = function
-         | [] -> ()
-         | expr :: [] -> f true expr
-         | expr :: exprs -> f false expr; return_last exprs in
-      return_last expr_list
-   | TArrayDecl el
-   | TNew (_,_,el) ->
-      List.iter (f true ) el
-   | TBlock el ->
-      List.iter (f false ) el
-   | TObjectDecl fl ->
-      List.iter (fun (_,e) -> f true e) fl
-   | TCall (e,el) ->
-      f true e;
-      List.iter (f true) el
-   | TVar (_,eo) ->
-      (match eo with None -> () | Some e -> f true e)
-   | TFunction fu ->
-      f false fu.tf_expr
-   | TIf (e,e1,e2) ->
-      f true e;
-      f retval e1;
-      (match e2 with None -> () | Some e -> f retval e)
-   | TSwitch (e,cases,def) ->
-      f true e;
-      List.iter (fun (el,e2) -> List.iter (f true) el; f retval e2) cases;
-      (match def with None -> () | Some e -> f retval e)
-(*    | TMatch (e,_,cases,def) ->
-      f true e;
-      List.iter (fun (_,_,e) -> f false e) cases;
-      (match def with None -> () | Some e -> f false e) *)
-   | TTry (e,catches) ->
-      f retval e;
-      List.iter (fun (_,e) -> f false e) catches
-   | TReturn eo ->
-      (match eo with None -> () | Some e -> f true e)
-   | TCast (e,None) ->
-      f retval e
-   | TCast (e,_) ->
-      f true e
-;;
-
-
-(* Convert an array to a comma separated list of values *)
-let array_arg_list inList =
-   let i = ref (0-1) in
-   String.concat "," (List.map (fun _ -> incr i; "inArgs[" ^ (string_of_int !i) ^ "]"  ) inList)
-
-let list_num l = string_of_int (List.length l);;
-
-
-let only_int_cases cases =
-   match cases with
-   | [] -> false
-   | _ ->
-   not (List.exists (fun (cases,expression) ->
-         List.exists (fun case -> match case.eexpr with TConst (TInt _) -> false | _ -> true ) cases
-            ) cases );;
-
-(* See if there is a haxe break statement that will be swollowed by c++ break *)
-exception BreakFound;;
-
-let contains_break expression =
-   try (
-   let rec check_all expression =
-      Type.iter (fun expr -> match expr.eexpr with
-         | TBreak -> raise BreakFound
-         | TFor _
-         | TFunction _
-         | TWhile (_,_,_) -> ()
-         | _ -> check_all expr;
-         ) expression in
-   check_all expression;
-   false;
-   ) with BreakFound -> true;;
-
-
-(* Decide is we should look the field up by name *)
-let dynamic_internal = function | "__Is" -> true | _ -> false
-
-
-let rec is_null expr =
-   match expr.eexpr with
-   | TConst TNull -> true
-   | TParenthesis expr | TMeta (_,expr) -> is_null expr
-   | TCast (e,None) -> is_null e
-   | _ -> false
-;;
-
-
-let find_undeclared_variables_ctx ctx undeclared declarations this_suffix allow_this expression =
-   let output = ctx.ctx_output in
-   let rec find_undeclared_variables undeclared declarations this_suffix allow_this expression =
-      match expression.eexpr with
-      | TVar (tvar,optional_init) ->
-            Hashtbl.add declarations (keyword_remap tvar.v_name) ();
-            if (ctx.ctx_debug_level>1) then
-               output ("/* found var " ^ tvar.v_name ^ "*/ ");
-            (match optional_init with
-            | Some expression -> find_undeclared_variables undeclared declarations this_suffix allow_this expression
-            | _ -> ())
-      | TFunction func -> List.iter ( fun (tvar, opt_val) ->
-            if (ctx.ctx_debug_level>1) then
-               output ("/* found arg " ^ tvar.v_name ^ " = " ^ (type_string tvar.v_type) ^ " */ ");
-            Hashtbl.add declarations (keyword_remap tvar.v_name) () ) func.tf_args;
-            find_undeclared_variables undeclared declarations this_suffix false func.tf_expr
-      | TTry (try_block,catches) ->
-         find_undeclared_variables undeclared declarations this_suffix allow_this try_block;
-         List.iter (fun (tvar,catch_expt) ->
-            let old_decs = Hashtbl.copy declarations in
-            Hashtbl.add declarations (keyword_remap tvar.v_name) ();
-            find_undeclared_variables undeclared declarations this_suffix allow_this catch_expt;
-            Hashtbl.clear declarations;
-            Hashtbl.iter ( Hashtbl.add declarations ) old_decs
-            ) catches;
-      | TLocal tvar ->
-         let name = keyword_remap tvar.v_name in
-         if  not (Hashtbl.mem declarations name) then
-            Hashtbl.replace undeclared name (type_string expression.etype)
-(*       | TMatch (condition, enum, cases, default) ->
-         find_undeclared_variables undeclared declarations this_suffix allow_this condition;
-         List.iter (fun (case_ids,params,expression) ->
-            let old_decs = Hashtbl.copy declarations in
-            (match params with
-            | None -> ()
-            | Some l -> List.iter (fun (opt_var) ->
-               match opt_var with | Some v -> Hashtbl.add declarations (keyword_remap v.v_name) () | _ -> ()  )
-               l  );
-            find_undeclared_variables undeclared declarations this_suffix allow_this expression;
-            Hashtbl.clear declarations;
-            Hashtbl.iter ( Hashtbl.add declarations ) old_decs
-            ) cases;
-         (match default with | None -> ()
-         | Some expr ->
-            find_undeclared_variables undeclared declarations this_suffix allow_this expr;
-         ); *)
-      | TFor (tvar, init, loop) ->
-         let old_decs = Hashtbl.copy declarations in
-         Hashtbl.add declarations (keyword_remap tvar.v_name) ();
-         find_undeclared_variables undeclared declarations this_suffix allow_this init;
-         find_undeclared_variables undeclared declarations this_suffix allow_this loop;
-         Hashtbl.clear declarations;
-         Hashtbl.iter ( Hashtbl.add declarations ) old_decs
-      | TConst TSuper
-      | TConst TThis ->
-         if  ((not (Hashtbl.mem declarations "this")) && allow_this) then
-            Hashtbl.replace undeclared "this" (type_string_suff this_suffix expression.etype true)
-      | TBlock expr_list ->
-         let old_decs = Hashtbl.copy declarations in
-         List.iter (find_undeclared_variables undeclared declarations this_suffix allow_this ) expr_list;
-         (* what is the best way for this ? *)
-         Hashtbl.clear declarations;
-         Hashtbl.iter ( Hashtbl.add declarations ) old_decs
-      | _ -> Type.iter (find_undeclared_variables undeclared declarations this_suffix allow_this) expression
-   in
-   find_undeclared_variables undeclared declarations this_suffix allow_this expression
-;;
-
-
-let rec is_dynamic_in_cpp ctx expr =
-   let expr_type = type_string ( match follow expr.etype with TFun (args,ret) -> ret | _ -> expr.etype) in
-   ctx.ctx_dbgout ( "/* idic: " ^ expr_type ^ " */" );
-   if ( expr_type="Dynamic" || expr_type="cpp::ArrayBase") then
-      true
-   else begin
-      let result = (
-      match expr.eexpr with
-       | TEnumParameter( obj, _, index ) ->
-         true (* TODO? *)
-      | TField( obj, field ) ->
-         let name = field_name field in
-         ctx.ctx_dbgout ("/* ?tfield "^name^" */");
-            if (is_dynamic_member_lookup_in_cpp ctx obj field) then
-            (
-               ctx.ctx_dbgout "/* tf=dynobj */";
-               true
-            )
-            else if (is_dynamic_member_return_in_cpp ctx obj field)  then
-            (
-               ctx.ctx_dbgout "/* tf=dynret */";
-               true
-            )
-            else
-            (
-               ctx.ctx_dbgout "/* tf=notdyn */";
-               false
-            )
-      | TConst TThis when ((not ctx.ctx_real_this_ptr) && ctx.ctx_dynamic_this_ptr) ->
-            ctx.ctx_dbgout ("/* dthis */"); true
-      | TArray (obj,index) -> let dyn = is_dynamic_in_cpp ctx obj in
-            ctx.ctx_dbgout ("/* aidr:" ^ (if dyn then "Dyn" else "Not") ^ " */");
-            dyn;
-      | TTypeExpr _ -> false
-      | TCall(func,args) ->
-               (match follow func.etype with
-               | TFun (args,ret) -> ctx.ctx_dbgout ("/* ret = "^ (type_string ret) ^" */");
-                  is_dynamic_in_cpp ctx func
-               | _ -> ctx.ctx_dbgout "/* not TFun */";  true
-         );
-      | TParenthesis(expr) | TMeta(_,expr) -> is_dynamic_in_cpp ctx expr
-      | TCast (e,None) -> (type_string expr.etype) = "Dynamic"
-      | TLocal { v_name = "__global__" } -> false
-      | TConst TNull -> true
-      | _ -> ctx.ctx_dbgout "/* other */";  false (* others ? *) )
-      in
-      ctx.ctx_dbgout (if result then "/* Y */" else "/* N */" );
-      result
-   end
-
-and is_dynamic_member_lookup_in_cpp ctx field_object field =
-   let member = field_name field in
-   ctx.ctx_dbgout ("/*mem."^member^".*/");
-   if (is_internal_member member) then false else
-   if (is_pointer field_object.etype true) then false else
-   if (match field_object.eexpr with | TTypeExpr _ -> ctx.ctx_dbgout "/*!TTypeExpr*/"; true | _ -> false) then false else
-   if (is_dynamic_in_cpp ctx field_object) then true else
-   if (is_array field_object.etype) then false else (
-   let tstr = type_string field_object.etype in
-   ctx.ctx_dbgout ("/* ts:"^tstr^"*/");
-   match tstr with
-      (* Internal classes have no dynamic members *)
-      | "::String" | "Null" | "::hx::Class" | "::Enum" | "::Math" | "::ArrayAccess" -> ctx.ctx_dbgout ("/* ok:" ^ (type_string field_object.etype)  ^ " */"); false
-      | "Dynamic" -> true
-      | name ->
-            let full_name = name ^ "." ^ member in
-            ctx.ctx_dbgout ("/* t:" ^ full_name ^ " */");
-            try ( let mem_type = (Hashtbl.find ctx.ctx_class_member_types full_name) in
-               ctx.ctx_dbgout ("/* =" ^ mem_type ^ "*/");
-               false )
-            with Not_found -> not (is_extern_class_instance field_object)
-   )
-and is_dynamic_member_return_in_cpp ctx field_object field =
-   let member = field_name field in
-   if (is_array field_object.etype) then false else
-   if (is_pointer field_object.etype true) then false else
-   if (is_internal_member member) then false else
-   match field_object.eexpr with
-   | TTypeExpr t ->
-         let full_name = "::" ^ (join_class_path (t_path t) "::" ) ^ "." ^ member in
-         ctx.ctx_dbgout ("/*static:"^ full_name^"*/");
-         ( try ( let mem_type = (Hashtbl.find ctx.ctx_class_member_types full_name) in mem_type="Dynamic"||mem_type="cpp::ArrayBase" )
-         with Not_found -> true )
-   | _ ->
-      let tstr = type_string field_object.etype in
-      (match tstr with
-         (* Internal classes have no dynamic members *)
-         | "::String" | "Null" | "::hx::Class" | "::Enum" | "::Math" | "::ArrayAccess" -> false
-         | "Dynamic" | "cpp::ArrayBase" -> ctx.ctx_dbgout "/*D*/"; true
-         | name ->
-               let full_name = name ^ "." ^ member in
-               ctx.ctx_dbgout ("/*R:"^full_name^"*/");
-               try ( let mem_type = (Hashtbl.find ctx.ctx_class_member_types full_name) in mem_type="Dynamic"||mem_type="cpp::ArrayBase" )
-               with Not_found -> true )
-;;
-
-let cast_if_required ctx expr to_type =
-   let expr_type = (type_string expr.etype) in
-   ctx.ctx_dbgout ( "/* cir: " ^ expr_type ^ " */" );
-   if (is_dynamic_in_cpp ctx expr) then
-      ctx.ctx_output (".Cast< " ^ to_type ^ " >()" )
-;;
-
-
-let is_matching_interface_type t0 t1 =
-    (match (follow t0),(follow t1) with
-    | TInst (k0,_), TInst(k1,_) -> k0==k1
-    | _ -> false
-    )
-;;
-
-
-
-let default_value_string = function
-   | TInt i -> Printf.sprintf "%ld" i
-   | TFloat float_as_string -> "((Float)" ^ float_as_string ^ ")"
-   | TString s -> str s
-   | TBool b -> (if b then "true" else "false")
-   | TNull -> "null()"
-   | _ -> "/* Hmmm */"
-;;
-
-let generate_default_values ctx args prefix =
-   List.iter ( fun (v,o) -> let type_str = type_string v.v_type in
-   let name = (keyword_remap v.v_name) in
-   match o with
-   | Some TNull -> ()
-   | Some const ->
-      ctx.ctx_output (type_str ^ " " ^ name ^ " = " ^ prefix ^ name ^ ".Default(" ^
-         (default_value_string const) ^ ");\n")
-   | _ -> () ) args;;
-
-let return_type_string t =
-   match t with
-   |  TFun (_,ret) -> type_string ret
-   | _ -> ""
-;;
-
-
-let get_return_type field =
-   match follow field.cf_type with
-      | TFun (_,return_type) -> return_type
-      | _ -> raise Not_found
-;;
-
-
-
-let has_default_values args =
-   List.exists ( fun (_,o) -> match o with
-            | Some TNull -> false
-            | Some _ -> true
-            | _ -> false ) args ;;
-
-exception PathFound of string;;
-
-
-let strip_file ctx file = (match Common.defined ctx Common.Define.AbsolutePath with
-   | true -> file
-   | false -> let flen = String.length file in
-   (* Not quite right - should probably test is file exists *)
-   try
-      List.iter (fun path ->
-         let plen = String.length path in
-         if (flen>plen && path=(String.sub file 0 plen ))
-            then raise (PathFound (String.sub file plen (flen-plen)) ) )
-         (ctx.class_path @ ctx.std_path);
-      file;
-   with PathFound tail ->
-      tail)
-;;
-
-let hx_stack_push ctx output clazz func_name pos =
-   if ctx.ctx_debug_level > 0 then begin
-      let stripped_file = strip_file ctx.ctx_common pos.pfile in
-      let qfile = "\"" ^ (Ast.s_escape stripped_file) ^ "\"" in
-      ctx.ctx_file_info := PMap.add stripped_file pos.pfile !(ctx.ctx_file_info);
-      if (ctx.ctx_debug_level>0) then begin
-         let hash_class_func = gen_hash 0 (clazz^"."^func_name) in
-         let hash_file = gen_hash 0 stripped_file in
-         output ("HX_STACK_FRAME(\"" ^ clazz ^ "\",\"" ^ func_name ^ "\"," ^ hash_class_func ^ ",\"" ^
-               clazz ^ "." ^ func_name ^ "\"," ^ qfile ^ "," ^
-               (string_of_int (Lexer.get_error_line pos) ) ^  "," ^ hash_file ^ ")\n")
-      end
-   end
-;;
-
-
-(*
-   This is the big one.
-   Once you get inside a function, all code is generated (recursively) as a "expression".
-   "retval" is tracked to determine whether the value on an expression is actually used.
-   eg, if the result of a block (ie, the last expression in the list) is used, then
-   we have to do some funky stuff to generate a local function.
-   Some things that change less often are stored in the context and are extracted
-   at the top for simplicity.
-*)
-
-let gen_expression_tree ctx retval expression_tree set_var tail_code =
- let writer = ctx.ctx_writer in
- let output_i = writer#write_i in
- let output = ctx.ctx_output in
-
- let rec define_local_function_ctx func_name func_def =
-   let remap_this = function | "this" -> "__this" | other -> other in
-   let rec define_local_function func_name func_def =
-      let declarations = Hashtbl.create 0 in
-      let undeclared = Hashtbl.create 0 in
-      (* '__global__', '__cpp__' are always defined *)
-      Hashtbl.add declarations "__global__" ();
-      Hashtbl.add declarations "__cpp__" ();
-      Hashtbl.add declarations "__trace" ();
-      (* Add args as defined variables *)
-      List.iter ( fun (arg_var, opt_val) ->
-         if (ctx.ctx_debug_level>1) then
-            output ("/* found arg " ^ arg_var.v_name ^ " = " ^ (type_string arg_var.v_type) ^" */ ");
-         Hashtbl.add declarations (keyword_remap arg_var.v_name) () ) func_def.tf_args;
-      find_undeclared_variables_ctx ctx undeclared declarations "" true func_def.tf_expr;
-
-      let has_this = Hashtbl.mem undeclared "this" in
-      if (has_this) then Hashtbl.remove undeclared "this";
-      let typed_vars = hash_iterate undeclared (fun key value -> value ^ "," ^ (keyword_remap key) ) in
-      let func_name_sep = func_name ^ (if List.length typed_vars > 0 then "," else "") in
-      output_i ("HX_BEGIN_LOCAL_FUNC_S" ^ (list_num typed_vars) ^ "(" ^
-         (if has_this then "hx::LocalThisFunc," else "hx::LocalFunc,") ^ func_name_sep ^
-                  (String.concat "," typed_vars) ^ ")\n" );
-      output_i ("int __ArgCount() const { return " ^ (string_of_int (List.length func_def.tf_args)) ^"; }\n");
-
-      (* actual function, called "run" *)
-      let args_and_types = List.map
-            (fun (v,_) -> (type_string v.v_type) ^ " " ^ (keyword_remap v.v_name) ) func_def.tf_args in
-      let block = is_block func_def.tf_expr in
-      let func_type = type_string func_def.tf_type in
-      output_i (func_type ^ " run(" ^ (gen_arg_list func_def.tf_args "__o_") ^ ")");
-
-      let close_defaults =
-         if (has_default_values func_def.tf_args) then begin
-            writer#begin_block;
-            output_i "";
-            generate_default_values ctx func_def.tf_args "__o_";
-            output_i "";
-            true;
-         end
-      else
-         false in
-
-
-      let pop_real_this_ptr = clear_real_this_ptr ctx true in
-
-      writer#begin_block;
-      if (ctx.ctx_debug_level>0) then begin
-         hx_stack_push ctx output_i "*" func_name func_def.tf_expr.epos;
-         if (has_this && ctx.ctx_debug_level>0) then
-            output_i ("HX_STACK_THIS(__this.mPtr)\n");
-            List.iter (fun (v,_) -> output_i ("HX_STACK_ARG(" ^ (keyword_remap v.v_name) ^ ",\"" ^ v.v_name ^"\")\n") )
-            func_def.tf_args;
-      end;
-
-      if (block) then begin
-         output_i "";
-         gen_expression false func_def.tf_expr;
-         output_i "return null();\n";
-      end else begin
-         (* Save old values, and equalize for new input ... *)
-         let pop_names = push_anon_names ctx in
-
-         find_local_functions_and_return_blocks_ctx false func_def.tf_expr;
-
-         (match func_def.tf_expr.eexpr with
-         | TReturn (Some return_expression) when (func_type<>"Void") ->
-            output_i "return ";
-            gen_expression true return_expression;
-         | TReturn (Some return_expression) ->
-            output_i "";
-            gen_expression false return_expression;
-         | _ ->
-            output_i "";
-            gen_expression false (to_block func_def.tf_expr);
-         );
-         output ";\n";
-         output_i "return null();\n";
-         pop_names();
-      end;
-      writer#end_block;
-
-      if close_defaults then writer#end_block;
-      pop_real_this_ptr();
-
-      let return = if (type_string func_def.tf_type ) = "Void" then "(void)" else "return" in
-      output_i ("HX_END_LOCAL_FUNC" ^ (list_num args_and_types) ^ "(" ^ return ^ ")\n\n");
-
-      Hashtbl.replace ctx.ctx_local_function_args func_name
-         (if (ctx.ctx_real_this_ptr) then
-            String.concat "," (hash_keys undeclared)
-         else
-            String.concat "," (List.map remap_this (hash_keys undeclared)) )
-   in
-   define_local_function func_name func_def
-
- and find_local_functions_and_return_blocks_ctx retval expression =
-   let rec find_local_functions_and_return_blocks retval expression =
-      match expression.eexpr with
-      | TBlock _ ->
-         if (retval) then begin
-            define_local_return_block_ctx expression (next_anon_function_name ctx) true;
-         end  (* else we are done *)
-      | TTry (_, _)
-      | TSwitch (_, _, _) when retval ->
-            define_local_return_block_ctx expression (next_anon_function_name ctx) true;
-      | TObjectDecl ( ("fileName" , { eexpr = (TConst (TString file)) }) ::
-         ("lineNumber" , { eexpr = (TConst (TInt line)) }) ::
-            ("className" , { eexpr = (TConst (TString class_name)) }) ::
-               ("methodName", { eexpr = (TConst (TString meth)) }) :: [] ) -> ()
-      | TObjectDecl decl_list ->
-            let name = next_anon_function_name ctx in
-            define_local_return_block_ctx expression name true;
-      | TFunction func ->
-         let func_name = next_anon_function_name ctx in
-         output "\n";
-         define_local_function_ctx func_name func
-      | TField (obj,_) | TEnumParameter (obj,_,_) when (is_null obj) -> ( )
-      | TArray (obj,_) when (is_null obj) -> ( )
-      | TIf ( _ , _ , _ ) when retval -> (* ? operator style *)
-         iter_retval find_local_functions_and_return_blocks retval expression
-      | TSwitch (_, _, _) when retval -> ( )
-      (* | TMatch ( cond , _, _, _) *)
-      | TWhile ( cond , _, _ )
-      | TIf ( cond , _, _ )
-      | TSwitch ( cond , _, _) -> iter_retval find_local_functions_and_return_blocks true cond
-      | _ -> iter_retval find_local_functions_and_return_blocks retval expression
-   in find_local_functions_and_return_blocks retval expression
-
- and define_local_return_block_ctx expression name retval =
-   let check_this = function | "this" when not ctx.ctx_real_this_ptr -> "__this" | x -> x in
-   let rec define_local_return_block expression  =
-      let declarations = Hashtbl.create 0 in
-      let undeclared = Hashtbl.create 0 in
-      (* '__global__' is always defined *)
-      Hashtbl.add declarations "__global__" ();
-      Hashtbl.add declarations "__cpp__" ();
-      Hashtbl.add declarations "__trace" ();
-      find_undeclared_variables_ctx ctx undeclared declarations "_obj" true expression;
-
-      let vars = (hash_keys undeclared) in
-      let args = String.concat "," (List.map check_this (hash_keys undeclared)) in
-      Hashtbl.replace ctx.ctx_local_return_block_args name args;
-      output_i ("struct " ^ name);
-      writer#begin_block;
-      let ret_type = if (not retval) then "Void" else
-         match expression.eexpr with
-         | TObjectDecl _ -> "Dynamic"
-         | _ -> type_string expression.etype in
-      (* TODO - analyse usage *)
-      let pass_by_value name = (String.length name >=5 ) && (String.sub name 0 5 = "_this") in
-      output_i ("inline static " ^ ret_type ^ " Block( ");
-      output (String.concat "," (
-         (List.map
-            (fun var ->
-               let var_type = Hashtbl.find undeclared var in
-               (* Args passed into inline-block should be references, so they can be changed.
-                  Fake 'this' pointers can't be changed, so needn't be references *)
-               match var with
-               | "this" -> "hx::ObjectPtr< " ^ var_type ^ " > __this"
-               | name when (pass_by_value name)  -> var_type ^ " " ^ name
-               | name -> var_type ^ " &" ^name
-            ) vars) ) );
-      output (")");
-      let return_data = ret_type <> "Void" in
-      writer#begin_block;
-      hx_stack_push ctx output_i "*" "closure" expression.epos;
-      output_i "";
-
-      let pop_real_this_ptr = clear_real_this_ptr ctx false in
-      (match expression.eexpr with
-      | TObjectDecl decl_list ->
-         writer#begin_block;
-         output_i "hx::Anon __result = hx::Anon_obj::Create();\n";
-         let pop_names = push_anon_names ctx in
-         List.iter (function (name,value) ->
-            find_local_functions_and_return_blocks_ctx true value;
-            output_i ( "__result->Add(" ^ (str name) ^ " , ");
-            gen_expression true value;
-            output (if is_function_expr value then ",true" else ",false" );
-            output (");\n");
-         ) decl_list;
-         pop_names();
-         output_i "return __result;\n";
-         writer#end_block;
-      | TBlock _ ->
-         ctx.ctx_return_from_block <- return_data;
-         ctx.ctx_return_from_internal_node <- false;
-         gen_expression false expression;
-      | TCall(func,args) ->
-         writer#begin_block;
-         let pop_names = push_anon_names ctx in
-         find_local_functions_and_return_blocks_ctx true func;
-         List.iter (find_local_functions_and_return_blocks_ctx true) args;
-         ctx.ctx_tcall_expand_args <- true;
-         gen_expression return_data expression;
-         output ";\n";
-         pop_names();
-         writer#end_block;
-      | _ ->
-         ctx.ctx_return_from_block <- false;
-         ctx.ctx_return_from_internal_node <- return_data;
-         gen_expression false (to_block expression);
-      );
-      output_i "return null();\n";
-      writer#end_block;
-      pop_real_this_ptr();
-      writer#end_block_line;
-      output ";\n";
-   in
-   define_local_return_block expression
-
-
- and gen_expression retval expression =
-   let calling = ctx.ctx_calling in
-   ctx.ctx_calling <- false;
-   let assigning = ctx.ctx_assigning in
-   ctx.ctx_assigning <- false;
-   let return_from_block = ctx.ctx_return_from_block in
-   ctx.ctx_return_from_block <- false;
-   let tcall_expand_args = ctx.ctx_tcall_expand_args in
-   ctx.ctx_tcall_expand_args <- false;
-   let return_from_internal_node = ctx.ctx_return_from_internal_node in
-   ctx.ctx_return_from_internal_node <- false;
-   let dump_src_pos = ctx.ctx_dump_src_pos in
-   ctx.ctx_dump_src_pos <- (fun() -> ());
-
-   (* Annotate source code with debug - can get a bit verbose.  Mainly for debugging code gen,
-      rather than the run time *)
-   if (ctx.ctx_debug_level>1) then begin
-      (*if calling then output "/* Call */";*)
-      (*if ctx.ctx_real_this_ptr then output "/* this */" else output "/* FAKE __this */";*)
-      output (debug_expression expression (ctx.ctx_debug_level>1) );
-   end;
-
-   (* Write comma separated list of variables - useful for function args. *)
-   let rec gen_expression_list expressions =
-      (match expressions with
-      | [] -> ()
-      | [single] -> gen_expression true single
-      | first :: remaining ->
-         gen_expression true first;
-         output ",";
-         gen_expression_list remaining
-      ) in
-
-   let rec gen_bin_op_string expr1 op expr2 =
-      let cast = (match op with
-         | ">>" | "<<" | "&" | "|" | "^"  -> "int("
-         | "&&" | "||" -> "bool("
-         | "/" -> "Float("
-         | _ -> "") in
-      if (op <> "=") then output "(";
-      if ( cast <> "") then output cast;
-      gen_expression true expr1;
-      if ( cast <> "") then output ")";
-
-      output (" " ^ op ^ " ");
-
-      if ( cast <> "") then output cast;
-      gen_expression true expr2;
-      if ( cast <> "") then output ")";
-      if (op <> "=") then output ")";
-   in
-   let rec is_const_string_term expr =
-      match expr.eexpr with
-      | TConst( TString _ ) -> true
-      | TBinop (OpAdd,e1,e2) -> (is_const_string_term e1) && (is_const_string_term e2 )
-      | _ -> false
-   in
-   let rec combine_string_terms expr =
-      match expr.eexpr with
-      | TConst( TString s ) -> s
-      | TBinop (OpAdd,e1,e2) -> (combine_string_terms e1) ^ (combine_string_terms e2 )
-      | _ -> ""
-   in
-   let rec gen_bin_op op expr1 expr2 =
-      match op with
-      | Ast.OpAdd when (is_const_string_term expr1) && (is_const_string_term expr2) ->
-         output (str ((combine_string_terms expr1) ^ (combine_string_terms expr2)) )
-      | Ast.OpAssign -> ctx.ctx_assigning <- true;
-                        gen_bin_op_string expr1 "=" expr2
-      | Ast.OpUShr ->
-         output "hx::UShr(";
-         gen_expression true expr1;
-         output ",";
-         gen_expression true expr2;
-         output ")";
-      | Ast.OpMod ->
-         output "hx::Mod(";
-         gen_expression true expr1;
-         output ",";
-         gen_expression true expr2;
-         output ")";
-
-      | Ast.OpAssignOp bin_op ->
-         output (match bin_op with
-            | Ast.OpAdd -> "hx::AddEq("
-            | Ast.OpMult -> "hx::MultEq("
-            | Ast.OpDiv -> "hx::DivEq("
-            | Ast.OpSub -> "hx::SubEq("
-            | Ast.OpAnd -> "hx::AndEq("
-            | Ast.OpOr  -> "hx::OrEq("
-            | Ast.OpXor  -> "hx::XorEq("
-            | Ast.OpShl  -> "hx::ShlEq("
-            | Ast.OpShr  -> "hx::ShrEq("
-            | Ast.OpUShr  -> "hx::UShrEq("
-            | Ast.OpMod  -> "hx::ModEq("
-            | _ -> error "Unknown OpAssignOp" expression.epos );
-         ctx.ctx_assigning <- true;
-         gen_expression true expr1;
-         output ",";
-         gen_expression true expr2;
-         output ")"
-      | Ast.OpNotEq -> gen_bin_op_string expr1 "!=" expr2
-      | Ast.OpEq -> gen_bin_op_string expr1 "==" expr2
-      | _ ->  gen_bin_op_string expr1 (Ast.s_binop op) expr2
-      in
-
-   let gen_array_cast cast_name real_type call =
-   output (cast_name ^ "< " ^ real_type ^ " >" ^ call)
-   in
-   let rec check_array_element_cast array_type cast_name call =
-      match follow array_type with
-      | TInst (klass,[element]) ->
-         ( match type_string element with
-         | _ when is_struct_access element -> ()
-         | x when cant_be_null element -> ()
-         | _ when is_interface_type element -> ()
-         | "::String" | "Dynamic" -> ()
-         | real_type -> gen_array_cast cast_name real_type call
-         )
-      | TAbstract (abs,pl) when abs.a_impl <> None ->
-         check_array_element_cast (Abstract.get_underlying_type abs pl) cast_name call
-      | _ -> ()
-   in
-   let rec check_array_cast array_type =
-      match follow array_type with
-      | x when is_interface_type x -> ()
-      | TInst (klass,[element]) ->
-         let name = type_string element in
-         if ( is_object name && not (is_interface_type element) ) then
-            gen_array_cast ".StaticCast" "Array<Dynamic>" "()"
-         else
-            gen_array_cast ".StaticCast" (type_string array_type) "()"
-      | TAbstract (abs,pl) when abs.a_impl <> None ->
-         check_array_cast (Abstract.get_underlying_type abs pl)
-      | _ -> ()
-   in
-
-   let rec gen_tfield field_object field =
-      let member = (field_name field) in
-      let remap_name = keyword_remap member in
-      let already_dynamic = ref false in
-      (match field_object.eexpr with
-      (* static access ... *)
-      | TTypeExpr type_def ->
-         (match get_field_access_meta field Meta.Native with
-         | "" ->
-            let class_name = "::" ^ (join_class_path_remap (t_path type_def) "::" ) in
-            if (class_name="::String") then
-               output ("::String::" ^ remap_name)
-            else
-               output (class_name ^ "_obj::" ^ remap_name);
-         | native -> output native
-         )
-      (* Special internal access *)
-      | TLocal { v_name = "__global__" } ->
-         output ("::" ^ member )
-      | TConst TSuper -> output (if ctx.ctx_real_this_ptr then "this" else "__this");
-                  output ("->super::" ^ remap_name)
-      | TConst TThis when ctx.ctx_real_this_ptr -> output ( "this->" ^ remap_name )
-      | TConst TNull -> output "null()"
-      | _ ->
-         gen_expression true field_object;
-         ctx.ctx_dbgout "/* TField */";
-         (* toString is the only internal member that can be set... *)
-         let settingInternal = assigning && member="toString" in
-         let isString = (type_string field_object.etype)="::String" in
-         if (is_struct_access field_object.etype) then
-            output ( "." ^ member )
-         else if (is_internal_member member && not settingInternal) then begin
-            output ( (if isString then "." else "->") ^ member );
-         end else if (settingInternal || is_dynamic_member_lookup_in_cpp ctx field_object field) then begin
-            if assigning then
-               output ( "->__FieldRef(" ^ (str member) ^ ")" )
-            else
-               output ( "->__Field(" ^ (str member) ^ ", hx::paccDynamic )" );
-            already_dynamic := true;
-         end else begin
-            if (isString) then
-               output ( "." ^ remap_name )
-            else begin
-               cast_if_required ctx field_object (type_string field_object.etype);
-               let remap_name = if (type_string field_object.etype)="cpp::ArrayBase" then
-                   match remap_name with
-                   | "length" -> remap_name
-                   | _ -> "__" ^ remap_name
-               else
-                  remap_name
-               in
-               output ( "->" ^ remap_name );
-               if (calling && (is_array field_object.etype) && remap_name="iterator" ) then
-                  check_array_element_cast field_object.etype "Fast" "";
-
-               already_dynamic := (match field with
-                  | FInstance(_,_,var) when is_var_field var -> true
-                  | _ -> false);
-            end;
-         end;
-      );
-      if ( (not !already_dynamic) && (not calling) && (not assigning) && (is_function_member expression) ) then
-         output "_dyn()";
-   in
-   let gen_local_block_call () =
-      let func_name = use_anon_function_name ctx in (
-         try
-         output ( func_name ^ "::Block(" ^
-            (Hashtbl.find ctx.ctx_local_return_block_args func_name) ^ ")" )
-         with Not_found ->
-         (*error ("Block function " ^ func_name ^ " not found" ) expression.epos;*)
-         output ("/* Block function " ^ func_name ^ " not found */" );
-         )
-   in
-
-   match expression.eexpr with
-   | TConst TNull when not retval ->
-      output "Dynamic()";
-   | TCall (func, arg_list) when (match func.eexpr with
-         | TLocal { v_name = "__cpp__" } -> true
-         | _ -> false) ->
-      ( match arg_list with
-      | [{ eexpr = TConst (TString code) }] -> output (format_code code);
-      | ({ eexpr = TConst (TString code) } as ecode) :: tl ->
-         Codegen.interpolate_code ctx.ctx_common (format_code code) tl output (gen_expression true) ecode.epos
-      | _ -> error "__cpp__'s first argument must be a string" func.epos;
-      )
-   | TCall (func, arg_list) when tcall_expand_args->
-      let arg_string = ref "" in
-      let idx = ref 0 in
-      List.iter (fun arg ->
-         let a_name = "__a" ^ string_of_int(!idx) in
-         arg_string := !arg_string ^ (if !arg_string<>"" then "," else "") ^ a_name;
-         idx := !idx + 1;
-         output_i ( (type_string arg.etype) ^ " " ^ a_name ^ " = ");
-         gen_expression true arg;
-         output ";\n";
-      ) arg_list;
-      output_i (if retval then "return " else "");
-      ctx.ctx_calling <- true;
-      gen_expression true func;
-      output ("(" ^ !arg_string ^ ");\n");
-   | TCall (func, arg_list) when is_fromStaticFunction_call func ->
-      (match arg_list with
-         | [ {eexpr = TField( _, FStatic(klass,field)) } ] ->
-            let signature = cpp_function_signature field.cf_type "" in
-            let name = keyword_remap field.cf_name in
-            let void_cast = has_meta_key field.cf_meta Meta.Void in
-            output ("::cpp::Function< " ^ signature ^">(");
-            if (void_cast) then output "hx::AnyCast(";
-            output ("&::" ^(join_class_path klass.cl_path "::")^ "_obj::" ^ name );
-            if (void_cast) then output ")";
-            output (" )");
-         | _ -> error "fromStaticFunction must take a static function" expression.epos;
-      )
-
-   | TCall (func, [arg]) when is_addressOf_call func && not (is_lvalue arg) ->
-      error "addressOf must take a local or member variable" expression.epos;
-
-   | TCall (func, arg_list) ->
-      let rec is_variable e = match e.eexpr with
-      | TField _ | TEnumParameter _ -> false
-      | TLocal { v_name = "__global__" } -> false
-      | TParenthesis p | TMeta(_,p) -> is_variable p
-      | _ -> true
-      in
-      let expr_type = type_string expression.etype in
-      let rec is_fixed_override e = (not (is_scalar expr_type)) && match e.eexpr with
-      | TField(obj,FInstance(_,_,field) ) ->
-         let cpp_type = member_type ctx obj field.cf_name in
-         (not (is_scalar cpp_type)) && (
-            let fixed = (cpp_type<>"?") && (expr_type<>"Dynamic") && (cpp_type<>"Dynamic") &&
-               (cpp_type<>expr_type) && (expr_type<>"Void") && (cpp_type<>"cpp::ArrayBase") in
-            if (fixed && (ctx.ctx_debug_level>1) ) then begin
-               output ("/* " ^ (cpp_type) ^ " != " ^ expr_type ^ " -> cast */");
-            end;
-            fixed
-         )
-      | TParenthesis p | TMeta(_,p) -> is_fixed_override p
-      | _ -> false
-      in
-      let check_extern_pointer_cast e = match (remove_parens e).eexpr with
-      | TField (_,FInstance(class_def,_,_) )
-      | TField (_,FStatic(class_def,_) )
-         when class_def.cl_extern ->
-         (try
-            let return_type = expression.etype in
-            (is_pointer return_type false) &&
-               ( output ( (type_string return_type) ^ "(" ); true; )
-         with Not_found -> false )
-      | _ -> false
-      in
-      let is_super = (match func.eexpr with | TConst TSuper -> true | _ -> false ) in
-      if (ctx.ctx_debug_level>1) then output ("/* TCALL ret=" ^ expr_type ^ "*/");
-      let cast_result =  (not is_super) && (is_fixed_override func) in
-      if (cast_result) then output ("hx::TCast< " ^ expr_type ^ " >::cast(");
-      let cast_result = cast_result || check_extern_pointer_cast func in
-
-      (* If a static function has @:native('new abc')
-          c++ new has lower precedence than in haxe so ( ) must be used *)
-      let paren_result =
-        if is_native_with_space func then
-           ( output "("; true )
-        else
-           false
-      in
-      ctx.ctx_calling <- true;
-      gen_expression true func;
-
-      output "(";
-      gen_expression_list arg_list;
-      output ")";
-      if paren_result then
-         output ")";
-
-      if (cast_result) then output (")");
-      if ( (is_variable func) && (not (is_cpp_function_member func) ) &&
-           (expr_type<>"Dynamic" && expr_type<>"cpp::ArrayBase" ) && (not is_super)  ) then
-         ctx.ctx_output (".Cast< " ^ expr_type ^ " >()" );
-
-      let rec cast_array_output func =
-         match func.eexpr with
-            | TField(obj,field) when is_array obj.etype ->
-               (match field_name field with
-                  | "pop" | "shift" | "__unsafe_get" | "__unsafe_set" -> check_array_element_cast obj.etype ".StaticCast" "()"
-                  | "map" -> check_array_cast expression.etype
-                  | _ -> ()
-               )
-            | TParenthesis p | TMeta(_,p) -> cast_array_output p
-            | _ -> ()
-      in
-      cast_array_output func;
-
-   | TBlock expr_list ->
-      if (retval) then
-         gen_local_block_call()
-      else begin
-         writer#begin_block;
-         dump_src_pos();
-         (* Save old values, and equalize for new input ... *)
-         let pop_names = push_anon_names ctx in
-         let remaining = ref (List.length expr_list) in
-         List.iter (fun expression ->
-            let want_value = (return_from_block && !remaining = 1) in
-            find_local_functions_and_return_blocks_ctx want_value expression;
-            if (ctx.ctx_debug_level>0) then
-               output_i ("HX_STACK_LINE(" ^ (string_of_int (Lexer.get_error_line expression.epos)) ^ ")\n" );
-            output_i "";
-            ctx.ctx_return_from_internal_node <- return_from_internal_node;
-            if (want_value) then output "return ";
-            gen_expression want_value expression;
-            decr remaining;
-            writer#terminate_line
-            ) expr_list;
-         writer#end_block;
-         pop_names()
-      end
-   | TTypeExpr type_expr ->
-      let klass = "::" ^ (join_class_path_remap (t_path type_expr) "::" ) in
-      let klass1 = if klass="::Array" then "Array<int>" else klass in
-      output ("hx::ClassOf< " ^ klass1 ^ " >()")
-   | TReturn _ when retval ->
-      unsupported expression.epos
-   | TReturn optional_expr ->
-      output "";
-      ( match optional_expr with
-      | Some return_expression when ( (type_string expression.etype)="Void") ->
-         output "return null(";
-         gen_expression true return_expression;
-         output ")";
-      | Some return_expression ->
-         output "return ";
-         gen_expression true return_expression
-      | _ -> output (if ctx.ctx_real_void then "return" else "return null()")
-      )
-
-   | TConst const ->
-      (match const with
-      | TInt i when  ctx.ctx_for_extern -> output (Printf.sprintf "%ld" i)
-      | TInt i -> output (Printf.sprintf "(int)%ld"  i)
-      | TFloat float_as_string -> output ("((Float)" ^ float_as_string ^")")
-      | TString s when ctx.ctx_for_extern -> output ("\"" ^ (escape_extern s) ^ "\"")
-      | TString s -> output (str s)
-      | TBool b -> output (if b then "true" else "false")
-      (*| TNull -> output ("((" ^ (type_string expression.etype) ^ ")null())")*)
-      | TNull -> output (if ctx.ctx_for_extern then "null" else "null()")
-      | TThis -> output (if ctx.ctx_real_this_ptr then "hx::ObjectPtr<OBJ_>(this)" else "__this")
-      | TSuper when calling ->
-         output (if ctx.ctx_real_this_ptr then
-               "super::__construct"
-            else
-               ("__this->" ^ ctx.ctx_class_super_name ^ "::__construct") )
-      | TSuper -> output ("hx::ObjectPtr<super>(" ^ (if ctx.ctx_real_this_ptr then "this" else "__this.mPtr") ^ ")")
-      )
-
-
-   | TLocal v -> output (keyword_remap v.v_name);
-   | TArray (array_expr,_) when (is_null array_expr) -> output "Dynamic()"
-   | TArray (array_expr,index) ->
-      let dynamic =  is_dynamic_in_cpp ctx array_expr || (type_string array_expr.etype) = "cpp::ArrayBase" in
-      if ( assigning && (not dynamic) ) then begin
-         if (is_array_implementer array_expr.etype) then begin
-            output "hx::__ArrayImplRef(";
-            gen_expression true array_expr;
-            output ",";
-            gen_expression true index;
-            output ")";
-         end else begin
-            gen_expression true array_expr;
-            output "[";
-            gen_expression true index;
-            output "]";
-         end
-      end else if (assigning) then begin
-         (* output (" /*" ^ (type_string array_expr.etype) ^ " */ "); *)
-         output "hx::IndexRef((";
-         gen_expression true array_expr;
-         output ").mPtr,";
-         gen_expression true index;
-         output ")";
-      end else if ( dynamic ) then begin
-         gen_expression true array_expr;
-         output "->__GetItem(";
-         gen_expression true index;
-         output ")";
-      end else begin
-         gen_expression true array_expr;
-         output "->__get(";
-         gen_expression true index;
-         output ")";
-         if not (is_pointer array_expr.etype true) then
-            check_array_element_cast array_expr.etype ".StaticCast" "()";
-      end
-   (* Get precidence matching haxe ? *)
-   | TBinop (op,expr1,expr2) -> gen_bin_op op expr1 expr2
-   | TField (expr,_) | TEnumParameter (expr,_,_) when (is_null expr) ->
-         output "hx::Throw(HX_CSTRING(\"Invalid field access on null object\"))"
-   | TEnumParameter (expr,ef,i) ->
-      let enum = match follow ef.ef_type with
-         | TEnum(en,_) | TFun(_,TEnum(en,_)) -> en
-         | _ -> assert false
-      in
-      output (  "(::" ^ (join_class_path_remap enum.e_path "::") ^ "(");
-      gen_expression true expr;
-      output ( "))->__Param(" ^ (string_of_int i) ^ ")")
-   | TField (field_object,field) ->
-      gen_tfield field_object field
-
-   | TParenthesis expr when not retval ->
-         gen_expression retval expr;
-   | TParenthesis expr -> output "("; gen_expression retval expr; output ")"
-   | TMeta (_,expr) -> gen_expression retval expr;
-   | TObjectDecl (
-      ("fileName" , { eexpr = (TConst (TString file)) }) ::
-         ("lineNumber" , { eexpr = (TConst (TInt line)) }) ::
-            ("className" , { eexpr = (TConst (TString class_name)) }) ::
-               ("methodName", { eexpr = (TConst (TString meth)) }) :: [] ) ->
-      output ("hx::SourceInfo(" ^ (str file) ^ "," ^ (Printf.sprintf "%ld" line) ^ "," ^
-         (str class_name) ^ "," ^ (str meth) ^ ")" )
-   | TObjectDecl decl_list -> gen_local_block_call()
-   | TArrayDecl decl_list ->
-      (* gen_type output expression.etype; *)
-      let tstr = (type_string_suff "_obj" expression.etype true) in
-      if tstr="Dynamic" then
-         output "Dynamic( Array_obj<Dynamic>::__new()"
-      else
-         output ( (type_string_suff "_obj" expression.etype true) ^ "::__new()");
-      List.iter ( fun elem -> output ".Add(";
-                     gen_expression true elem;
-                     output ")" ) decl_list;
-      if tstr="Dynamic" then output ")";
-   | TNew (klass,params,expressions) ->
-      let is_param_array = match klass.cl_path with
-      | ([],"Array") when is_dynamic_array_param (List.hd params) -> true | _ -> false
-      in
-      if is_param_array then
-            output "Dynamic( Array_obj<Dynamic>::__new() )"
-      else begin
-         if (klass.cl_path = ([],"String")) then
-            output "::String("
-         else
-            output ( ( class_string klass "_obj" params true) ^ "::__new(" );
-         gen_expression_list expressions;
-         output ")"
-      end
-   | TUnop (Ast.NegBits,Ast.Prefix,expr) ->
-      output "~(int)(";
-      gen_expression true expr;
-      output ")"
-   | TUnop (op,Ast.Prefix,expr) ->
-      ctx.ctx_assigning <- (match op with Ast.Increment | Ast.Decrement -> true | _ ->false);
-      output (Ast.s_unop op);
-      output "(";
-      gen_expression true expr;
-      output ")"
-   | TUnop (op,Ast.Postfix,expr) ->
-      ctx.ctx_assigning <- true;
-      output "(";
-      gen_expression true expr;
-      output ")";
-      output (Ast.s_unop op)
-   | TFunction func ->
-      let func_name = use_anon_function_name ctx in
-      (
-      try
-         output ( " Dynamic(new " ^ func_name ^ "(" ^
-            (Hashtbl.find ctx.ctx_local_function_args func_name) ^ "))" )
-      with Not_found ->
-         (*error ("function " ^ func_name ^ " not found.") expression.epos; *)
-         output ("function " ^ func_name ^ " not found.");
-      )
-
-   | TVar (tvar,optional_init) ->
-      let count = ref 1 in (* TODO: this section can be simplified *)
-      if (retval && !count==1) then
-         (match optional_init with
-         | None -> output "null()"
-         | Some expression -> gen_expression true expression )
-      else begin
-      let type_name = (type_string tvar.v_type) in
-         output (if type_name="Void" then "Dynamic" else type_name );
-         let name = (keyword_remap tvar.v_name) in
-         output (" " ^ name );
-         (match optional_init with
-         | None -> ()
-         | Some expression -> output " = "; gen_expression true expression);
-         count := !count -1;
-      if (ctx.ctx_debug_level>0) then
-            output (";\t\tHX_STACK_VAR(" ^name ^",\""^ tvar.v_name ^"\")");
-         if (!count > 0) then begin output ";\n"; output_i "" end
-      end
-   | TFor (tvar, init, loop) ->
-      output ("for(::cpp::FastIterator_obj< " ^  (type_string tvar.v_type) ^
-            " > *__it = ::cpp::CreateFastIterator< "^(type_string tvar.v_type) ^ " >(");
-      gen_expression true init;
-      output (");  __it->hasNext(); )");
-      ctx.ctx_writer#begin_block;
-      output_i ( (type_string tvar.v_type) ^ " " ^ (keyword_remap tvar.v_name) ^ " = __it->next();\n" );
-      output_i "";
-      gen_expression false loop;
-      output ";\n";
-      ctx.ctx_writer#end_block;
-   | TIf (condition, if_expr, optional_else_expr)  ->
-      (match optional_else_expr with
-      | Some else_expr ->
-         if (retval) then begin
-            output "(  (";
-            gen_expression true condition;
-            output ") ? ";
-            let type_str = match (type_string expression.etype) with
-            | "Void" -> "Dynamic"
-            | other -> other
-            in
-            output (type_str ^ "(");
-            gen_expression true if_expr;
-            output ") : ";
-
-            output (type_str ^ "(");
-            gen_expression true else_expr;
-            output ") )";
-         end else begin
-            output "if (";
-            gen_expression true condition;
-            output ")";
-            gen_expression false (to_block if_expr);
-            output_i "else";
-            gen_expression false (to_block else_expr);
-         end
-      | _ -> output "if (";
-         gen_expression true condition;
-         output ")";
-         gen_expression false (to_block if_expr);
-      )
-   | TWhile (condition, repeat, Ast.NormalWhile ) ->
-         output  "while(";
-         gen_expression true condition;
-         output ")";
-         gen_expression false (to_block repeat)
-   | TWhile (condition, repeat, Ast.DoWhile ) ->
-         output "do";
-         gen_expression false (to_block repeat);
-         output "while(";
-         gen_expression true condition;
-         output ")"
-
-   (* These have already been defined in find_local_return_blocks ... *)
-   | TTry (_,_)
-   | TSwitch (_,_,_) when (retval && (not return_from_internal_node) ) ->
-      gen_local_block_call()
-   | TSwitch (condition,cases,optional_default)  ->
-      let switch_on_int_constants = (only_int_cases cases) && (not (contains_break expression)) in
-      if (switch_on_int_constants) then begin
-         output "switch( (int)";
-         gen_expression true condition;
-         output ")";
-         ctx.ctx_writer#begin_block;
-         List.iter (fun (cases_list,expression) ->
-            output_i "";
-            List.iter (fun value -> output "case ";
-                        gen_expression true value;
-                        output ": " ) cases_list;
-            ctx.ctx_return_from_block <- return_from_internal_node;
-            gen_expression false (to_block expression);
-            output_i ";break;\n";
-            ) cases;
-         (match optional_default with | None -> ()
-         | Some default ->
-            output_i "default: ";
-            ctx.ctx_return_from_block <- return_from_internal_node;
-            gen_expression false (to_block default);
-         );
-         ctx.ctx_writer#end_block;
-      end else begin
-         let tmp_name = get_switch_var ctx in
-         output ( (type_string condition.etype) ^ " " ^ tmp_name ^ " = " );
-         gen_expression true condition;
-         output ";\n";
-         let else_str = ref "" in
-         if (List.length cases > 0) then
-            List.iter (fun (cases,expression) ->
-               output_i ( !else_str ^ "if ( ");
-               else_str := "else ";
-               let or_str = ref "" in
-               List.iter (fun value ->
-                  output (!or_str ^ " ( " ^ tmp_name ^ "==");
-                  gen_expression true value;
-                  output ")";
-                  or_str := " || ";
-                  ) cases;
-               output (")");
-               ctx.ctx_return_from_block <- return_from_internal_node;
-               gen_expression false (to_block expression);
-               ) cases;
-         (match optional_default with | None -> ()
-         | Some default ->
-            output_i ( !else_str ^ " ");
-            ctx.ctx_return_from_block <- return_from_internal_node;
-            gen_expression false (to_block default);
-            output ";\n";
-         );
-      end
-   | TTry (expression, catch_list) ->
-      output "try\n";
-      output_i "{\n";
-      let counter = ref 0 in
-      List.iter (fun (v, e) ->
-         let type_name = type_string v.v_type in
-            output_i ("HX_STACK_CATCHABLE(" ^ type_name ^ ", " ^ string_of_int !counter ^ ");\n");
-               counter := !counter + 1;)
-            catch_list;
-      output_i("");
-      (* Move this "inside" the try call ... *)
-      ctx.ctx_return_from_block <-return_from_internal_node;
-      gen_expression false (to_block expression);
-      output_i "}\n";
-      if (List.length catch_list > 0 ) then begin
-         output_i "catch(Dynamic __e)";
-         ctx.ctx_writer#begin_block;
-         let seen_dynamic = ref false in
-         let else_str = ref "" in
-         List.iter (fun (v,expression) ->
-            let type_name = type_string v.v_type in
-            if (type_name="Dynamic") then begin
-               seen_dynamic := true;
-               output_i !else_str;
-            end else
-               output_i (!else_str ^ "if (__e.IsClass< " ^ type_name ^ " >() )");
-            ctx.ctx_writer#begin_block;
-            output_i "HX_STACK_BEGIN_CATCH\n";
-            output_i (type_name ^ " " ^ v.v_name ^ " = __e;");
-            (* Move this "inside" the catch call too ... *)
-            ctx.ctx_return_from_block <-return_from_internal_node;
-            gen_expression false (to_block expression);
-            ctx.ctx_writer#end_block;
-            else_str := "else ";
-            ) catch_list;
-         if (not !seen_dynamic) then begin
-            output_i "else {\n";
-               output_i "    HX_STACK_DO_THROW(__e);\n";
-               output_i "}\n";
-         end;
-         ctx.ctx_writer#end_block;
-      end;
-   | TBreak -> output "break"
-   | TContinue -> output "continue"
-   | TThrow expression ->
-         output "HX_STACK_DO_THROW(";
-         gen_expression true expression;
-         output ")";
-   | TCast (cast,None) when (not retval) || (type_string expression.etype) = "Void" ->
-      gen_expression retval cast;
-   | TCast (cast,None) ->
-      let ret_type = type_string expression.etype in
-      let from_type = if is_dynamic_in_cpp ctx cast then "Dynamic" else type_string cast.etype in
-      if (from_type = ret_type) then begin
-         gen_expression true cast
-      end else begin
-         output ("((" ^ ret_type ^ ")(");
-         gen_expression true cast;
-         output "))";
-      end;
-   | TCast (e1,Some t) ->
-      let class_name = (join_class_path_remap (t_path t) "::" ) in
-      if (class_name="Array") then
-         output ("hx::TCastToArray(" )
-      else
-         output ("hx::TCast< ::" ^ class_name ^ " >::cast(" );
-      gen_expression true e1;
-      output ")";
- in
-
- if (set_var<>"") then begin
-    find_local_functions_and_return_blocks_ctx true expression_tree;
-    output set_var;
- end;
- gen_expression retval expression_tree;
- output tail_code
-;;
-
-
-
-(*
-let is_dynamic_haxe_method f =
-   match follow f.cf_type with
-   | TFun _ when f.cf_expr = None -> true
-   | _ ->
-      (match f.cf_expr with
-      | Some { eexpr = TFunction fd } when f.cf_set = MethodAccess true -> true
-      | Some { eexpr = TFunction fd } when f.cf_set = NormalAccess -> true
-      | _ -> false);;
-*)
-
-let is_dynamic_haxe_method f =
-      (match f.cf_expr, f.cf_kind with
-      | Some { eexpr = TFunction _ }, (Var _ | Method MethDynamic) -> true
-      | _ -> false);;
-
-
-let is_data_member field =
-   match field.cf_expr with
-   | Some { eexpr = TFunction function_def } -> is_dynamic_haxe_method field
-   | _ -> true;;
-
-
-let is_override class_def field =
-   List.exists (fun f -> f.cf_name = field) class_def.cl_overrides
-;;
-
-let rec all_virtual_functions clazz =
-   (List.fold_left (fun result elem -> match follow elem.cf_type, elem.cf_kind  with
-      | _, Method MethDynamic -> result
-      | TFun (args,return_type), Method _  when not (is_override clazz elem.cf_name ) -> (elem,args,return_type) :: result
-      | _,_ -> result ) [] clazz.cl_ordered_fields)
-   @ (match clazz.cl_super with
-   | Some def -> all_virtual_functions (fst def)
-   | _ -> [] )
-;;
-
-let reflective class_def field = not (
-    (Meta.has Meta.NativeGen class_def.cl_meta) ||
-    (Meta.has Meta.Unreflective class_def.cl_meta) ||
-    (Meta.has Meta.Unreflective field.cf_meta) ||
-    (match field.cf_type with
-       | TInst (klass,_) ->  Meta.has Meta.Unreflective klass.cl_meta
-       | _ -> false
-    )
-)
-;;
-
-
-
-
-
-let field_arg_count field =
-   match follow field.cf_type, field.cf_kind  with
-      | _, Method MethDynamic -> -1
-      | TFun (args,return_type), Method _  -> List.length args
-      | _,_ -> -1
-;;
-
-
-            (* external mem  Dynamic & *)
-
-let gen_field ctx class_def class_name ptr_name dot_name is_static is_interface field =
-   let output = ctx.ctx_output in
-   ctx.ctx_real_this_ptr <- not is_static;
-   let remap_name = keyword_remap field.cf_name in
-   let decl = get_meta_string field.cf_meta Meta.Decl in
-   let has_decl = decl <> "" in
-   let nativeGen = has_meta_key class_def.cl_meta Meta.NativeGen in
-   if (is_interface) then begin
-      (* Just the dynamic glue  - not even that ... *)
-      ()
-   end else (match  field.cf_expr with
-   (* Function field *)
-   | Some { eexpr = TFunction function_def } ->
-      let return_type = (type_string function_def.tf_type) in
-      let nargs = string_of_int (List.length function_def.tf_args) in
-      let is_void = (type_string function_def.tf_type ) = "Void" in
-      let ret = if is_void  then "(void)" else "return " in
-      let output_i = ctx.ctx_writer#write_i in
-      let orig_debug = ctx.ctx_debug_level in
-      let dump_src = if ((Meta.has Meta.NoStack field.cf_meta)||(Meta.has Meta.NoDebug field.cf_meta) || orig_debug<1 || nativeGen) then begin
-         ctx.ctx_debug_level <- 0;
-         (fun()->())
-      end else begin
-         (fun() ->
-         hx_stack_push ctx output_i dot_name field.cf_name function_def.tf_expr.epos;
-         if (not is_static) then output_i ("HX_STACK_THIS(this)\n");
-         List.iter (fun (v,_) -> output_i ("HX_STACK_ARG(" ^ (keyword_remap v.v_name) ^ ",\"" ^ v.v_name ^"\")\n") )
-            function_def.tf_args )
-      end in
-
-      if (not (is_dynamic_haxe_method field)) then begin
-         (* The actual function definition *)
-         let real_void = is_void  && (has_meta_key field.cf_meta Meta.Void) in
-         let fake_void = is_void  && not real_void in
-         output (if real_void then "void" else return_type );
-         output (" " ^ class_name ^ "::" ^ remap_name ^ "( " );
-         output (gen_arg_list function_def.tf_args "__o_");
-         output ")";
-         ctx.ctx_real_this_ptr <- true;
-         ctx.ctx_real_void <- real_void;
-         ctx.ctx_dynamic_this_ptr <- false;
-         let code = (get_code field.cf_meta Meta.FunctionCode) in
-         let tail_code = (get_code field.cf_meta Meta.FunctionTailCode) in
-         if (has_default_values function_def.tf_args) then begin
-            ctx.ctx_writer#begin_block;
-            generate_default_values ctx function_def.tf_args "__o_";
-            dump_src();
-            output code;
-            gen_expression_tree ctx false function_def.tf_expr "" tail_code;
-            if (fake_void) then output "return null();\n";
-            ctx.ctx_writer#end_block;
-         end else begin
-            let add_block = is_void || (code <> "") || (tail_code <> "") in
-            if (add_block) then ctx.ctx_writer#begin_block;
-            ctx.ctx_dump_src_pos <- dump_src;
-            output code;
-            gen_expression_tree ctx false (to_block function_def.tf_expr) "" tail_code;
-            if (add_block) then begin
-               if (fake_void) then output "return null();\n";
-               ctx.ctx_writer#end_block;
-            end;
-         end;
-
-         output "\n\n";
-         let nonVirtual = has_meta_key field.cf_meta Meta.NonVirtual in
-         let doDynamic =  (nonVirtual || not (is_override class_def field.cf_name ) ) && (reflective class_def field ) in
-         (* generate dynamic version too ... *)
-         if ( doDynamic ) then begin
-            if (is_static) then output "STATIC_";
-            output ("HX_DEFINE_DYNAMIC_FUNC" ^ nargs ^ "(" ^ class_name ^ "," ^
-                     remap_name ^ "," ^ ret ^ ")\n\n");
-         end;
-
-      end else begin
-         ctx.ctx_real_this_ptr <- false;
-         ctx.ctx_dynamic_this_ptr <- false;
-         let func_name = "__default_" ^ (remap_name) in
-         output ("HX_BEGIN_DEFAULT_FUNC(" ^ func_name ^ "," ^ class_name ^ ")\n");
-         output return_type;
-         output (" run(" ^ (gen_arg_list function_def.tf_args "__o_") ^ ")");
-         ctx.ctx_dump_src_pos <- dump_src;
-         if (is_void) then begin
-            ctx.ctx_writer#begin_block;
-            generate_default_values ctx function_def.tf_args "__o_";
-            gen_expression_tree ctx false function_def.tf_expr "" "";
-            output "return null();\n";
-            ctx.ctx_writer#end_block;
-         end else if (has_default_values function_def.tf_args) then begin
-            ctx.ctx_writer#begin_block;
-            generate_default_values ctx function_def.tf_args "__o_";
-            gen_expression_tree ctx false function_def.tf_expr "" "";
-            ctx.ctx_writer#end_block;
-         end else
-            gen_expression_tree ctx false (to_block function_def.tf_expr) "" "";
-
-         output ("HX_END_LOCAL_FUNC" ^ nargs ^ "(" ^ ret ^ ")\n");
-         output ("HX_END_DEFAULT_FUNC\n\n");
-
-         if (is_static) then
-            output ( "Dynamic " ^ class_name ^ "::" ^ remap_name ^ ";\n\n");
-      end;
-      ctx.ctx_debug_level <- orig_debug
-
-   (* Data field *)
-   | _ when has_decl ->
-      if is_static then begin
-         output ( class_name ^ "::" ^ remap_name ^ "_decl ");
-         output ( " " ^ class_name ^ "::" ^ remap_name ^ ";\n\n");
-      end
-   | _ ->
-      if is_static && (not (is_extern_field field)) then begin
-         gen_type ctx field.cf_type;
-         output ( " " ^ class_name ^ "::" ^ remap_name ^ ";\n\n");
-      end
-   )
-   ;;
-
-
-
-
-
-let gen_field_init ctx field =
-   let output = ctx.ctx_output in
-   let remap_name = keyword_remap field.cf_name in
-   (match  field.cf_expr with
-   (* Function field *)
-   | Some { eexpr = TFunction function_def } ->
-
-      if (is_dynamic_haxe_method field) then begin
-         let func_name = "__default_" ^ (remap_name) in
-         output ( "\t" ^ remap_name ^ " = new " ^ func_name ^ ";\n\n" );
-      end
-
-   (* Data field *)
-   | _ -> (match field.cf_expr with
-      | Some expr ->
-         let var_name = ( match remap_name with
-                  | "__meta__" -> "\t__mClass->__meta__="
-                  | "__rtti" -> "\t__mClass->__rtti__="
-                  | _ -> "\t" ^ remap_name ^ "= ") in
-         gen_expression_tree ctx true expr var_name ";\n";
-      | _ -> ( )
-      );
-   )
-   ;;
-
-
-
-let has_field_init field =
-   match field.cf_expr with
-   (* Function field *)
-   | Some { eexpr = TFunction function_def } -> is_dynamic_haxe_method field
-   (* Data field *)
-   | Some _ -> true
-   | _ -> false
-;;
-
-
-let gen_member_def ctx class_def is_static is_interface field =
-   let output = ctx.ctx_output in
-   let remap_name = keyword_remap field.cf_name in
-   let nativeGen = has_meta_key class_def.cl_meta Meta.NativeGen in
-
-   if (is_interface) then begin
-      match follow field.cf_type, field.cf_kind with
-      | _, Method MethDynamic  -> ()
-      | TFun (args,return_type), Method _  ->
-         output ( (if (not is_static) then "		virtual " else "		" ) ^ type_string return_type);
-         output (" " ^ remap_name ^ "( " );
-         output (gen_tfun_interface_arg_list args);
-         output (if (not is_static) then ")=0;\n" else ");\n");
-         if reflective class_def field then begin
-            output ("virtual Dynamic " ^ remap_name ^ "_dyn()=0;\n" );
-         end
-      | _  ->  ( )
-   end else begin
-   let decl = get_meta_string field.cf_meta Meta.Decl in
-   let has_decl = decl <> "" in
-   if (has_decl) then
-      output ( "      typedef " ^ decl ^ ";\n" );
-   output (if is_static then "\t\tstatic " else "\t\t");
-   (match  field.cf_expr with
-   | Some { eexpr = TFunction function_def } ->
-      let nonVirtual = has_meta_key field.cf_meta Meta.NonVirtual in
-      let doDynamic =  (nonVirtual || not (is_override class_def field.cf_name ) ) && (reflective class_def field ) in
-      if ( is_dynamic_haxe_method field ) then begin
-         if ( doDynamic ) then begin
-            output ("Dynamic " ^ remap_name ^ ";\n");
-            output (if is_static then "\t\tstatic " else "\t\t");
-            output ("inline Dynamic &" ^ remap_name ^ "_dyn() " ^ "{return " ^ remap_name^ "; }\n")
-         end
-      end else begin
-         let return_type = (type_string function_def.tf_type) in
-
-         if ( not is_static && not nonVirtual ) then output "virtual ";
-         output (if return_type="Void" && (has_meta_key field.cf_meta Meta.Void) then "void" else return_type );
-
-         output (" " ^ remap_name ^ "( " );
-         output (gen_arg_list function_def.tf_args "" );
-         output ");\n";
-         if ( doDynamic ) then begin
-            output (if is_static then "\t\tstatic " else "\t\t");
-            output ("Dynamic " ^ remap_name ^ "_dyn();\n" )
-         end;
-      end;
-      output "\n";
-   | _ when has_decl ->
-      output ( remap_name ^ "_decl " ^ remap_name ^ ";\n" );
-      (* Variable access *)
-   | _ ->
-      (* Variable access *)
-      gen_type ctx field.cf_type;
-      output (" " ^ remap_name ^ ";\n" );
-
-      (* Add a "dyn" function for variable to unify variable/function access *)
-      (match follow field.cf_type with
-      | _ when nativeGen  -> ()
-      | TFun (_,_) ->
-         output (if is_static then "\t\tstatic " else "\t\t");
-         gen_type ctx field.cf_type;
-         output (" &" ^ remap_name ^ "_dyn() { return " ^ remap_name ^ ";}\n" )
-      | _ ->  (match field.cf_kind with
-         | Var { v_read = AccCall } when (not is_static) && (is_dynamic_accessor ("get_" ^ field.cf_name) "get" field class_def) ->
-            output ("\t\tDynamic get_" ^ field.cf_name ^ ";\n" )
-         | _ -> ()
-         );
-         (match field.cf_kind with
-         | Var { v_write = AccCall } when (not is_static) &&  (is_dynamic_accessor ("set_" ^ field.cf_name) "set" field class_def) ->
-            output ("\t\tDynamic set_" ^ field.cf_name ^ ";\n" )
-         | _ -> ()
-         )
-      )
-   );
-   end
-   ;;
-
-let path_of_string path =
-   ["@verbatim"], path
-;;
-
-
-(*
-   Get a list of all classes referred to by the class/enum definition
-   These are used for "#include"ing the appropriate header files,
-   or for building the dependencies in the Build.xml file
-*)
-let find_referenced_types ctx obj super_deps constructor_deps header_only for_depends include_super_args =
-   let types = ref PMap.empty in
-   let rec add_type in_path =
-      if ( not (PMap.mem in_path !types)) then begin
-         types := (PMap.add in_path () !types);
-         try
-            List.iter add_type (Hashtbl.find super_deps in_path);
-         with Not_found -> ()
-      end
-   in
-   let add_extern_class klass =
-      let include_file = get_meta_string_path klass.cl_meta (if for_depends then Meta.Depend else Meta.Include) in
-      if (include_file<>"") then
-         add_type ( path_of_string include_file )
-      else if (not for_depends) && (has_meta_key klass.cl_meta Meta.Include) then
-         add_type klass.cl_path
-   in
-   let add_native_gen_class klass =
-      let include_file = get_meta_string_path klass.cl_meta (if for_depends then Meta.Depend else Meta.Include) in
-      if (include_file<>"") then
-         add_type ( path_of_string include_file )
-      else if for_depends then
-         add_type klass.cl_path
-      else
-         add_type ( path_of_string ( (join_class_path klass.cl_path "/") ^ ".h") )
-   in
-   let visited = ref [] in
-   let rec visit_type in_type =
-      if not (List.exists (fun t2 -> Type.fast_eq in_type t2) !visited) then begin
-         visited := in_type :: !visited;
-         begin match follow in_type with
-         | TMono r -> (match !r with None -> () | Some t -> visit_type t)
-         | TEnum (enum,params) -> add_type enum.e_path
-         (* If a class has a template parameter, then we treat it as dynamic - except
-            for the Array, Class, FastIterator or Pointer classes, for which we do a fully typed object *)
-         | TInst (klass,params) ->
-            (match klass.cl_path with
-            | ([],"Array") | ([],"Class") | (["cpp"],"FastIterator")
-            | (["cpp"],"Pointer") | (["cpp"],"ConstPointer") | (["cpp"],"Function")
-            | (["cpp"],"RawPointer") | (["cpp"],"RawConstPointer") -> List.iter visit_type params
-            | _ when is_native_gen_class klass -> add_native_gen_class klass
-            | _ when is_extern_class klass -> add_extern_class klass
-            | _ -> (match klass.cl_kind with KTypeParameter _ -> () | _ -> add_type klass.cl_path);
-            )
-         | TFun (args,haxe_type) -> visit_type haxe_type;
-            List.iter (fun (_,_,t) -> visit_type t; ) args;
-         | _ -> ()
-         end;
-         visited := List.tl !visited;
-      end
-   in
-   let rec visit_params expression =
-      begin
-      let rec visit_expression = fun expression ->
-         (* Expand out TTypeExpr (ie, the name of a class, as used for static access etc ... *)
-         (match expression.eexpr with
-            | TTypeExpr type_def -> ( match type_def with
-               | TClassDecl class_def when is_native_gen_class class_def -> add_native_gen_class class_def
-               | TClassDecl class_def when is_extern_class class_def -> add_extern_class class_def
-               | _ -> add_type (t_path type_def)
-               )
-
-            (* Must visit the types, Type.iter will visit the expressions ... *)
-            | TTry (e,catches) ->
-               List.iter (fun (v,_) -> visit_type v.v_type) catches
-            (* Must visit the enum param types, Type.iter will visit the rest ... *)
-(*             | TMatch (_,enum,cases,_) ->
-               add_type (fst enum).e_path;
-               List.iter (fun (case_ids,params,expression) ->
-                  (match params with
-                  | None -> ()
-                  | Some l -> List.iter (function None -> () | Some v -> visit_type v.v_type) l  ) ) cases; *)
-            (* Must visit type too, Type.iter will visit the expressions ... *)
-            | TNew  (klass,params,_) -> begin
-               visit_type (TInst (klass,params));
-               try
-               let construct_type = Hashtbl.find constructor_deps klass.cl_path in
-                  visit_type construct_type.cf_type
-               with Not_found -> ();
-               end
-            (* Must visit type too, Type.iter will visit the expressions ... *)
-            | TVar (v,_) ->
-               visit_type v.v_type
-            (* Must visit enum type too, Type.iter will visit the expressions ... *)
-            | TEnumParameter (_,ef,_) -> visit_type (follow ef.ef_type)
-            (* Must visit args too, Type.iter will visit the expressions ... *)
-            | TFunction func_def ->
-               List.iter (fun (v,_) -> visit_type v.v_type) func_def.tf_args;
-            | TConst TSuper ->
-               (match follow expression.etype with
-               | TInst (klass,params) ->
-                  (try let construct_type = Hashtbl.find constructor_deps klass.cl_path in
-                     visit_type construct_type.cf_type
-                  with Not_found -> () )
-               | _ -> print_endline ("TSuper : Odd etype ?" ^ ( (type_string expression.etype)) )
-               )
-            | _ -> ()
-         );
-         Type.iter visit_expression expression;
-         visit_type (follow expression.etype)
-      in
-      visit_expression expression
-      end
-   in
-   let visit_field field =
-      (* Add the type of the expression ... *)
-      visit_type field.cf_type;
-      if (not header_only) then
-         (match field.cf_expr with
-         | Some expression -> visit_params expression | _ -> ());
-   in
-   let visit_class class_def =
-      let fields = List.append class_def.cl_ordered_fields class_def.cl_ordered_statics in
-      let fields_and_constructor = List.append fields
-         (match class_def.cl_constructor with | Some expr -> [expr] | _ -> [] ) in
-      List.iter visit_field fields_and_constructor;
-      if (include_super_args) then
-         List.iter visit_field (List.map (fun (a,_,_) -> a ) (all_virtual_functions class_def ));
-
-      (* Add super & interfaces *)
-      if is_native_gen_class class_def then
-         add_native_gen_class class_def
-      else
-         add_type class_def.cl_path;
-   in
-   let visit_enum enum_def =
-      add_type enum_def.e_path;
-      PMap.iter (fun _ constructor ->
-         (match constructor.ef_type with
-         | TFun (args,_) ->
-            List.iter (fun (_,_,t) -> visit_type t; ) args;
-         | _ -> () );
-         ) enum_def.e_constrs;
-      if (not header_only) then begin
-         let meta = Codegen.build_metadata ctx (TEnumDecl enum_def) in
-         match meta with Some expr -> visit_params expr | _ -> ();
-      end;
-   in
-   let inc_cmp i1 i2 =
-      String.compare (join_class_path i1 ".") (join_class_path i2 ".")
-   in
-
-   (* Body of main function *)
-   (match obj with
-   | TClassDecl class_def -> visit_class class_def;
-      (match class_def.cl_init with Some expression -> visit_params expression | _ -> ())
-   | TEnumDecl enum_def -> visit_enum enum_def
-   | TTypeDecl _ | TAbstractDecl _ -> (* These are expanded *) ());
-
-   List.sort inc_cmp (List.filter (fun path -> (include_class_header path) ) (pmap_keys !types))
-   ;;
-
-
-let generate_main_header output_main =
-   output_main "#include <hxcpp.h>\n\n";
-   output_main "#include <stdio.h>\n\n";
-   output_main "extern \"C\" void __hxcpp_main();\n\n";
-   output_main "extern \"C\" void __hxcpp_lib_main();\n\n"
-;;
-
-let generate_main_footer1 output_main =
-   output_main "void __hxcpp_main() {\n";;
-
-let generate_main_footer2 output_main =
-   output_main "	}\n\n";
-   output_main "void __hxcpp_lib_main() {\n";
-   output_main "	HX_TOP_OF_STACK\n";
-   output_main "	hx::Boot();\n";
-   output_main "	__boot_all();\n";
-   output_main "	__hxcpp_main();\n";
-   output_main "	}\n"
-;;
-
-
-let generate_main common_ctx member_types super_deps class_def file_info =
-   (* main routine should be a single static function *)
-   let main_expression =
-      (match class_def.cl_ordered_statics with
-      | [{ cf_expr = Some expression }] -> expression;
-      | _ -> assert false ) in
-   ignore(find_referenced_types common_ctx (TClassDecl class_def) super_deps (Hashtbl.create 0) false false false);
-   let depend_referenced = find_referenced_types common_ctx (TClassDecl class_def) super_deps (Hashtbl.create 0) false true false in
-   let generate_startup filename is_main =
-      (*make_class_directories base_dir ( "src" :: []);*)
-      let cpp_file = new_cpp_file common_ctx common_ctx.file ([],filename) in
-      let output_main = (cpp_file#write) in
-
-      generate_main_header output_main;
-
-      List.iter ( add_include cpp_file ) depend_referenced;
-      output_main "\n\n";
-
-      if is_main then output_main "\n#include <hx/HxcppMain.h>\n\n";
-
-      generate_main_footer1 output_main;
-      gen_expression_tree (new_context common_ctx cpp_file 1 file_info) false main_expression "" ";\n";
-      generate_main_footer2 output_main;
-      cpp_file#close;
-   in
-   generate_startup "__main__" true;
-   generate_startup "__lib__" false
-   ;;
-
-let generate_dummy_main common_ctx =
-   let generate_startup filename is_main =
-      let main_file = new_cpp_file common_ctx common_ctx.file ([],filename) in
-      let output_main = (main_file#write) in
-      generate_main_header output_main;
-      if is_main then output_main "\n#include <hx/HxcppMain.h>\n\n";
-      generate_main_footer1 output_main;
-      generate_main_footer2 output_main;
-      main_file#close;
-   in
-   generate_startup "__main__" true;
-   generate_startup "__lib__" false
-   ;;
-
-let generate_boot common_ctx boot_enums boot_classes nonboot_classes init_classes =
-   (* Write boot class too ... *)
-   let base_dir = common_ctx.file in
-   let boot_file = new_cpp_file common_ctx base_dir ([],"__boot__") in
-   let output_boot = (boot_file#write) in
-   output_boot "#include <hxcpp.h>\n\n";
-   List.iter ( fun class_path -> boot_file#add_include class_path )
-      (boot_enums @ boot_classes @ nonboot_classes);
-
-   output_boot "\nvoid __files__boot();\n";
-   output_boot "\nvoid __boot_all()\n{\n";
-   output_boot "__files__boot();\n";
-   output_boot "hx::RegisterResources( hx::GetResources() );\n";
-   List.iter ( fun class_path ->
-      output_boot ("::" ^ ( join_class_path_remap class_path "::" ) ^ "_obj::__register();\n") )
-         (boot_enums @ boot_classes @ nonboot_classes);
-
-   let dump_boot =
-      List.iter ( fun class_path ->
-         output_boot ("::" ^ ( join_class_path_remap class_path "::" ) ^ "_obj::__boot();\n") ) in
-
-   dump_boot boot_enums;
-
-   List.iter ( fun class_path ->
-      output_boot ("::" ^ ( join_class_path_remap class_path "::" ) ^ "_obj::__init__();\n") ) (List.rev init_classes);
-
-   dump_boot (List.filter  (fun path -> is_cpp_class path )  (List.rev boot_classes));
-   dump_boot (List.filter  (fun path -> not (is_cpp_class path) )  (List.rev boot_classes));
-
-   output_boot "}\n\n";
-   boot_file#close;;
-
-
-let generate_files common_ctx file_info =
-   (* Write __files__ class too ... *)
-   let base_dir = common_ctx.file in
-   let files_file = new_cpp_file common_ctx base_dir ([],"__files__") in
-   let output_files = (files_file#write) in
-   let types = common_ctx.types in
-   output_files "#include <hxcpp.h>\n\n";
-   output_files "namespace hx {\n";
-   output_files "const char *__hxcpp_all_files[] = {\n";
-   output_files "#ifdef HXCPP_DEBUGGER\n";
-   List.iter ( fun file -> output_files ((const_char_star file)^",\n" ) )
-      ( List.sort String.compare ( pmap_keys !file_info) );
-   output_files "#endif\n";
-   output_files " 0 };\n";
-   output_files "\n";
-
-   output_files "const char *__hxcpp_all_files_fullpath[] = {\n";
-   output_files "#ifdef HXCPP_DEBUGGER\n";
-   List.iter ( fun file -> output_files ((const_char_star (
-      Common.get_full_path (try Common.find_file common_ctx file with Not_found -> file)
-      ))^",\n" ) )
-      ( List.sort String.compare ( pmap_keys !file_info) );
-   output_files "#endif\n";
-   output_files " 0 };\n";
-   output_files "\n";
-
-
-   output_files "const char *__hxcpp_all_classes[] = {\n";
-   output_files "#ifdef HXCPP_DEBUGGER\n";
-   List.iter ( fun object_def ->
-   (match object_def with
-      | TClassDecl class_def when is_extern_class class_def -> ( )
-      | TClassDecl class_def when class_def.cl_interface -> ( )
-      | TClassDecl class_def ->
-         output_files ((const_char_star (join_class_path class_def.cl_path "." )) ^ ",\n")
-      | _ -> ( )
-      )
-   ) types;
-   output_files "#endif\n";
-   output_files " 0 };\n";
-
-   output_files "} // namespace hx\n";
-   output_files "void __files__boot() { __hxcpp_set_debugger_info(hx::__hxcpp_all_classes, hx::__hxcpp_all_files_fullpath); }\n";
-
-   files_file#close;;
-
-
-let begin_header_file output_h def_string =
-   output_h ("#ifndef INCLUDED_" ^ def_string ^ "\n");
-   output_h ("#define INCLUDED_" ^ def_string ^ "\n\n");
-   output_h "#ifndef HXCPP_H\n";
-   output_h "#include <hxcpp.h>\n";
-   output_h "#endif\n\n";;
-
-let end_header_file output_h def_string =
-   output_h ("\n#endif /* INCLUDED_" ^ def_string ^ " */ \n");;
-
-let new_placed_cpp_file common_ctx class_path =
-   let base_dir = common_ctx.file in
-
-   if (Common.defined common_ctx Define.Vcproj ) then begin
-      make_class_directories base_dir ("src"::[]);
-      cached_source_writer common_ctx
-         ( base_dir ^ "/src/" ^ ( String.concat "-" (fst class_path) ) ^ "-" ^
-         (snd class_path) ^ (source_file_extension common_ctx) )
-   end else
-      new_cpp_file common_ctx common_ctx.file class_path;;
-
-
-
-let generate_enum_files common_ctx enum_def super_deps meta file_info =
-   let class_path = enum_def.e_path in
-   let just_class_name =  (snd class_path) in
-   let class_name =  just_class_name ^ "_obj" in
-   let remap_class_name =  ("::" ^ (join_class_path_remap class_path "::") )  in
-   (*let cpp_file = new_cpp_file common_ctx.file class_path in*)
-   let cpp_file = new_placed_cpp_file common_ctx class_path in
-   let output_cpp = (cpp_file#write) in
-   let debug = if (has_meta_key enum_def.e_meta Meta.NoDebug) || ( Common.defined  common_ctx Define.NoDebug)
-      then 0 else 1 in
-
-   let ctx = new_context common_ctx cpp_file debug file_info in
-
-   if (debug>1) then
-      print_endline ("Found enum definition:" ^ (join_class_path  class_path "::" ));
-
-   output_cpp "#include <hxcpp.h>\n\n";
-
-   let referenced = find_referenced_types common_ctx (TEnumDecl enum_def) super_deps (Hashtbl.create 0) false false false in
-   List.iter (add_include cpp_file) referenced;
-
-   gen_open_namespace output_cpp class_path;
-   output_cpp "\n";
-
-   PMap.iter (fun _ constructor ->
-      let name = keyword_remap constructor.ef_name in
-      match constructor.ef_type with
-      | TFun (args,_) ->
-         output_cpp (remap_class_name ^ "  " ^ class_name ^ "::" ^ name ^ "(" ^
-            (gen_tfun_arg_list args) ^")\n");
-         output_cpp ("\t{ return hx::CreateEnum< " ^ class_name ^ " >(" ^ (str name) ^ "," ^
-            (string_of_int constructor.ef_index) ^ ",hx::DynamicArray(0," ^
-            (string_of_int (List.length args)) ^  ")" );
-         List.iter (fun (arg,_,_) -> output_cpp (".Add(" ^ (keyword_remap arg) ^ ")")) args;
-         output_cpp "); }\n\n"
-
-      | _ ->
-         output_cpp ( remap_class_name ^ " " ^ class_name ^ "::" ^ name ^ ";\n\n" )
-   ) enum_def.e_constrs;
-
-
-
-
-   output_cpp ("HX_DEFINE_CREATE_ENUM(" ^ class_name ^ ")\n\n");
-   output_cpp ("int " ^ class_name ^ "::__FindIndex(::String inName)\n{\n");
-   PMap.iter (fun _ constructor ->
-      let name = constructor.ef_name in
-      let idx = string_of_int constructor.ef_index in
-      output_cpp ("\tif (inName==" ^ (str name) ^ ") return " ^ idx ^ ";\n") ) enum_def.e_constrs;
-   output_cpp ("\treturn super::__FindIndex(inName);\n");
-   output_cpp ("}\n\n");
-
-   let constructor_arg_count constructor =
-      (match constructor.ef_type with | TFun(args,_) -> List.length args | _ -> 0 )
-   in
-
-
-   (* Dynamic versions of constructors *)
-   let dump_dynamic_constructor _ constr =
-      let count = constructor_arg_count constr in
-      if (count>0) then begin
-         let nargs = string_of_int count in
-         output_cpp ("STATIC_HX_DEFINE_DYNAMIC_FUNC" ^ nargs ^ "(" ^ class_name ^ "," ^
-               (keyword_remap constr.ef_name) ^ ",return)\n\n");
-      end
-   in
-   PMap.iter dump_dynamic_constructor enum_def.e_constrs;
-
-
-   output_cpp ("int " ^ class_name ^ "::__FindArgCount(::String inName)\n{\n");
-   PMap.iter (fun _ constructor ->
-      let name = constructor.ef_name in
-      let count = string_of_int (constructor_arg_count constructor) in
-      output_cpp ("\tif (inName==" ^ (str name) ^ ") return " ^ count ^ ";\n") ) enum_def.e_constrs;
-      output_cpp ("\treturn super::__FindArgCount(inName);\n");
-      output_cpp ("}\n\n");
-
-   (* Dynamic "Get" Field function - string version *)
-   output_cpp ("Dynamic " ^ class_name ^ "::__Field(const ::String &inName,hx::PropertyAccess inCallProp)\n{\n");
-   let dump_constructor_test _ constr =
-      output_cpp ("\tif (inName==" ^ (str constr.ef_name) ^ ") return " ^
-                  (keyword_remap constr.ef_name) );
-      if ( (constructor_arg_count constr) > 0 ) then output_cpp "_dyn()";
-      output_cpp (";\n")
-   in
-   PMap.iter dump_constructor_test enum_def.e_constrs;
-   output_cpp ("\treturn super::__Field(inName,inCallProp);\n}\n\n");
-
-   output_cpp "static ::String sStaticFields[] = {\n";
-   let sorted =
-      List.sort (fun f1 f2 -> (PMap.find f1 enum_def.e_constrs ).ef_index -
-               (PMap.find f2 enum_def.e_constrs ).ef_index )
-         (pmap_keys enum_def.e_constrs) in
-
-    List.iter (fun name -> output_cpp ("\t" ^ (str name) ^ ",\n") ) sorted;
-
-   output_cpp "\t::String(null()) };\n\n";
-
-   (* ENUM - Mark static as used by GC *)
-   output_cpp "static void sMarkStatics(HX_MARK_PARAMS) {\n";
-   PMap.iter (fun _ constructor ->
-      let name = keyword_remap constructor.ef_name in
-      match constructor.ef_type with
-      | TFun (_,_) -> ()
-      | _ -> output_cpp ("\tHX_MARK_MEMBER_NAME(" ^ class_name ^ "::" ^ name ^ ",\"" ^ name ^ "\");\n") )
-   enum_def.e_constrs;
-   output_cpp "};\n\n";
-
-   (* ENUM - Visit static as used by GC *)
-   output_cpp "#ifdef HXCPP_VISIT_ALLOCS\n";
-   output_cpp "static void sVisitStatic(HX_VISIT_PARAMS) {\n";
-   output_cpp ("\tHX_VISIT_MEMBER_NAME(" ^ class_name ^ "::__mClass,\"__mClass\");\n");
-   PMap.iter (fun _ constructor ->
-      let name = keyword_remap constructor.ef_name in
-      match constructor.ef_type with
-      | TFun (_,_) -> ()
-      | _ -> output_cpp ("\tHX_VISIT_MEMBER_NAME(" ^ class_name ^ "::" ^ name ^ ",\"" ^ name ^ "\");\n") )
-   enum_def.e_constrs;
-   output_cpp "};\n";
-   output_cpp "#endif\n\n";
-
-   output_cpp "static ::String sMemberFields[] = { ::String(null()) };\n";
-
-   output_cpp ("hx::Class " ^ class_name ^ "::__mClass;\n\n");
-
-   output_cpp ("Dynamic __Create_" ^ class_name ^ "() { return new " ^ class_name ^ "; }\n\n");
-
-   output_cpp ("void " ^ class_name ^ "::__register()\n{\n");
-   let text_name = str (join_class_path class_path ".") in
-   output_cpp ("\nhx::Static(__mClass) = hx::RegisterClass(" ^ text_name ^
-               ", hx::TCanCast< " ^ class_name ^ " >,sStaticFields,sMemberFields,\n");
-   output_cpp ("\t&__Create_" ^ class_name ^ ", &__Create,\n");
-   output_cpp ("\t&super::__SGetClass(), &Create" ^ class_name ^ ", sMarkStatics\n");
-   output_cpp("#ifdef HXCPP_VISIT_ALLOCS\n    , sVisitStatic\n#endif\n");
-   output_cpp ("#ifdef HXCPP_SCRIPTABLE\n    , 0\n#endif\n");
-   output_cpp (");\n}\n\n");
-
-   output_cpp ("void " ^ class_name ^ "::__boot()\n{\n");
-   (match meta with
-      | Some expr ->
-         let ctx = new_context common_ctx cpp_file 1 file_info in
-         gen_expression_tree ctx true expr  "__mClass->__meta__ = " ";\n";
-      | _ -> () );
-   PMap.iter (fun _ constructor ->
-      let name = constructor.ef_name in
-      match constructor.ef_type with
-      | TFun (_,_) -> ()
-      | _ ->
-         output_cpp ( "hx::Static(" ^ (keyword_remap name) ^ ") = hx::CreateEnum< " ^ class_name ^ " >(" ^ (str name) ^  "," ^
-            (string_of_int constructor.ef_index) ^ ");\n" )
-   ) enum_def.e_constrs;
-   output_cpp ("}\n\n");
-
-
-
-
-   output_cpp "\n";
-   gen_close_namespace output_cpp class_path;
-   cpp_file#close;
-
-   let h_file = new_header_file common_ctx common_ctx.file class_path in
-   let super = "hx::EnumBase_obj" in
-   let output_h = (h_file#write) in
-   let def_string = join_class_path class_path "_"  in
-   ctx.ctx_output <- output_h;
-
-   begin_header_file output_h def_string;
-
-   List.iter (gen_forward_decl h_file ) referenced;
-
-   gen_open_namespace output_h class_path;
-
-   output_h "\n\n";
-   output_h ("class " ^ class_name ^ " : public " ^ super ^ "\n");
-   output_h ("{\n\ttypedef " ^ super ^ " super;\n");
-   output_h ("\t\ttypedef " ^ class_name ^ " OBJ_;\n");
-   output_h "\n\tpublic:\n";
-   output_h ("\t\t" ^ class_name ^ "() {};\n");
-   output_h ("\t\tHX_DO_ENUM_RTTI;\n");
-   output_h ("\t\tstatic void __boot();\n");
-   output_h ("\t\tstatic void __register();\n");
-      output_h ("\t\t::String GetEnumName( ) const { return " ^
-                           (str (join_class_path class_path "."))  ^ "; }\n" );
-   output_h ("\t\t::String __ToString() const { return " ^
-                           (str (just_class_name ^ ".") )^ " + tag; }\n\n");
-
-
-   PMap.iter (fun _ constructor ->
-      let name = keyword_remap constructor.ef_name in
-      output_h ( "\t\tstatic " ^  remap_class_name ^ " " ^ name );
-      match constructor.ef_type with
-      | TFun (args,_) ->
-         output_h ( "(" ^ (gen_tfun_arg_list args) ^");\n");
-         output_h ( "\t\tstatic Dynamic " ^ name ^ "_dyn();\n");
-      | _ ->
-         output_h ";\n";
-         output_h ( "\t\tstatic inline " ^  remap_class_name ^ " " ^ name ^
-                  "_dyn() { return " ^name ^ "; }\n" );
-   ) enum_def.e_constrs;
-
-   output_h "};\n\n";
-
-   gen_close_namespace output_h class_path;
-
-   end_header_file output_h def_string;
-   h_file#close;
-   let depend_referenced = find_referenced_types common_ctx (TEnumDecl enum_def) super_deps (Hashtbl.create 0) false true false in
-   depend_referenced;;
-
-
-let list_iteri func in_list =
-   let idx = ref 0 in
-   List.iter (fun elem -> func !idx elem; idx := !idx + 1 ) in_list
-;;
-
-let has_new_gc_references class_def =
-   match class_def.cl_dynamic with
-   | Some _ -> true
-   | _ -> (
-      let is_gc_reference field =
-      (should_implement_field field) && (is_data_member field) &&
-         match type_string field.cf_type with
-            | "bool" | "int" | "Float" -> false
-            | _ -> true
-      in
-      List.exists is_gc_reference class_def.cl_ordered_fields
-      )
-;;
-
-
-let rec has_gc_references class_def =
-   ( match class_def.cl_super with
-   | Some def when has_gc_references (fst def) -> true
-   | _ -> false )
-   || has_new_gc_references class_def
-;;
-
-let rec find_next_super_iteration class_def =
-   match class_def.cl_super with
-   | Some  (klass,params) when has_new_gc_references klass -> class_string klass "_obj" params true
-   | Some  (klass,_) -> find_next_super_iteration klass
-   | _ -> "";
-;;
-
-let has_init_field class_def =
-   match class_def.cl_init with
-   | Some _ -> true
-   | _ -> false;;
-
-
-let is_abstract_impl class_def = match class_def.cl_kind with
-   | KAbstractImpl _ -> true
-   | _ -> false
-;;
-
-let variable_field field =
-   (match field.cf_expr with
-   | Some { eexpr = TFunction function_def } -> is_dynamic_haxe_method field
-   | _ -> true)
-;;
-
-let is_readable class_def field =
-   (match field.cf_kind with
-   | Var { v_read = AccNever } when (is_extern_field field) -> false
-   | Var { v_read = AccInline } -> false
-   | Var _ when is_abstract_impl class_def -> false
-   | _ -> true)
-;;
-
-let is_writable class_def field =
-   (match field.cf_kind with
-   | Var { v_write = AccNever } when (is_extern_field field) -> false
-   | Var { v_read = AccInline } -> false
-   | Var _ when is_abstract_impl class_def -> false
-   | _ -> true)
-;;
-
-
-let statics_except_meta class_def = (List.filter (fun static -> static.cf_name <> "__meta__" && static.cf_name <> "__rtti") class_def.cl_ordered_statics);;
-
-let has_set_member_field class_def =
-   implement_dynamic_here class_def || (
-      let reflect_fields = List.filter (reflective class_def) (class_def.cl_ordered_fields) in
-      let reflect_writable = List.filter (is_writable class_def) reflect_fields in
-      List.exists variable_field reflect_writable
-   )
-;;
-
-
-let has_set_static_field class_def =
-      let reflect_fields = List.filter (reflective class_def) (statics_except_meta class_def) in
-      let reflect_writable = List.filter (is_writable class_def) reflect_fields in
-      List.exists variable_field reflect_writable
-;;
-
-
-let has_get_fields class_def =
-   implement_dynamic_here class_def || (
-      let is_data_field field = (match follow field.cf_type with | TFun _ -> false | _ -> true) in
-      List.exists is_data_field class_def.cl_ordered_fields
-   )
-;;
-
-let has_get_member_field class_def =
-   implement_dynamic_here class_def || (
-      let reflect_fields = List.filter (reflective class_def) (class_def.cl_ordered_fields) in
-      List.exists (is_readable class_def) reflect_fields
-   )
-;;
-
-
-let has_get_static_field class_def =
-      let reflect_fields = List.filter (reflective class_def) (statics_except_meta class_def) in
-      List.exists (is_readable class_def) reflect_fields
-;;
-
-
-
-
-
-
-
-let has_boot_field class_def =
-   List.exists has_field_init (List.filter should_implement_field class_def.cl_ordered_statics);
-;;
-
-
-let is_macro meta =
-   Meta.has Meta.Macro meta
-;;
-
-
-let access_str a = match a with
-   | AccNormal -> "AccNormal"
-   | AccNo -> "AccNo"
-   | AccNever -> "AccNever"
-   | AccResolve -> "AccResolve"
-   | AccCall -> "AccCall"
-   | AccInline -> "AccInline"
-   | AccRequire(_,_) -> "AccRequire" ;;
-
-let generate_class_files common_ctx member_types super_deps constructor_deps class_def file_info inScriptable =
-   let class_path = class_def.cl_path in
-   let nativeGen = has_meta_key class_def.cl_meta Meta.NativeGen in
-   let class_name = (snd class_path) ^ (if nativeGen then "" else "_obj") in
-   let dot_name = join_class_path class_path "." in
-   let smart_class_name =  (snd class_path)  in
-   (*let cpp_file = new_cpp_file common_ctx.file class_path in*)
-   let cpp_file = new_placed_cpp_file common_ctx class_path in
-   let output_cpp = (cpp_file#write) in
-   let debug = if (has_meta_key class_def.cl_meta Meta.NoDebug) || ( Common.defined  common_ctx Define.NoDebug)
-      then 0 else 1 in
-   let scriptable = inScriptable && not class_def.cl_private in
-   let ctx = new_context common_ctx cpp_file debug file_info in
-
-
-   ctx.ctx_class_name <- "::" ^ (join_class_path class_def.cl_path "::");
-   ctx.ctx_class_super_name <- (match class_def.cl_super with
-      | Some (klass, params) -> class_string klass "_obj" params true
-      | _ -> "");
-   ctx.ctx_class_member_types <- member_types;
-   if (debug>1) then print_endline ("Found class definition:" ^ ctx.ctx_class_name);
-
-   let ptr_name = "hx::ObjectPtr< " ^ class_name ^ " >" in
-   let constructor_arg_var_list =
-      match class_def.cl_constructor with
-      | Some definition ->
-               (match definition.cf_expr with
-                  | Some { eexpr = TFunction function_def } ->
-                     List.map (fun (v,o) -> (v.v_name, gen_arg_type_name v.v_name o v.v_type "__o_"))
-                           function_def.tf_args;
-                  | _ ->
-                     (match follow definition.cf_type with
-                        | TFun (args,_) -> List.map (fun (a,_,t) -> (a, (type_string t, a)) )  args
-                        | _ -> [])
-               )
-      | _ -> [] in
-   let constructor_type_var_list =
-      List.map snd constructor_arg_var_list in
-   let constructor_var_list = List.map snd constructor_type_var_list in
-   let constructor_type_args = String.concat ","
-            (List.map (fun (t,a) -> t ^ " " ^ a) constructor_type_var_list) in
-   let constructor_args = String.concat "," constructor_var_list in
-
-   let implement_dynamic = implement_dynamic_here class_def in
-
-   output_cpp "#include <hxcpp.h>\n\n";
-
-   let force_field = scriptable && (has_get_member_field class_def) in
-   let field_integer_dynamic = force_field || (has_field_integer_lookup class_def) in
-   let field_integer_numeric = force_field || (has_field_integer_numeric_lookup class_def) in
-
-   let all_referenced = find_referenced_types ctx.ctx_common (TClassDecl class_def) super_deps constructor_deps false false scriptable in
-   List.iter ( add_include cpp_file  ) all_referenced;
-
-
-   (* All interfaces (and sub-interfaces) implemented *)
-   let implemented_hash = Hashtbl.create 0 in
-   List.iter (fun imp ->
-      let rec descend_interface interface =
-         let imp_path = (fst interface).cl_path in
-         let interface_name = "::" ^ (join_class_path_remap imp_path "::" ) in
-         if ( not (Hashtbl.mem implemented_hash interface_name) ) then begin
-            Hashtbl.add implemented_hash interface_name ();
-            List.iter descend_interface (fst interface).cl_implements;
-         end;
-         match (fst interface).cl_super with
-         | Some (interface,params) -> descend_interface (interface,params)
-         | _ -> ()
-      in descend_interface imp
-   ) (real_interfaces class_def.cl_implements);
-   let implemented = hash_keys implemented_hash in
-
-   if (scriptable) then
-      output_cpp "#include <hx/Scriptable.h>\n";
-
-   output_cpp ( get_class_code class_def Meta.CppFileCode );
-   let inc = get_meta_string_path class_def.cl_meta Meta.CppInclude in
-   if (inc<>"") then
-      output_cpp ("#include \"" ^ inc ^ "\"\n");
-
-   gen_open_namespace output_cpp class_path;
-   output_cpp "\n";
-
-   output_cpp ( get_class_code class_def Meta.CppNamespaceCode );
-
-   if (not class_def.cl_interface) && not nativeGen then begin
-      output_cpp ("Void " ^ class_name ^ "::__construct(" ^ constructor_type_args ^ ")\n{\n");
-      (match class_def.cl_constructor with
-         | Some definition ->
-               (match  definition.cf_expr with
-               | Some { eexpr = TFunction function_def } ->
-                  if has_meta_key definition.cf_meta Meta.NoDebug then ctx.ctx_debug_level <- 0;
-                  if ctx.ctx_debug_level >0 then begin
-                     hx_stack_push ctx output_cpp dot_name "new" function_def.tf_expr.epos;
-                     output_cpp "HX_STACK_THIS(this)\n";
-                     List.iter (fun (a,(t,o)) -> output_cpp ("HX_STACK_ARG(" ^ (keyword_remap o) ^ ",\"" ^ a ^"\")\n") ) constructor_arg_var_list;
-                  end;
-
-                  if (has_default_values function_def.tf_args) then begin
-                     generate_default_values ctx function_def.tf_args "__o_";
-                  end;
-                  gen_expression_tree ctx false (to_block function_def.tf_expr) "" ";\n";
-                  ctx.ctx_debug_level <- debug;
-               | _ -> ()
-               )
-         | _ -> ());
-         output_cpp "\treturn null();\n";
-         output_cpp "}\n\n";
-
-      (* Destructor goes in the cpp file so we can "see" the full definition of the member vars *)
-      output_cpp ( "//" ^ class_name ^ "::~" ^ class_name ^ "() { }\n\n");
-      output_cpp ("Dynamic " ^ class_name ^ "::__CreateEmpty() { return  new " ^ class_name ^ "; }\n");
-
-      output_cpp (ptr_name ^ " " ^ class_name ^ "::__new(" ^constructor_type_args ^")\n");
-
-      let create_result () =
-         output_cpp ("{  " ^ ptr_name ^ " _result_ = new " ^ class_name ^ "();\n");
-         in
-      create_result ();
-      output_cpp ("\t_result_->__construct(" ^ constructor_args ^ ");\n");
-      output_cpp ("\treturn _result_;}\n\n");
-
-      output_cpp ("Dynamic " ^ class_name ^ "::__Create(hx::DynamicArray inArgs)\n");
-      create_result ();
-      output_cpp ("\t_result_->__construct(" ^ (array_arg_list constructor_var_list) ^ ");\n");
-      output_cpp ("\treturn _result_;}\n\n");
-      if ( (List.length implemented) > 0 ) then begin
-         output_cpp ("hx::Object *" ^ class_name ^ "::__ToInterface(const hx::type_info &inType) {\n");
-         List.iter (fun interface_name ->
-            output_cpp ("\tif (inType==typeid( " ^ interface_name ^ "_obj)) " ^
-               "return operator " ^ interface_name ^ "_obj *();\n");
-            ) implemented;
-         output_cpp ("\treturn super::__ToInterface(inType);\n}\n\n");
-
-
-         List.iter (fun interface_name ->
-            output_cpp (class_name ^ "::operator " ^ interface_name ^ "_obj *()\n\t" ^
-               "{ return new " ^ interface_name ^ "_delegate_< " ^ class_name ^" >(this); }\n" );
-         ) implemented;
-      end;
-
-   end;
-
-   (match class_def.cl_init with
-   | Some expression ->
-      output_cpp ("void " ^ class_name^ "::__init__() {\n");
-      hx_stack_push ctx output_cpp dot_name "__init__" expression.epos;
-      gen_expression_tree (new_context common_ctx cpp_file debug file_info) false (to_block expression) "" "";
-      output_cpp "}\n\n";
-   | _ -> ());
-
-   let statics_except_meta = statics_except_meta class_def in
-   let implemented_fields = List.filter should_implement_field statics_except_meta in
-   let dump_field_name = (fun field -> output_cpp ("\t" ^  (str field.cf_name) ^ ",\n")) in
-   let implemented_instance_fields = List.filter should_implement_field class_def.cl_ordered_fields in
-
-   List.iter
-      (gen_field ctx class_def class_name smart_class_name dot_name false class_def.cl_interface)
-      class_def.cl_ordered_fields;
-   List.iter
-      (gen_field ctx class_def class_name smart_class_name dot_name true class_def.cl_interface) statics_except_meta;
-   output_cpp "\n";
-
-   let override_iteration = (not nativeGen) && (has_new_gc_references class_def) in
-
-   (* Initialise non-static variables *)
-   if ( (not class_def.cl_interface) && (not nativeGen) ) then begin
-      output_cpp (class_name ^ "::" ^ class_name ^  "()\n{\n");
-      if (implement_dynamic) then
-         output_cpp "\tHX_INIT_IMPLEMENT_DYNAMIC;\n";
-      List.iter
-         (fun field -> let remap_name = keyword_remap field.cf_name in
-            match field.cf_expr with
-            | Some { eexpr = TFunction function_def } ->
-                  if (is_dynamic_haxe_method field) then
-                     output_cpp ("\t" ^ remap_name ^ " = new __default_" ^ remap_name ^ "(this);\n")
-            | _ -> ()
-         )
-         class_def.cl_ordered_fields;
-      output_cpp "}\n\n";
-
-
-      let dump_field_iterator macro field =
-         if (is_data_member field) then begin
-            let remap_name = keyword_remap field.cf_name in
-            output_cpp ("\t" ^ macro ^ "(" ^ remap_name ^ ",\"" ^ field.cf_name^ "\");\n");
-
-               (match field.cf_kind with Var { v_read = AccCall } when (is_dynamic_accessor ("get_" ^ field.cf_name) "get" field class_def) ->
-                  let name = "get_" ^ field.cf_name in
-                  output_cpp ("\t" ^ macro ^ "(" ^ name ^ "," ^ "\"" ^ name ^ "\");\n" ) | _ -> ());
-               (match field.cf_kind with Var { v_write = AccCall } when  (is_dynamic_accessor ("set_" ^ field.cf_name) "set" field class_def) ->
-                  let name = "set_" ^ field.cf_name in
-                  output_cpp ("\t" ^ macro ^ "(" ^ name ^ "," ^ "\"" ^ name ^ "\");\n" ) | _ -> ());
-            end
-      in
-
-
-      if (override_iteration) then begin
-         let super_needs_iteration = find_next_super_iteration class_def in
-         (* MARK function - explicitly mark all child pointers *)
-         output_cpp ("void " ^ class_name ^ "::__Mark(HX_MARK_PARAMS)\n{\n");
-         output_cpp ("\tHX_MARK_BEGIN_CLASS(" ^ smart_class_name ^ ");\n");
-         if (implement_dynamic) then
-            output_cpp "\tHX_MARK_DYNAMIC;\n";
-         List.iter (dump_field_iterator "HX_MARK_MEMBER_NAME") implemented_instance_fields;
-         (match super_needs_iteration with
-         | "" -> ()
-         | super -> output_cpp ("\t" ^ super^"::__Mark(HX_MARK_ARG);\n" ) );
-         output_cpp "\tHX_MARK_END_CLASS();\n";
-         output_cpp "}\n\n";
-
-         (* Visit function - explicitly visit all child pointers *)
-         output_cpp ("void " ^ class_name ^ "::__Visit(HX_VISIT_PARAMS)\n{\n");
-         if (implement_dynamic) then
-            output_cpp "\tHX_VISIT_DYNAMIC;\n";
-         List.iter (dump_field_iterator "HX_VISIT_MEMBER_NAME") implemented_instance_fields;
-         (match super_needs_iteration with
-         | "" -> ()
-         | super -> output_cpp ("\t" ^ super ^ "::__Visit(HX_VISIT_ARG);\n") );
-         output_cpp "}\n\n";
-      end;
-
-
-      let reflect_member_fields = List.filter (reflective class_def) class_def.cl_ordered_fields in
-      let reflect_member_readable = List.filter (is_readable class_def) reflect_member_fields in
-      let reflect_member_writable = List.filter (is_writable class_def) reflect_member_fields in
-      let reflect_write_member_variables = List.filter variable_field reflect_member_writable in
-
-      let reflect_static_fields = List.filter (reflective class_def) (statics_except_meta) in
-      let reflect_static_readable = List.filter (is_readable class_def) reflect_static_fields in
-      let reflect_static_writable = List.filter (is_writable class_def) reflect_static_fields in
-      let reflect_write_static_variables = List.filter variable_field reflect_static_writable in
-
-      let dump_quick_field_test fields =
-         if ( (List.length fields) > 0) then begin
-            let len = function (_,l,_) -> l in
-            let sfields = List.sort (fun f1 f2 -> (len f1)-(len f2)) fields in
-            let len_case = ref (-1) in
-            output_cpp "\tswitch(inName.length) {\n";
-            List.iter (fun (field,l,result) ->
-               if (l <> !len_case) then begin
-                  if (!len_case>=0) then output_cpp "\t\tbreak;\n";
-                  output_cpp ("\tcase " ^ (string_of_int l) ^ ":\n");
-                  len_case := l;
-               end;
-               output_cpp ("\t\tif (HX_FIELD_EQ(inName,\"" ^  (Ast.s_escape field)  ^ "\") ) { " ^ result ^ " }\n");
-            ) sfields;
-            output_cpp "\t}\n";
-         end;
-      in
-
-      let checkPropCall field = if ( (has_meta_key class_def.cl_meta Meta.NativeProperty) ||
-                                     (has_meta_key field.cf_meta Meta.NativeProperty) ||
-                                     (Common.defined common_ctx Define.ForceNativeProperty) )
-         then
-            "inCallProp != hx::paccNever"
-         else
-            "inCallProp == hx::paccAlways"
-      in
-
-
-
-      if (has_get_member_field class_def) then begin
-         (* Dynamic "Get" Field function - string version *)
-         output_cpp ("Dynamic " ^ class_name ^ "::__Field(const ::String &inName,hx::PropertyAccess inCallProp)\n{\n");
-         let get_field_dat = List.map (fun f ->
-            (f.cf_name, String.length f.cf_name,
-               (match f.cf_kind with
-               | Var { v_read = AccCall } when is_extern_field f -> "if (" ^ (checkPropCall f) ^ ") return " ^(keyword_remap ("get_" ^ f.cf_name)) ^ "()"
-               | Var { v_read = AccCall } -> "return " ^ (checkPropCall f) ^ " ? " ^ (keyword_remap ("get_" ^ f.cf_name)) ^ "() : " ^
-                     ((keyword_remap f.cf_name) ^ if (variable_field f) then "" else "_dyn()")
-               | _ -> "return " ^ ((keyword_remap f.cf_name) ^ if (variable_field f) then "" else "_dyn()")
-               ) ^ ";"
-            ) )
-         in
-         dump_quick_field_test (get_field_dat reflect_member_readable);
-         if (implement_dynamic) then
-            output_cpp "\tHX_CHECK_DYNAMIC_GET_FIELD(inName);\n";
-         output_cpp ("\treturn super::__Field(inName,inCallProp);\n}\n\n");
-
-         (* Dynamic "Get" Field function - int version *)
-         if ( field_integer_numeric || field_integer_dynamic) then begin
-            let dump_static_ids = (fun field ->
-               let remap_name = keyword_remap field.cf_name in
-               output_cpp ("static int __id_" ^ remap_name ^ " = __hxcpp_field_to_id(\"" ^
-                              (field.cf_name) ^ "\");\n");
-               ) in
-            List.iter dump_static_ids reflect_member_readable;
-            output_cpp "\n\n";
-
-
-            let output_ifield return_type function_name all_fields =
-            output_cpp (return_type ^" " ^ class_name ^ "::" ^ function_name ^ "(int inFieldID)\n{\n");
-            let dump_field_test = (fun f ->
-               let remap_name = keyword_remap f.cf_name in
-               output_cpp ("\tif (inFieldID==__id_" ^ remap_name ^ ") return "  ^
-                  ( if (return_type="Float") then "hx::ToDouble( " else "" ) ^
-                  (match f.cf_kind with
-                  | Var { v_read = AccCall } -> (keyword_remap ("get_" ^ f.cf_name)) ^ "()"
-                  | _ -> (remap_name ^ if ( variable_field f) then "" else "_dyn()")
-                  ) ^ ( if (return_type="Float") then " ) " else "" ) ^ ";\n");
-               ) in
-            List.iter dump_field_test (List.filter (fun f -> all_fields || (is_numeric_field f)) reflect_member_readable);
-            if (implement_dynamic) then
-               output_cpp "\tHX_CHECK_DYNAMIC_GET_INT_FIELD(inFieldID);\n";
-            output_cpp ("\treturn super::" ^ function_name ^ "(inFieldID);\n}\n\n");
-            in
-
-            if (field_integer_dynamic) then output_ifield "Dynamic" "__IField" true;
-            if (field_integer_numeric) then output_ifield "double" "__INumField" false;
-         end;
-      end;
-
-      if (has_get_static_field class_def) then begin
-         output_cpp ("bool " ^ class_name ^ "::__GetStatic(const ::String &inName, Dynamic &outValue, hx::PropertyAccess inCallProp)\n{\n");
-         let get_field_dat = List.map (fun f ->
-            (f.cf_name, String.length f.cf_name,
-               (match f.cf_kind with
-               | Var { v_read = AccCall } when is_extern_field f -> "if (" ^ (checkPropCall f) ^ ") { outValue = " ^(keyword_remap ("get_" ^ f.cf_name)) ^ "(); return true; }"
-               | Var { v_read = AccCall } -> "outValue = " ^ (checkPropCall f) ^ " ? " ^ (keyword_remap ("get_" ^ f.cf_name)) ^ "() : " ^
-                     ((keyword_remap f.cf_name) ^ if (variable_field f) then "" else "_dyn()") ^ "; return true;";
-               | _ -> "outValue = " ^ ((keyword_remap f.cf_name) ^ (if (variable_field f) then "" else "_dyn()") ^ "; return true; ")
-               )
-            ) )
-         in
-         dump_quick_field_test (get_field_dat reflect_static_readable);
-         output_cpp ("\treturn false;\n}\n\n");
-      end;
-
-      (* Dynamic "Set" Field function *)
-      if (has_set_member_field class_def) then begin
-
-         output_cpp ("Dynamic " ^ class_name ^ "::__SetField(const ::String &inName,const Dynamic &inValue,hx::PropertyAccess inCallProp)\n{\n");
-
-         let set_field_dat = List.map (fun f ->
-            let default_action =
-               (keyword_remap f.cf_name) ^ "=inValue.Cast< " ^ (type_string f.cf_type) ^ " >();" ^
-                  " return inValue;" in
-            (f.cf_name, String.length f.cf_name,
-               (match f.cf_kind with
-               | Var { v_write = AccCall } -> "if (" ^ (checkPropCall f) ^ ") return " ^ (keyword_remap ("set_" ^ f.cf_name)) ^ "(inValue);"
-                  ^ ( if is_extern_field f then "" else default_action )
-               | _ -> default_action
-               )
-            )
-         ) in
-
-         dump_quick_field_test (set_field_dat reflect_write_member_variables);
-         if (implement_dynamic) then begin
-            output_cpp ("\ttry { return super::__SetField(inName,inValue,inCallProp); }\n");
-            output_cpp ("\tcatch(Dynamic e) { HX_DYNAMIC_SET_FIELD(inName,inValue); }\n");
-            output_cpp "\treturn inValue;\n}\n\n";
-         end else
-            output_cpp ("\treturn super::__SetField(inName,inValue,inCallProp);\n}\n\n");
-      end;
-
-      if (has_set_static_field class_def) then begin
-
-         output_cpp ("bool " ^ class_name ^ "::__SetStatic(const ::String &inName,Dynamic &ioValue,hx::PropertyAccess inCallProp)\n{\n");
-
-         let set_field_dat = List.map (fun f ->
-            let default_action =
-               (keyword_remap f.cf_name) ^ "=ioValue.Cast< " ^ (type_string f.cf_type) ^ " >(); return true;" in
-            (f.cf_name, String.length f.cf_name,
-               (match f.cf_kind with
-               | Var { v_write = AccCall } -> "if (" ^ (checkPropCall f) ^ ")  ioValue = " ^ (keyword_remap ("set_" ^ f.cf_name)) ^ "(ioValue);"
-                  ^ ( if is_extern_field f then "" else " else " ^ default_action )
-               | _ -> default_action
-               )
-            )
-         ) in
-
-         dump_quick_field_test (set_field_dat reflect_write_static_variables);
-         output_cpp ("\treturn false;\n}\n\n");
-      end;
-
-
-
-
-      (* For getting a list of data members (eg, for serialization) *)
-      if (has_get_fields class_def) then begin
-         let append_field =
-            (fun field -> output_cpp ("\toutFields->push(" ^( str field.cf_name )^ ");\n")) in
-         let is_data_field field = (match follow field.cf_type with | TFun _ -> false | _ -> true) in
-
-         output_cpp ("void " ^ class_name ^ "::__GetFields(Array< ::String> &outFields)\n{\n");
-         List.iter append_field (List.filter is_data_field class_def.cl_ordered_fields);
-         if (implement_dynamic) then
-            output_cpp "\tHX_APPEND_DYNAMIC_FIELDS(outFields);\n";
-         output_cpp "\tsuper::__GetFields(outFields);\n";
-         output_cpp "};\n\n";
-      end;
-
-      let storage field = match type_string field.cf_type with
-         | "bool" -> "hx::fsBool"
-         | "int" -> "hx::fsInt"
-         | "Float" -> "hx::fsFloat"
-         | "::String" -> "hx::fsString"
-         | str -> "hx::fsObject" ^ " /*" ^ str ^ "*/ "
-         in
-      let dump_member_storage = (fun field ->
-         output_cpp ("\t{" ^ (storage field) ^ ",(int)offsetof(" ^ class_name ^"," ^ (keyword_remap field.cf_name) ^")," ^
-            (str field.cf_name) ^ "},\n")
-         )
-      in
-      let dump_static_storage = (fun field ->
-         output_cpp ("\t{" ^ (storage field) ^ ",(void *) &" ^ class_name ^"::" ^ (keyword_remap field.cf_name) ^"," ^
-            (str field.cf_name) ^ "},\n")
-         )
-      in
-
-      output_cpp "#if HXCPP_SCRIPTABLE\n";
-
-      let stored_fields = List.filter is_data_member implemented_instance_fields in
-      if ( (List.length stored_fields) > 0) then begin
-         output_cpp "static hx::StorageInfo sMemberStorageInfo[] = {\n";
-         List.iter dump_member_storage stored_fields;
-         output_cpp "\t{ hx::fsUnknown, 0, null()}\n};\n";
-      end else
-         output_cpp "static hx::StorageInfo *sMemberStorageInfo = 0;\n";
-
-      let stored_statics = List.filter is_data_member implemented_fields in
-      if ( (List.length stored_statics) > 0) then begin
-         output_cpp "static hx::StaticInfo sStaticStorageInfo[] = {\n";
-         List.iter dump_static_storage stored_statics;
-         output_cpp "\t{ hx::fsUnknown, 0, null()}\n};\n";
-      end else
-         output_cpp "static hx::StaticInfo *sStaticStorageInfo = 0;\n";
-
-      output_cpp "#endif\n\n";
-   end; (* cl_interface *)
-
-   let reflective_members = List.filter (reflective class_def) implemented_instance_fields in
-   let sMemberFields = if List.length reflective_members>0 then begin
-      output_cpp "static ::String sMemberFields[] = {\n";
-      List.iter dump_field_name  reflective_members;
-      output_cpp "\t::String(null()) };\n\n";
-      "sMemberFields"
-   end else
-      "0 /* sMemberFields */";
-   in
-
-   if (not nativeGen) then begin
-      (* Mark static variables as used *)
-      output_cpp "static void sMarkStatics(HX_MARK_PARAMS) {\n";
-      output_cpp ("\tHX_MARK_MEMBER_NAME(" ^ class_name ^ "::__mClass,\"__mClass\");\n");
-      List.iter (fun field ->
-         if (is_data_member field) then
-            output_cpp ("\tHX_MARK_MEMBER_NAME(" ^ class_name ^ "::" ^ (keyword_remap field.cf_name) ^ ",\"" ^  field.cf_name ^ "\");\n") )
-         implemented_fields;
-      output_cpp "};\n\n";
-
-      (* Visit static variables *)
-      output_cpp "#ifdef HXCPP_VISIT_ALLOCS\n";
-      output_cpp "static void sVisitStatics(HX_VISIT_PARAMS) {\n";
-      output_cpp ("\tHX_VISIT_MEMBER_NAME(" ^ class_name ^ "::__mClass,\"__mClass\");\n");
-      List.iter (fun field ->
-         if (is_data_member field) then
-            output_cpp ("\tHX_VISIT_MEMBER_NAME(" ^ class_name ^ "::" ^ (keyword_remap field.cf_name) ^ ",\"" ^  field.cf_name ^ "\");\n") )
-         implemented_fields;
-      output_cpp "};\n\n";
-      output_cpp "#endif\n\n";
-   end;
-
-   let script_type t optional = if optional then "Object" else
-   match type_string t with
-   | "bool" -> "Int"
-   | "int" -> "Int"
-   | "Float" -> "Float"
-   | "::String" -> "String"
-   | "Null" -> "Void"
-   | "Void" -> "Void"
-   | _ -> "Object"
-   in
-   let script_signature t optional = match script_type t optional with
-   | "Bool" -> "b"
-   | "Int" -> "i"
-   | "Float" -> "f"
-   | "String" -> "s"
-   | "Void" -> "v"
-   | _ -> "o"
-   in
-   let script_size_type t optional = match script_type t optional with
-   | "Object" -> "void *"
-   | x -> x
-   in
-
-   let generate_script_function isStatic field scriptName callName =
-      match follow field.cf_type  with
-      | TFun (args,return_type) ->
-         output_cpp ("\nstatic void " ^ scriptName ^ "(hx::CppiaCtx *ctx) {\n");
-         let ret = script_signature return_type false in
-         if (ret<>"v") then output_cpp ("ctx->return" ^ (script_type return_type false) ^ "(");
-         if isStatic then
-            output_cpp (class_name ^ "::" ^ callName ^ "(")
-         else
-            output_cpp ("((" ^  class_name ^ "*)ctx->getThis())->" ^  callName ^ "(");
-
-         let (signature,_,_) = List.fold_left (fun (signature,sep,size) (_,opt,t) ->
-            output_cpp (sep ^ "ctx->get" ^ (script_type t opt) ^ "(" ^ size ^ ")");
-            (signature ^ (script_signature t opt ), ",", (size^"+sizeof(" ^ (script_size_type t opt) ^ ")") ) ) (ret,"","sizeof(void*)")  args
-         in
-
-         output_cpp ")";
-         if (ret<>"v") then output_cpp (")");
-         output_cpp (";\n}\n");
-         signature;
-      | _ -> ""
-   in
-
-
-   if (scriptable && not nativeGen) then begin
-      let dump_script_field idx (field,f_args,return_t) =
-      let args = if (class_def.cl_interface) then
-            gen_tfun_interface_arg_list f_args
-         else
-            gen_tfun_arg_list f_args in
-      let names = List.map (fun (n,_,_) -> keyword_remap n) f_args in
-      let return_type = type_string return_t in
-      let ret = if (return_type="Void") then " " else "return " in
-      let name = keyword_remap field.cf_name in
-      let vtable =  "__scriptVTable[" ^ (string_of_int (idx+1) ) ^ "] " in
-      let args_varray = (List.fold_left (fun l n -> l ^ ".Add(" ^ n ^ ")") "Array<Dynamic>()" names) in
-      output_cpp ("	" ^ return_type ^ " " ^ name ^ "( " ^ args ^ " ) { ");
-      output_cpp ("\n\tif (" ^ vtable ^ ") {\n" );
-      output_cpp ("\t\thx::CppiaCtx *__ctx = hx::CppiaCtx::getCurrent();\n" );
-      output_cpp ("\t\thx::AutoStack __as(__ctx);\n" );
-      output_cpp ("\t\t__ctx->pushObject(" ^ (if class_def.cl_interface then "mDelegate.mPtr" else "this" ) ^");\n" );
-      List.iter (fun (name,opt, t ) ->
-         output_cpp ("\t\t__ctx->push" ^ (script_type t opt) ^ "(" ^ (keyword_remap name) ^ ");\n" );
-      ) f_args;
-      output_cpp ("\t\t" ^ ret ^ "__ctx->run" ^ (script_type return_t false) ^ "(" ^ vtable ^ ");\n" );
-      output_cpp ("\t}  else " ^ ret );
-
-      if (class_def.cl_interface) then begin
-         output_cpp (" mDelegate->__Field(HX_CSTRING(\"" ^ field.cf_name ^ "\"), hx::paccNever)");
-         if (List.length names <= 5) then
-            output_cpp ("->__run(" ^ (String.concat "," names) ^ ");")
-         else
-            output_cpp ("->__Run(" ^ args_varray ^ ");");
-      end else
-         output_cpp (class_name ^ "::" ^ name ^ "(" ^ (String.concat "," names)^ ");");
-      output_cpp ("return null(); }\n");
-      if (class_def.cl_interface) then begin
-      output_cpp ("	Dynamic " ^ name ^ "_dyn() { return mDelegate->__Field(HX_CSTRING(\"" ^ field.cf_name ^ "\"), hx::paccNever); }\n\n");
-
-      end
-      in
-
-      let not_toString = fun (field,args,_) -> field.cf_name<>"toString" || class_def.cl_interface in
-      let functions = List.filter not_toString (all_virtual_functions class_def) in
-      let new_sctipt_functions = List.filter (fun (f,_,_) -> not (is_override class_def f.cf_name) ) functions in
-      let sctipt_name = class_name ^ "__scriptable" in
-      output_cpp ("class " ^ sctipt_name ^ " : public " ^ class_name ^ " {\n" );
-      output_cpp ("   typedef "^sctipt_name ^" __ME;\n");
-      output_cpp ("   typedef "^class_name ^" super;\n");
-      let has_funky_toString = List.exists (fun f -> f.cf_name="toString") class_def.cl_ordered_statics  ||
-                              List.exists (fun f -> f.cf_name="toString" && field_arg_count f <> 0) class_def.cl_ordered_fields in
-      let super_string = if has_funky_toString then class_name ^ "::super" else class_name in
-      output_cpp ("   typedef "^ super_string ^" __superString;\n");
-      if (class_def.cl_interface) then
-         output_cpp ("   HX_DEFINE_SCRIPTABLE_INTERFACE\n")
-      else begin
-         output_cpp ("   HX_DEFINE_SCRIPTABLE(HX_ARR_LIST" ^ (string_of_int (List.length constructor_var_list) ) ^ ")\n");
-         if (not implement_dynamic) then
-            output_cpp "\tHX_DEFINE_SCRIPTABLE_DYNAMIC;\n";
-      end;
-
-      list_iteri dump_script_field functions;
-      output_cpp ("};\n\n");
-
-      if (List.length new_sctipt_functions) > 0 then begin
-         let sigs = Hashtbl.create 0 in
-         List.iter (fun (f,_,_) ->
-            let s = generate_script_function false f ("__s_" ^f.cf_name) (keyword_remap f.cf_name) in
-            Hashtbl.add sigs f.cf_name s
-         ) new_sctipt_functions;
-
-         output_cpp "static hx::ScriptNamedFunction __scriptableFunctions[] = {\n";
-         List.iter (fun (f,_,_) ->
-            let s = try Hashtbl.find sigs f.cf_name with Not_found -> "v" in
-            output_cpp ("  hx::ScriptNamedFunction(\"" ^ f.cf_name ^ "\",__s_" ^ f.cf_name ^ ",\"" ^ s ^ "\"),\n" ) ) new_sctipt_functions;
-         output_cpp "  hx::ScriptNamedFunction(0,0,0) };\n";
-      end else
-         output_cpp "static hx::ScriptNamedFunction *__scriptableFunctions = 0;\n";
-   end;
-
-
-
-   let class_name_text = join_class_path class_path "." in
-
-   (* Initialise static in boot function ... *)
-   if (not class_def.cl_interface && not nativeGen) then begin
-      (* Remap the specialised "extern" classes back to the generic names *)
-      output_cpp ("hx::Class " ^ class_name ^ "::__mClass;\n\n");
-      if (scriptable) then begin
-         (match class_def.cl_constructor with
-            | Some field  ->
-                  let signature = generate_script_function false field "__script_construct_func" "__construct" in
-                  output_cpp ("hx::ScriptFunction " ^ class_name ^ "::__script_construct(__script_construct_func,\"" ^ signature ^ "\");\n");
-            | _ ->
-                  output_cpp ("hx::ScriptFunction " ^ class_name ^ "::__script_construct(0,0);\n");
-         );
-      end;
-
-      let reflective_statics = List.filter (reflective class_def) implemented_fields in
-      let sStaticFields = if List.length reflective_statics > 0 then begin
-         output_cpp "static ::String sStaticFields[] = {\n";
-         List.iter dump_field_name  reflective_statics;
-         output_cpp "\t::String(null()) };\n\n";
-         "sStaticFields";
-      end else
-        "0 /* sStaticFields */"
-      in
-
-      output_cpp ("void " ^ class_name ^ "::__register()\n{\n");
-      output_cpp ("\thx::Static(__mClass) = new hx::Class_obj();\n");
-      output_cpp ("\t__mClass->mName = " ^  (str class_name_text)  ^ ";\n");
-      output_cpp ("\t__mClass->mSuper = &super::__SGetClass();\n");
-      output_cpp ("\t__mClass->mConstructEmpty = &__CreateEmpty;\n");
-      output_cpp ("\t__mClass->mConstructArgs = &__Create;\n");
-      output_cpp ("\t__mClass->mGetStaticField = &" ^ (
-         if (has_get_static_field class_def) then class_name ^ "::__GetStatic;\n" else "hx::Class_obj::GetNoStaticField;\n" ));
-      output_cpp ("\t__mClass->mSetStaticField = &" ^ (
-         if (has_set_static_field class_def) then class_name ^ "::__SetStatic;\n" else "hx::Class_obj::SetNoStaticField;\n" ));
-      output_cpp ("\t__mClass->mMarkFunc = sMarkStatics;\n");
-      output_cpp ("\t__mClass->mStatics = hx::Class_obj::dupFunctions(" ^ sStaticFields ^ ");\n");
-      output_cpp ("\t__mClass->mMembers = hx::Class_obj::dupFunctions(" ^ sMemberFields ^ ");\n");
-      output_cpp ("\t__mClass->mCanCast = hx::TCanCast< " ^ class_name ^ " >;\n");
-      output_cpp ("#ifdef HXCPP_VISIT_ALLOCS\n\t__mClass->mVisitFunc = sVisitStatics;\n#endif\n");
-      output_cpp ("#ifdef HXCPP_SCRIPTABLE\n\t__mClass->mMemberStorageInfo = sMemberStorageInfo;\n#endif\n");
-      output_cpp ("#ifdef HXCPP_SCRIPTABLE\n\t__mClass->mStaticStorageInfo = sStaticStorageInfo;\n#endif\n");
-      output_cpp ("\thx::RegisterClass(__mClass->mName, __mClass);\n");
-      if (scriptable) then
-         output_cpp ("  HX_SCRIPTABLE_REGISTER_CLASS(\""^class_name_text^"\"," ^ class_name ^ ");\n");
-      output_cpp ("}\n\n");
-
-   end else if not nativeGen then begin
-      output_cpp ("hx::Class " ^ class_name ^ "::__mClass;\n\n");
-
-      output_cpp ("void " ^ class_name ^ "::__register()\n{\n");
-
-      output_cpp ("\thx::Static(__mClass) = new hx::Class_obj();\n");
-      output_cpp ("\t__mClass->mName = " ^  (str class_name_text)  ^ ";\n");
-      output_cpp ("\t__mClass->mSuper = &super::__SGetClass();\n");
-      output_cpp ("\t__mClass->mMarkFunc = sMarkStatics;\n");
-      (*output_cpp ("\t__mClass->mStatics = hx::Class_obj::dupFunctions(" ^ sStaticFields ^ ");\n");*)
-      output_cpp ("\t__mClass->mMembers = hx::Class_obj::dupFunctions(" ^ sMemberFields ^ ");\n");
-      output_cpp ("\t__mClass->mCanCast = hx::TCanCast< " ^ class_name ^ " >;\n");
-      output_cpp ("#ifdef HXCPP_VISIT_ALLOCS\n\t__mClass->mVisitFunc = sVisitStatics;\n#endif\n");
-      output_cpp ("\thx::RegisterClass(__mClass->mName, __mClass);\n");
-      if (scriptable) then
-         output_cpp ("  HX_SCRIPTABLE_REGISTER_INTERFACE(\""^class_name_text^"\"," ^ class_name ^ ");\n");
-      output_cpp ("}\n\n");
-   end;
-
-   if (has_boot_field class_def) then begin
-      output_cpp ("void " ^ class_name ^ "::__boot()\n{\n");
-      List.iter (gen_field_init ctx ) (List.filter should_implement_field class_def.cl_ordered_statics);
-      output_cpp ("}\n\n");
-   end;
-
-
-   gen_close_namespace output_cpp class_path;
-
-   cpp_file#close;
-
-
-   let h_file = new_header_file common_ctx common_ctx.file class_path in
-   let super = match class_def.cl_super with
-      | Some (klass,params) -> (class_string klass "_obj" params true)
-      | _ when nativeGen -> ""
-      | _ -> if (class_def.cl_interface) then "hx::Interface" else "hx::Object"
-      in
-   let output_h = (h_file#write) in
-   let def_string = join_class_path class_path "_"  in
-   ctx.ctx_output <- output_h;
-
-   begin_header_file output_h def_string;
-
-   (* Include the real header file for the super class *)
-   (match class_def.cl_super with
-   | Some super ->
-      let super_path = (fst super).cl_path in
-      h_file#add_include super_path
-   | _ -> () );
-
-   (* And any interfaces ... *)
-   List.iter (fun imp-> h_file#add_include (fst imp).cl_path)
-      (real_interfaces class_def.cl_implements);
-
-   (* Only need to foreward-declare classes that are mentioned in the header file
-      (ie, not the implementation)  *)
-   let referenced = find_referenced_types ctx.ctx_common (TClassDecl class_def) super_deps (Hashtbl.create 0) true false scriptable in
-   List.iter ( gen_forward_decl h_file ) referenced;
-
-   output_h ( get_class_code class_def Meta.HeaderCode );
-   let inc = get_meta_string_path class_def.cl_meta Meta.HeaderInclude in
-   if (inc<>"") then
-      output_h ("#include \"" ^ inc ^ "\"\n");
-
-   gen_open_namespace output_h class_path;
-   output_h "\n\n";
-   output_h ( get_class_code class_def Meta.HeaderNamespaceCode );
-
-   let extern_class =  Common.defined common_ctx Define.DllExport in
-   let attribs = "HXCPP_" ^ (if extern_class then "EXTERN_" else "") ^ "CLASS_ATTRIBUTES " in
-
-   if (super="") then begin
-      output_h ("class " ^ attribs ^ " " ^ class_name);
-      output_h "{\n\tpublic:\n";
-   end else begin
-      output_h ("class " ^ attribs ^ " " ^ class_name ^ " : public " ^ super );
-      output_h "{\n\tpublic:\n";
-      output_h ("\t\ttypedef " ^ super ^ " super;\n");
-      output_h ("\t\ttypedef " ^ class_name ^ " OBJ_;\n");
-   end;
-
-   if (not class_def.cl_interface && not nativeGen) then begin
-      output_h ("\t\t" ^ class_name ^  "();\n");
-      output_h ("\t\tVoid __construct(" ^ constructor_type_args ^ ");\n");
-      output_h "\n\tpublic:\n";
-      let new_arg = if (has_gc_references class_def) then "true" else "false" in
-      output_h ("\t\tinline void *operator new( size_t inSize, bool inContainer=" ^ new_arg
-         ^",const char *inName=" ^ (const_char_star class_name_text )^ ")\n" );
-      output_h ("\t\t\t{ return hx::Object::operator new(inSize,inContainer,inName); }\n" );
-      output_h ("\t\tstatic " ^ptr_name^ " __new(" ^constructor_type_args ^");\n");
-      output_h ("\t\tstatic Dynamic __CreateEmpty();\n");
-      output_h ("\t\tstatic Dynamic __Create(hx::DynamicArray inArgs);\n");
-      if (scriptable) then
-         output_h ("\t\tstatic hx::ScriptFunction __script_construct;\n");
-      output_h ("\t\t//~" ^ class_name ^ "();\n\n");
-      output_h ("\t\tHX_DO_RTTI_ALL;\n");
-      if (has_get_member_field class_def) then
-         output_h ("\t\tDynamic __Field(const ::String &inString, hx::PropertyAccess inCallProp);\n");
-      if (has_get_static_field class_def) then
-         output_h ("\t\tstatic bool __GetStatic(const ::String &inString, Dynamic &outValue, hx::PropertyAccess inCallProp);\n");
-      if (has_set_member_field class_def) then
-         output_h ("\t\tDynamic __SetField(const ::String &inString,const Dynamic &inValue, hx::PropertyAccess inCallProp);\n");
-      if (has_set_static_field class_def) then
-         output_h ("\t\tstatic bool __SetStatic(const ::String &inString, Dynamic &ioValue, hx::PropertyAccess inCallProp);\n");
-      if (has_get_fields class_def) then
-         output_h ("\t\tvoid __GetFields(Array< ::String> &outFields);\n");
-
-      if (field_integer_dynamic) then output_h "\t\tDynamic __IField(int inFieldID);\n";
-      if (field_integer_numeric) then output_h "\t\tdouble __INumField(int inFieldID);\n";
-      if (implement_dynamic) then
-         output_h ("\t\tHX_DECLARE_IMPLEMENT_DYNAMIC;\n");
-      output_h ("\t\tstatic void __register();\n");
-      if (override_iteration) then begin
-         output_h ("\t\tvoid __Mark(HX_MARK_PARAMS);\n");
-         output_h ("\t\tvoid __Visit(HX_VISIT_PARAMS);\n");
-      end;
-
-      if ( (List.length implemented) > 0 ) then begin
-         output_h "\t\thx::Object *__ToInterface(const hx::type_info &inType);\n";
-
-         List.iter (fun interface_name ->
-            output_h ("\t\toperator " ^ interface_name ^ "_obj *();\n")
-         ) implemented;
-      end;
-
-      if (has_init_field class_def) then
-         output_h "\t\tstatic void __init__();\n\n";
-      output_h ("\t\t::String __ToString() const { return " ^ (str smart_class_name) ^ "; }\n\n");
-   end else if not nativeGen then begin
-      output_h ("\t\tHX_DO_INTERFACE_RTTI;\n");
-   end;
-   if (has_boot_field class_def) then
-      output_h ("\t\tstatic void __boot();\n");
-
-
-   (match class_def.cl_array_access with
-   | Some t -> output_h ("\t\ttypedef " ^ (type_string t) ^ " __array_access;\n")
-   | _ -> ());
-
-
-   List.iter (gen_member_def ctx class_def true class_def.cl_interface) (List.filter should_implement_field class_def.cl_ordered_statics);
-
-   if class_def.cl_interface then begin
-      let dumped = ref PMap.empty in
-      let rec dump_def interface =
-         List.iter (fun field -> try ignore (PMap.find field.cf_name !dumped) with Not_found ->
-         begin
-            dumped := PMap.add field.cf_name true !dumped;
-            gen_member_def ctx interface false true field
-         end
-         ) interface.cl_ordered_fields;
-         List.iter (fun impl -> dump_def (fst impl)) (real_interfaces interface.cl_implements);
-      in
-      (* Dump this class, not its super, but also its implements *)
-      dump_def class_def;
-      List.iter (fun impl -> dump_def (fst impl)) (real_interfaces class_def.cl_implements);
-   end else begin
-      List.iter (gen_member_def ctx class_def false false) (List.filter should_implement_field class_def.cl_ordered_fields);
-   end;
-
-
-   output_h ( get_class_code class_def Meta.HeaderClassCode );
-   output_h "};\n\n";
-
-   if (class_def.cl_interface && not nativeGen) then begin
-      output_h ("\n\n");
-      output_h ("template<typename IMPL>\n");
-      output_h ("class " ^ smart_class_name ^ "_delegate_ : public " ^ class_name^"\n");
-      output_h "{\n\tprotected:\n";
-      output_h ("\t\tIMPL *mDelegate;\n");
-      output_h "\tpublic:\n";
-      output_h ("\t\t" ^ smart_class_name ^ "_delegate_(IMPL *inDelegate) : mDelegate(inDelegate) {}\n");
-      output_h ("\t\thx::Object *__GetRealObject() { return mDelegate; }\n");
-      output_h ("\t\tvoid __Visit(HX_VISIT_PARAMS) { HX_VISIT_OBJECT(mDelegate); }\n");
-
-      let dumped = ref PMap.empty in
-      let rec dump_delegate interface =
-         List.iter (fun field -> try ignore (PMap.find field.cf_name !dumped) with Not_found ->
-         begin
-            dumped := PMap.add field.cf_name true !dumped;
-            match follow field.cf_type, field.cf_kind  with
-            | _, Method MethDynamic -> ()
-            | TFun (args,return_type), Method _ ->
-               let remap_name = keyword_remap field.cf_name in
-               output_h ( "		"  ^ (type_string return_type) ^ " " ^ remap_name ^ "( " );
-               output_h (gen_tfun_interface_arg_list args);
-               output_h (") { return mDelegate->" ^ remap_name^ "(");
-               output_h (String.concat "," (List.map (fun (name,opt,typ) -> (keyword_remap name)) args));
-               output_h ");}\n";
-               if reflective interface field then
-                  output_h ("		Dynamic " ^ remap_name ^ "_dyn() { return mDelegate->" ^ remap_name ^ "_dyn();}\n");
-            | _ -> ()
-         end
-         ) interface.cl_ordered_fields;
-
-         match interface.cl_super with | Some super -> dump_delegate (fst super) | _ -> ();
-         List.iter (fun impl -> dump_delegate (fst impl)) (real_interfaces interface.cl_implements);
-      in
-      dump_delegate class_def;
-      List.iter (fun impl -> dump_delegate (fst impl)) (real_interfaces class_def.cl_implements);
-      output_h "};\n\n";
-   end;
-
-
-   gen_close_namespace output_h class_path;
-
-   end_header_file output_h def_string;
-   h_file#close;
-   let depend_referenced = find_referenced_types ctx.ctx_common (TClassDecl class_def) super_deps constructor_deps false true false in
-   depend_referenced;;
-
-
-let write_resources common_ctx =
-
-   let idx = ref 0 in
-
-   Hashtbl.iter (fun _ data ->
-      let id = "__res_" ^ (string_of_int !idx) in
-      let resource_file = new_cpp_file common_ctx common_ctx.file (["resources"],id) in
-      resource_file#write "namespace hx {\n";
-      resource_file#write_i ("unsigned char " ^ id ^ "[] = {\n");
-      resource_file#write_i "0xff, 0xff, 0xff, 0xff,\n";
-      for i = 0 to String.length data - 1 do
-      let code = Char.code (String.unsafe_get data i) in
-         resource_file#write  (Printf.sprintf "%d," code);
-         if ( (i mod 10) = 9) then resource_file#write "\n";
-      done;
-      resource_file#write ("0x00 };\n");
-      incr idx;
-      resource_file#write ("}\n");
-      resource_file#close;
-   ) common_ctx.resources;
-
-
-   let resource_file = new_cpp_file common_ctx common_ctx.file ([],"__resources__") in
-   resource_file#write "#include <hxcpp.h>\n\n";
-   resource_file#write "namespace hx { \n\n";
-
-   idx := 0;
-   Hashtbl.iter (fun _ data ->
-      let id = "__res_" ^ (string_of_int !idx) in
-      resource_file#write_i ("extern unsigned char " ^ id ^ "[];\n");
-      incr idx;
-   ) common_ctx.resources;
-
-   resource_file#write "}\n\n";
-
-   idx := 0;
-   resource_file#write "hx::Resource __Resources[] =";
-   resource_file#begin_block;
-   Hashtbl.iter (fun name data ->
-      let id = "__res_" ^ (string_of_int !idx) in
-      resource_file#write_i
-         ("{ " ^ (str name) ^ "," ^ (string_of_int (String.length data)) ^ "," ^
-            "hx::" ^ id ^ " + 4 },\n");
-      incr idx;
-   ) common_ctx.resources;
-
-   resource_file#write_i "{::String(null()),0,0}";
-   resource_file#end_block_line;
-   resource_file#write ";\n\n";
-   resource_file#write "namespace hx { Resource *GetResources() { return __Resources; } } \n\n";
-   resource_file#close;;
-
-
-
-let write_build_data common_ctx filename classes main_deps boot_deps build_extra extern_src exe_name =
-   let buildfile = open_out filename in
-   let include_prefix = get_include_prefix common_ctx true in
-   let add_class_to_buildfile class_path deps  =
-      let cpp = (join_class_path class_path "/") ^ (source_file_extension common_ctx) in
-      output_string buildfile ( "  <file name=\"src/" ^ cpp ^ "\">\n" );
-      let project_deps = List.filter (fun path -> not (is_internal_class path) ) deps in
-      List.iter (fun path-> output_string buildfile ("   <depend name=\"" ^
-      ( match path with
-         | (["@verbatim"],file) -> file
-         | _ -> "include/" ^ include_prefix ^ (join_class_path path "/") ^ ".h" )
-      ^ "\"/>\n") ) project_deps;
-      output_string buildfile ( "  </file>\n" )
-   in
-   let add_classdef_to_buildfile (class_path, deps, _)  = add_class_to_buildfile class_path deps in
-
-   output_string buildfile "<xml>\n";
-   output_string buildfile ("<set name=\"HXCPP_API_LEVEL\" value=\"" ^
-            (Common.defined_value common_ctx Define.HxcppApiLevel) ^ "\" />\n");
-   output_string buildfile "<files id=\"haxe\">\n";
-   output_string buildfile "<compilerflag value=\"-Iinclude\"/>\n";
-   List.iter add_classdef_to_buildfile classes;
-   add_class_to_buildfile ( [] , "__boot__")  boot_deps;
-   add_class_to_buildfile ( [] , "__files__")  [];
-   add_class_to_buildfile ( [] , "__resources__")  [];
-   output_string buildfile "</files>\n";
-   output_string buildfile "<files id=\"__lib__\">\n";
-   output_string buildfile "<compilerflag value=\"-Iinclude\"/>\n";
-   add_class_to_buildfile ( [] , "__lib__") main_deps;
-   output_string buildfile "</files>\n";
-   output_string buildfile "<files id=\"__main__\">\n";
-   output_string buildfile "<compilerflag value=\"-Iinclude\"/>\n";
-   add_class_to_buildfile  ( [] , "__main__") main_deps;
-   output_string buildfile "</files>\n";
-   output_string buildfile "<files id=\"__resources__\">\n";
-   let idx = ref 0 in
-   Hashtbl.iter (fun _ data ->
-      let id = "__res_" ^ (string_of_int !idx) in
-      output_string buildfile ("<file name=\"src/resources/" ^ id ^ ".cpp\" />\n");
-      incr idx;
-   ) common_ctx.resources;
-   output_string buildfile "</files>\n";
-   output_string buildfile "<files id=\"__externs__\">\n";
-   List.iter (fun src -> output_string buildfile ("<file name=\"" ^src^ "\" />\n") ) extern_src;
-   output_string buildfile "</files>\n";
-   output_string buildfile ("<set name=\"HAXE_OUTPUT\" value=\"" ^ exe_name ^ "\" />\n");
-   output_string buildfile "<include name=\"${HXCPP}/build-tool/BuildCommon.xml\"/>\n";
-   output_string buildfile build_extra;
-   output_string buildfile "</xml>\n";
-   close_out buildfile;;
-
-let write_build_options common_ctx filename defines =
-   let writer = cached_source_writer common_ctx filename in
-   PMap.iter ( fun name value -> match name with
-      | "true" | "sys" | "dce" | "cpp" | "debug" -> ()
-      | _ ->  writer#write (name ^ "="^(escape_command value)^ "\n" ) ) defines;
-   let cmd = Unix.open_process_in "haxelib path hxcpp" in
-   writer#write ("hxcpp=" ^ (Pervasives.input_line cmd));
-   Pervasives.ignore (Unix.close_process_in cmd);
-   writer#close;;
-
-let create_member_types common_ctx =
-   let result = Hashtbl.create 0 in
-   let add_member class_name interface member =
-      match follow member.cf_type, member.cf_kind with
-      | _, Var _ when interface -> ()
-      | _, Method MethDynamic when interface -> ()
-      | TFun (_,ret), _ ->
-         (*print_endline (class_name ^ "." ^ member.cf_name ^ "=" ^  (type_string ret) );*)
-         Hashtbl.add result (class_name ^ "." ^ member.cf_name) (type_string ret)
-      | _,_ when not interface ->
-         Hashtbl.add result (class_name ^ "." ^ member.cf_name) (type_string member.cf_type)
-      | _ -> ()
-      in
-   List.iter (fun object_def ->
-      (match object_def with
-      | TClassDecl class_def ->
-         let class_name = "::" ^ (join_class_path class_def.cl_path "::") in
-         let rec add_all_fields class_def =
-            if class_def.cl_interface then
-               List.iter (fun impl -> add_all_fields (fst impl) ) class_def.cl_implements;
-            (match  class_def.cl_super with Some super -> add_all_fields (fst super) | _->(););
-            List.iter (add_member class_name class_def.cl_interface) class_def.cl_ordered_fields;
-            List.iter (add_member class_name class_def.cl_interface) class_def.cl_ordered_statics
-         in
-         add_all_fields class_def
-      | _ -> ( )
-      ) ) common_ctx.types;
-   result;;
-
-(* Builds inheritance tree, so header files can include parents defs.  *)
-let create_super_dependencies common_ctx =
-   let result = Hashtbl.create 0 in
-   List.iter (fun object_def ->
-      (match object_def with
-      | TClassDecl class_def when not class_def.cl_extern ->
-         let deps = ref [] in
-         (match class_def.cl_super with Some super ->
-            if not (fst super).cl_extern then
-               deps := ((fst super).cl_path) :: !deps
-         | _ ->() );
-         List.iter (fun imp -> if not (fst imp).cl_extern then deps := (fst imp).cl_path :: !deps) (real_interfaces class_def.cl_implements);
-         Hashtbl.add result class_def.cl_path !deps;
-      | TEnumDecl enum_def when not enum_def.e_extern ->
-         Hashtbl.add result enum_def.e_path [];
-      | _ -> () );
-      ) common_ctx.types;
-   result;;
-
-let create_constructor_dependencies common_ctx =
-   let result = Hashtbl.create 0 in
-   List.iter (fun object_def ->
-      (match object_def with
-      | TClassDecl class_def when not class_def.cl_extern ->
-         (match class_def.cl_constructor with
-         | Some func_def -> Hashtbl.add result class_def.cl_path func_def
-         | _ -> () )
-      | _ -> () );
-      ) common_ctx.types;
-   result;;
-
-(*
-
-  Exports can now be done with macros and a class list
-
-let rec s_type t =
-   let result =
-   match t with
-   | TMono r -> (match !r with | None -> "Dynamic" | Some t -> s_type t)
-   | TEnum (e,tl) -> Ast.s_type_path e.e_path ^ s_type_params tl
-   | TInst (c,tl) -> Ast.s_type_path c.cl_path ^ s_type_params tl
-   | TType (t,tl) -> Ast.s_type_path t.t_path ^ s_type_params tl
-   | TAbstract (abs,pl) when abs.a_impl <> None ->
-      s_type (Abstract.get_underlying_type abs pl);
-   | TAbstract (a,tl) -> Ast.s_type_path a.a_path ^ s_type_params tl
-   | TFun ([],t) -> "Void -> " ^ s_fun t false
-   | TFun (l,t) ->
-      String.concat " -> " (List.map (fun (s,b,t) ->
-         (if b then "?" else "") ^ (""(*if s = "" then "" else s ^ " : "*)) ^ s_fun t true
-      ) l) ^ " -> " ^ s_fun t false
-   | TAnon a ->
-   let fl = PMap.fold (fun f acc -> ((if Meta.has Meta.Optional f.cf_meta then " ?" else " ") ^ f.cf_name ^ " : " ^ s_type f.cf_type) :: acc) a.a_fields [] in
-      "{" ^ (if not (is_closed a) then "+" else "") ^  String.concat "," fl ^ " }"
-   | TDynamic t2 -> "Dynamic" ^ s_type_params (if t == t2 then [] else [t2])
-   | TLazy f -> s_type (!f())
-   in
-   if result="Array<haxe.io.Unsigned_char__>" then "haxe.io.BytesData" else result
-
-and s_fun t void =
-   match follow t with
-   | TFun _ -> "(" ^ s_type t ^ ")"
-   | TAbstract ({ a_path = ([],"Void") },[]) when void -> "(" ^ s_type t ^ ")"
-   | TMono r -> (match !r with | None -> s_type t | Some t -> s_fun t void)
-   | TLazy f -> s_fun (!f()) void
-   | _ ->  (s_type t)
-
-and s_type_params = function
-   | [] -> ""
-   | l -> "< " ^ String.concat ", " (List.map s_type  l) ^ " >"
-
-;;
-
-
-let gen_extern_class common_ctx class_def file_info =
-   let file = new_source_file common_ctx common_ctx.file  "extern" ".hx" class_def.cl_path in
-   let path = class_def.cl_path in
-
-   let rec remove_all_prefix class_def field t =
-      let path = class_def.cl_path in
-      let filterPath = fst path @ [snd path] in
-      let rec remove_prefix t = match t with
-         | TInst ({cl_path=[f],suffix } as cval ,tl) when f=field ->
-               TInst ( { cval with cl_path = ([],suffix) }, List.map remove_prefix tl)
-         | TInst ({cl_path=cpath,suffix } as cval ,tl) when cpath=filterPath ->
-               TInst ( { cval with cl_path = ([],suffix) }, List.map remove_prefix tl)
-         | TInst (cval,tl) -> TInst ( cval, List.map remove_prefix tl)
-         (*| TInst ({cl_path=prefix} as cval ,tl) ->
-            TInst ( { cval with cl_path = ([],snd cval.cl_path) }, List.map (remove_prefix field) tl)*)
-         | t -> Type.map remove_prefix t
-      in
-      let t = remove_prefix t in
-      let superred = (match class_def.cl_super with
-         | Some (super,_) -> remove_all_prefix super field t
-         | _ -> t )
-      in
-      List.fold_left ( fun t (impl,_) -> remove_all_prefix impl field t ) superred class_def.cl_implements;
-      (*
-      remove_prefix t
-      *)
-   in
-
-
-   let params = function [] -> "" | l ->  "< " ^ (String.concat "," (List.map (fun (n,t) -> n) l) ^ " >")  in
-   let output = file#write in
-
-   let print_field stat f =
-      let s_type t = s_type (remove_all_prefix class_def f.cf_name t) in
-      let args  = function  TFun (args,_) ->
-         String.concat "," (List.map (fun (name,opt,t) -> (if opt then "?" else "") ^ name ^":"^ (s_type t)) args) | _ -> "" in
-      let ret  = function  TFun (_,ret) -> s_type ret | _ -> "Dynamic" in
-      let override = if (is_override class_def f.cf_name ) then "override " else "" in
-
-      output ("\t" ^ (if stat then "static " else "") ^ (if f.cf_public then "public " else "") );
-      let s_access mode op name = match mode with
-         | AccNormal -> "default"
-         | AccNo -> "null"
-         | AccNever -> "never"
-         | AccResolve -> "resolve"
-         | AccCall -> op ^ "_" ^ name
-         | AccInline -> "default"
-         | AccRequire (n,_) -> "require " ^ n
-      in
-      (match f.cf_kind, f.cf_name with
-      | Var { v_read = AccInline; v_write = AccNever },_ ->
-         (match f.cf_expr with Some expr ->
-            output ("inline var " ^ f.cf_name ^ ":" ^ (s_type f.cf_type) ^ "=" );
-            let ctx = (new_extern_context common_ctx file 1 file_info) in
-            gen_expression ctx true expr;
-         | _ -> ()  )
-      | Var { v_read = AccNormal; v_write = AccNormal },_ -> output ("var " ^ f.cf_name ^ ":" ^ (s_type f.cf_type))
-      | Var v,_ -> output ("var " ^ f.cf_name ^ "(" ^ (s_access v.v_read "get" f.cf_name) ^ "," ^ (s_access v.v_write "set" f.cf_name) ^ "):" ^ (s_type f.cf_type))
-      | Method _, "new" -> output ("function new(" ^ (args f.cf_type) ^ "):Void")
-      | Method MethDynamic, _  -> output ("dynamic function " ^ f.cf_name ^ (params f.cf_params) ^ "(" ^ (args f.cf_type) ^ "):" ^ (ret f.cf_type) )
-      | Method _, _  -> output (override ^ "function " ^ f.cf_name ^ (params f.cf_params) ^ "(" ^ (args f.cf_type) ^ "):" ^ (ret f.cf_type) )
-      );
-      output ";\n\n";
-   in
-
-   let s_type t = s_type (remove_all_prefix class_def "*" t) in
-   let c = class_def in
-   output ( "package " ^ (String.concat "." (fst path)) ^ ";\n" );
-   output ( "@:include extern " ^ (if c.cl_private then "private " else "") ^ (if c.cl_interface then "interface" else "class")
-            ^ " " ^ (snd path) ^ (params c.cl_params) );
-   (match c.cl_super with None -> () | Some (c,pl) -> output (" extends " ^  (s_type (TInst (c,pl)))));
-   List.iter (fun (c,pl) -> output ( " implements " ^ (s_type (TInst (c,pl))))) (real_interfaces c.cl_implements);
-   (match c.cl_dynamic with None -> () | Some t -> output (" implements Dynamic< " ^ (s_type t) ^ " >"));
-   (match c.cl_array_access with None -> () | Some t -> output (" implements ArrayAccess< " ^ (s_type t) ^ " >"));
-   output "{\n";
-   (match c.cl_constructor with
-   | None -> ()
-   | Some f -> print_field false f);
-   let is_public f = f.cf_public in
-   List.iter (print_field false) (List.filter is_public c.cl_ordered_fields);
-   List.iter (print_field true) (List.filter is_public c.cl_ordered_statics);
-   output "}";
-   output "\n";
-   file#close
-;;
-
-
-
-
-let gen_extern_enum common_ctx enum_def file_info =
-   let path = enum_def.e_path in
-   let file = new_source_file common_ctx common_ctx.file  "extern" ".hx" path in
-   let output = file#write in
-
-   let params = function [] -> "" | l ->  "< " ^ (String.concat "," (List.map (fun (n,t) -> n) l) ^ " >")  in
-   output ( "package " ^ (String.concat "." (fst path)) ^ ";\n" );
-   output ( "@:include extern " ^ (if enum_def.e_private then "private " else "")
-            ^ " enum " ^ (snd path) ^ (params enum_def.e_params) );
-   output " {\n";
-   let sorted_items = List.sort (fun f1 f2 -> (f1.ef_index - f2.ef_index ) ) (pmap_values enum_def.e_constrs) in
-   List.iter (fun constructor ->
-      let name = keyword_remap constructor.ef_name in
-      match constructor.ef_type with
-      | TFun (args,_) ->
-         output ( name ^ "(" );
-         output ( String.concat "," (List.map (fun (arg,_,t) -> arg ^ ":" ^ (s_type t) ) args) );
-         output ");\n\n";
-      | _ -> output ( name ^ ";\n\n" )
-   ) sorted_items;
-
-   output "}\n";
-   file#close
-;;
-*)
-
-let is_this expression =
-   match (remove_parens expression).eexpr with
-   | TConst TThis -> true
-   | _ -> false
-;;
-
-let is_super expression =
-   match (remove_parens expression).eexpr with
-   | TConst TSuper -> true
-   | _ -> false
-;;
-
-
-let is_assign_op op =
-   match op with
-   | OpAssign
-   | OpAssignOp _ -> true
-   | _ -> false
-;;
-
-let rec script_type_string haxe_type =
-   match haxe_type with
-   | TType ({ t_path = ([],"Null") },[t]) ->
-      (match follow t with
-      | TAbstract ({ a_path = [],"Int" },_)
-      | TAbstract ({ a_path = [],"Float" },_)
-      | TAbstract ({ a_path = [],"Bool" },_)
-      | TInst ({ cl_path = [],"Int" },_)
-      | TInst ({ cl_path = [],"Float" },_)
-      | TEnum ({ e_path = [],"Bool" },_) -> "Dynamic"
-      | _ -> script_type_string t)
-   | TInst ({cl_path=[],"Null"},[t]) ->
-      (match follow t with
-      | TAbstract ({ a_path = [],"Int" },_)
-      | TAbstract ({ a_path = [],"Float" },_)
-      | TAbstract ({ a_path = [],"Bool" },_)
-      | TInst ({ cl_path = [],"Int" },_)
-      | TInst ({ cl_path = [],"Float" },_)
-      | TEnum ({ e_path = [],"Bool" },_) -> "Dynamic"
-      | _ -> script_type_string t )
-   | _ ->
-      match follow haxe_type with
-      | TType ({t_path = [],"Array"},params) -> "Array"
-      | TInst ({cl_path=[],"Array"},params) ->
-         (match params with
-         | [t] ->
-            (match type_string_suff "" t false with
-            | "int" -> "Array.int"
-            | "Float" -> "Array.Float"
-            | "bool" -> "Array.bool"
-            | "::String" -> "Array.String"
-            | "unsigned char" -> "Array.unsigned char"
-            | "Dynamic" -> "Array.Any"
-            | _ -> "Array.Object"
-            )
-         | _ -> "Array.Object"
-         )
-     | TAbstract (abs,pl) when abs.a_impl <> None ->
-         script_type_string  (Abstract.get_underlying_type abs pl);
-     | _ ->
-         type_string_suff "" haxe_type false
-;;
-
-type array_of =
-   | ArrayInterface of int
-   | ArrayData of string
-   | ArrayObject
-   | ArrayAny
-   | ArrayNone
-;;
-
-let is_template_type t =
-   false
-;;
-
-let rec is_dynamic_in_cppia ctx expr =
-   match expr.eexpr with
-   | TCast(_,None) -> true
-   | _ -> is_dynamic_in_cpp ctx expr
-;;
-
-type cppia_op =
-	| IaFunction
-	| IaVar
-	| IaToInterface
-	| IaToDynArray
-	| IaToDataArray
-	| IaToInterfaceArray
-	| IaFun
-	| IaCast
-	| IaBlock
-	| IaBreak
-	| IaContinue
-	| IaIsNull
-	| IaNotNull
-	| IaSet
-	| IaCall
-	| IaCallGlobal
-	| IaCallStatic
-	| IaCallMember
-	| IaCallSuper
-	| IaCallThis
-	| IaCallSuperNew
-	| IaCreateEnum
-	| IaADef
-	| IaIf
-	| IaIfElse
-	| IaFStatic
-	| IaFName
-	| IaFThisInst
-	| IaFLink
-	| IaFThisName
-	| IaFEnum
-	| IaThrow
-	| IaArrayI
-	| IaPlusPlus
-	| IaPlusPlusPost
-	| IaMinusMinus
-	| IaMinusMinusPost
-	| IaNeg
-	| IaBitNot
-	| IaLogicNot
-	| IaTVars
-	| IaVarDecl
-	| IaVarDeclI
-	| IaNew
-	| IaReturn
-	| IaRetVal
-	| IaPosInfo
-	| IaObjDef
-	| IaClassOf
-	| IaWhile
-	| IaFor
-	| IaEnumI
-	| IaSwitch
-	| IaTry
-	| IaImplDynamic
-   | IaConstInt
-   | IaConstFloat
-   | IaConstString
-   | IaConstFalse
-   | IaConstTrue
-   | IaConstNull
-   | IaConsThis
-   | IaConstSuper
-   | IaCastInt
-   | IaCastBool
-   | IaInterface
-   | IaClass
-   | IaAccessNormal
-   | IaAccessNot
-   | IaAccessResolve
-   | IaAccessCall
-   | IaEnum
-   | IaInline
-   | IaMain
-   | IaNoMain
-   | IaResources
-   | IaReso
-   | IaNoCast
-   | IaAccessCallNative
-
-	| IaBinOp of Ast.binop
-;;
-
-let cppia_op_info = function
-	| IaFunction -> ("FUNCTION", 1)
-	| IaVar      -> ("VAR", 2)
-	| IaToInterface -> ("TOINTERFACE", 3)
-	| IaToDynArray -> ("TODYNARRAY", 4)
-	| IaToDataArray -> ("TODATAARRAY", 5)
-	| IaToInterfaceArray -> ("TOINTERFACEARRAY", 6)
-	| IaFun -> ("FUN", 7)
-	| IaCast -> ("CAST", 8)
-	| IaBlock -> ("BLOCK", 9)
-	| IaBreak -> ("BREAK", 10)
-	| IaContinue -> ("CONTINUE", 11)
-	| IaIsNull -> ("ISNULL", 12)
-	| IaNotNull -> ("NOTNULL", 13)
-	| IaSet -> ("SET", 14)
-	| IaCall -> ("CALL", 15)
-	| IaCallGlobal -> ("CALLGLOBAL", 16)
-	| IaCallStatic -> ("CALLSTATIC", 17)
-	| IaCallMember -> ("CALLMEMBER", 18)
-	| IaCallSuper -> ("CALLSUPER", 19)
-	| IaCallThis -> ("CALLTHIS", 20)
-	| IaCallSuperNew -> ("CALLSUPERNEW", 21)
-	| IaCreateEnum -> ("CREATEENUM", 22)
-	| IaADef -> ("ADEF", 23)
-	| IaIf -> ("IF", 24)
-	| IaIfElse -> ("IFELSE", 25)
-	| IaFName -> ("FNAME", 27)
-	| IaFStatic -> ("FSTATIC", 28)
-	| IaFThisInst -> ("FTHISINST", 29)
-	| IaFLink -> ("FLINK", 30)
-	| IaFThisName -> ("FTHISNAME", 31)
-	| IaFEnum -> ("FENUM", 32)
-	| IaThrow -> ("THROW", 33)
-	| IaArrayI -> ("ARRAYI", 34)
-	| IaPlusPlus -> ("++", 35)
-	| IaPlusPlusPost -> ("+++", 36)
-	| IaMinusMinus -> ("--", 37)
-	| IaMinusMinusPost -> ("---", 38)
-	| IaNeg -> ("NEG", 39)
-	| IaBitNot -> ("~", 40)
-	| IaLogicNot -> ("!", 41)
-	| IaTVars -> ("TVARS", 42)
-	| IaVarDecl -> ("VARDECL", 43)
-	| IaVarDeclI -> ("VARDECLI", 44)
-	| IaNew -> ("NEW", 45)
-	| IaReturn -> ("RETURN", 46)
-	| IaRetVal -> ("RETVAL", 47)
-	| IaPosInfo -> ("POSINFO", 48)
-	| IaObjDef -> ("OBJDEF", 49)
-	| IaClassOf -> ("CLASSOF", 50)
-	| IaWhile -> ("WHILE", 51)
-	| IaFor -> ("FOR", 52)
-	| IaEnumI -> ("ENUMI", 53)
-	| IaSwitch -> ("SWITCH", 54)
-	| IaTry -> ("TRY", 55)
-	| IaImplDynamic -> ("IMPLDYNAMIC", 56)
-   | IaConstInt -> ("i", 57)
-   | IaConstFloat -> ("f", 58)
-   | IaConstString -> ("s", 59)
-   | IaConstFalse -> ("false", 60)
-   | IaConstTrue -> ("true", 61)
-   | IaConstNull -> ("NULL", 62)
-   | IaConsThis -> ("THIS", 63)
-   | IaConstSuper -> ("SUPER", 64)
-   | IaCastInt -> ("CASTINT", 65)
-   | IaCastBool -> ("CASTBOOL", 66)
-   | IaInterface -> ("INTERFACE", 67)
-   | IaClass -> ("CLASS", 68)
-   | IaAccessNormal -> ("N", 69)
-   | IaAccessNot  -> ("n", 70)
-   | IaAccessResolve  -> ("R", 71)
-   | IaAccessCall -> ("C", 72)
-   | IaEnum -> ("ENUM", 73)
-   | IaInline -> ("INLINE", 74)
-   | IaMain -> ("MAIN", 75)
-   | IaNoMain -> ("NOMAIN", 76)
-   | IaResources -> ("RESOURCES", 77)
-   | IaReso -> ("RESO", 78)
-	| IaNoCast -> ("NOCAST", 79)
-   | IaAccessCallNative -> ("V", 80)
-
-	| IaBinOp OpAdd -> ("+", 101)
-	| IaBinOp OpMult -> ("*", 102)
-	| IaBinOp OpDiv -> ("/", 103)
-	| IaBinOp OpSub -> ("-", 104)
-	| IaBinOp OpAssign -> ("=", 105)
-	| IaBinOp OpEq -> ("==", 106)
-	| IaBinOp OpNotEq -> ("!=", 107)
-	| IaBinOp OpGte -> (">=", 108)
-	| IaBinOp OpLte -> ("<=", 109)
-	| IaBinOp OpGt -> (">", 110)
-	| IaBinOp OpLt -> ("<", 111)
-	| IaBinOp OpAnd -> ("&", 112)
-	| IaBinOp OpOr -> ("|", 113)
-	| IaBinOp OpXor -> ("^", 114)
-	| IaBinOp OpBoolAnd -> ("&&", 115)
-	| IaBinOp OpBoolOr -> ("||", 116)
-	| IaBinOp OpShr -> (">>", 117)
-	| IaBinOp OpUShr -> (">>>", 118)
-	| IaBinOp OpShl -> ("<<", 119)
-	| IaBinOp OpMod -> ("%", 120)
-	| IaBinOp OpInterval -> ("...", 121)
-	| IaBinOp OpArrow -> ("=>", 122)
-	| IaBinOp OpAssignOp OpAdd -> ("+=", 201)
-	| IaBinOp OpAssignOp OpMult -> ("*=", 202)
-	| IaBinOp OpAssignOp OpDiv -> ("/=", 203)
-	| IaBinOp OpAssignOp OpSub -> ("-=", 204)
-
-
-	| IaBinOp OpAssignOp OpAnd -> ("&=", 212)
-	| IaBinOp OpAssignOp OpOr -> ("|=", 213)
-	| IaBinOp OpAssignOp OpXor -> ("^=", 214)
-	| IaBinOp OpAssignOp OpBoolAnd -> ("&&=", 215)
-	| IaBinOp OpAssignOp OpBoolOr -> ("||=", 216)
-	| IaBinOp OpAssignOp OpShr -> (">>=", 217)
-	| IaBinOp OpAssignOp OpUShr -> (">>>=", 218)
-	| IaBinOp OpAssignOp OpShl -> ("<<=", 219)
-	| IaBinOp OpAssignOp OpMod -> ("%=", 220)
-
-	| IaBinOp OpAssignOp OpInterval
-	| IaBinOp OpAssignOp OpAssign
-	| IaBinOp OpAssignOp OpEq
-	| IaBinOp OpAssignOp OpNotEq
-	| IaBinOp OpAssignOp OpGte
-	| IaBinOp OpAssignOp OpLte
-	| IaBinOp OpAssignOp OpGt
-	| IaBinOp OpAssignOp OpLt
-	| IaBinOp OpAssignOp OpAssignOp _
-	| IaBinOp OpAssignOp OpArrow -> assert false
-;;
-
-class script_writer common_ctx ctx filename asciiOut =
-   object(this)
-   val debug = asciiOut
-   val indent_str = if asciiOut then "\t" else ""
-   val mutable indent = ""
-   val mutable indents = []
-   val mutable just_finished_block = false
-   val mutable classCount = 0
-   val mutable return_type = TMono(ref None)
-   val buffer = Buffer.create 0
-   val identTable = Hashtbl.create 0
-   val fileTable = Hashtbl.create 0
-   val identBuffer = Buffer.create 0
-
-   method stringId name =
-      try ( Hashtbl.find identTable name )
-      with Not_found -> begin
-         let size = Hashtbl.length identTable in
-         Hashtbl.add identTable name size;
-         Buffer.add_string identBuffer ((string_of_int (String.length name)) ^ " " ^ name ^ "\n");
-         size;
-      end
-   method incClasses = classCount <- classCount +1
-
-   method stringText name = (string_of_int (this#stringId name)) ^ " "
-   val typeTable = Hashtbl.create 0
-   val typeBuffer = Buffer.create 0
-   method typeId name =
-      let name = if name="::hx::Class" then "::Class" else name in
-      try ( Hashtbl.find typeTable name )
-      with Not_found -> begin
-         let size = Hashtbl.length typeTable in
-         Hashtbl.add typeTable name size;
-         Buffer.add_string typeBuffer ((string_of_int (String.length name)) ^ " " ^ name ^ "\n");
-         size;
-      end
-   method write str = if asciiOut then
-          Buffer.add_string buffer str
-      else begin
-         let push i = Buffer.add_char buffer (Char.chr i) in
-         let pushI32 i = push (Int32.to_int (Int32.logand i (Int32.of_int 255))) in
-         List.iter (fun i ->
-            if ((Int32.compare i Int32.zero) >= 0) && ((Int32.compare i (Int32.of_int 254)) < 0) then
-               pushI32 i
-            else if ((Int32.compare i Int32.zero) >= 0) && ((Int32.compare i (Int32.of_int 65536)) < 0) then begin
-               push 254;
-               pushI32 i;
-               pushI32 (Int32.shift_right i 8);
-            end else begin
-               push 255;
-               pushI32 i;
-               pushI32 (Int32.shift_right i 8);
-               pushI32 (Int32.shift_right i 16);
-               pushI32 (Int32.shift_right i 24);
-            end
-         ) (List.map Int32.of_string (Str.split (Str.regexp "[\n\t ]+") str) );
-      end;
-      just_finished_block <- false
-   method typeTextString typeName = (string_of_int (this#typeId typeName)) ^ " "
-   method typeText typeT = (string_of_int (this#typeId (script_type_string typeT))) ^ " "
-   method writeType typeT = this#write (this#typeText typeT)
-   method boolText value = if value then "1" else "0"
-   method writeBool value = this#write (if value then "1 " else "0 ")
-   method staticText value = if value then "1" else "0"
-   method writeData str = Buffer.add_string buffer str;
-   method wint ival = this#write ((string_of_int ival)^" ")
-   method ident name = this#wint (this#stringId name)
-   method instText clazz = match clazz.cl_path with
-      | ([],"Array") -> string_of_int (this#typeId "Array< ::Dynamic >") ^ " "
-      | _ -> this#typeText (TInst(clazz,[]))
-   method instName clazz = this#write (this#instText clazz)
-   method enumText e = this#typeText (TEnum(e,[]))
-   method enumName e = this#write (this#enumText e)
-   method close =
-      let out_file = open_out_bin filename in
-      output_string out_file (if asciiOut then "CPPIA\n" else "CPPIB\n");
-      let idents =  Buffer.contents identBuffer in
-      output_string out_file ((string_of_int (Hashtbl.length identTable)) ^ "\n");
-      output_string out_file idents;
-      let types =  Buffer.contents typeBuffer in
-      output_string out_file ((string_of_int (Hashtbl.length typeTable)) ^ "\n");
-      output_string out_file types;
-      output_string out_file ( (string_of_int classCount) ^ "\n" );
-      let contents = Buffer.contents buffer in
-      output_string out_file contents;
-      close_out out_file
-   method fileId file =
-      try ( Hashtbl.find fileTable file )
-      with Not_found -> begin
-         let stripped_file = strip_file common_ctx file in
-         let result = this#stringId stripped_file in
-         Hashtbl.add fileTable file result;
-         result;
-      end
-   method constText c = match c with
-   | TInt i -> (this#op IaConstInt) ^ (Printf.sprintf "%ld " i)
-   | TFloat f -> (this#op IaConstFloat) ^ (this#stringText f)
-   | TString s -> (this#op IaConstString) ^ (this#stringText s)
-   | TBool true -> (this#op IaConstTrue)
-   | TBool false -> (this#op IaConstFalse)
-   | TNull -> (this#op IaConstNull)
-   | TThis ->  (this#op IaConsThis)
-   | TSuper ->  (this#op IaConstSuper)
-
-   method get_array_type t =
-      match follow t with
-      | TInst ({cl_path=[],"Array"},[param]) ->
-            let typeName = type_string_suff "" param false in
-            (match typeName with
-            | "::String"  -> ArrayData "String"
-            | "int" | "Float" | "bool" | "String" | "unsigned char" ->
-               ArrayData typeName
-            | "cpp::ArrayBase" | "Dynamic" -> ArrayAny
-            | _ when is_interface_type param -> ArrayInterface (this#typeId (script_type_string param))
-            | _ -> ArrayObject
-            )
-      | TAbstract (abs,pl) when abs.a_impl <> None ->
-            this#get_array_type  (Abstract.get_underlying_type abs pl);
-      | _ -> ArrayNone;
-
-   method pushReturn inType =
-      let oldReturnType = return_type in
-      return_type <- inType;
-      fun () -> return_type <- oldReturnType;
-   method fileText file = string_of_int (this#fileId file)
-   method indent_one = this#write indent_str
-   method push_indent = indents <- indent_str::indents; indent <- String.concat "" indents
-   method pop_indent = match indents with
-                     | h::tail -> indents <- tail; indent <- String.concat "" indents
-                     | [] -> indent <- "/*?*/";
-   method write_i x = this#write (indent ^ x)
-   method get_indent = indent
-   method begin_expr = this#push_indent
-   method end_expr = if not just_finished_block then this#write "\n"; this#pop_indent; just_finished_block <- true
-   method op x = match cppia_op_info x with
-      | (name,index) -> (if debug then name else string_of_int index) ^ " "
-   method writeOp o = this#write (this#op o)
-   method writeOpLine o = this#write ((this#op o) ^ "\n")
-   method voidFunc isStatic isDynamic funcName fieldExpression =
-      this#write ( (this#op IaFunction) ^ (this#staticText isStatic) ^ " " ^(this#boolText isDynamic) ^ " " ^(this#stringText funcName) ^ " ");
-      this#write ((this#typeTextString "Void") ^ "0\n");
-         this#gen_expression fieldExpression
-   method func isStatic isDynamic funcName ret args isInterface fieldExpression =
-      this#write ( (this#op IaFunction) ^ (this#staticText isStatic) ^ " " ^(this#boolText isDynamic) ^ " " ^(this#stringText funcName) ^ " ");
-      this#write ((this#typeText ret) ^ (string_of_int (List.length args)) ^ " ");
-      List.iter (fun (name,opt,typ) -> this#write ( (this#stringText name) ^ (this#boolText opt) ^ " " ^ (this#typeText typ) ^ " " )) args;
-      this#write "\n";
-      if (not isInterface) then begin
-         match fieldExpression with
-         | Some ({ eexpr = TFunction function_def } as e) -> this#gen_expression e
-         | _ -> print_endline ("Missing function body for " ^ funcName );
-      end
-   method var readAcc writeAcc isExtern isStatic name varType varExpr =
-      this#write ( (this#op IaVar) ^ (this#staticText isStatic) ^ " " ^ (this#op readAcc) ^ (this#op writeAcc) ^
-         (this#boolText isExtern) ^ " " ^ (this#stringText name)^ (this#typeText varType) ^
-         (match varExpr with Some _ -> "1\n" | _ -> "0\n" ) );
-      match varExpr with
-      | Some expression -> this#gen_expression expression
-      | _ -> ()
-   method implDynamic = this#writeOpLine IaImplDynamic;
-   method writeVar v =
-      this#ident v.v_name;
-      this#wint v.v_id;
-      this#writeBool v.v_capture;
-      this#writeType v.v_type;
-   method writeList prefix len = this#write (prefix ^" "  ^ (string_of_int (len)) ^ "\n");
-   method writePos expr = if debug then
-      this#write ( (this#fileText expr.epos.pfile) ^ "\t" ^ (string_of_int (Lexer.get_error_line expr.epos) ) ^ indent);
-   method checkCast toType expr forceCast fromGenExpression=
-   let write_cast text =
-      if (not fromGenExpression) then
-         this#writePos expr;
-      this#write (text ^"\n" );
-      this#begin_expr;
-      this#gen_expression expr;
-      this#end_expr;
-      true;
-   in
-   let was_cast =
-      if (is_interface_type toType) then begin
-         if (is_dynamic_in_cppia ctx expr) then begin
-            write_cast ( (this#op IaToInterface) ^ (this#typeText toType) ^ " " ^ (this#typeTextString "Dynamic") )
-         end else if (not (is_matching_interface_type toType expr.etype)) then begin
-            write_cast ( (this#op IaToInterface) ^ (this#typeText toType) ^ " " ^ (this#typeText expr.etype) )
-         end else
-            false
-      end else begin
-        let get_array_expr_type expr =
-            if is_dynamic_in_cppia ctx expr then
-               ArrayNone
-            else
-               this#get_array_type expr.etype
-            in
-         match (this#get_array_type toType), (get_array_expr_type expr) with
-         | ArrayAny, _ -> false
-         | ArrayObject, ArrayData _ -> write_cast (this#op IaToDynArray)
-         | ArrayData t, ArrayNone
-         | ArrayData t, ArrayObject
-         | ArrayData t, ArrayAny -> write_cast ((this#op IaToDataArray)  ^ (this#typeTextString ("Array." ^ t)))
-         | ArrayInterface t, ArrayNone
-         | ArrayInterface t, ArrayAny -> write_cast ((this#op IaToInterfaceArray) ^ (string_of_int t))
-         | _,_ -> (* a0,a1 ->
-               let arrayString a =
-                  match a with
-                  | ArrayNone -> "ArrayNone"
-                  | ArrayAny -> "ArrayAny"
-                  | ArrayObject -> "ArrayObject"
-                  | ArrayData _ -> "ArrayData"
-                  | ArrayInterface _ -> "ArrayInterface"
-            in
-            this#write ("NOCAST " ^ (arrayString a0) ^ "=" ^ (arrayString a1));  *)
-            false
-      end
-   in
-
-   if (not was_cast) then begin
-      if (forceCast) then begin
-         let op =match (type_string expr.etype) with
-         | "int" -> IaCastInt
-         | "bool" -> IaCastBool
-         | _ when is_interface_type toType -> IaNoCast
-         | _ -> IaCast
-         in
-         this#writeOpLine op;
-      end;
-      this#gen_expression expr;
-   end
-   method gen_expression expr =
-   let expression = remove_parens expr in
-   this#begin_expr;
-   (*this#write ( (this#fileText expression.epos.pfile) ^ "\t" ^ (string_of_int (Lexer.get_error_line expression.epos) ) ^ indent);*)
-   this#writePos expression;
-   (match expression.eexpr with
-   | TFunction function_def -> this#write ( (this#op IaFun) ^ (this#typeText function_def.tf_type) ^ (string_of_int (List.length function_def.tf_args)) ^ "\n" );
-         List.iter (fun(arg,init) ->
-            this#write (indent ^ indent_str );
-            this#writeVar arg;
-            match init with
-            | Some const -> this#write ("1 " ^ (this#constText const) ^ "\n")
-            | _ -> this#write "0\n";
-         ) function_def.tf_args;
-         let pop = this#pushReturn function_def.tf_type in
-         this#gen_expression function_def.tf_expr;
-         pop ();
-   | TBlock expr_list -> this#writeList (this#op IaBlock) (List.length expr_list);
-         List.iter this#gen_expression expr_list;
-   | TConst const -> this#write (this#constText const)
-   | TBreak -> this#writeOp IaBreak
-   | TContinue -> this#writeOp IaContinue
-
-   | TBinop (op,e1,e2) when op=OpAssign ->
-      this#writeOpLine IaSet;
-      this#gen_expression e1;
-      this#checkCast e1.etype e2 false false;
-   | TBinop (OpEq ,e1, { eexpr = TConst TNull } ) -> this#writeOpLine IaIsNull;
-      this#gen_expression e1;
-   | TBinop (OpNotEq ,e1, { eexpr = TConst TNull }) -> this#writeOpLine IaNotNull;
-      this#gen_expression e1;
-   | TBinop (OpEq , { eexpr = TConst TNull }, e1) -> this#writeOpLine IaIsNull;
-      this#gen_expression e1;
-   | TBinop (OpNotEq, { eexpr = TConst TNull }, e1) -> this#writeOpLine IaNotNull;
-      this#gen_expression e1;
-   | TBinop (op,e1,e2) -> this#writeOpLine (IaBinOp op);
-      this#gen_expression e1;
-      this#gen_expression e2;
-   | TThrow e -> this#writeOpLine IaThrow;
-      this#gen_expression e;
-   | TArrayDecl expr_list ->
-      this#write ( (this#op IaADef) ^ (this#typeText expression.etype) ^ " " ^(string_of_int (List.length expr_list))^"\n");
-      List.iter this#gen_expression expr_list;
-   | TIf (e,e1,e2) ->
-      (match e2 with
-      | None ->
-         this#writeOpLine IaIf;
-         this#gen_expression e;
-         this#gen_expression e1;
-      | Some elze ->
-         this#writeOpLine IaIfElse;
-         this#gen_expression e;
-         this#gen_expression e1;
-         this#gen_expression elze; )
-   | TCall (func, arg_list) ->
-      let argN = (string_of_int (List.length arg_list)) ^ " " in
-      let is_real_function field =
-         match field.cf_kind with
-         | Method MethNormal | Method MethInline-> true
-         | _ -> false;
-      in
-      let gen_call () =
-         (match (remove_parens func).eexpr with
-         | TField ( { eexpr = TLocal  { v_name = "__global__" }}, field ) ->
-                  this#write ( (this#op IaCallGlobal) ^ (this#stringText (field_name field)) ^ argN ^ "\n");
-         | TField (obj,FStatic (class_def,field) ) when is_real_function field ->
-                  this#write ( (this#op IaCallStatic) ^ (this#instText class_def) ^ " " ^ (this#stringText field.cf_name) ^
-                     argN ^ "\n");
-         | TField (obj,FInstance (_,_,field) ) when (is_this obj) && (is_real_function field) ->
-                  this#write ( (this#op IaCallThis) ^ (this#typeText obj.etype) ^ " " ^ (this#stringText field.cf_name) ^
-                     argN ^ "\n");
-         | TField (obj,FInstance (_,_,field) ) when is_super obj ->
-                  this#write ( (this#op IaCallSuper) ^ (this#typeText obj.etype) ^ " " ^ (this#stringText field.cf_name) ^
-                     argN ^ "\n");
-         | TField (obj,FInstance (_,_,field) ) when is_real_function field ->
-                  this#write ( (this#op IaCallMember) ^ (this#typeText obj.etype) ^ " " ^ (this#stringText field.cf_name) ^
-                     argN ^ "\n");
-                  this#gen_expression obj;
-         | TField (obj,FDynamic (name) )  when (is_internal_member name || (type_string obj.etype = "::String" && name="cca") ) ->
-                  this#write ( (this#op IaCallMember) ^ (this#typeText obj.etype) ^ " " ^ (this#stringText name) ^
-                     argN ^ "\n");
-                  this#gen_expression obj;
-         | TConst TSuper -> this#write ((this#op IaCallSuperNew) ^ (this#typeText func.etype) ^ " " ^ argN ^ "\n");
-         | TField (_,FEnum (enum,field)) -> this#write ((this#op IaCreateEnum) ^ (this#enumText enum) ^ " " ^ (this#stringText field.ef_name) ^ argN ^ "\n");
-         | _ -> this#write ( (this#op IaCall) ^ argN ^ "\n");
-                  this#gen_expression func;
-         );
-         let matched_args = match func.etype with
-            | TFun (args,_) ->
-               ( try (
-                  List.iter2 (fun (_,_,protoT) arg -> this#checkCast protoT arg false false)  args arg_list;
-                  true; )
-               with Invalid_argument _ -> (*print_endline "Bad count?";*) false )
-            | _ -> false
-         in
-         if not matched_args then
-            List.iter this#gen_expression arg_list;
-      in
-      (match (remove_parens func).eexpr with
-         | TField(obj,field) when is_array_or_dyn_array obj.etype && (field_name field)="map" ->
-            (match this#get_array_type expression.etype with
-            | ArrayData t ->
-                this#write ( (this#op IaToDataArray) ^ (this#typeTextString ("Array." ^ t)) ^ "\n");
-                this#begin_expr;
-                this#writePos func;
-                gen_call();
-                this#end_expr;
-            | ArrayInterface t ->
-                this#write ( (this#op IaToInterfaceArray) ^ (string_of_int t) ^ "\n");
-                this#begin_expr;
-                this#writePos func;
-                gen_call();
-                this#end_expr;
-            | _ -> gen_call();
-            )
-         | _ -> gen_call();
-      );
-   | TField (obj, acc) ->
-      let typeText = this#typeText obj.etype in
-      (match acc with
-      | FDynamic name -> this#write ( (this#op IaFName) ^ typeText ^ " " ^ (this#stringText name) ^ "\n");
-            this#gen_expression obj;
-      | FStatic (class_def,field) -> this#write ( (this#op IaFStatic)  ^ (this#instText class_def) ^ " " ^ (this#stringText field.cf_name) );
-      | FInstance (_,_,field) when is_this obj -> this#write ( (this#op IaFThisInst) ^ typeText ^ " " ^ (this#stringText field.cf_name) );
-      | FInstance (_,_,field) -> this#write ( (this#op IaFLink) ^ typeText ^ " " ^ (this#stringText field.cf_name) ^ "\n");
-            this#gen_expression obj;
-
-      | FClosure (_,field) when is_this obj -> this#write ( (this#op IaFThisName) ^typeText ^ " " ^  (this#stringText field.cf_name) ^ "\n")
-      | FAnon (field) when is_this obj -> this#write ( (this#op IaFThisName) ^typeText ^ " " ^  (this#stringText field.cf_name) ^ "\n")
-
-      | FClosure (_,field)
-      | FAnon (field) -> this#write ( (this#op IaFName) ^typeText ^ " " ^  (this#stringText field.cf_name) ^ "\n");
-            this#gen_expression obj;
-
-      | FEnum (enum,field) -> this#write ( (this#op IaFEnum)  ^ (this#enumText enum) ^ " " ^ (this#stringText field.ef_name) );
-      )
-   | TArray (e1, e2) -> this#write ((this#op IaArrayI) ^ (this#typeText e1.etype) ^ "\n");
-      this#gen_expression e1;
-      this#gen_expression e2;
-   | TUnop (op, flag, e) ->
-      this#writeOpLine (match op,flag with
-      | Increment, Prefix -> IaPlusPlus
-      | Increment, _ -> IaPlusPlusPost
-      | Decrement, Prefix -> IaMinusMinus
-      | Decrement, _ -> IaMinusMinusPost
-      | Not, _ -> IaLogicNot
-      | Neg, _ -> IaNeg
-      | NegBits, _ -> IaBitNot );
-      this#gen_expression e;
-   (* TODO - lval op-assign local/member/array *)
-   | TLocal var -> this#write ((this#op IaVar) ^ (string_of_int var.v_id) );
-
-   | TVar (tvar,optional_init) ->
-         this#write ( (this#op IaTVars) ^ (string_of_int (1)) ^ "\n");
-            this#write ("\t\t" ^ indent);
-            (match optional_init with
-            | None -> this#writeOp IaVarDecl;
-                     this#writeVar tvar;
-            | Some init ->this#writeOp IaVarDeclI;
-                     let init = remove_parens init in
-                     this#writeVar tvar;
-                     this#write (" " ^ (this#typeText init.etype));
-                     this#write "\n";
-                     this#checkCast tvar.v_type init false false);
-   | TNew (clazz,params,arg_list) ->
-      this#write ((this#op IaNew) ^ (this#typeText (TInst(clazz,params))) ^ (string_of_int (List.length arg_list)) ^ "\n");
-      let rec matched_args clazz = match clazz.cl_constructor, clazz.cl_super with
-         | None, Some super -> matched_args (fst super)
-         | None, _ -> false
-         | Some ctr, _ ->
-            (match ctr.cf_type with
-            | TFun(args,_) ->
-               ( try (
-                  List.iter2 (fun (_,_,protoT) arg -> this#checkCast protoT arg false false)  args arg_list;
-                  true; )
-                 with Invalid_argument _ -> (*print_endline "Bad count?";*) false )
-            | _ -> false
-            )
-      in
-      if not (matched_args clazz) then
-         List.iter this#gen_expression arg_list;
-
-   | TReturn optval -> (match optval with
-         | None -> this#writeOpLine IaReturn;
-         | Some value -> this#write ( (this#op IaRetVal) ^ (this#typeText value.etype) ^ "\n");
-              this#checkCast return_type value false false;
-         )
-   | TObjectDecl (
-      ("fileName" , { eexpr = (TConst (TString file)) }) ::
-         ("lineNumber" , { eexpr = (TConst (TInt line)) }) ::
-            ("className" , { eexpr = (TConst (TString class_name)) }) ::
-               ("methodName", { eexpr = (TConst (TString meth)) }) :: [] ) ->
-            this#write ( (this#op IaPosInfo) ^ (this#stringText file) ^ (Printf.sprintf "%ld" line) ^ " " ^
-                        (this#stringText class_name) ^ " " ^  (this#stringText meth))
-
-   | TObjectDecl values ->this#write ( (this#op IaObjDef) ^ (string_of_int (List.length values)));
-         this#write " ";
-         List.iter (fun (name,_) -> this#write (this#stringText name)  ) values;
-         this#write "\n";
-         List.iter (fun (_,e) -> this#gen_expression e ) values;
-   | TTypeExpr type_expr ->
-         let klass = "::" ^ (join_class_path (t_path type_expr) "::" ) in
-         this#write ((this#op IaClassOf) ^ (string_of_int (this#typeId klass)))
-   | TWhile (e1,e2,flag) -> this#write ( (this#op IaWhile) ^ (if flag=NormalWhile then "1" else "0" ) ^ "\n");
-         this#gen_expression e1;
-         this#gen_expression e2;
-   | TFor (tvar,init,loop) -> this#writeOp IaFor;
-         this#writeVar tvar;
-         this#write "\n";
-         this#gen_expression init;
-         this#gen_expression loop;
-   | TEnumParameter (expr,ef,i) ->
-         let enum = match follow ef.ef_type with
-            | TEnum(en,_) | TFun(_,TEnum(en,_)) -> en
-            | _ -> assert false
-         in
-         this#write ( (this#op IaEnumI) ^ (this#typeText (TEnum(enum,[])) ) ^ (string_of_int i) ^ "\n");
-         this#gen_expression expr;
-   | TSwitch (condition,cases,optional_default)  ->
-         this#write ( (this#op IaSwitch) ^ (string_of_int (List.length cases)) ^ " " ^
-                           (match optional_default with None -> "0" | Some _ -> "1") ^ "\n");
-         this#gen_expression condition;
-         List.iter (fun (cases_list,expression) ->
-            this#writeList ("\t\t\t"^indent) (List.length cases_list);
-            List.iter (fun value -> this#gen_expression value ) cases_list;
-            this#gen_expression expression;
-         ) cases;
-         (match optional_default with None -> () | Some expr -> this#gen_expression expr);
-   | TTry (e,catches)  ->
-         this#writeList (this#op IaTry) (List.length catches);
-         this#gen_expression e;
-         List.iter ( fun (tvar,catch_expr) ->
-            this#write ("\t\t\t"^indent);
-            this#writeVar tvar;
-            this#write "\n";
-            this#gen_expression catch_expr;
-         ) catches;
-   | TCast (cast,None) -> this#checkCast expression.etype cast true true;
-   | TCast (cast,Some _) -> this#checkCast expression.etype cast true true;
-   | TParenthesis _ -> error "Unexpected parens" expression.epos
-   | TMeta(_,_) -> error "Unexpected meta" expression.epos
-   );
-   this#end_expr;
-end;;
-
-let generate_script_class common_ctx script class_def =
-   script#incClasses;
-   script#writeOp (if class_def.cl_interface then IaInterface else IaClass );
-   script#instName class_def;
-   (match class_def.cl_super with
-      | None -> script#ident ""
-      | Some (c,_) -> script#instName c);
-   script#wint (List.length class_def.cl_implements);
-   List.iter (fun(c,_) -> script#instName c) class_def.cl_implements;
-   script#write "\n";
-   (* Looks like some map impl classes have their bodies discarded - not sure best way to filter *)
-   let non_dodgy_function field =
-      class_def.cl_interface ||
-      match field.cf_kind, field.cf_expr with
-      | Var _, _ -> true
-      | Method MethDynamic, _ -> true
-      | Method _, Some _ -> true
-      | _ -> false
-   in
-   let ordered_statics = List.filter non_dodgy_function class_def.cl_ordered_statics in
-   let ordered_fields = List.filter non_dodgy_function class_def.cl_ordered_fields in
-   script#write ((string_of_int ( (List.length ordered_fields) +
-                                 (List.length ordered_statics) +
-                                 (match class_def.cl_constructor with Some _ -> 1 | _ -> 0 ) +
-                                 (if (implement_dynamic_here class_def) then 1 else 0) +
-                                 (match class_def.cl_init with Some _ -> 1 | _ -> 0 ) ) )
-                                 ^ "\n");
-
-   let generate_field isStatic field =
-      match field.cf_kind, follow field.cf_type with
-      | Var { v_read = AccInline; v_write = AccNever },_ ->
-         script#writeOpLine IaInline;
-      | Var v,_ ->
-         let mode_code mode = match mode with
-         | AccNormal -> IaAccessNormal
-         | AccNo -> IaAccessNot
-         | AccNever -> IaAccessNot
-         | AccResolve -> IaAccessResolve
-         | AccCall -> if ( (has_meta_key class_def.cl_meta Meta.NativeProperty) ||
-                           (has_meta_key field.cf_meta Meta.NativeProperty) ||
-                           (Common.defined common_ctx Define.ForceNativeProperty) )
-                         then IaAccessCallNative else IaAccessCall;
-         | AccInline -> IaAccessNormal
-         | AccRequire (_,_) -> IaAccessNormal
-         in
-         let isExtern = is_extern_field field in
-         script#var (mode_code v.v_read) (mode_code v.v_write) isExtern isStatic field.cf_name field.cf_type field.cf_expr
-      | Method MethDynamic, TFun(args,ret) ->
-         script#func isStatic true field.cf_name ret args class_def.cl_interface field.cf_expr
-      | Method _, TFun(args,ret) when field.cf_name="new" ->
-         script#func true false "new" (TInst(class_def,[])) args false field.cf_expr
-      | Method _, TFun (args,ret) ->
-         script#func isStatic false field.cf_name ret args class_def.cl_interface field.cf_expr
-      | Method _, _ -> print_endline ("Unknown method type " ^ (join_class_path class_def.cl_path "." )
-                     ^ "." ^field.cf_name )
-   in
-   (match class_def.cl_constructor with
-      | Some field  -> generate_field true field
-      | _ -> () );
-   (match class_def.cl_init with
-      | Some expression  -> script#voidFunc true false "__init__" expression
-      | _ -> () );
-
-   List.iter (generate_field false) ordered_fields;
-   List.iter (generate_field true) ordered_statics;
-   if (implement_dynamic_here class_def) then
-      script#implDynamic;
-   script#write "\n";
-;;
-
-let generate_script_enum common_ctx script enum_def meta =
-   script#incClasses;
-   let sorted_items = List.sort (fun f1 f2 -> (f1.ef_index - f2.ef_index ) ) (pmap_values enum_def.e_constrs) in
-   script#writeList ((script#op IaEnum) ^ (script#enumText enum_def)) (List.length sorted_items);
-
-   List.iter (fun constructor ->
-      let name = script#stringText constructor.ef_name in
-      match constructor.ef_type with
-      | TFun (args,_) ->
-         script#write ( name ^ " " ^ (string_of_int (List.length args)) );
-         List.iter (fun (arg,_,t) -> script#write ( " " ^ (script#stringText arg) ^ " " ^ (script#typeText t) ) ) args;
-         script#write "\n";
-      | _ -> script#write ( name ^ " 0\n" )
-   ) sorted_items;
-
-   match meta with
-   | Some expr -> script#write "1\n";
-      script#gen_expression expr
-   | _ -> script#write "0\n";
-   script#write "\n"
-;;
-
-
-let generate_cppia common_ctx =
-   let debug = 1 in
-   let null_file = new source_writer common_ctx ignore (fun () -> () ) in
-   let ctx = new_context common_ctx null_file debug (ref PMap.empty) in
-   ctx.ctx_class_member_types <- create_member_types common_ctx;
-   let script = new script_writer common_ctx ctx common_ctx.file common_ctx.debug in
-   ignore (script#stringId "");
-   ignore (script#typeId "");
-
-      List.iter (fun object_def ->
-      (match object_def with
-      | TClassDecl class_def when class_def.cl_extern  ->
-         () (*if (gen_externs) then gen_extern_class common_ctx class_def;*)
-      | TClassDecl class_def ->
-         let is_internal = is_internal_class class_def.cl_path in
-         if (is_internal || (is_macro class_def.cl_meta)) then
-            ( if (debug>1) then print_endline (" internal class " ^ (join_class_path class_def.cl_path ".") ))
-         else begin
-            ctx.ctx_class_name <- "::" ^ (join_class_path class_def.cl_path "::");
-            generate_script_class common_ctx script class_def
-         end
-      | TEnumDecl enum_def when enum_def.e_extern -> ()
-      | TEnumDecl enum_def ->
-         let is_internal = is_internal_class enum_def.e_path in
-         if (is_internal) then
-            (if (debug>1) then print_endline (" internal enum " ^ (join_class_path enum_def.e_path ".") ))
-         else begin
-            let meta = Codegen.build_metadata common_ctx object_def in
-            if (enum_def.e_extern) then
-               (if (debug>1) then print_endline ("external enum " ^  (join_class_path enum_def.e_path ".") ));
-            ctx.ctx_class_name <- "*";
-            generate_script_enum common_ctx script enum_def meta
-         end
-      | TTypeDecl _ | TAbstractDecl _ -> (* already done *) ()
-      );
-   ) common_ctx.types;
-
-   (match common_ctx.main with
-   | None -> script#writeOpLine IaNoMain;
-   | Some e -> script#writeOpLine IaMain;
-         script#gen_expression e
-   );
-
-   script#write ( (script#op IaResources) ^ (string_of_int (Hashtbl.length common_ctx.resources)) ^ "\n");
-   Hashtbl.iter (fun name data ->
-      script#write ((script#op IaReso) ^ (script#stringText name) ^  (string_of_int (String.length data)) ^ "\n");
-   ) common_ctx.resources;
-   Hashtbl.iter (fun _ data -> script#writeData data) common_ctx.resources;
-
-   script#close
-;;
-
-
-(*
- The common_ctx contains the haxe AST in the "types" field and the resources
-*)
-let generate_source common_ctx =
-   make_base_directory common_ctx.file;
-
-   let debug = 1 in
-   let exe_classes = ref [] in
-   let boot_classes = ref [] in
-   let boot_enums = ref [] in
-   let nonboot_classes = ref [] in
-   let init_classes = ref [] in
-   let file_info = ref PMap.empty in
-   let class_text path = join_class_path path "::" in
-   let member_types = create_member_types common_ctx in
-   let super_deps = create_super_dependencies common_ctx in
-   let constructor_deps = create_constructor_dependencies common_ctx in
-   let main_deps = ref [] in
-   let extern_src = ref [] in
-   let build_xml = ref "" in
-   let scriptable = (Common.defined common_ctx Define.Scriptable) in
-
-   List.iter (fun object_def ->
-      (match object_def with
-      | TClassDecl class_def when is_extern_class class_def ->
-         build_xml := !build_xml ^ (get_class_code class_def Meta.BuildXml);
-         let source = get_meta_string_path class_def.cl_meta Meta.SourceFile in
-         if (source<>"") then
-            extern_src := source :: !extern_src;
-      | TClassDecl class_def ->
-         let name =  class_text class_def.cl_path in
-         let is_internal = is_internal_class class_def.cl_path in
-         if (is_internal || (is_macro class_def.cl_meta)) then
-            ( if (debug>1) then print_endline (" internal class " ^ name ))
-         else begin
-            build_xml := !build_xml ^ (get_class_code class_def Meta.BuildXml);
-            if (has_init_field class_def) then
-               init_classes := class_def.cl_path ::  !init_classes;
-            if (has_boot_field class_def) then
-               boot_classes := class_def.cl_path ::  !boot_classes
-            else if not (has_meta_key class_def.cl_meta Meta.NativeGen) then
-               nonboot_classes := class_def.cl_path ::  !nonboot_classes;
-            let deps = generate_class_files common_ctx
-               member_types super_deps constructor_deps class_def file_info scriptable in
-            exe_classes := (class_def.cl_path, deps, object_def)  ::  !exe_classes;
-         end
-      | TEnumDecl enum_def when enum_def.e_extern -> ()
-      | TEnumDecl enum_def ->
-         let name =  class_text enum_def.e_path in
-         let is_internal = is_internal_class enum_def.e_path in
-         if (is_internal) then
-            (if (debug>1) then print_endline (" internal enum " ^ name ))
-         else begin
-            let meta = Codegen.build_metadata common_ctx object_def in
-            if (enum_def.e_extern) then
-               (if (debug>1) then print_endline ("external enum " ^ name ));
-            boot_enums := enum_def.e_path :: !boot_enums;
-            let deps = generate_enum_files common_ctx enum_def super_deps meta file_info in
-            exe_classes := (enum_def.e_path, deps, object_def) :: !exe_classes;
-         end
-      | TTypeDecl _ | TAbstractDecl _ -> (* already done *) ()
-      );
-   ) common_ctx.types;
-
-
-   (match common_ctx.main with
-   | None -> generate_dummy_main common_ctx
-   | Some e ->
-      let main_field = { cf_name = "__main__"; cf_type = t_dynamic; cf_expr = Some e; cf_pos = e.epos; cf_public = true; cf_meta = []; cf_overloads = []; cf_doc = None; cf_kind = Var { v_read = AccNormal; v_write = AccNormal; }; cf_params = [] } in
-      let class_def = { null_class with cl_path = ([],"@Main"); cl_ordered_statics = [main_field] } in
-      main_deps := find_referenced_types common_ctx (TClassDecl class_def) super_deps constructor_deps false true false;
-      generate_main common_ctx member_types super_deps class_def file_info
-   );
-
-   generate_boot common_ctx !boot_enums !boot_classes !nonboot_classes !init_classes;
-
-   generate_files common_ctx file_info;
-
-   write_resources common_ctx;
-
-   (* Output class info if requested *)
-   if (scriptable || (Common.defined common_ctx Define.DllExport) ) then begin
-      let filename =
-         try
-            let value = Common.defined_value common_ctx Define.DllExport in
-            if value="1" then raise Not_found;
-            value
-         with Not_found -> "export_classes.info"
-      in
-      if (filename <> "") then begin
-         let escape s =
-            let b = Buffer.create 0 in
-            for i = 0 to String.length s - 1 do
-               let c = String.unsafe_get s i in
-               match c with
-               | '\\' -> Buffer.add_char b c; Buffer.add_char b c;
-               | ' ' -> Buffer.add_char b '\\'; Buffer.add_char b 's';
-               | '\n' -> Buffer.add_char b '\\'; Buffer.add_char b 'n';
-               | _ -> Buffer.add_char b c;
-            done;
-            Buffer.contents b;
-         in
-
-         let exeClasses = open_out filename in
-         let out = output_string exeClasses in
-         let outline str = output_string exeClasses (str ^ "\n") in
-         let spath path = (join_class_path path ".") in
-         let rec stype = function
-            | TMono r -> (match !r with None -> "Dynamic" | Some t -> stype t)
-            | TAbstract ({ a_path = ([],"Void") },[]) -> "void"
-            | TAbstract ({ a_path = ([],"Bool") },[]) -> "bool"
-            | TAbstract ({ a_path = ([],"Float") },[]) -> "float"
-            | TAbstract ({ a_path = ([],"Int") },[]) -> "int"
-            | TAbstract( { a_path = ([], "EnumValue") }, _  ) -> "Dynamic"
-            | TEnum (enum,params) -> spath enum.e_path
-            | TInst (klass,params) ->
-               (match klass.cl_path, params with
-               (* Array class *)
-               (*|  ([],"Array") when is_dynamic_array_param (List.hd params) -> "Dynamic" *)
-               | _,_ when is_dynamic_type_param klass.cl_kind -> "Dynamic"
-               | ([],"Array"), [t] -> "Array<" ^ (stype t) ^ ">"
-               | (["haxe";"io"],"Unsigned_char__"),_ -> "uint8"
-               | ([],"EnumValue"),_ -> "Dynamic"
-               | ([],"Null"),[t] when cant_be_null t -> "Null<" ^ (stype t) ^ ">"
-               | ([],"Null"),[t] -> (stype t)
-               | _ -> spath klass.cl_path
-               )
-            | TType (type_def,params) ->
-               (match type_def.t_path, params with
-               | ([],"Null"),[t] when cant_be_null t -> "Null<" ^ (stype t) ^ ">"
-               | ([],"Array"), [t] -> "Array< " ^ (stype (follow t) ) ^ " >"
-               | _,_ ->  stype (apply_params type_def.t_params params type_def.t_type)
-               )
-            | TLazy func -> stype ((!func)())
-            | TAbstract (abs,pl) when abs.a_impl <> None ->
-               stype (Abstract.get_underlying_type abs pl)
-            | TAbstract (abs,_) -> spath abs.a_path
-            | TFun (args,ret) -> "fun<" ^ (List.fold_left (fun s (_,opt,t) -> s ^ (if opt then "?" else "") ^ (stype t) ^ ",") "" args) ^ (stype ret) ^ ">"
-            | _ -> "Dynamic"
-            in
-         List.iter (fun (name,_,def) ->
-            match def with
-            | TClassDecl class_def ->
-                outline ((if class_def.cl_interface then "interface " else "class ") ^ (spath name) );
-                (match class_def.cl_super with
-                | Some (super,_) -> outline ("super " ^ (spath super.cl_path) )
-                | _ -> () );
-                List.iter ( fun(c,_) -> out ("implements " ^ (spath c.cl_path) ^ "\n") ) class_def.cl_implements;
-                (match class_def.cl_dynamic with None -> () | Some t -> outline ("implementsdynamic " ^ (stype t)));
-                (match class_def.cl_array_access with None -> () | Some t -> outline ("arrayaccess " ^ (stype t)));
-
-                let args  = function
-                   | TFun (args,_) ->
-                       List.iter  (fun (name,opt,t) ->
-                          outline ("arg " ^ name ^ (if opt then " ? " else " : ") ^ (stype t) )
-                       ) args;
-                   | _ -> () in
-                let ret  = function  TFun (_,ret) -> stype ret | _ -> "Dynamic" in
-
-                let print_field stat f =
-                   let pub = if f.cf_public then "pub " else "priv " in
-                   let stat = pub ^ ( if stat then "s " else "m " ) in
-                   (match f.cf_kind, f.cf_name with
-                   | Var { v_read = AccInline; v_write = AccNever },_ ->
-                        outline ("inlinevar " ^ f.cf_name ^ " " ^ (stype f.cf_type) )
-                   | Var { v_read = AccNormal; v_write = AccNormal },_ ->
-                        outline ("var " ^ stat ^ f.cf_name ^ " " ^ (stype f.cf_type) )
-                   | Var v,_ ->
-                        let saccess = function | AccNormal -> "v" | AccNo -> "0" | AccNever -> "!"
-                           | AccResolve -> "r" | AccCall -> "c" | AccInline -> "i" | AccRequire (_,_) -> "v" in
-                        outline ("property " ^ stat ^ (saccess v.v_read) ^ " " ^ (saccess v.v_write)
-                           ^ " " ^ f.cf_name ^ " " ^ (stype f.cf_type) )
-                   | Method _, "new" ->
-                        outline ("function " ^ stat ^ "new " ^ (ret f.cf_type) );
-                        args f.cf_type
-                   | Method MethDynamic, _  ->
-                        outline ("dynamicfunction " ^ stat ^ f.cf_name ^ " " ^ (ret f.cf_type) );
-                        args f.cf_type
-                   | Method _, _  ->
-                        outline ("function " ^ stat ^ f.cf_name ^ " " ^ (ret f.cf_type) );
-                        args f.cf_type
-                  ) in
-                (match class_def.cl_constructor with | None -> () | Some f -> print_field false f);
-                List.iter (print_field false) class_def.cl_ordered_fields;
-                List.iter (print_field true) class_def.cl_ordered_statics;
-            | TEnumDecl enum_def ->
-                out ("enum " ^ (spath name) ^ "\n");
-                let sorted_items = List.sort (fun f1 f2 -> (f1.ef_index - f2.ef_index ) ) (pmap_values enum_def.e_constrs) in
-                List.iter (fun constructor ->
-                   outline ("constructor " ^ constructor.ef_name);
-                   match constructor.ef_type with
-                   | TFun (args,_) -> List.iter (fun (arg,_,t) -> outline ("eparam " ^ arg ^ " " ^ (stype t) ) ) args;
-                   | _ -> ()
-                ) sorted_items;
-            | _ -> ()
-            ) !exe_classes;
-
-         (* Output file info too *)
-         List.iter ( fun file ->
-               let full_path = Common.get_full_path (try Common.find_file common_ctx file with Not_found -> file) in
-               out ("file " ^ (escape file) ^ " " ^ (escape full_path) ^"\n") )
-            ( List.sort String.compare ( pmap_keys !file_info) );
-         close_out exeClasses;
-     end;
-   end;
-
-   let output_name = match  common_ctx.main_class with
-   | Some path -> (snd path)
-   | _ -> "output" in
-
-   write_build_data common_ctx (common_ctx.file ^ "/Build.xml") !exe_classes !main_deps (!boot_enums@ !boot_classes) !build_xml !extern_src output_name;
-   let cmd_defines = ref "" in
-   PMap.iter ( fun name value -> match name with
-      | "true" | "sys" | "dce" | "cpp" | "debug" -> ()
-      | _ -> cmd_defines := !cmd_defines ^ " -D" ^ name ^ "=\"" ^ (escape_command value) ^ "\"" ) common_ctx.defines;
-   write_build_options common_ctx (common_ctx.file ^ "/Options.txt") common_ctx.defines;
-   if ( not (Common.defined common_ctx Define.NoCompilation) ) then begin
-      let old_dir = Sys.getcwd() in
-      Sys.chdir common_ctx.file;
-      let cmd = ref "haxelib run hxcpp Build.xml haxe" in
-      if (common_ctx.debug) then cmd := !cmd ^ " -Ddebug";
-      cmd := !cmd ^ !cmd_defines;
-      cmd := List.fold_left (fun cmd path -> cmd ^ " -I\"" ^ (escape_command path) ^ "\"" ) !cmd common_ctx.class_path;
-      print_endline !cmd;
-      if common_ctx.run_command !cmd <> 0 then failwith "Build failed";
-      Sys.chdir old_dir;
-   end
-   ;;
-
-let generate common_ctx =
-   if (Common.defined common_ctx Define.Cppia) then
-      generate_cppia common_ctx
-   else
-      generate_source common_ctx
-;;
-
-

+ 156 - 2
haxe.hxproj

@@ -150,15 +150,169 @@
     <hidden path="appveyor.yml" />
     <hidden path="version.ml" />
     <hidden path="lib" />
+    <hidden path="Makefile.version_extra" />
+    <hidden path="genheaps.cmx" />
+    <hidden path="genheaps.obj" />
+    <hidden path="genheaps.cmi" />
+    <hidden path="genhl.cmx" />
+    <hidden path="genhl.obj" />
+    <hidden path="genhl.cmi" />
+    <hidden path="src\version.obj" />
+    <hidden path="src\version.cmi" />
+    <hidden path="src\version.cmx" />
+    <hidden path="src\main.obj" />
+    <hidden path="src\main.cmx" />
+    <hidden path="src\main.cmi" />
+    <hidden path="src\typing\typer.obj" />
+    <hidden path="src\typing\typer.cmx" />
+    <hidden path="src\typing\typer.cmi" />
+    <hidden path="src\typing\typeload.obj" />
+    <hidden path="src\typing\typeload.cmx" />
+    <hidden path="src\typing\typeload.cmi" />
+    <hidden path="src\typing\typecore.obj" />
+    <hidden path="src\typing\typecore.cmx" />
+    <hidden path="src\typing\typecore.cmi" />
+    <hidden path="src\typing\type.obj" />
+    <hidden path="src\typing\type.cmx" />
+    <hidden path="src\typing\type.cmi" />
+    <hidden path="src\typing\matcher.obj" />
+    <hidden path="src\typing\matcher.cmx" />
+    <hidden path="src\typing\matcher.cmi" />
+    <hidden path="src\typing\common.obj" />
+    <hidden path="src\typing\common.cmx" />
+    <hidden path="src\typing\common.cmi" />
+    <hidden path="src\syntax\parser.obj" />
+    <hidden path="src\syntax\parser.cmx" />
+    <hidden path="src\syntax\parser.cmi" />
+    <hidden path="src\syntax\lexer.obj" />
+    <hidden path="src\syntax\lexer.ml" />
+    <hidden path="src\syntax\lexer.cmx" />
+    <hidden path="src\syntax\lexer.cmi" />
+    <hidden path="src\syntax\ast.obj" />
+    <hidden path="src\syntax\ast.cmx" />
+    <hidden path="src\syntax\ast.cmi" />
+    <hidden path="src\optimization\optimizer.obj" />
+    <hidden path="src\optimization\optimizer.cmx" />
+    <hidden path="src\optimization\optimizer.cmi" />
+    <hidden path="src\optimization\filters.obj" />
+    <hidden path="src\optimization\filters.cmx" />
+    <hidden path="src\optimization\filters.cmi" />
+    <hidden path="src\optimization\dce.obj" />
+    <hidden path="src\optimization\dce.cmi" />
+    <hidden path="src\optimization\analyzer.obj" />
+    <hidden path="src\optimization\dce.cmx" />
+    <hidden path="src\optimization\analyzer.cmx" />
+    <hidden path="src\optimization\analyzer.cmi" />
+    <hidden path="src\macro\interp.obj" />
+    <hidden path="src\macro\interp.cmx" />
+    <hidden path="src\macro\interp.cmi" />
+    <hidden path="src\generators\genxml.obj" />
+    <hidden path="src\generators\genxml.cmx" />
+    <hidden path="src\generators\genxml.cmi" />
+    <hidden path="src\generators\genswf9.obj" />
+    <hidden path="src\generators\genswf9.cmi" />
+    <hidden path="src\generators\genswf9.cmx" />
+    <hidden path="src\generators\genswf.obj" />
+    <hidden path="src\generators\genswf.cmx" />
+    <hidden path="src\generators\genswf.cmi" />
+    <hidden path="src\generators\genpy.obj" />
+    <hidden path="src\generators\genpy.cmx" />
+    <hidden path="src\generators\genpy.cmi" />
+    <hidden path="src\generators\genphp.obj" />
+    <hidden path="src\generators\genphp.cmx" />
+    <hidden path="src\generators\genneko.obj" />
+    <hidden path="src\generators\genphp.cmi" />
+    <hidden path="src\generators\genneko.cmx" />
+    <hidden path="src\generators\genneko.cmi" />
+    <hidden path="src\generators\genjs.obj" />
+    <hidden path="src\generators\genjs.cmx" />
+    <hidden path="src\generators\genjs.cmi" />
+    <hidden path="src\generators\genjava.obj" />
+    <hidden path="src\generators\genjava.cmx" />
+    <hidden path="src\generators\genjava.cmi" />
+    <hidden path="src\generators\genhl.obj" />
+    <hidden path="src\generators\genhl.cmx" />
+    <hidden path="src\generators\genhl.cmi" />
+    <hidden path="src\generators\gencs.obj" />
+    <hidden path="src\generators\gencs.cmx" />
+    <hidden path="src\generators\gencs.cmi" />
+    <hidden path="src\generators\gencpp.obj" />
+    <hidden path="src\generators\gencpp.cmx" />
+    <hidden path="src\generators\gencpp.cmi" />
+    <hidden path="src\generators\gencommon.obj" />
+    <hidden path="src\generators\gencommon.cmx" />
+    <hidden path="src\generators\gencommon.cmi" />
+    <hidden path="src\generators\genas3.obj" />
+    <hidden path="src\generators\genas3.cmx" />
+    <hidden path="src\generators\genas3.cmi" />
+    <hidden path="src\generators\codegen.obj" />
+    <hidden path="src\generators\codegen.cmx" />
+    <hidden path="src\generators\codegen.cmi" />
+    <hidden path="src\optimization\analyzerConfig.cmi" />
+    <hidden path="src\optimization\analyzerConfig.cmx" />
+    <hidden path="src\optimization\analyzerConfig.obj" />
+    <hidden path="src\optimization\analyzerTexpr.cmi" />
+    <hidden path="src\optimization\analyzerTexpr.cmx" />
+    <hidden path="src\optimization\analyzerTexpr.obj" />
+    <hidden path="src\optimization\analyzerTexprTransformer.cmi" />
+    <hidden path="src\optimization\analyzerTexprTransformer.cmx" />
+    <hidden path="src\optimization\analyzerTexprTransformer.obj" />
+    <hidden path="src\optimization\analyzerTypes.cmi" />
+    <hidden path="src\optimization\analyzerTypes.cmx" />
+    <hidden path="src\optimization\analyzerTypes.obj" />
+    <hidden path="src\json.cmi" />
+    <hidden path="src\json.cmx" />
+    <hidden path="src\json.o" />
+    <hidden path="src\main.o" />
+    <hidden path="src\version.o" />
+    <hidden path="src\display\display.cmx" />
+    <hidden path="src\display\display.o" />
+    <hidden path="src\display\display.cmi" />
+    <hidden path="src\generators\codegen.o" />
+    <hidden path="src\generators\genas3.o" />
+    <hidden path="src\generators\gencommon.o" />
+    <hidden path="src\generators\gencpp.o" />
+    <hidden path="src\generators\gencs.o" />
+    <hidden path="src\generators\genhl.o" />
+    <hidden path="src\generators\genjava.o" />
+    <hidden path="src\generators\genjs.o" />
+    <hidden path="src\generators\genlua.cmi" />
+    <hidden path="src\generators\genlua.cmx" />
+    <hidden path="src\generators\genlua.o" />
+    <hidden path="src\generators\genneko.o" />
+    <hidden path="src\generators\genphp.o" />
+    <hidden path="src\generators\genpy.o" />
+    <hidden path="src\generators\genswf.o" />
+    <hidden path="src\generators\genswf9.o" />
+    <hidden path="src\generators\genxml.o" />
+    <hidden path="src\macro\interp.o" />
+    <hidden path="src\optimization\analyzer.o" />
+    <hidden path="src\optimization\analyzerConfig.o" />
+    <hidden path="src\optimization\analyzerTexpr.o" />
+    <hidden path="src\optimization\analyzerTexprTransformer.o" />
+    <hidden path="src\optimization\analyzerTypes.o" />
+    <hidden path="src\optimization\dce.o" />
+    <hidden path="src\optimization\filters.o" />
+    <hidden path="src\optimization\optimizer.o" />
+    <hidden path="src\syntax\ast.o" />
+    <hidden path="src\syntax\lexer.o" />
+    <hidden path="src\syntax\parser.o" />
+    <hidden path="src\typing\common.o" />
+    <hidden path="src\typing\matcher.o" />
+    <hidden path="src\typing\type.o" />
+    <hidden path="src\typing\typecore.o" />
+    <hidden path="src\typing\typeload.o" />
+    <hidden path="src\typing\typer.o" />
+    <hidden path=".travis" />
   </hiddenPaths>
   <!-- Executed before build -->
-  <preBuildCommand>make -j4 MSVC=1 FD_OUTPUT=1 -f Makefile.win kill haxe</preBuildCommand>
+  <preBuildCommand>make -j4 FD_OUTPUT=1 -f Makefile.win kill haxe</preBuildCommand>
   <!-- Executed after build -->
   <postBuildCommand alwaysRun="False" />
   <!-- Other project options -->
   <options>
     <option showHiddenPaths="False" />
-    <option testMovie="Default" />
+    <option testMovie="Custom" />
     <option testMovieCommand="" />
   </options>
   <!-- Plugin storage -->

+ 1 - 1
libs

@@ -1 +1 @@
-Subproject commit 7a83e902634e1db204f6e3a48c2439f63d83c141
+Subproject commit 39f1f79b52fbc02d3b89cda6cad4f08dd88b849a

+ 0 - 1454
matcher.ml

@@ -1,1454 +0,0 @@
-(*
- * Copyright (C)2005-2013 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 Ast
-open Common
-open Type
-open Typecore
-
-type pvar = tvar * pos
-
-type con_def =
-	| CEnum of tenum * tenum_field
-	| CConst of tconstant
-	| CAny
-	| CType of module_type
-	| CArray of int
-	| CFields of int * (string * tclass_field) list
-	| CExpr of texpr
-
-and con = {
-	c_def : con_def;
-	c_type : t;
-	c_pos : pos;
-}
-
-and st_def =
-	| SVar of tvar
-	| SField of st * tclass_field
-	| SEnum of st * tenum_field * int
-	| SArray of st * int
-	| STuple of st * int * int
-
-and st = {
-	st_def : st_def;
-	st_type : t;
-	st_pos : pos;
-}
-
-and dt =
-	| Switch of st * (con * dt) list
-	| Bind of ((tvar * pos) * st) list * dt
-	| Goto of int
-	| Expr of int
-	| Guard of int * dt * dt option
-
-(* Pattern *)
-
-type pat_def =
-	| PAny
-	| PVar of pvar
-	| PCon of con * pat list
-	| POr of pat * pat
-	| PBind of pvar * pat
-	| PTuple of pat array
-
-and pat = {
-	p_def : pat_def;
-	p_type : t;
-	p_pos : pos;
-}
-
-type out = {
-	mutable o_pos : pos;
-	o_id : int;
-	o_catch_all : bool;
-	mutable o_num_paths : int;
-}
-
-type pat_vec = pat array * out
-type pat_matrix = pat_vec list
-
-(* Context *)
-
-type pattern_ctx = {
-	mutable pc_locals : (string, pvar) PMap.t;
-	mutable pc_sub_vars : (string, pvar) PMap.t option;
-	mutable pc_reify : bool;
-	mutable pc_is_complex : bool;
-}
-
-type matcher = {
-	ctx : typer;
-	need_val : bool;
-	dt_lut : dt DynArray.t;
-	dt_cache : (dt,int) Hashtbl.t;
-	mutable dt_count : int;
-	mutable outcomes : out list;
-	mutable toplevel_or : bool;
-	mutable has_extractor : bool;
-	mutable expr_map : (int,texpr * texpr option) PMap.t;
-	mutable is_exhaustive : bool;
-}
-
-type type_finiteness =
-	| Infinite (* type has inifite constructors (e.g. Int, String) *)
-	| CompileTimeFinite (* type is considered finite only at compile-time but has inifite possible run-time values (enum abstracts) *)
-	| RunTimeFinite (* type is truly finite (Bool, enums) *)
-
-exception Not_exhaustive of pat * st
-exception Unrecognized_pattern of Ast.expr
-
-let arity con = match con.c_def with
-	| CEnum (_,{ef_type = TFun(args,_)}) -> List.length args
-	| CEnum _ -> 0
-	| CConst _ -> 0
-	| CType mt -> 0
-	| CArray i -> i
-	| CFields (i,_) -> i
-	| CExpr _ -> 0
-	| CAny -> 0
-
-let mk_st def t p = {
-	st_def = def;
-	st_type = t;
-	st_pos = p;
-}
-
-let mk_out mctx id e eg is_catch_all p =
-	let out = {
-		o_pos = p;
-		o_id = id;
-		o_catch_all = is_catch_all;
-		o_num_paths = 0;
-	} in
-	mctx.outcomes <- out :: mctx.outcomes;
-	mctx.expr_map <- PMap.add id (e,eg) mctx.expr_map;
-	out
-
-let clone_out mctx out p =
-	let out = {out with o_pos = p; } in
-	mctx.outcomes <- out :: mctx.outcomes;
-	out
-
-let get_guard mctx id =
-	snd (PMap.find id mctx.expr_map)
-
-let get_expr mctx id =
-	fst (PMap.find id mctx.expr_map)
-
-let mk_pat pdef t p = {
-	p_def = pdef;
-	p_type = t;
-	p_pos = p;
-}
-
-let mk_con cdef t p = {
-	c_def = cdef;
-	c_type = t;
-	c_pos = p;
-}
-
-let mk_con_pat cdef pl t p = mk_pat (PCon(mk_con cdef t p,pl)) t p
-
-let mk_any t p = mk_pat PAny t p
-
-let any = mk_any t_dynamic Ast.null_pos
-
-let fake_tuple_type = TInst(mk_class null_module ([],"-Tuple") null_pos, [])
-
-let mk_type_pat ctx mt t p =
-	let rec loop = function
-		| TClassDecl _ -> "Class"
-		| TEnumDecl _ -> "Enum"
-		| TAbstractDecl a when Meta.has Meta.RuntimeValue a.a_meta -> "Class"
-		| TTypeDecl t ->
-			begin match follow (monomorphs t.t_params t.t_type) with
-				| TInst(c,_) -> loop (TClassDecl c)
-				| TEnum(en,_) -> loop (TEnumDecl en)
-				| TAbstract(a,_) -> loop (TAbstractDecl a)
-				| _ -> error "Cannot use this type as a value" p
-			end
-		| _ -> error "Cannot use this type as a value" p
-	in
-	let tcl = Typeload.load_instance ctx {tname=loop mt;tpackage=[];tsub=None;tparams=[]} p true in
-	let t2 = match tcl with TAbstract(a,_) -> TAbstract(a,[mk_mono()]) | _ -> assert false in
-	unify ctx t t2 p;
-	mk_con_pat (CType mt) [] t2 p
-
-let mk_subs st con =
-	let map = match follow st.st_type with
-		| TInst(c,pl) -> apply_params c.cl_params pl
-		| TEnum(en,pl) -> apply_params en.e_params pl
-		| TAbstract(a,pl) -> apply_params a.a_params pl
-		| _ -> fun t -> t
-	in
-	match con.c_def with
-	| CFields (_,fl) -> List.map (fun (s,cf) -> mk_st (SField(st,cf)) (map cf.cf_type) st.st_pos) fl
-	| CEnum (en,({ef_type = TFun _} as ef)) ->
-		let rec loop t = match follow t with
-			| TEnum(_,pl) -> pl
-			| TAbstract({a_path = [],"EnumValue"},[]) -> []
-			| TAbstract(a,pl) -> loop (Abstract.get_underlying_type a pl)
-			| _ -> []
-		in
-		let pl = loop con.c_type in
-		begin match apply_params en.e_params pl (monomorphs ef.ef_params ef.ef_type) with
-			| TFun(args,r) ->
-				ExtList.List.mapi (fun i (_,_,t) ->
-					mk_st (SEnum(st,ef,i)) t st.st_pos
-				) args
-			| _ ->
-				assert false
-		end
-	| CArray 0 -> []
-	| CArray i ->
-		let t = match follow con.c_type with TInst({cl_path=[],"Array"},[t]) -> t | TDynamic _ as t -> t | _ -> assert false in
-		ExtList.List.init i (fun i -> mk_st (SArray(st,i)) t st.st_pos)
-	| CEnum _ | CConst _ | CType _ | CExpr _ | CAny ->
-		[]
-
-let get_tuple_params t = match t with
-	| TFun(tl,tr) when tr == fake_tuple_type -> Some tl
-	| _ -> None
-
-(* Printing *)
-
-let s_type = s_type (print_context())
-
-let rec s_con con = match con.c_def with
-	| CEnum(_,ef) -> ef.ef_name
-	| CAny -> "_"
-	| CConst c -> s_const c
-	| CType mt -> s_type_path (t_path mt)
-	| CArray i -> "[" ^(string_of_int i) ^ "]"
-	| CFields (_,fl) -> String.concat "," (List.map (fun (s,_) -> s) fl)
-	| CExpr e -> s_expr s_type e
-
-let rec s_pat pat = match pat.p_def with
-	| PVar (v,_) -> v.v_name
-	| PCon (c,[]) -> s_con c
-	| PCon (c,pl) -> s_con c ^ "(" ^ (String.concat "," (List.map s_pat pl)) ^ ")"
-	| POr (pat1,pat2) -> s_pat pat1 ^ " | " ^ s_pat pat2
-	| PAny -> "_"
-	| PBind((v,_),pat) -> v.v_name ^ "=" ^ s_pat pat
-	| PTuple pl -> "(" ^ (String.concat " " (Array.to_list (Array.map s_pat pl))) ^ ")"
-
-let rec s_pat_vec pl =
-	String.concat " " (Array.to_list (Array.map s_pat pl))
-
-let rec s_pat_matrix pmat =
-	String.concat "\n" (List.map (fun (pl,out) -> (s_pat_vec pl) ^ "->" ^ "") pmat)
-
-let st_args l r v =
-	(if l > 0 then (String.concat "," (ExtList.List.make l "_")) ^ "," else "")
-	^ v ^
-	(if r > 0 then "," ^ (String.concat "," (ExtList.List.make r "_")) else "")
-
-let rec s_st st =
-	(match st.st_def with
-	| SVar v -> v.v_name
-	| SEnum (st,ef,i) -> s_st st ^ "." ^ ef.ef_name ^ "." ^ (string_of_int i)
-	| SArray (st,i) -> s_st st ^ "[" ^ (string_of_int i) ^ "]"
-	| STuple (st,i,a) -> "(" ^ (st_args i (a - i - 1) (s_st st)) ^ ")"
-	| SField (st,cf) -> s_st st ^ "." ^ cf.cf_name)
-
-(* Pattern parsing *)
-
-let unify_enum_field en pl ef t =
-	let t2 = match follow ef.ef_type with
-		| TFun(_,r) -> r
-		| t2 -> t2
-	in
-	let t2 = (apply_params en.e_params pl (monomorphs ef.ef_params t2)) in
-	Type.unify t2 t
-
-let unify ctx a b p =
-	try unify_raise ctx a b p with Error (Unify l,p) -> error (error_msg (Unify l)) p
-
-let rec is_value_type = function
-	| TMono r ->
-		(match !r with None -> false | Some t -> is_value_type t)
-	| TType (t,tl) ->
-		is_value_type (apply_params t.t_params tl t.t_type)
-	| TInst({cl_path=[],"String"},[]) ->
-		true
-	| TAbstract _ ->
-		true
-	| _ ->
-		false
-
-(* 	Determines if a type allows null-matching. This is similar to is_nullable, but it infers Null<T> on monomorphs *)
-let rec matches_null ctx t = match t with
-	| TMono r ->
-		(match !r with None -> r := Some (ctx.t.tnull (mk_mono())); true | Some t -> matches_null ctx t)
-	| TType ({ t_path = ([],"Null") },[_]) ->
-		true
-	| TLazy f ->
-		matches_null ctx (!f())
-	| TType (t,tl) ->
-		matches_null ctx (apply_params t.t_params tl t.t_type)
-	| TFun _ ->
-		false
-	| TAbstract (a,_) -> not (Meta.has Meta.NotNull a.a_meta)
-	| _ ->
-		true
-
-let to_pattern ctx e t =
-	let perror p = error "Unrecognized pattern" p in
-	let verror n p = error ("Variable " ^ n ^ " must appear exactly once in each sub-pattern") p in
-	let mk_var tctx s t p =
-		let v = match tctx.pc_sub_vars with
-			| Some vmap -> fst (try PMap.find s vmap with Not_found -> verror s p)
-			| None -> alloc_var s t
-		in
-		unify ctx t v.v_type p;
-		if PMap.mem s tctx.pc_locals then verror s p;
-		tctx.pc_locals <- PMap.add s (v,p) tctx.pc_locals;
-		v
-	in
-	let check_texpr_pattern e t p =
-		let ec = match Optimizer.make_constant_expression ctx ~concat_strings:true e with Some e -> e | None -> e in
-		match ec.eexpr with
-			| TField (_,FEnum (en,ef)) ->
-				begin try unify_raise ctx ec.etype t ec.epos with Error (Unify _,_) -> raise Not_found end;
-				begin try
-					unify_enum_field en (List.map (fun _ -> mk_mono()) en.e_params) ef t;
-				with Unify_error l ->
-					error (error_msg (Unify l)) p
-				end;
-				mk_con_pat (CEnum(en,ef)) [] t p
-			| TConst c | TCast({eexpr = TConst c},None) ->
-				begin try unify_raise ctx ec.etype t ec.epos with Error (Unify _,_) -> raise Not_found end;
-				unify ctx ec.etype t p;
-				mk_con_pat (CConst c) [] t p
-			| TTypeExpr mt ->
-				mk_type_pat ctx mt t p
-			| _ ->
-				raise Not_found
-	in
-	let rec loop pctx e t =
-		let p = pos e in
-		match fst e with
-		| ECheckType(e, CTPath({tpackage=["haxe";"macro"]; tname="Expr"})) ->
-			let old = pctx.pc_reify in
-			pctx.pc_reify <- true;
-			let e = loop pctx e t in
-			pctx.pc_reify <- old;
-			e
-		| EParenthesis e ->
-			loop pctx e t
-		| ECast(e1,None) ->
-			loop pctx e1 t
-		| EConst(Ident "null") ->
-			if not (matches_null ctx t) then error ("Null-patterns are only allowed on nullable types (found " ^ (s_type t) ^ ")") p;
-			mk_con_pat (CConst TNull) [] t p
-		| EConst((Ident ("false" | "true") | Int _ | String _ | Float _) as c) ->
-			let e = Codegen.type_constant ctx.com c p in
-			unify ctx e.etype t p;
-			let c = match e.eexpr with TConst c -> c | _ -> assert false in
-			mk_con_pat (CConst c) [] t p
-		| EMeta((Meta.Macro,[],_),(ECall (e1,args),_)) ->
-			let path, field, args = Codegen.get_macro_path ctx e1 args p in
-			begin match ctx.g.do_macro ctx MExpr path field args p with
-				| Some e ->	loop pctx e t
-				| None -> error "Macro failure" p
-			end
-		| EField _ ->
-			let e = type_expr ctx e (WithType t) in
-			let e = match Optimizer.make_constant_expression ctx ~concat_strings:true e with Some e -> e | None -> e in
-			(match e.eexpr with
-			| TConst c | TCast({eexpr = TConst c},None) ->
-				mk_con_pat (CConst c) [] t p
-			| TTypeExpr mt ->
-				mk_type_pat ctx mt t p
-			| TField(_, FStatic(_,cf)) when is_value_type cf.cf_type ->
-				ignore (follow cf.cf_type);
-				begin match cf.cf_expr with
-				| Some e ->
-					(try check_texpr_pattern e t p with Not_found -> mk_con_pat (CExpr e) [] cf.cf_type p)
-				| None ->
-					mk_con_pat (CExpr e) [] cf.cf_type p
-				end
-			| TField(_, FEnum(en,ef)) ->
-				begin try
-					unify_enum_field en (List.map (fun _ -> mk_mono()) en.e_params) ef t
-				with Unify_error l ->
-					error (error_msg (Unify l)) p
-				end;
-				mk_con_pat (CEnum(en,ef)) [] t p
-			| _ -> error "Constant expression expected" p)
-		| ECall(ec,el) ->
-			let ec = type_expr ctx ec (WithType t) in
-			(match follow ec.etype with
-			| TEnum(en,pl)
-			| TFun(_,TEnum(en,pl)) ->
-				let ef = match ec.eexpr with
-					| TField (_,FEnum (_,f)) -> f
-					| _ -> error ("Expected constructor for enum " ^ (s_type_path en.e_path)) p
-				in
-				let monos = List.map (fun _ -> mk_mono()) ef.ef_params in
-				let tl,r = match apply_params en.e_params pl (apply_params ef.ef_params monos ef.ef_type) with
-					| TFun(args,r) ->
-						unify ctx r t p;
-						List.map (fun (n,_,t) -> t) args,r
-					| _ -> error "No arguments expected" p
-				in
-				let rec loop2 i el tl = match el,tl with
-					| (EConst(Ident "_"),pany) :: [], t :: tl ->
-						let pat = mk_pat PAny t_dynamic pany in
-						(ExtList.List.make ((List.length tl) + 1) pat)
-					| e :: el, t :: tl ->
-						let pat = loop pctx e t in
-						pat :: loop2 (i + 1) el tl
-					| e :: _, [] ->
-						error "Too many arguments" (pos e);
-					| [],_ :: _ ->
-						error "Not enough arguments" p;
-					| [],[] ->
-						[]
-				in
-				let el = loop2 0 el tl in
-				List.iter2 (fun m (_,t) -> match follow m with TMono _ -> Type.unify m t | _ -> ()) monos ef.ef_params;
-				pctx.pc_is_complex <- true;
-				mk_con_pat (CEnum(en,ef)) el r p
-			| _ -> perror p)
-		| EConst(Ident "_") ->
-			begin match get_tuple_params t with
-			| Some tl ->
-				let pl = List.map (fun (_,_,t) -> mk_any t p) tl in
-				mk_pat (PTuple (Array.of_list pl)) t_dynamic p
-			| None ->
-				mk_any t p
-			end
-		| EConst(Ident s) ->
-			begin try
-				let ec = match follow t with
-					| TEnum(en,pl) ->
-						let ef = try
-							PMap.find s en.e_constrs
-						with Not_found when not (is_lower_ident s) ->
-							error (string_error s en.e_names ("Expected constructor for enum " ^ (s_type_path en.e_path))) p
-						in
-						(match ef.ef_type with
-							| TFun (args,_) ->
-								let msg = Printf.sprintf "Enum constructor %s.%s requires parameters %s"
-									(s_type_path en.e_path)
-									ef.ef_name
-									(String.concat ", " (List.map (fun (n,_,t) -> n ^ ":" ^ (s_type t)) args))
-								in
-								error msg p
-							| _ -> ());
-						let et = mk (TTypeExpr (TEnumDecl en)) (TAnon { a_fields = PMap.empty; a_status = ref (EnumStatics en) }) p in
-						mk (TField (et,FEnum (en,ef))) (apply_params en.e_params pl ef.ef_type) p
-					| TAbstract({a_impl = Some c} as a,_) when Meta.has Meta.Enum a.a_meta ->
-						let cf = PMap.find s c.cl_statics in
-						Type.unify (follow cf.cf_type) t;
-						let e = begin match cf.cf_expr with
-						| Some ({eexpr = TConst c | TCast({eexpr = TConst c},None)} as e) -> e
-						| _ -> raise Not_found
-						end in
-						e
-					| _ ->
-						let old = ctx.untyped in
-						ctx.untyped <- true;
-						let e = try type_expr ctx e (WithType t) with _ -> ctx.untyped <- old; raise Not_found in
-						ctx.untyped <- old;
-						e
-				in
-				check_texpr_pattern ec t p
-			with Not_found ->
-				begin match get_tuple_params t with
-					| Some tl ->
-						let s = String.concat "," (List.map (fun (_,_,t) -> s_type t) tl) in
-						error ("Pattern should be tuple [" ^ s ^ "]") p
-					| None ->
-						if not (is_lower_ident s) && s.[0] <> '`' then error "Capture variables must be lower-case" p;
-						let v = mk_var pctx s t p in
-						mk_pat (PVar (v,p)) v.v_type p
-				end
-			end
-		| (EObjectDecl fl) ->
-			let is_matchable cf = match cf.cf_kind with Method _ -> false | _ -> true in
-			let is_valid_field_name fields co n p =
-				try
-					let cf = PMap.find n fields in
-					begin match co with
-					| Some c when not (Typer.can_access ctx c cf false) -> error ("Cannot match against private field " ^ n) p
-					| _ -> ()
-					end
-				with Not_found ->
-					error ((s_type t) ^ " has no field " ^ n ^ " that can be matched against") p;
-			in
-			pctx.pc_is_complex <- true;
-			let loop_fields fields =
-				let sl,pl,i = PMap.foldi (fun n cf (sl,pl,i) ->
-					if not (is_matchable cf) then
-						sl,pl,i
-					else
-						let pat = try
-							if pctx.pc_reify && cf.cf_name = "pos" then raise Not_found;
-							loop pctx (List.assoc cf.cf_name fl) cf.cf_type
-						with Not_found ->
-							(mk_any cf.cf_type p)
-						in
-						(n,cf) :: sl,pat :: pl,i + 1
-				) fields ([],[],0) in
-				mk_con_pat (CFields(i,sl)) pl t p
-			in
-			let fields = match follow t with
-				| TAnon {a_fields = fields} ->
-					fields
-				| TInst(c,tl) ->
-					let fields = ref PMap.empty in
-					let rec loop c tl =
-						begin match c.cl_super with
-							| Some (csup,tlsup) -> loop csup (List.map (apply_params c.cl_params tl) tlsup)
-							| None -> ()
-						end;
-						PMap.iter (fun n cf -> fields := PMap.add n {cf with cf_type = apply_params c.cl_params tl (monomorphs cf.cf_params cf.cf_type)} !fields) c.cl_fields
-					in
-					loop c tl;
-					!fields
-				| TAbstract({a_impl = Some c} as a,tl) ->
-					let fields = List.fold_left (fun acc cf ->
-						if Meta.has Meta.Impl cf.cf_meta then
-							PMap.add cf.cf_name cf acc
-						else acc
-					) PMap.empty c.cl_ordered_statics in
-					PMap.map (fun cf -> {cf with cf_type = apply_params a.a_params tl (monomorphs cf.cf_params cf.cf_type)}) fields
-				| _ ->
-					error ((s_type t) ^ " cannot be matched against a structure") p
-			in
-			List.iter (fun (n,(_,p)) -> is_valid_field_name fields None n p) fl;
-			loop_fields fields
-		| EArrayDecl [] ->
-			mk_con_pat (CArray 0) [] t p
-		| EArrayDecl el ->
-			pctx.pc_is_complex <- true;
-			begin match follow t with
-				| TInst({cl_path=[],"Array"},[t2]) | (TDynamic _ as t2) ->
-					let pl = ExtList.List.mapi (fun i e ->
-						loop pctx e t2
-					) el in
-					mk_con_pat (CArray (List.length el)) pl t p
-				| TFun(tl,tr) when tr == fake_tuple_type ->
-					let pl = try
-						List.map2 (fun e (_,_,t) -> loop pctx e t) el tl
-					with Invalid_argument _ ->
-						error ("Invalid number of arguments: expected " ^ (string_of_int (List.length tl)) ^ ", found " ^ (string_of_int (List.length el))) p
-					in
-					mk_pat (PTuple (Array.of_list pl)) t p
-				| _ ->
-					error ((s_type t) ^ " should be Array") p
-			end
-		| EBinop(OpAssign,(EConst(Ident s),p2),e1) ->
-			let v = mk_var pctx s t p in
-			let pat1 = loop pctx e1 t in
-			mk_pat (PBind((v,p),pat1)) t p2
-		| EBinop(OpOr,(EBinop(OpOr,e1,e2),p2),e3) ->
-			loop pctx (EBinop(OpOr,e1,(EBinop(OpOr,e2,e3),p2)),p) t
-		| EBinop(OpOr,e1,e2) ->
-			let old = pctx.pc_locals in
-			let pat1 = loop pctx e1 t in
-			begin match pat1.p_def with
-				| PAny | PVar _ ->
-					display_error ctx "This pattern is unused" (pos e2);
-					pat1
-				| _ ->
-					let pctx2 = {
-						pc_sub_vars = Some pctx.pc_locals;
-						pc_locals = old;
-						pc_reify = pctx.pc_reify;
-						pc_is_complex = pctx.pc_is_complex;
-					} in
-					let pat2 = loop pctx2 e2 t in
-					pctx.pc_is_complex <- pctx2.pc_is_complex;
-					PMap.iter (fun s (_,p) -> if not (PMap.mem s pctx2.pc_locals) then verror s p) pctx.pc_locals;
-					mk_pat (POr(pat1,pat2)) pat2.p_type (punion pat1.p_pos pat2.p_pos);
-			end
-		| _ ->
-			raise (Unrecognized_pattern e)
-	in
-	let pctx = {
-		pc_locals = PMap.empty;
-		pc_sub_vars = None;
-		pc_reify = false;
-		pc_is_complex = false;
-	} in
-	let x = loop pctx e t in
-	x, pctx.pc_locals, pctx.pc_is_complex
-
-let get_pattern_locals ctx e t =
-	try
-		let _,locals,_ = to_pattern ctx e t in
-		PMap.foldi (fun n v acc -> PMap.add n v acc) locals PMap.empty
-	with Unrecognized_pattern _ ->
-		PMap.empty
-
-(* Match compilation *)
-
-let expr_eq e1 e2 = e1 == e2 || match e1.eexpr,e2.eexpr with
-	| TConst ct1,TConst ct2 ->
-		ct1 = ct2
-	| TField(_,FStatic(c1,cf1)),TField(_,FStatic(c2,cf2)) ->
-		c1 == c2 && cf1.cf_name = cf2.cf_name
-	| _ ->
-		false
-
-let unify_con con1 con2 = match con1.c_def,con2.c_def with
-	| CExpr e1, CExpr e2 ->
-		expr_eq e1 e2
-	| CConst c1,CConst c2 ->
-		c1 = c2
-	| CEnum(e1,ef1),CEnum(e2,ef2) ->
-		e1 == e2 && ef1.ef_name = ef2.ef_name
-	| CFields (i1,fl1),CFields (i2,fl2) ->
-		(try
-			List.iter (fun (s,_) -> if not (List.mem_assoc s fl1) then raise Not_found) fl2;
-			true
-		with Not_found ->
-			false)
-	| CType mt1,CType mt2 ->
-		t_path mt1 = t_path mt2
-	| CArray a1, CArray a2 ->
-		a1 == a2
-	| CAny, CAny ->
-		true
-	| _ ->
-		false
-
-let array_tl arr = Array.sub arr 1 (Array.length arr - 1)
-
-let spec mctx con pmat =
-	let a = arity con in
-	let r = DynArray.create () in
-	let add pv out =
-		DynArray.add r (pv,out)
-	in
-	let rec loop2 pv out = match pv.(0).p_def with
-		| PCon(c2,pl) when unify_con c2 con ->
-			add (Array.append (Array.of_list pl) (array_tl pv)) out
-		| PCon(c2,pl) ->
-			()
-		| PAny | PVar _->
-			add (Array.append (Array.make a (mk_any (pv.(0).p_type) (pv.(0).p_pos))) (array_tl pv)) out
-		| PBind(_,pat) ->
-			loop2 (Array.append [|pat|] (array_tl pv)) out
-		| PTuple tl ->
-			loop2 tl out
-		| POr _ ->
-			assert false
-	in
-	let rec loop pmat = match pmat with
-		| (pv,out) :: pl ->
-			loop2 pv out;
-			loop pl
-		| [] ->
-			()
-	in
-	loop pmat;
-	DynArray.to_list r
-
-let default mctx pmat =
-	let r = DynArray.create () in
-	let add pv out =
-		DynArray.add r (pv,out)
-	in
-	let rec loop2 pv out = match pv.(0).p_def with
-		| PCon _ ->
-			()
-		| PAny | PVar _->
-			add (array_tl pv) out
-		| PBind(_,pat) ->
-			loop2 (Array.append [|pat|] (array_tl pv)) out
-		| PTuple tl ->
-			loop2 tl out
-		| POr _ ->
-			assert false
-	in
-	let rec loop pmat = match pmat with
-		| (pv,out) :: pl ->
-			loop2 pv out;
-			loop pl;
-		| [] ->
-			()
-	in
-	loop pmat;
-	DynArray.to_list r
-
-let pick_column pmat =
-	let rec loop i pv = if Array.length pv = 0 then -1 else match pv.(0).p_def with
-		| PVar _ | PAny ->
-			loop (i + 1) (array_tl pv)
-		| PTuple pl ->
-			loop i pl
-		| _ ->
-			i
-	in
-	loop 0 (fst (List.hd pmat))
-
-let swap_pmat_columns i pmat =
-	List.map (fun (pv,out) ->
-		let pv = match pv with [|{p_def = PTuple pt}|] -> pt | _ -> pv in
-		let tmp = pv.(i) in
-		Array.set pv i pv.(0);
-		Array.set pv 0 tmp;
-		pv,out
-	) pmat
-
-let swap_columns i (row : 'a list) : 'a list =
-	match row with
-	| rh :: rt ->
-		let rec loop count acc col = match col with
-			| [] -> acc
-			| ch :: cl when i = count ->
-				ch :: (List.rev acc) @ [rh] @ cl
-			| ch :: cl ->
-				loop (count + 1) (ch :: acc) cl
-		in
-		loop 1 [] rt
-	| _ ->
-		[]
-
-let expand_or mctx (pmat : pat_matrix) =
-	let rec loop pat = match pat.p_def with
-		| POr(pat1,pat2) ->
-			let pat1 = loop pat1 in
-			let pat2 = loop pat2 in
-			pat1 @ pat2
-		| PBind(v,pat1) ->
-			let pat1 = loop pat1 in
-			List.map (fun pat1 ->
-				{pat with p_def = PBind(v,pat1)}
-			) pat1
-		| PTuple(pl) ->
-			let pat1 = loop pl.(0) in
-			List.map (fun pat1 ->
-				let a1 = Array.copy pl in
-				a1.(0) <- pat1;
-				{pat with p_def = PTuple a1}
-			) pat1
-		| _ ->
-			[pat]
-	in
-	let rec loop2 pmat = match pmat with
-		| (pv,out) :: pmat ->
-			let pat = loop pv.(0) in
-			let pat' = ExtList.List.mapi (fun i pat ->
-				(* TODO: This should really be active, but currently causes problems with or-patterns in
-				   tuples (issue #2610). We will disable this for the 3.1.0 release, which means issue
-				   #2508 is open again. *)
-				(* let out = if i = 0 then out else clone_out mctx out pat.p_pos in *)
-				let a1 = Array.copy pv in
-				a1.(0) <- pat;
-				a1,out
-			) pat in
-			pat' @ (loop2 pmat)
-		| [] ->
-			[]
-	in
-	loop2 pmat
-
-let column_sigma mctx st pmat =
-	let acc = ref [] in
-	let bindings = ref [] in
-	let unguarded = Hashtbl.create 0 in
-	let add c g =
-		if not (List.exists (fun c2 -> unify_con c2 c) !acc) then acc := c :: !acc;
-		if not g then Hashtbl.replace unguarded c.c_def true;
-	in
-	let bind_st out st v =
-		if not (List.exists (fun ((v2,p),_) -> v2.v_id == (fst v).v_id) !bindings) then bindings := (v,st) :: !bindings
-	in
-	let rec loop pmat = match pmat with
-		| (pv,out) :: pr ->
-			let rec loop2 out = function
-				| PCon (c,_) ->
-					add c ((get_guard mctx out.o_id) <> None);
-				| PVar v ->
-					bind_st out st v;
-				| PBind(v,pat) ->
-					bind_st out st v;
-					loop2 out pat.p_def
-				| PAny ->
-					()
-				| PTuple tl ->
-					loop2 out tl.(0).p_def
-				| POr _ ->
-					assert false
-			in
-			loop2 out pv.(0).p_def;
-			loop pr
-		| [] ->
-			()
-	in
-	loop pmat;
-	List.rev_map (fun con -> con,not (Hashtbl.mem unguarded con.c_def)) !acc,!bindings
-
-let rec all_ctors mctx t =
-	let h = ref PMap.empty in
-	if is_explicit_null t then h := PMap.add (CConst TNull) Ast.null_pos !h;
-	match follow t with
-	| TAbstract({a_path = [],"Bool"},_) ->
-		h := PMap.add (CConst(TBool true)) Ast.null_pos !h;
-		h := PMap.add (CConst(TBool false)) Ast.null_pos !h;
-		h,RunTimeFinite
-	| TAbstract({a_impl = Some c} as a,pl) when Meta.has Meta.Enum a.a_meta ->
-		List.iter (fun cf ->
-			ignore(follow cf.cf_type);
-			if Meta.has Meta.Impl cf.cf_meta then match cf.cf_expr with
-				| Some {eexpr = TConst c | TCast ({eexpr = TConst c},None)} -> h := PMap.add (CConst c) cf.cf_pos !h
-				| _ -> ()
-		) c.cl_ordered_statics;
-		h,CompileTimeFinite
-	| TAbstract(a,pl) when not (Meta.has Meta.CoreType a.a_meta) -> all_ctors mctx (Abstract.get_underlying_type a pl)
-	| TInst({cl_path=[],"String"},_)
-	| TInst({cl_path=[],"Array"},_) ->
-		h,Infinite
-	| TEnum(en,pl) ->
-		PMap.iter (fun _ ef ->
-			let tc = monomorphs mctx.ctx.type_params t in
-			try unify_enum_field en pl ef tc;
-				h := PMap.add (CEnum(en,ef)) ef.ef_pos !h
-			with Unify_error _ ->
-				()
-		) en.e_constrs;
-		h,RunTimeFinite
-	| TAnon a ->
-		h,CompileTimeFinite
-	| TInst(_,_) ->
-		h,CompileTimeFinite
-	| _ ->
-		h,Infinite
-
-let rec collapse_pattern pl = match pl with
-	| pat :: [] ->
-		pat
-	| pat :: pl ->
-		let pat2 = collapse_pattern pl in
-		mk_pat (POr(pat,pat2)) pat.p_type (punion pat.p_pos pat2.p_pos)
-	| [] ->
-		assert false
-
-let bind_remaining out pv stl =
-	let rec loop stl pv =
-		if Array.length pv = 0 then
-			[]
-		else
-			match stl,pv.(0).p_def with
-			| st :: stl,PAny ->
-				loop stl (array_tl pv)
-			| st :: stl,PVar v ->
-				(v,st) :: loop stl (array_tl pv)
-			| stl,PTuple pl ->
-				loop stl pl
-			| _ :: _,_->
-				loop stl (array_tl pv)
-			| [],_ ->
-				[]
-	in
-	loop stl pv
-
-let get_cache mctx dt =
-	match dt with Goto _ -> dt | _ ->
-		try
-			Goto (Hashtbl.find mctx.dt_cache dt)
-		with Not_found ->
-			Hashtbl.replace mctx.dt_cache dt mctx.dt_count;
-			mctx.dt_count <- mctx.dt_count + 1;
-			DynArray.add mctx.dt_lut dt;
-			dt
-
-let rec compile mctx stl pmat toplevel =
-	let guard id dt1 dt2 = get_cache mctx (Guard(id,dt1,dt2)) in
-	let expr id = get_cache mctx (Expr id) in
-	let bind bl dt = get_cache mctx (Bind(bl,dt)) in
-	let switch st cl = get_cache mctx (Switch(st,cl)) in
-	get_cache mctx (match pmat with
-	| [] ->
-		(match stl with
-		| st :: stl ->
-			let all,inf = all_ctors mctx st.st_type in
-			let pl = PMap.foldi (fun cd p acc -> (mk_con_pat cd [] t_dynamic p) :: acc) !all [] in
-			begin match pl,inf with
-				| _,Infinite
-				| [],_ ->
-					raise (Not_exhaustive(any,st))
-				| _ ->
-					raise (Not_exhaustive(collapse_pattern pl,st))
-			end
-		| _ ->
-			(* This can happen in cases a value is required and all default cases are guarded (issue #3150).
-			   Not a particularly elegant solution, may want to revisit this later. *)
-			raise Exit)
-	| ([|{p_def = PTuple pt}|],out) :: pl ->
-		compile mctx stl ((pt,out) :: pl) toplevel
-	| (pv,out) :: pl ->
-		let i = pick_column pmat in
-		if i = -1 then begin
-			out.o_num_paths <- out.o_num_paths + 1;
-			let bl = bind_remaining out pv stl in
-			let dt = match (get_guard mctx out.o_id) with
-				| None ->
-					expr out.o_id
-				| Some _ ->
-					let dt = match pl,mctx.need_val with
-						| [],false ->
-							None
-						| _ ->
-							Some (compile mctx stl pl false)
-					in
-					guard out.o_id (expr out.o_id) dt
-			in
-			(if bl = [] then dt else bind bl dt)
-		end else if i > 0 then begin
-			let pmat = swap_pmat_columns i pmat in
-			let stls = swap_columns i stl in
-			compile mctx stls pmat toplevel
-		end else begin
-			let st_head,st_tail = match stl with st :: stl -> st,stl | _ -> assert false in
-			let pmat = expand_or mctx pmat in
-			let sigma,bl = column_sigma mctx st_head pmat in
-			let all,inf = all_ctors mctx pv.(0).p_type in
-			let cases = List.map (fun (c,g) ->
-				if not g then all := PMap.remove c.c_def !all;
-				let spec = spec mctx c pmat in
-				let hsubs = mk_subs st_head c in
-				let subs = hsubs @ st_tail in
-				let dt = compile mctx subs spec false in
-				c,dt
-			) sigma in
-			let def = default mctx pmat in
-			let dt = match def,cases with
-			| _ when inf = RunTimeFinite && PMap.is_empty !all ->
-				switch st_head cases
-			| [],_ when inf = CompileTimeFinite && PMap.is_empty !all ->
-				switch st_head cases
-			| [],_ when inf = Infinite && not mctx.need_val && toplevel ->
-				(* ignore exhaustiveness, but mark context so we do not generate @:exhaustive metadata *)
-				mctx.is_exhaustive <- false;
-				switch st_head cases
-			| [],_ when inf = Infinite ->
-				raise (Not_exhaustive(any,st_head))
-			| [],_ ->
-				let pl = PMap.foldi (fun cd p acc -> (mk_con_pat cd [] t_dynamic p) :: acc) !all [] in
-				(* toplevel null can be omitted because the French dig runtime errors (issue #3054) *)
-				if toplevel && (match pl with
-					| [{p_def = PCon ({c_def = (CConst TNull)},_)}] -> true
-					| _ -> false) then
-						switch st_head cases
-				else
-					raise (Not_exhaustive(collapse_pattern pl,st_head))
-			| def,[] ->
-				compile mctx st_tail def false
-			| def,_ ->
-				let cdef = mk_con CAny t_dynamic st_head.st_pos in
-				let def = try
-					compile mctx st_tail def false
-				with Exit ->
-					raise (Not_exhaustive(any,st_head))
-				in
-				let cases = cases @ [cdef,def] in
-				switch st_head cases
-			in
-			if bl = [] then dt else bind bl dt
-		end)
-
-let rec collapse_case el = match el with
-	| e :: [] ->
-		e
-	| e :: el ->
-		let e2 = collapse_case el in
-		EBinop(OpOr,e,e2),punion (pos e) (pos e2)
-	| [] ->
-		assert false
-
-let mk_const ctx p = function
-	| TString s -> mk (TConst (TString s)) ctx.com.basic.tstring p
-	| TInt i -> mk (TConst (TInt i)) ctx.com.basic.tint p
-	| TFloat f -> mk (TConst (TFloat f)) ctx.com.basic.tfloat p
-	| TBool b -> mk (TConst (TBool b)) ctx.com.basic.tbool p
-	| TNull -> mk (TConst TNull) (ctx.com.basic.tnull (mk_mono())) p
-	| _ -> error "Unsupported constant" p
-
-let rec convert_st ctx st = match st.st_def with
-	| SVar v -> mk (TLocal v) v.v_type st.st_pos
-	| SField (sts,cf) ->
-		let e = convert_st ctx sts in
-		Typer.acc_get ctx (Typer.type_field ctx e cf.cf_name st.st_pos Typer.MGet) st.st_pos
-	| SArray (sts,i) -> mk (TArray(convert_st ctx sts,mk_const ctx st.st_pos (TInt (Int32.of_int i)))) st.st_type st.st_pos
-	| STuple (st,_,_) -> convert_st ctx st
-	| SEnum (sts,ef,i) -> mk (TEnumParameter(convert_st ctx sts, ef, i)) st.st_type st.st_pos
-
-let convert_con ctx con = match con.c_def with
-	| CConst c -> mk_const ctx con.c_pos c
-	| CType mt -> mk (TTypeExpr mt) t_dynamic con.c_pos
-	| CExpr e -> e
-	| CEnum(e,ef) -> mk_const ctx con.c_pos (TInt (Int32.of_int ef.ef_index))
-	| CArray i -> mk_const ctx con.c_pos (TInt (Int32.of_int i))
-	| CAny | CFields _ -> assert false
-
-let convert_switch mctx st cases loop =
-	let ctx = mctx.ctx in
-	let e_st = convert_st ctx st in
-	let p = e_st.epos in
-	let mk_index_call () =
-		let ttype = match follow (Typeload.load_instance ctx { tpackage = ["std"]; tname="Type"; tparams=[]; tsub = None} p true) with TInst(c,_) -> c | t -> assert false in
-		let cf = PMap.find "enumIndex" ttype.cl_statics in
-		let ec = (!type_module_type_ref) ctx (TClassDecl ttype) None p in
-		let ef = mk (TField(ec, FStatic(ttype,cf))) (tfun [e_st.etype] ctx.t.tint) p in
-		let e = make_call ctx ef [e_st] ctx.t.tint p in
-		e
-	in
-	let wrap_exhaustive e =
-		if mctx.is_exhaustive then
-			mk (TMeta((Meta.Exhaustive,[],e.epos),e)) e.etype e.epos
-		else
-			e
-	in
-	let e = match follow st.st_type with
-	| TEnum(_) ->
-		wrap_exhaustive (mk_index_call())
-	| TAbstract(a,pl) when (match Abstract.get_underlying_type a pl with TEnum(_) -> true | _ -> false) ->
-		wrap_exhaustive (mk_index_call())
-	| TInst({cl_path = [],"Array"},_) as t ->
-		mk (TField (e_st,quick_field t "length")) ctx.t.tint p
-	| TAbstract(a,_) when Meta.has Meta.Enum a.a_meta ->
-		wrap_exhaustive (e_st)
-	| TAbstract({a_path = [],"Bool"},_) ->
-		wrap_exhaustive (e_st)
-	| _ ->
-		let rec loop cases = match cases with
-			| [] -> e_st
-			| (con,_) :: cases ->
-				begin match con.c_def with
-					| CEnum _ -> mk_index_call()
-					| CArray _ -> mk (TField (e_st,FDynamic "length")) ctx.t.tint p
-					| _ -> loop cases
-				end
-		in
-		loop cases
-	in
-	let null = ref None in
-	let def = ref None in
-	let cases = List.filter (fun (con,dt) ->
-		match con.c_def with
-		| CConst TNull ->
-			null := Some (loop dt);
-			false
-		| CAny ->
-			def := Some (loop dt);
-			false
-		| _ ->
-			true
-	) cases in
-	let dt = match cases with
-		| [{c_def = CFields _},dt] -> loop dt
-		| _ -> DTSwitch(e, List.map (fun (c,dt) -> convert_con ctx c, loop dt) cases, !def)
-	in
-	match !null with
-	| None when is_explicit_null st.st_type && (!def <> None || not mctx.need_val) ->
-		let econd = mk (TBinop(OpNotEq,e_st,mk (TConst TNull) st.st_type p)) ctx.t.tbool p in
-		DTGuard(econd,dt,!def)
-	| None ->
-		dt
-	| Some dt_null ->
-		let t = match ctx.t.tnull ctx.t.tint with
-			| TType(t,_) ->TType(t,[st.st_type])
-			| t -> t
-		in
-		let e_null = mk (TConst TNull) t p in
-		let econd = mk (TBinop(OpEq,e_st, e_null)) ctx.t.tbool p in
-		DTGuard(econd,dt_null,Some dt)
-
-(* Decision tree compilation *)
-
-let transform_extractors eval cases p =
-	let efail = (EThrow(EConst(Ident "false"),p)),p in
-	let cfail = [(EConst (Ident "_"),p)],None,Some efail in
-	let has_extractor = ref false in
-	let rec loop cases = match cases with
-		| (epat,eg,e) :: cases ->
-			let ex = ref [] in
-			let exc = ref 0 in
-			let rec find_ex in_or e = match fst e with
-				| EBinop(OpArrow,_,_) when in_or ->
-					error "Extractors in or patterns are not allowed" (pos e)
-				| EBinop(OpArrow, e1, e2) ->
-					let ec = EConst (Ident ("__ex" ^ string_of_int (!exc))),snd e in
-					let rec map_left e = match fst e with
-						| EConst(Ident "_") -> ec
-						| _ -> Ast.map_expr map_left e
-					in
-					let ecall = map_left e1 in
-					ex := (ecall,e2) :: !ex;
-					incr exc;
-					has_extractor := true;
-					ec
-				| EBinop(OpOr,e1,e2) ->
-					let e1 = find_ex true e1 in
-					let e2 = find_ex true e2 in
-					(EBinop(OpOr,e1,e2)),(pos e)
-				| _ ->
-					Ast.map_expr (find_ex in_or) e
-			in
-			let p = match e with None -> p | Some e -> pos e in
-			let epat = match epat with
-				| [epat] -> [find_ex false epat]
-				| _ -> List.map (find_ex true) epat
-			in
-			let cases = loop cases in
-			if !exc = 0 then
-				(epat,eg,e) :: cases
-			else begin
-				let esubjects = EArrayDecl (List.map fst !ex),p in
-				let case1 = [EArrayDecl (List.map snd !ex),p],eg,e in
-				let cases2 = match cases with
-					| [] -> [case1]
-					| [[EConst (Ident "_"),_],_,e] -> case1 :: [[(EConst (Ident "_"),p)],None,e]
-					| _ ->
-						case1 :: [[(EConst (Ident "_"),p)],None,Some (ESwitch(eval,cases,None),p)]
-				in
-				let eswitch = (ESwitch(esubjects,cases2,None)),p in
-				let case = epat,None,Some eswitch in
-				begin match epat with
-					| [EConst(Ident _),_] ->
-						[case;cfail]
-					| _ ->
-						case :: cases
-				end
-			end
-		| [] ->
-			[]
-	in
-	let cases = loop cases in
-	cases,!has_extractor
-
-let extractor_depth = ref 0
-
-let match_expr ctx e cases def with_type p =
-	let need_val,with_type,tmono = match with_type with
-		| NoValue -> false,NoValue,None
-		| WithType t | WithTypeResume t when (match follow t with TMono _ -> true | _ -> false) ->
-			(* we don't want to unify with each case individually, but instead at the end after unify_min *)
-			true,Value,Some with_type
-		| t -> true,t,None
-	in
-	(* turn default into case _ *)
-	let cases = match cases,def with
-		| [],None -> []
-		| cases,Some def ->
-			let p = match def with
-				| None -> p
-				| Some (_,p) -> p
-			in
-			cases @ [[(EConst(Ident "_")),p],None,def]
-		| _ -> cases
-	in
-	let cases,has_extractor = transform_extractors e cases p in
-	(* type subject(s) *)
-	let array_match = ref false in
-	let evals = match fst e with
-		| EArrayDecl el | EParenthesis(EArrayDecl el,_) when (match el with [(EFor _ | EWhile _),_] -> false | _ -> true) ->
-			array_match := true;
-			List.map (fun e -> type_expr ctx e Value) el
-		| _ ->
-			let e = type_expr ctx e Value in
-			begin match follow e.etype with
-			(* TODO: get rid of the XmlType check *)
-			| TEnum(en,_) when (match en.e_path with (["neko" | "php" | "flash" | "cpp"],"XmlType") -> true | _ -> Meta.has Meta.FakeEnum en.e_meta) ->
-				raise Exit
-			| TAbstract({a_path=[],("Int" | "Float" | "Bool")},_) | TInst({cl_path = [],"String"},_) when (Common.defined ctx.com Common.Define.NoPatternMatching) ->
-				raise Exit;
-			| _ ->
-				()
-			end;
-			[e]
-	in
-	let var_inits = ref [] in
-	let save = save_locals ctx in
-	let a = List.length evals in
-	(* turn subjects to subterms and handle variable initialization where necessary *)
-	let stl = ExtList.List.mapi (fun i e ->
-		let rec loop e = match e.eexpr with
-			| TParenthesis e | TMeta(_,e) ->
-				loop e
-			| TLocal v ->
-				mk_st (SVar v) e.etype e.epos
-			| _ ->
-				let v = gen_local ctx e.etype in
-				var_inits := (v, Some e) :: !var_inits;
-				ctx.locals <- PMap.add v.v_name v ctx.locals;
-				mk_st (SVar v) e.etype e.epos
-		in
-		let st = loop e in
-		if a = 1 then st else mk_st (STuple(st,i,a)) st.st_type st.st_pos
-	) evals in
-	let tl = List.map (fun st -> st.st_type) stl in
-	(* create matcher context *)
-	let mctx = {
-		ctx = ctx;
-		need_val = need_val;
-		outcomes = [];
-		toplevel_or = false;
-		dt_lut = DynArray.create ();
-		dt_cache = Hashtbl.create 0;
-		dt_count = 0;
-		has_extractor = has_extractor;
-		expr_map = PMap.empty;
-		is_exhaustive = true;
-	} in
-	(* flatten cases *)
-	let cases = List.map (fun (el,eg,e) ->
-		List.iter (fun e -> match fst e with EBinop(OpOr,_,_) -> mctx.toplevel_or <- true; | _ -> ()) el;
-		match el with
-			| [] ->
-				let p = match e with None -> p | Some e -> pos e in
-				error "case without a pattern is not allowed" p
-			| _ ->
-				collapse_case el,eg,e
-	) cases in
-	let is_complex = ref false in
-	if mctx.has_extractor then incr extractor_depth;
-	let add_pattern_locals (pat,locals,complex) =
-		PMap.iter (fun n (v,p) -> ctx.locals <- PMap.add n v ctx.locals) locals;
-		if complex then is_complex := true;
-		pat
-	in
-	(* evaluate patterns *)
-	let pl = ExtList.List.mapi (fun i (ep,eg,e) ->
-		let save = save_locals ctx in
-		(* type case patterns *)
-		let pl,restore,with_type =
-			try
-				(* context type parameters are turned into monomorphs until the pattern has been typed *)
-				let monos = List.map (fun _ -> mk_mono()) ctx.type_params in
-				let t = match tl with [t] when not !array_match -> t | tl -> tfun tl fake_tuple_type in
-				let t = apply_params ctx.type_params monos t in
-				let pl = [add_pattern_locals (to_pattern ctx ep t)] in
-				let old_ret = ctx.ret in
-				ctx.ret <- apply_params ctx.type_params monos ctx.ret;
-				let restore = PMap.fold (fun v acc ->
-					(* apply context monomorphs to locals and replace them back after typing the case body *)
-					let t = v.v_type in
-					v.v_type <- apply_params ctx.type_params monos v.v_type;
-					(fun () -> v.v_type <- t) :: acc
-				) ctx.locals [fun() -> ctx.ret <- old_ret] in
-				(* turn any still unknown types back to type parameters *)
-				List.iter2 (fun m (_,t) -> match follow m with TMono _ -> Type.unify m t | _ -> ()) monos ctx.type_params;
-				pl,restore,(match with_type with
-					| WithType t -> WithType (apply_params ctx.type_params monos t)
-					| WithTypeResume t -> WithTypeResume (apply_params ctx.type_params monos t)
-					| _ -> with_type);
-			with Unrecognized_pattern (e,p) ->
-				error "Case expression must be a constant value or a pattern, not an arbitrary expression" p
-		in
-		let is_catch_all = match pl with
-			| [{p_def = PAny | PVar _}] -> true
-			| _ -> false
-		in
-		(* type case body *)
-		let e = match e with
-			| None ->
-				mk (TBlock []) ctx.com.basic.tvoid (pos ep)
-			| Some e ->
-				type_expr ctx e with_type
-		in
-		let e = match with_type with
-			| WithType t ->
-				Codegen.AbstractCast.cast_or_unify ctx t e e.epos;
-			| WithTypeResume t ->
-				(try Codegen.AbstractCast.cast_or_unify_raise ctx t e e.epos with Error (Unify l,p) -> raise (Typer.WithTypeError (l,p)));
-			| _ -> e
-		in
-		(* type case guard *)
-		let eg = match eg with
-			| None -> None
-			| Some e ->
-				let eg = type_expr ctx e (WithType ctx.com.basic.tbool) in
-				unify ctx eg.etype ctx.com.basic.tbool eg.epos;
-				Some eg
-		in
-		List.iter (fun f -> f()) restore;
-		save();
-		let out = mk_out mctx i e eg is_catch_all (pos ep) in
-		Array.of_list pl,out
-	) cases in
-	let check_unused () =
-		let unused p =
-			display_error ctx "This pattern is unused" p;
-			let old_error = ctx.on_error in
-			ctx.on_error <- (fun ctx s p -> ctx.on_error <- old_error; raise Exit);
-			let check_expr e p =
-				try begin match fst e with
-						| EConst(Ident ("null" | "true" | "false")) -> ()
-						| EConst(Ident _) ->
-							ignore (type_expr ctx e Value);
-							display_error ctx "Case expression must be a constant value or a pattern, not an arbitrary expression" (pos e)
-						| _ -> ()
-				end with Exit -> ()
-			in
-			let rec loop prev cl = match cl with
-				| (_,Some _,_) :: cl -> loop prev cl
-				| ((e,p2),_,_) :: cl ->
-					if p2.pmin >= p.pmin then check_expr prev p else loop (e,p2) cl
-				| [] ->
-					check_expr prev p
-			in
-			(match cases with (e,_,_) :: cl -> loop e cl | [] -> assert false);
-			ctx.on_error <- old_error;
-		in
-		let had_catch_all = ref false in
-		List.iter (fun out ->
-			if out.o_catch_all && not !had_catch_all then
-				had_catch_all := true
-			else if out.o_num_paths = 0 then begin
-				unused out.o_pos;
-				if mctx.toplevel_or then begin match evals with
-					| [{etype = t}] when (match follow t with TAbstract({a_path=[],"Int"},[]) -> true | _ -> false) ->
-						display_error ctx "Note: Int | Int is an or-pattern now" p;
-					| _ -> ()
-				end;
-			end
-		) (List.rev mctx.outcomes);
-	in
-	let dt = try
-		(* compile decision tree *)
-		compile mctx stl pl true
-	with Not_exhaustive(pat,st) ->
-		let rec s_st_r top pre st v = match st.st_def with
-			| SVar v1 ->
-				if not pre then v else begin try
-					let e = match List.assoc v1 !var_inits with Some e -> e | None -> assert false in
-					(Type.s_expr_pretty "" (Type.s_type (print_context())) e) ^ v
-				with Not_found ->
-					v1.v_name ^ v
-				end
-			| STuple(st,i,a) ->
-				let r = a - i - 1 in
-				Printf.sprintf "[%s]" (st_args i r (s_st_r top false st v))
-			| SArray(st,i) ->
-				s_st_r false true st (Printf.sprintf "[%i]%s" i (if top then " = " ^ v else v))
-			| SField({st_def = SVar v1},cf) when v1.v_name.[0] = '`' ->
-				cf.cf_name ^ (if top then " = " ^ v else v)
-			| SField(st,cf) ->
-				s_st_r false true st (Printf.sprintf ".%s%s" cf.cf_name (if top then " = " ^ v else v))
-			| SEnum(st,ef,i) ->
-				let len = match follow ef.ef_type with TFun(args,_) -> List.length args | _ -> 0 in
-				s_st_r false false st (Printf.sprintf "%s(%s)" ef.ef_name (st_args i (len - 1 - i) v))
-		in
-		let pat = match follow st.st_type with
-			| TAbstract({a_impl = Some cl} as a,_) when Meta.has Meta.Enum a.a_meta ->
-				let rec s_pat pat = match pat.p_def with
-					| PCon ({c_def = CConst c},[]) when c <> TNull ->
-						let cf = List.find (fun cf ->
-							match cf.cf_expr with
-							| Some ({eexpr = TConst c2 | TCast({eexpr = TConst c2},None)}) -> c = c2
-							| _ -> false
-						) cl.cl_ordered_statics in
-						cf.cf_name
-					| PVar (v,_) -> v.v_name
-					| PCon (c,[]) -> s_con c
-					| PCon (c,pl) -> s_con c ^ "(" ^ (String.concat "," (List.map s_pat pl)) ^ ")"
-					| POr (pat1,pat2) -> s_pat pat1 ^ " | " ^ s_pat pat2
-					| PAny -> "_"
-					| PBind((v,_),pat) -> v.v_name ^ "=" ^ s_pat pat
-					| PTuple pl -> "(" ^ (String.concat " " (Array.to_list (Array.map s_pat pl))) ^ ")"
-				in
-				s_pat pat
-			| _ ->
-				s_pat pat
-		in
-		let msg = "Unmatched patterns: " ^ (s_st_r true false st pat) in
-		if !extractor_depth > 0 then begin
-			display_error ctx msg st.st_pos;
-			error "Note: Patterns with extractors may require a default pattern" st.st_pos;
-		end else
-			error msg st.st_pos
-	in
-	save();
-	(* check for unused patterns *)
-	if !extractor_depth = 0 then check_unused();
-	if mctx.has_extractor then decr extractor_depth;
-	(* determine type of switch statement *)
-	let t = if not need_val then
-		mk_mono()
-	else match with_type with
-		| WithType t | WithTypeResume t -> t
-		| _ -> try Typer.unify_min_raise ctx (List.rev_map (fun (_,out) -> get_expr mctx out.o_id) (List.rev pl)) with Error (Unify l,p) -> error (error_msg (Unify l)) p
-	in
-	(* unify with expected type if necessary *)
-	begin match tmono with
-		| None -> ()
-		| Some (WithType t2) -> unify ctx t2 t p
-		| Some (WithTypeResume t2) -> (try unify_raise ctx t2 t p with Error (Unify l,p) -> raise (Typer.WithTypeError (l,p)))
-		| _ -> assert false
-	end;
-	(* count usage *)
-	let usage = Array.make (DynArray.length mctx.dt_lut) 0 in
-	(* we always want to keep the first part *)
-	let first = (match dt with Goto i -> i | _ -> Hashtbl.find mctx.dt_cache dt) in
-	Array.set usage first 2;
-	let rec loop dt = match dt with
-		| Goto i -> Array.set usage i ((Array.get usage i) + 1)
-		| Switch(st,cl) -> List.iter (fun (_,dt) -> loop dt) cl
-		| Bind(bl,dt) -> loop dt
-		| Expr e -> ()
-		| Guard(e,dt1,dt2) ->
-			loop dt1;
-			match dt2 with None -> () | Some dt -> (loop dt)
-	in
-	DynArray.iter loop mctx.dt_lut;
-	(* filter parts that will be inlined and keep a map to them*)
-	let map = Array.make (DynArray.length mctx.dt_lut) 0 in
-	let lut = DynArray.create() in
-	let rec loop i c =
-		if c < DynArray.length mctx.dt_lut then begin
-			let i' = if usage.(c) > 1 then begin
-				DynArray.add lut (DynArray.get mctx.dt_lut c);
-				i + 1
-			end else i in
-			Array.set map c i;
-			loop i' (c + 1)
-		end
-	in
-	loop 0 0;
-	(* reindex *)
-	let rec loop dt = match dt with
-		| Goto i -> if usage.(i) > 1 then DTGoto (map.(i)) else loop (DynArray.get mctx.dt_lut i)
-		| Switch(st,cl) -> convert_switch mctx st cl loop
-		| Bind(bl,dt) -> DTBind(List.map (fun (v,st) -> v,convert_st ctx st) bl,loop dt)
-		| Expr id -> DTExpr (get_expr mctx id)
-		| Guard(id,dt1,dt2) -> DTGuard((match get_guard mctx id with Some e -> e | None -> assert false),loop dt1, match dt2 with None -> None | Some dt -> Some (loop dt))
-	in
-	let lut = DynArray.map loop lut in
-	{
-		dt_first = map.(first);
-		dt_dt_lookup = DynArray.to_array lut;
-		dt_type = t;
-		dt_var_init = List.rev !var_inits;
-		dt_is_complex = !is_complex;
-	}
-;;
-match_expr_ref := match_expr;
-get_pattern_locals_ref := get_pattern_locals

+ 422 - 0
src/display/display.ml

@@ -0,0 +1,422 @@
+open Ast
+open Common
+open Type
+open Typecore
+
+(* order of these variants affects output sorting *)
+type display_field_kind =
+	| FKVar
+	| FKMethod
+	| FKType
+	| FKPackage
+
+exception Diagnostics 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 DisplayToplevel of IdentifierType.t list
+
+let is_display_file file =
+	file <> "?" && Common.unique_full_path file = (!Parser.resume_display).pfile
+
+let encloses_position p_target p =
+	p.pmin <= p_target.pmin && p.pmax >= p_target.pmax
+
+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
+						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
+
+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;
+	}
+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... *) ()
+				| _ -> ()
+			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;
+		| _ ->
+			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])
+		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
+			end
+		| [] ->
+			(IDK,null_pos)
+	in
+	loop [] None None path
+
+let process_expr com e = match com.display with
+	| DMToplevel -> find_enclosing com e
+	| DMPosition | DMUsage | DMType -> find_before_pos com e
+	| _ -> e
+
+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 mark_import_position com p =
+	try
+		let r = PMap.find p com.shared.shared_display_information.import_positions in
+		r := true
+	with Not_found ->
+		()
+
+module Diagnostics = struct
+	module DiagnosticsKind = struct
+		type t =
+			| DKUnusedImport
+			| DKUnresolvedIdentifier
+			| DKCompilerError
+
+		let to_int = function
+			| DKUnusedImport -> 0
+			| DKUnresolvedIdentifier -> 1
+			| DKCompilerError -> 2
+	end
+
+	type t = DiagnosticsKind.t * pos
+
+	module UnresolvedIdentifierSuggestion = struct
+		type t =
+			| UISImport
+			| UISTypo
+
+		let to_int = function
+			| UISImport -> 0
+			| UISTypo -> 1
+	end
+
+	open UnresolvedIdentifierSuggestion
+	open DiagnosticsKind
+
+	let print_diagnostics com =
+		let diag = DynArray.create() in
+		let add dk p sev args =
+			DynArray.add diag (dk,p,sev,args)
+		in
+		begin match !(Common.global_cache) with
+			| None ->
+				()
+			| 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
+				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;
+		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
+

文件差异内容过多而无法显示
+ 188 - 764
src/generators/codegen.ml


+ 71 - 33
genas3.ml → src/generators/genas3.ml

@@ -1,23 +1,20 @@
 (*
- * Copyright (C)2005-2013 Haxe Foundation
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
+	The 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 Type
@@ -62,6 +59,20 @@ let is_special_compare e1 e2 =
 	| TInst ({ cl_path = ["flash"],"NativeXml" } as c,_) , _ | _ , TInst ({ cl_path = ["flash"],"NativeXml" } as c,_) -> Some c
 	| _ -> None
 
+let is_fixed_override cf t =
+	let is_type_parameter c = match c.cl_kind with
+		| KTypeParameter _ -> true
+		| _ -> false
+	in
+	match follow cf.cf_type,follow t with
+	| TFun(_,r1),TFun(_,r2) ->
+		begin match follow r1,follow r2 with
+		| TInst(c1,_),TInst(c2,_) when c1 != c2 && not (is_type_parameter c1) && not (is_type_parameter c2) -> true
+		| _ -> false
+		end
+	| _ ->
+		false
+
 let protect name =
 	match name with
 	| "Error" | "Namespace" -> "_" ^ name
@@ -135,7 +146,7 @@ let valid_as3_ident s =
 
 let anon_field s =
 	let s = s_ident s in
-	if not (valid_as3_ident s) then "\"" ^ s ^ "\"" else s
+	if not (valid_as3_ident s) then "\"" ^ (Ast.s_escape s) ^ "\"" else s
 
 let rec create_dir acc = function
 	| [] -> ()
@@ -287,10 +298,7 @@ let rec type_str ctx t p =
 				| TAbstract ({ a_path = [],"UInt" },_)
 				| TAbstract ({ a_path = [],"Int" },_)
 				| TAbstract ({ a_path = [],"Float" },_)
-				| TAbstract ({ a_path = [],"Bool" },_)
-				| TInst ({ cl_path = [],"Int" },_)
-				| TInst ({ cl_path = [],"Float" },_)
-				| TEnum ({ e_path = [],"Bool" },_) -> "*"
+				| TAbstract ({ a_path = [],"Bool" },_) -> "*"
 				| _ -> type_str ctx t p)
 			| _ -> assert false);
 		| _ -> type_str ctx (apply_params t.t_params args t.t_type) p)
@@ -521,6 +529,14 @@ let rec gen_call ctx e el r =
 		spr ctx "(";
 		concat ctx "," (gen_value ctx) el;
 		spr ctx ")"
+	| TField (e1,FInstance(_,_,cf)),el when is_fixed_override cf e.etype ->
+		let s = type_str ctx r e.epos in
+		spr ctx "((";
+		gen_value ctx e;
+		spr ctx "(";
+		concat ctx "," (gen_value ctx) el;
+		spr ctx ")";
+		print ctx ") as %s)" s
 	| _ ->
 		gen_value ctx e;
 		spr ctx "(";
@@ -824,7 +840,7 @@ and gen_value ctx e =
 	let value block =
 		let old = ctx.in_value in
 		let t = type_str ctx e.etype e.epos in
-		let r = alloc_var (gen_local ctx "$r") e.etype in
+		let r = alloc_var (gen_local ctx "$r") e.etype e.epos in
 		ctx.in_value <- Some r;
 		if ctx.in_static then
 			print ctx "function() : %s " t
@@ -948,7 +964,7 @@ and gen_value ctx e =
 		v()
 
 let final m =
-	if Ast.Meta.has Ast.Meta.Final m then "final " else ""
+	if Ast.Meta.has Ast.Meta.Final m then " final " else ""
 
 let generate_field ctx static f =
 	newline ctx;
@@ -1024,8 +1040,11 @@ let generate_field ctx static f =
 		let gen_init () = match f.cf_expr with
 			| None -> ()
 			| Some e ->
-				print ctx " = ";
-				gen_value ctx e
+				if not static || (match e.eexpr with | TConst _ | TFunction _ | TTypeExpr _ -> true | _ -> false) then begin
+					print ctx " = ";
+					gen_value ctx e
+				end else
+					Codegen.ExtClass.add_static_init ctx.curclass f e e.epos
 		in
 		if is_getset then begin
 			let t = type_str ctx f.cf_type p in
@@ -1100,13 +1119,33 @@ let generate_class ctx c =
 	);
 	List.iter (generate_field ctx false) c.cl_ordered_fields;
 	List.iter (generate_field ctx true) c.cl_ordered_statics;
+	let has_init = match c.cl_init with
+		| None -> false
+		| Some e ->
+			newline ctx;
+			spr ctx "static static_init function init() : void";
+			gen_expr ctx (mk_block e);
+			true;
+	in
 	cl();
 	newline ctx;
 	print ctx "}";
 	pack();
 	newline ctx;
 	print ctx "}";
-	newline ctx
+	if has_init then begin
+		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);
+	end;
+	newline ctx;
+	if c.cl_interface && Ast.Meta.has (Ast.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);
+		newline ctx
+	end
 
 let generate_main ctx inits =
 	ctx.curclass <- { null_class with cl_path = [],"__main__" };
@@ -1214,11 +1253,10 @@ let generate com =
 				| ["flash"],"FlashXml__" -> { c with cl_path = [],"Xml" }
 				| (pack,name) -> { c with cl_path = (pack,protect name) }
 			) in
-			(match c.cl_init with
-			| None -> ()
-			| Some e -> inits := e :: !inits);
 			if c.cl_extern then
-				()
+				(match c.cl_init with
+				| None -> ()
+				| Some e -> inits := e :: !inits)
 			else
 				let ctx = init infos c.cl_path in
 				generate_class ctx c;

+ 259 - 177
gencommon.ml → src/generators/gencommon.ml

@@ -1,23 +1,20 @@
 (*
- * Copyright (C)2005-2013 Haxe Foundation
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
+	The 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.
  *)
 
 (*
@@ -51,6 +48,8 @@ open Option
 open Printf
 open ExtString
 
+let alloc_var n t = alloc_var n t null_pos
+
 let debug_type_ctor = function
 	| TMono _ -> "TMono"
 	| TEnum _ -> "TEnum"
@@ -116,48 +115,11 @@ let v_undefined = alloc_var "__undefined__" t_dynamic
 
 let undefined pos = { eexpr = TLocal(v_undefined); etype = t_dynamic; epos = pos }
 
-module ExprHashtblHelper =
-struct
-	type hash_texpr_t =
-	{
-		hepos : pos;
-		heexpr : int;
-		hetype : int;
-	}
-
-	let mk_heexpr = function
-		| TConst _ -> 0 | TLocal _ -> 1 | TArray _ -> 3 | TBinop _ -> 4 | TField _ -> 5 | TTypeExpr _ -> 7 | TParenthesis _ -> 8 | TObjectDecl _ -> 9
-		| TArrayDecl _ -> 10 | TCall _ -> 11 | TNew _ -> 12 | TUnop _ -> 13 | TFunction _ -> 14 | TVar _ -> 15 | TBlock _ -> 16 | TFor _ -> 17 | TIf _ -> 18 | TWhile _ -> 19
-		| TSwitch _ -> 20 (* | TPatMatch _ -> 21 *) | TTry _ -> 22 | TReturn _ -> 23 | TBreak -> 24 | TContinue -> 25 | TThrow _ -> 26 | TCast _ -> 27 | TMeta _ -> 28 | TEnumParameter _ -> 29
-
-	let mk_heetype = function
-		| TMono _ -> 0 | TEnum _ -> 1 | TInst _ -> 2 | TType _ -> 3 | TFun _ -> 4
-		| TAnon _ -> 5 | TDynamic _ -> 6 | TLazy _ -> 7 | TAbstract _ -> 8
-
-	let mk_type e =
-		{
-			hepos = e.epos;
-			heexpr = mk_heexpr e.eexpr;
-			hetype = mk_heetype e.etype;
-		}
-end;;
-
 let path_of_md_def md_def =
 	match md_def.m_types with
 		| [TClassDecl c] -> c.cl_path
 		| _ -> md_def.m_path
 
-open ExprHashtblHelper;;
-(* Expression Hashtbl. This shouldn't be kept indefinately as it's not a weak Hashtbl. *)
-module ExprHashtbl = Hashtbl.Make(
-		struct
-			type t = Type.texpr
-
-			let equal = (==)
-			let hash t = Hashtbl.hash (mk_type t)
-		end
-);;
-
 (* ******************************************* *)
 (*	Gen Common
 
@@ -260,9 +222,9 @@ let path_s path =
 	| TMono r -> (match !r with | Some t -> t_to_md t | None -> assert false)
 	| _ -> assert false
 
-let get_cl mt = match mt with | TClassDecl cl -> cl | _ -> failwith ("Unexpected module type of '" ^ path_s (t_path mt) ^ "'")
+let get_cl mt = match mt with | TClassDecl cl -> cl | _ -> failwith (Printf.sprintf "Unexpected module type (class expected) for %s: %s" (path_s (t_path mt)) (s_module_type_kind mt))
 
-let get_abstract mt = match mt with | TAbstractDecl a -> a | _ -> failwith ("Unexpected module type of '" ^ path_s (t_path mt) ^ "'")
+let get_abstract mt = match mt with | TAbstractDecl a -> a | _ -> failwith (Printf.sprintf "Unexpected module type (abstract expected) for %s: %s" (path_s (t_path mt)) (s_module_type_kind mt))
 
 let get_tdef mt = match mt with | TTypeDecl t -> t | _ -> assert false
 
@@ -273,17 +235,20 @@ let is_void t = match follow t with
 			true
 	| _ -> false
 
-let mk_local var pos = { eexpr = TLocal(var); etype = var.v_type; epos = pos }
+let mk_local = Codegen.ExprBuilder.make_local
 
 (* this function is used by CastDetection module *)
 let get_fun t =
 	match follow t with | TFun(r1,r2) -> (r1,r2) | _ -> (trace (s_type (print_context()) (follow t) )); assert false
 
-let mk_cast t e =
-	{ eexpr = TCast(e, None); etype = t; epos = e.epos }
+let mk_cast t e = Type.mk_cast e t e.epos
+
+(** TODO: when adding new AST, make a new cast type for those fast casts. For now, we're using this hack
+ *        of using null_class to tell a fast cast from a normal one. Also note that this only works since both
+ *        C# and Java do not use the second part of TCast for anything *)
+let mk_castfast t e = { e with eexpr = TCast(e, Some (TClassDecl null_class)); etype = t }
 
-let mk_classtype_access cl pos =
-	{ eexpr = TTypeExpr(TClassDecl(cl)); etype = anon_of_classtype cl; epos = pos }
+let mk_classtype_access cl pos = Codegen.ExprBuilder.make_static_this cl pos
 
 let mk_static_field_access_infer cl field pos params =
 	try
@@ -781,11 +746,6 @@ let new_ctx con =
 
 		gspecial_vars = Hashtbl.create 0;
 	} in
-
-	(*gen.gtools.r_create_empty <-
-	gen.gtools.r_get_class <-
-	gen.gtools.r_fields <- *)
-
 	gen
 
 let init_ctx gen =
@@ -1193,7 +1153,7 @@ let get_real_fun gen t =
 	| TFun(args,t) -> TFun(List.map (fun (n,o,t) -> n,o,gen.greal_type t) args, gen.greal_type t)
 	| _ -> t
 
-let mk_int gen i pos = { eexpr = TConst(TInt ( Int32.of_int i)); etype = gen.gcon.basic.tint; epos = pos }
+let mk_int gen i pos =  Codegen.ExprBuilder.make_int gen.gcon i pos
 
 let mk_return e = { eexpr = TReturn (Some e); etype = e.etype; epos = e.epos }
 
@@ -1375,7 +1335,8 @@ let find_first_declared_field gen orig_cl ?get_vmtype ?exact_field field =
 	in
 	loop_cl 0 orig_cl (List.map snd orig_cl.cl_params) (List.map snd orig_cl.cl_params);
 	match !chosen with
-	| None -> None
+	| None ->
+		None
 	| Some(_,f,c,tl,tlch) ->
 		if !is_overload && not (Meta.has Meta.Overload f.cf_meta) then
 			f.cf_meta <- (Meta.Overload,[],f.cf_pos) :: f.cf_meta;
@@ -1574,10 +1535,10 @@ struct
 		basically, everything that is extern is assumed to not be hxgen, unless meta :hxgen is set, and
 		everything that is not extern is assumed to be hxgen, unless meta :nativegen is set
 	*)
-	let rec default_hxgen_func md =
+	let rec default_hxgen_func gen md =
 		match md with
 			| TClassDecl { cl_kind = KAbstractImpl a } ->
-				default_hxgen_func (TAbstractDecl a)
+				default_hxgen_func gen (TAbstractDecl a)
 			| TClassDecl cl ->
 				let rec is_hxgen_class (c,_) =
 					if c.cl_extern then begin
@@ -1601,11 +1562,21 @@ struct
 				in
 
 				is_hxgen_class (cl,[])
-			| TEnumDecl e -> if e.e_extern then Meta.has Meta.HxGen e.e_meta else not (Meta.has Meta.NativeGen e.e_meta)
+			| TEnumDecl e -> if e.e_extern then Meta.has Meta.HxGen e.e_meta else
+				if Meta.has Meta.NativeGen e.e_meta then
+					if Meta.has Meta.FlatEnum e.e_meta then
+						false
+					else begin
+						gen.gcon.error "Only flat enums may be @:nativeGen" e.e_pos;
+						true
+					end
+				else
+					true
+				(* not (Meta.has Meta.NativeGen e.e_meta) *)
 			| TAbstractDecl a when Meta.has Meta.CoreType a.a_meta -> not (Meta.has Meta.NativeGen a.a_meta)
 			| TAbstractDecl a -> (match follow a.a_this with
 				| TInst _ | TEnum _ | TAbstract _ ->
-					default_hxgen_func (t_to_md (follow a.a_this))
+					default_hxgen_func gen (t_to_md (follow a.a_this))
 				| _ ->
 					not (Meta.has Meta.NativeGen a.a_meta))
 			| TTypeDecl t -> (* TODO see when would we use this *)
@@ -1617,7 +1588,7 @@ struct
 	*)
 	let run_filter gen is_hxgen_func =
 		let filter md =
-			let meta = if is_hxgen_func md then Meta.HxGen else Meta.NativeGen in
+			let meta = if is_hxgen_func gen md then Meta.HxGen else Meta.NativeGen in
 			begin
 				match md with
 					| TClassDecl cl -> cl.cl_meta <- (meta, [], cl.cl_pos) :: cl.cl_meta
@@ -3083,7 +3054,7 @@ struct
 										| _ ->
 											let i = ref 0 in
 											let t = TFun(List.map (fun e -> incr i; "arg" ^ (string_of_int !i), false, e.etype) params, e.etype) in
-											dynamic_func_call { e with eexpr = TCall( mk_cast t (run e1), List.map run params ) }
+											dynamic_func_call { e with eexpr = TCall( mk_castfast t (run e1), List.map run params ) }
 							)
 						(* | FNotFound ->
 							{ e with eexpr = TCall({ e1 with eexpr = TField(run ecl, f) }, List.map run params) }
@@ -3095,7 +3066,7 @@ struct
 								| _ ->
 									let i = ref 0 in
 									let t = TFun(List.map (fun e -> incr i; "arg" ^ (string_of_int !i), false, e.etype) params, e.etype) in
-									dynamic_func_call { e with eexpr = TCall( mk_cast t (run e1), List.map run params ) }
+									dynamic_func_call { e with eexpr = TCall( mk_castfast t (run e1), List.map run params ) }
 					)
 				| TField(ecl, FClosure (_,cf)) ->
 					transform_closure e (run ecl) cf.cf_name
@@ -3115,7 +3086,7 @@ struct
 									("p" ^ (string_of_int !i), false, e.etype)
 								) params, e.etype)
 							in
-							fun e -> mk_cast t e
+							fun e -> mk_castfast t e
 					in
 					dynamic_func_call { e with eexpr = TCall(run (may_cast tc), List.map run params) }
 				| _ -> Type.map_expr run e
@@ -4145,19 +4116,6 @@ struct
 
 	let priority = max_dep -. 20.
 
-	let rec deep_follow gen t = match run_follow gen t with
-		| TInst(c,tl) ->
-			TInst(c,List.map (deep_follow gen) tl)
-		| TEnum(e,tl) ->
-			TEnum(e,List.map (deep_follow gen) tl)
-		| TAbstract(a,tl) ->
-			TAbstract(a,List.map (deep_follow gen) tl)
-		| TType(t,tl) ->
-			TType(t,List.map (deep_follow gen) tl)
-		| TFun(args,ret) ->
-			TFun(List.map (fun (n,o,t) -> n,o,deep_follow gen t) args, deep_follow gen ret)
-		| t -> t
-
 	(* this function will receive the original function argument, the applied function argument and the original function parameters. *)
 	(* from this info, it will infer the applied tparams for the function *)
 	(* this function is used by CastDetection module *)
@@ -4173,8 +4131,6 @@ struct
 
 			(try
 				List.iter2 (fun a o ->
-					let o = deep_follow gen o in
-					let a = deep_follow gen a in
 					unify a o
 					(* type_eq EqStrict a o *)
 				) applied original
@@ -4838,6 +4794,7 @@ struct
 							iface.cl_array_access <- Option.map (apply_params (cl.cl_params) (List.map (fun _ -> t_dynamic) cl.cl_params)) cl.cl_array_access;
 							iface.cl_extern <- cl.cl_extern;
 							iface.cl_module <- cl.cl_module;
+							iface.cl_private <- cl.cl_private;
 							iface.cl_meta <-
 								(Meta.HxGen, [], cl.cl_pos)
 								::
@@ -4879,14 +4836,15 @@ struct
 								| _ ->
 									let is_override = List.memq cf cl.cl_overrides in
 									let cf_type = if is_override && not (Meta.has Meta.Overload cf.cf_meta) then
-										match field_access gen (TInst(cl, List.map snd cl.cl_params)) cf.cf_name with
-											| FClassField(_,_,_,_,_,actual_t,_) -> actual_t
+										match find_first_declared_field gen cl cf.cf_name with
+											| Some(_,_,declared_t,_,_,_,_) -> declared_t
 											| _ -> assert false
 									else
 										cf.cf_type
 									in
 
-									not (has_type_params cf_type)) cl.cl_ordered_fields
+									not (has_type_params cf_type)
+								) cl.cl_ordered_fields
 							in
 							let fields = List.map (fun f -> mk_class_field f.cf_name f.cf_type f.cf_public f.cf_pos f.cf_kind f.cf_params) fields in
 							let fields = iface_cf :: fields in
@@ -5964,6 +5922,16 @@ struct
 		let is_cl_related cl tl super superl = map_cls gen (gen.guse_tp_constraints || (match cl.cl_kind,super.cl_kind with KTypeParameter _, _ | _,KTypeParameter _ -> false | _ -> true)) (fun _ _ -> true) super cl tl in
 		is_cl_related cl tl super superl || is_cl_related super superl cl tl
 
+	let is_exactly_basic gen t1 t2 =
+		match gen.gfollow#run_f t1, gen.gfollow#run_f t2 with
+			| TAbstract(a1, []), TAbstract(a2, []) ->
+				a1 == a2 && Common.defined gen.gcon Define.FastCast
+			| TInst(c1, []), TInst(c2, []) ->
+				c1 == c2 && Common.defined gen.gcon Define.FastCast
+			| TEnum(e1, []), TEnum(e2, []) ->
+				e1 == e2 && Common.defined gen.gcon Define.FastCast
+			| _ ->
+				false
 
 	let rec is_unsafe_cast gen to_t from_t =
 		match (follow to_t, follow from_t) with
@@ -6049,19 +6017,21 @@ struct
 		let do_unsafe_cast () = do_unsafe_cast gen real_from_t real_to_t { e with etype = real_from_t } in
 		let to_t, from_t = real_to_t, real_from_t in
 
-		let mk_cast t e =
+		let mk_cast fast t e =
 			match e.eexpr with
 				(* TThrow is always typed as Dynamic, we just need to type it accordingly *)
 				| TThrow _ -> { e with etype = t }
-				| _ -> mk_cast t e
+				| _ -> if fast then mk_castfast t e else mk_cast t e
 		in
 
 		let e = { e with etype = real_from_t } in
 		if try fast_eq real_to_t real_from_t with Invalid_argument("List.for_all2") -> false then e else
 		match real_to_t, real_from_t with
 			(* string is the only type that can be implicitly converted from any other *)
+			| TInst( { cl_path = ([], "String") }, []), TInst( { cl_path = ([], "String") }, [] ) ->
+				mk_cast true to_t e
 			| TInst( { cl_path = ([], "String") }, []), _ ->
-				mk_cast to_t e
+				mk_cast false to_t e
 			| TInst(cl_to, params_to), TInst(cl_from, params_from) ->
 				let ret = ref None in
 				(*
@@ -6085,7 +6055,7 @@ struct
 								if we are already handling type parameter casts on other part of code (e.g. RealTypeParameters),
 								we'll just make a cast to indicate that this place needs type parameter-involved casting
 							*)
-							ret := Some (mk_cast to_t e);
+							ret := Some (mk_cast true to_t e);
 							true
 						end else
 							(*
@@ -6094,16 +6064,16 @@ struct
 							*)
 							try
 								List.iter2 (type_eq gen EqRightDynamic) tl params_to;
-								ret := Some (mk_cast to_t e);
+								ret := Some (mk_cast true to_t e);
 								true
 							with | Unify_error _ ->
-								ret := Some (mk_cast to_t (mk_cast (TInst(cl_to, List.map (fun _ -> t_dynamic) params_to)) e));
+								ret := Some (mk_cast true to_t (mk_cast true (TInst(cl_to, List.map (fun _ -> t_dynamic) params_to)) e));
 								true
 				) cl_to cl_from params_from);
 				if is_some !ret then
 					get !ret
 				else if is_cl_related gen cl_from params_from cl_to params_to then
-					mk_cast to_t e
+					mk_cast true to_t e
 				else
 					(* potential unsafe cast *)
 					(do_unsafe_cast ())
@@ -6115,18 +6085,18 @@ struct
 			| TMono _, _
 			| TDynamic _, _
 			| TAnon _, _ when gen.gneeds_box real_from_t ->
-				mk_cast to_t e
+				mk_cast false to_t e
 			| TMono _, _
 			| TDynamic _, _ -> e
 			| _, TMono _
-			| _, TDynamic _ -> mk_cast to_t e
+			| _, TDynamic _ -> mk_cast false to_t e
 			| TAnon (a_to), TAnon (a_from) ->
 				if a_to == a_from then
 					e
 				else if type_iseq gen to_t from_t then (* FIXME apply unify correctly *)
 					e
 				else
-					mk_cast to_t e
+					mk_cast true to_t e
 			| _, TAnon(anon) -> (try
 				let p2 = match !(anon.a_status) with
 				| Statics c -> TInst(c,List.map (fun _ -> t_dynamic) c.cl_params)
@@ -6139,7 +6109,7 @@ struct
 				| _ -> assert false in
 				handle_cast gen e real_to_t (gen.greal_type (TAbstract(tclass, [p2])))
 			with | Not_found ->
-				mk_cast to_t e)
+				mk_cast false to_t e)
 			| TAbstract (a_to, _), TAbstract(a_from, _) when a_to == a_from ->
 				e
 			| TAbstract _, TInst({ cl_kind = KTypeParameter _ }, _)
@@ -6149,11 +6119,11 @@ struct
 			| _, TAbstract _ ->
 				(try
 					unify from_t to_t;
-					mk_cast to_t e
+					mk_cast true to_t e
 				with | Unify_error _ ->
 					try
 						unify to_t from_t;
-						mk_cast to_t e
+						mk_cast true to_t e
 					with | Unify_error _ ->
 						do_unsafe_cast())
 			| TEnum(e_to, []), TEnum(e_from, []) ->
@@ -6200,13 +6170,13 @@ struct
 					e
 			| TType(t_to, _), TType(t_from,_) ->
 				if gen.gspecial_needs_cast real_to_t real_from_t then
-					mk_cast to_t e
+					mk_cast false to_t e
 				else
 					e
 			| TType _, _ when gen.gspecial_needs_cast real_to_t real_from_t ->
-				mk_cast to_t e
+				mk_cast false to_t e
 			| _, TType _ when gen.gspecial_needs_cast real_to_t real_from_t ->
-				mk_cast to_t e
+				mk_cast false to_t e
 			(*| TType(t_to, _), TType(t_from, _) ->
 				if t_to.t_path = t_from.t_path then
 					e
@@ -6219,15 +6189,15 @@ struct
 				if is_unsafe_cast gen real_to_t real_from_t then (* is_unsafe_cast will already follow both *)
 					(do_unsafe_cast ())
 				else
-					mk_cast to_t e
+					mk_cast false to_t e
 			| TAnon anon, _ ->
 				if PMap.is_empty anon.a_fields then
 					e
 				else
-					mk_cast to_t e
+					mk_cast true to_t e
 			| TFun(args, ret), TFun(args2, ret2) ->
 				let get_args = List.map (fun (_,_,t) -> t) in
-				(try List.iter2 (type_eq gen (EqBothDynamic)) (ret :: get_args args) (ret2 :: get_args args2); e with | Unify_error _ | Invalid_argument("List.iter2") -> mk_cast to_t e)
+				(try List.iter2 (type_eq gen (EqBothDynamic)) (ret :: get_args args) (ret2 :: get_args args2); e with | Unify_error _ | Invalid_argument("List.iter2") -> mk_cast true to_t e)
 			| _, _ ->
 				do_unsafe_cast ()
 
@@ -6361,8 +6331,16 @@ struct
 		let args,ret = get_fun tfun in
 		TFun(loop [] args elist, ret)
 
-	(*
+	let fastcast_if_needed gen expr real_to_t real_from_t =
+		if Common.defined gen.gcon Define.FastCast then begin
+			if type_iseq gen real_to_t real_from_t then
+				{ expr with etype = real_to_t }
+			else
+				mk_castfast real_to_t { expr with etype=real_from_t }
+		end else
+			handle_cast gen expr real_to_t real_from_t
 
+	(*
 		Type parameter handling
 		It will detect if/what type parameters were used, and call the cast handler
 		It will handle both TCall(TField) and TCall by receiving a texpr option field: e
@@ -6402,6 +6380,14 @@ struct
 					| TInst(_,params) -> params
 					| _ -> params
 				in
+				let local_mk_cast t expr =
+					(* handle_cast gen expr t expr.etype *)
+					if is_exactly_basic gen t expr.etype then
+						expr
+					else
+						mk_castfast t expr
+				in
+
 				let ecall = get e in
 				let ef = ref ef in
 				let is_overload = cf.cf_overloads <> [] || Meta.has Meta.Overload cf.cf_meta || (is_static && is_static_overload cl (field_name f)) in
@@ -6470,7 +6456,7 @@ struct
 					if is_void ecall.etype then
 						{ ecall with eexpr = TCall({ e1 with eexpr = TField(!ef, f) }, elist ) }
 					else
-						mk_cast ecall.etype { ecall with eexpr = TCall({ e1 with eexpr = TField(!ef, f) }, elist ) }
+						local_mk_cast ecall.etype { ecall with eexpr = TCall({ e1 with eexpr = TField(!ef, f) }, elist ) }
 				else begin
 					(* infer arguments *)
 					(* let called_t = TFun(List.map (fun e -> "arg",false,e.etype) elist, ecall.etype) in *)
@@ -6493,14 +6479,14 @@ struct
 					(* check types list *)
 					let new_ecall, elist = try
 						let elist = List.map2 (fun applied (_,_,funct) ->
-							match is_overload, applied.eexpr with
+							match is_overload || real_fparams <> [], applied.eexpr with
 							| true, TConst TNull ->
-								mk_cast (gen.greal_type funct) applied
+								mk_castfast (gen.greal_type funct) applied
 							| true, _ -> (* when not (type_iseq gen (gen.greal_type applied.etype) funct) -> *)
 								let ret = handle_cast gen applied (funct) (gen.greal_type applied.etype) in
 								(match ret.eexpr with
 								| TCast _ -> ret
-								| _ -> mk_cast (funct) ret)
+								| _ -> local_mk_cast (funct) ret)
 							| _ ->
 								handle_cast gen applied (funct) (gen.greal_type applied.etype)
 						) applied args_ft in
@@ -6604,6 +6590,73 @@ struct
 			| _ -> false
 		in
 
+		let binop_type op main_expr e1 e2 =
+			let name = Common.platform_name gen.gcon.platform in
+			let basic = gen.gcon.basic in
+			(* If either operand is of type decimal, the other operand is converted to type decimal, or a compile-time error occurs if the other operand is of type float or double.
+			 * Otherwise, if either operand is of type double, the other operand is converted to type double.
+			 * Otherwise, if either operand is of type float, the other operand is converted to type float.
+			 * Otherwise, if either operand is of type ulong, the other operand is converted to type ulong, or a compile-time error occurs if the other operand is of type sbyte, short, int, or long.
+			 * Otherwise, if either operand is of type long, the other operand is converted to type long.
+			 * Otherwise, if either operand is of type uint and the other operand is of type sbyte, short, or int, both operands are converted to type long.
+			 * Otherwise, if either operand is of type uint, the other operand is converted to type uint.
+			 * Otherwise, both operands are converted to type int.
+			 *  *)
+			let t1, t2 = follow (run_follow gen e1.etype), follow (run_follow gen e2.etype) in
+			match t1, t2 with
+				| TAbstract(a1,[]), TAbstract(a2,[]) when a1 == a2 ->
+					{ main_expr with eexpr = TBinop(op, e1, e2); etype = e1.etype }
+				| TInst(i1,[]), TInst(i2,[]) when i1 == i2 ->
+					{ main_expr with eexpr = TBinop(op, e1, e2); etype = e1.etype }
+				| TInst({ cl_path = ([],"String") },[]), _ when op = OpAdd ->
+					{ main_expr with eexpr = TBinop(op, e1, mk_cast basic.tstring e2); etype = basic.tstring }
+				| _, TInst({ cl_path = ([],"String") },[]) when op = OpAdd ->
+					{ main_expr with eexpr = TBinop(op, mk_cast basic.tstring e1, e2); etype = basic.tstring }
+				| TAbstract({ a_path = ([], "Float") }, []), _ ->
+					{ main_expr with eexpr = TBinop(op, e1, e2); etype = e1.etype }
+				| _, TAbstract({ a_path = ([], "Float") }, []) ->
+					{ main_expr with eexpr = TBinop(op, e1, e2); etype = e2.etype }
+				| TAbstract({ a_path = ([], "Single") }, []), _ ->
+					{ main_expr with eexpr = TBinop(op, e1, e2); etype = e1.etype }
+				| _, TAbstract({ a_path = ([], "Single") }, []) ->
+					{ main_expr with eexpr = TBinop(op, e1, e2); etype = e2.etype }
+				| TAbstract({ a_path = ([pf], "UInt64") }, []), _ when pf = name ->
+					{ main_expr with eexpr = TBinop(op, e1, e2); etype = e1.etype }
+				| _, TAbstract({ a_path = ([pf], "UInt64") }, []) when pf = name ->
+					{ main_expr with eexpr = TBinop(op, e1, e2); etype = e2.etype }
+				| TAbstract({ a_path = ([pf], "Int64") }, []), _ when pf = name ->
+					{ main_expr with eexpr = TBinop(op, e1, e2); etype = e1.etype }
+				| _, TAbstract({ a_path = ([pf], "Int64") }, []) when pf = name ->
+					{ main_expr with eexpr = TBinop(op, e1, e2); etype = e2.etype }
+				| TAbstract({ a_path = ([], "UInt") }, []), tother when like_int tother ->
+					let ti64 = mt_to_t_dyn ( get_type gen ([name], "Int64") ) in
+					let ret = { main_expr with eexpr = TBinop(op, e1, e2); etype = ti64 } in
+					if op <> OpDiv then
+						mk_cast t1 ret
+					else
+						ret
+				| tother, TAbstract({ a_path = ([], "UInt") }, []) when like_int tother ->
+					let ti64 = mt_to_t_dyn ( get_type gen ([name], "Int64") ) in
+					let ret = { main_expr with eexpr = TBinop(op, e1, e2); etype = ti64 } in
+					if op <> OpDiv then
+						mk_cast t2 ret
+					else
+						ret
+				| TAbstract({ a_path = ([], "UInt") }, []), _ ->
+					{ main_expr with eexpr = TBinop(op, e1, e2); etype = e1.etype }
+				| _, TAbstract({ a_path = ([], "UInt") }, []) ->
+					{ main_expr with eexpr = TBinop(op, e1, e2); etype = e2.etype }
+				| TAbstract(a1,[]), TAbstract(a2,[]) ->
+					{ main_expr with eexpr = TBinop(op, e1, e2); etype = basic.tint }
+				| _ ->
+					{ main_expr with eexpr = TBinop(op, e1, e2) }
+		in
+		let binop_type = if Common.defined gen.gcon Define.FastCast then
+			binop_type
+		else
+			fun op main_expr e1 e2 -> { main_expr with eexpr = TBinop(op, e1, e2) }
+		in
+
 		let rec run ?(just_type = false) e =
 			let handle = if not just_type then handle else fun e t1 t2 -> { e with etype = gen.greal_type t2 } in
 			let was_in_value = !in_value in
@@ -6632,7 +6685,12 @@ struct
 				| TBinop ( (Ast.OpShl | Ast.OpShr | Ast.OpUShr as op), e1, e2 ) ->
 					let e1 = run e1 in
 					let e2 = handle (run e2) (gen.gcon.basic.tint) e2.etype in
-					{ e with eexpr = TBinop(op, e1, e2) }
+					let rett = binop_type op e e1 e2 in
+					{ e with eexpr = TBinop(op, e1, e2); etype = rett.etype }
+				| TBinop( (OpAdd | OpMult | OpDiv | OpSub | OpAnd | OpOr | OpXor | OpMod) as op, e1, e2 ) ->
+					binop_type op e (run e1) (run e2)
+				| TBinop( (OpEq | OpNotEq | OpGt | OpGte | OpLt | OpLte | OpBoolAnd | OpBoolOr) as op, e1, e2 ) ->
+					handle { e with eexpr = TBinop(op, run e1, run e2) } e.etype gen.gcon.basic.tbool
 				| TField(ef, f) ->
 					handle_type_parameter gen None e (run ef) ~clean_ef:ef ~overloads_cast_to_base:overloads_cast_to_base f [] calls_parameters_explicitly
 				| TArrayDecl el ->
@@ -6718,15 +6776,17 @@ struct
 					{ e with eexpr = TNew(cl, tparams, List.map run eparams) })
 				| TArray(arr, idx) ->
 					let arr_etype = match follow arr.etype with
-					| (TInst _ as t) -> t
-					| TAbstract (a, pl) when not (Meta.has Meta.CoreType a.a_meta) ->
-						follow (Abstract.get_underlying_type a pl)
-					| t -> t in
+						| (TInst _ as t) -> t
+						| TAbstract (a, pl) when not (Meta.has Meta.CoreType a.a_meta) ->
+							follow (Abstract.get_underlying_type a pl)
+						| t -> t
+					in
+					let idx = run idx in
 					let idx = match gen.greal_type idx.etype with
-					| TAbstract({ a_path = [],"Int" },_) -> run idx
-					| _ -> match handle (run idx) gen.gcon.basic.tint (gen.greal_type idx.etype) with
-					| ({ eexpr = TCast _ } as idx) -> idx
-					| idx -> mk_cast gen.gcon.basic.tint idx
+						| TAbstract({ a_path = [],"Int" },_) -> idx
+						| _ -> match handle idx gen.gcon.basic.tint (gen.greal_type idx.etype) with
+							| ({ eexpr = TCast _ } as idx) -> idx
+							| idx -> mk_cast gen.gcon.basic.tint idx
 					in
 					let e = { e with eexpr = TArray(run arr, idx) } in
 					(* get underlying class (if it's a class *)
@@ -6742,7 +6802,8 @@ struct
 									let real_t = apply_params cl.cl_params params param in
 									(* see if it needs a cast *)
 
-									handle (e) (gen.greal_type e.etype) (gen.greal_type real_t)
+									fastcast_if_needed gen e (gen.greal_type e.etype) (gen.greal_type real_t)
+									(* handle (e) (gen.greal_type e.etype) (gen.greal_type real_t) *)
 							)
 						| _ -> Type.map_expr run e)
 				| TVar (v, eopt) ->
@@ -6792,6 +6853,8 @@ struct
 								mk_cast (gen.greal_type e.etype) enull
 					| _ when is_abstract_to_struct expr.etype && type_iseq gen e.etype (get_abstract_impl expr.etype) ->
 						run { expr with etype = expr.etype }
+					| _ when is_exactly_basic gen expr.etype e.etype ->
+						run { expr with etype = expr.etype }
 					| _ ->
 						match gen.greal_type e.etype, gen.greal_type expr.etype with
 							| (TInst(c,tl) as tinst1), TAbstract({ a_path = ["cs"],"Pointer" }, [tinst2]) when type_iseq gen tinst1 (gen.greal_type tinst2) ->
@@ -6818,7 +6881,12 @@ struct
 
 	let configure gen (mapping_func:texpr->texpr) =
 		gen.ghandle_cast <- (fun tto tfrom expr -> handle_cast gen expr (gen.greal_type tto) (gen.greal_type tfrom));
-		let map e = Some(mapping_func e) in
+		let map e = match gen.gcurrent_classfield with
+			| Some(cf) when Meta.has (Meta.Custom ":skipCastDetect") cf.cf_meta ->
+				None
+			| _ ->
+				Some(mapping_func e)
+		in
 		gen.gsyntax_filters#add ~name:name ~priority:(PCustom priority) map;
 		ReturnCast.configure gen
 
@@ -10798,7 +10866,10 @@ struct
 
 	let priority = solve_deps name [ DAfter ExpressionUnwrap.priority; DAfter ObjectDeclMap.priority; DAfter ArrayDeclSynf.priority ]
 
-	let is_int = like_int
+	let rec is_int t = match follow t with
+		| TInst({ cl_path = (["haxe";"lang"],"Null") }, [t]) -> is_int t
+		| t ->
+			like_int t && not (like_i64 t)
 
 	let is_exactly_int t = match follow t with
 		| TAbstract ({ a_path=[],"Int" }, []) -> true
@@ -11146,42 +11217,8 @@ struct
 					match decl with
 					| Some(f2,actual_t,_,t,declared_cl,_,_)
 						when not (Typeload.same_overload_args ~get_vmtype actual_t (get_real_fun gen f.cf_type) f2 f) ->
-							if Meta.has Meta.Overload f.cf_meta then begin
-								(* if it is overload, create another field with the requested type *)
-								let f3 = mk_class_field f.cf_name t f.cf_public f.cf_pos f.cf_kind f.cf_params in
-								let p = f.cf_pos in
-								let old_args, old_ret = get_fun f.cf_type in
-								let args, ret = get_fun t in
-								let tf_args = List.map (fun (n,o,t) -> alloc_var n t, None) args in
-								let f3_mk_return = if is_void ret then (fun e -> e) else (fun e -> mk_return (mk_cast ret e)) in
-								f3.cf_expr <- Some {
-									eexpr = TFunction({
-										tf_args = tf_args;
-										tf_type = ret;
-										tf_expr = mk_block (f3_mk_return {
-											eexpr = TCall(
-												{
-													eexpr = TField(
-														{ eexpr = TConst TThis; etype = TInst(c, List.map snd c.cl_params); epos = p },
-														FInstance(c,List.map snd c.cl_params,f));
-													etype = f.cf_type;
-													epos = p
-												},
-												List.map2 (fun (v,_) (_,_,t) -> mk_cast t (mk_local v p)) tf_args old_args);
-											etype = old_ret;
-											epos = p
-										})
-									});
-									etype = t;
-									epos = p;
-								};
-								gen.gafter_filters_ended <- ((fun () ->
-									f.cf_overloads <- f3 :: f.cf_overloads;
-								) :: gen.gafter_filters_ended);
-								f3
-							end else begin match f.cf_expr with
+							(match f.cf_expr with
 							| Some({ eexpr = TFunction(tf) } as e) ->
-								(* if it's not overload, just cast the vars *)
 								let actual_args, _ = get_fun (get_real_fun gen actual_t) in
 								let new_args, vardecl = List.fold_left2 (fun (args,vdecl) (v,_) (_,_,t) ->
 									if not (type_iseq (gen.greal_type v.v_type) (gen.greal_type t)) then begin
@@ -11190,17 +11227,62 @@ struct
 									end else
 										(v,None) :: args, vdecl
 								) ([],[]) tf.tf_args actual_args in
-
-								if vardecl <> [] then
-								f.cf_expr <- Some({ e with
-									eexpr = TFunction({ tf with
-										tf_args = List.rev new_args;
-										tf_expr = Type.concat { eexpr = TBlock(List.map (fun (v,ve) -> { eexpr = TVar(v,ve); etype = gen.gcon.basic.tvoid; epos = e.epos }) vardecl); etype = gen.gcon.basic.tvoid; epos = e.epos } tf.tf_expr
+								let block = { eexpr = TBlock(List.map (fun (v,ve) ->
+									{
+										eexpr = TVar(v,ve);
+										etype = gen.gcon.basic.tvoid;
+										epos = tf.tf_expr.epos
+									}) vardecl);
+									etype = gen.gcon.basic.tvoid;
+									epos = tf.tf_expr.epos
+								} in
+								if Meta.has Meta.Overload f.cf_meta then begin
+									(* if it is overload, create another field with the requested type *)
+									let f3 = mk_class_field f.cf_name t f.cf_public f.cf_pos f.cf_kind f.cf_params in
+									let p = f.cf_pos in
+									let old_args, old_ret = get_fun f.cf_type in
+									let args, ret = get_fun t in
+									let tf_args = List.map (fun (n,o,t) -> alloc_var n t, None) args in
+									let f3_mk_return = if is_void ret then (fun e -> e) else (fun e -> mk_return (mk_cast ret e)) in
+									f3.cf_expr <- Some {
+										eexpr = TFunction({
+											tf_args = List.rev new_args;
+											tf_type = ret;
+											tf_expr = Type.concat block (mk_block (f3_mk_return {
+												eexpr = TCall(
+													{
+														eexpr = TField(
+															{ eexpr = TConst TThis; etype = TInst(c, List.map snd c.cl_params); epos = p },
+															FInstance(c,List.map snd c.cl_params,f));
+														etype = f.cf_type;
+														epos = p
+													},
+													List.map2 (fun (v,_) (_,_,t) -> mk_cast t (mk_local v p)) tf_args old_args);
+												etype = old_ret;
+												epos = p
+											}))
+										});
+										etype = t;
+										epos = p;
+									};
+									(* make sure we skip cast detect - otherwise this new function will make the overload detection go crazy *)
+									f3.cf_meta <- (Meta.Custom(":skipCastDetect"), [], f3.cf_pos) :: f3.cf_meta;
+									gen.gafter_expr_filters_ended <- ((fun () ->
+										f.cf_overloads <- f3 :: f.cf_overloads;
+									) :: gen.gafter_expr_filters_ended);
+									f3
+								end else begin
+									(* if it's not overload, just cast the vars *)
+									if vardecl <> [] then
+									f.cf_expr <- Some({ e with
+										eexpr = TFunction({ tf with
+											tf_args = List.rev new_args;
+											tf_expr = Type.concat block tf.tf_expr
+										});
 									});
-								});
-								f
-							| _ -> f
-							end
+									f
+								end
+							| _ -> f)
 					| _ -> f
 				in
 				if not c.cl_extern then

+ 6957 - 0
src/generators/gencpp.ml

@@ -0,0 +1,6957 @@
+(*
+   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 Type
+open Common
+
+(*
+   Generators do not care about non-core-type abstracts, so let us follow them
+   away by default.
+*)
+let follow = Abstract.follow_with_abstracts
+
+(*
+   Code for generating source files.
+   It manages creating diretories, indents, blocks and only modifying files
+   when the content changes.
+*)
+
+(*
+   A class_path is made from a package (array of strings) and a class name.
+   Join these together, inclding a separator.  eg, "/" for includes : pack1/pack2/Name or "::"
+   for namespace "pack1::pack2::Name"
+*)
+let join_class_path path separator =
+   let result = match fst path, snd path with
+   | [], s -> s
+   | el, s -> String.concat separator el ^ separator ^ s in
+   if (String.contains result '+') then begin
+      let idx = String.index result '+' in
+      (String.sub result 0 idx) ^ (String.sub result (idx+1) ((String.length result) - idx -1 ) )
+   end else
+      result;;
+
+
+(* The internal classes are implemented by the core hxcpp system, so the cpp
+   classes should not be generated *)
+let is_internal_class = function
+   |  ([],"Int") | ([],"Void") |  ([],"String") | ([], "Null") | ([], "Float")
+   |  ([],"Array") | ([], "Class") | ([], "Enum") | ([], "Bool")
+   |  ([], "Dynamic") | ([], "ArrayAccess") | (["cpp"], "FastIterator")
+   |  (["cpp"],"Pointer") | (["cpp"],"ConstPointer")
+   |  (["cpp"],"RawPointer") | (["cpp"],"RawConstPointer")
+   |  (["cpp"],"Function") -> true
+   |  (["cpp"],"VirtualArray") -> true
+   |  ([],"Math") -> true
+   |  (["cpp"],"Int8") | (["cpp"],"UInt8") | (["cpp"],"Char")
+   |  (["cpp"],"Int16") | (["cpp"],"UInt16")
+   |  (["cpp"],"Int32") | (["cpp"],"UInt32")
+   |  (["cpp"],"Int64") | (["cpp"],"UInt64")
+   |  (["cpp"],"Float32") | (["cpp"],"Float64") -> true
+   | _ -> false;;
+
+let get_include_prefix common_ctx with_slash =
+   try
+     (Common.defined_value common_ctx Define.IncludePrefix) ^ (if with_slash then "/" else "")
+   with
+     Not_found -> ""
+;;
+
+
+let should_prefix_include = function
+   | x when is_internal_class x -> false
+   |  ([],"hxMath") -> true
+   | _ -> false;;
+
+class source_writer common_ctx write_header_func write_func close_func =
+   object(this)
+   val indent_str = "\t"
+   val mutable indent = ""
+   val mutable indents = []
+   val mutable just_finished_block = false
+   val mutable has_big_closures = false
+   method close = close_func(); ()
+   method write x = write_func x; just_finished_block <- false
+   method write_h x = write_header_func x; ()
+   method indent_one = this#write indent_str
+
+   method push_indent = indents <- indent_str::indents; indent <- String.concat "" indents
+   method pop_indent = match indents with
+                     | h::tail -> indents <- tail; indent <- String.concat "" indents
+                     | [] -> indent <- "/*?*/";
+   method write_i x = this#write (indent ^ x)
+   method get_indent = indent
+   method begin_block = this#write ("{\n"); this#push_indent
+   method end_block = this#pop_indent; this#write_i "}\n"; just_finished_block <- true
+   method end_block_line = this#pop_indent; this#write_i "}"; just_finished_block <- true
+   method terminate_line = this#write (if just_finished_block then "" else ";\n")
+   method add_big_closures = if not has_big_closures then begin
+     this#write_h "#include <hx/MacrosJumbo.h>";
+     has_big_closures <- true
+   end
+
+
+   method add_include class_path =
+      ( match class_path with
+         | (["@verbatim"],file) -> this#write_h ("#include \"" ^ file ^ "\"\n");
+         | _ ->
+            let prefix = if should_prefix_include class_path then "" else get_include_prefix common_ctx true in
+            this#write_h ("#ifndef INCLUDED_" ^ (join_class_path class_path "_") ^ "\n");
+            this#write_h ("#include <" ^ prefix ^ (join_class_path class_path "/") ^ ".h>\n");
+            this#write_h ("#endif\n")
+      )
+end;;
+
+let read_whole_file chan =
+   Std.input_all chan;;
+
+(* The cached_source_writer will not write to the file if it has not changed,
+   thus allowing the makefile dependencies to work correctly *)
+let cached_source_writer common_ctx filename =
+   let header = Buffer.create 0 in
+   let add_header str = Buffer.add_string header str in
+   let buffer = Buffer.create 0 in
+   let add_buf str = Buffer.add_string buffer str in
+   let close = fun() ->
+      Buffer.add_buffer header buffer;
+      let contents = Buffer.contents header in
+      let same =
+         try
+            let in_file = open_in filename in
+            let old_contents = read_whole_file in_file in
+            close_in in_file;
+            contents=old_contents
+         with _ ->
+            false
+      in
+      if not same then begin
+         let out_file = open_out filename in
+         output_string out_file contents;
+         close_out out_file;
+      end;
+   in
+   new source_writer common_ctx (add_header) (add_buf) (close)
+;;
+
+let make_class_directories = Common.mkdir_recursive;;
+
+let make_base_directory dir =
+   make_class_directories "" ( ( Str.split_delim (Str.regexp "[\\/]+") dir ) );;
+
+let new_source_file common_ctx base_dir sub_dir extension class_path =
+   let include_prefix = get_include_prefix common_ctx true in
+   let full_dir =
+      if (sub_dir="include") && (include_prefix<>"") then begin
+         let dir = match fst class_path with
+            | [] -> base_dir ^ "/include/" ^ (get_include_prefix common_ctx false)
+            | path -> base_dir ^ "/include/" ^ include_prefix ^ ( String.concat "/" path )
+         in
+         dir
+      end else begin
+         make_class_directories base_dir ( sub_dir :: (fst class_path));
+         base_dir ^ "/" ^ sub_dir ^ "/" ^ ( String.concat "/" (fst class_path) )
+      end
+   in
+   let file = cached_source_writer common_ctx (full_dir ^ "/" ^ ((snd class_path) ^ extension)) in
+   Codegen.map_source_header common_ctx (fun s -> file#write_h (Printf.sprintf "// %s\n" s));
+   file
+
+
+
+let source_file_extension common_ctx =
+   (* no need to -D file_extension if -D objc is defined *)
+   if Common.defined common_ctx Define.Objc then
+      ".mm"
+   else try
+      "." ^ (Common.defined_value common_ctx Define.FileExtension)
+   with
+     Not_found -> ".cpp"
+;;
+
+
+let new_cpp_file common_ctx base_dir = new_source_file common_ctx base_dir "src" (source_file_extension common_ctx);;
+
+let new_header_file common_ctx base_dir =
+   new_source_file common_ctx base_dir "include" ".h";;
+
+
+
+(* CPP code generation context *)
+
+type context =
+{
+   ctx_common : Common.context;
+
+   mutable ctx_debug_level : int;
+   (* cached as required *)
+   mutable ctx_file_info : (string,string) PMap.t ref;
+
+   (* Per file *)
+   ctx_output : string -> unit;
+   ctx_writer : source_writer;
+   ctx_file_id : int ref;
+
+   ctx_interface_slot : (string,int) Hashtbl.t ref;
+   ctx_interface_slot_count : int ref;
+   (* This is for returning from the child nodes of TMatch, TSwitch && TTry *)
+   mutable ctx_real_this_ptr : bool;
+   mutable ctx_class_member_types : (string,string) Hashtbl.t;
+}
+
+let new_context common_ctx debug file_info member_types =
+let null_file = new source_writer common_ctx ignore ignore (fun () -> () ) in
+let result =
+{
+   ctx_common = common_ctx;
+   ctx_writer = null_file;
+   ctx_file_id = ref (-1);
+   ctx_output = (null_file#write);
+   ctx_interface_slot = ref (Hashtbl.create 0);
+   ctx_interface_slot_count = ref 2;
+   ctx_debug_level = if Common.defined_value_safe common_ctx Define.AnnotateSource <>"" then 2 else debug;
+   ctx_real_this_ptr = true;
+   ctx_class_member_types =  member_types;
+   ctx_file_info = file_info;
+} in
+result
+
+
+let file_context ctx writer debug =
+   { ctx with
+      ctx_writer = writer;
+      ctx_output = (writer#write);
+      ctx_file_id = ref (-1);
+   }
+;;
+
+
+(* The internal header files are also defined in the hx/Object.h file, so you do
+   #include them separately.  However, Math classes has its
+   own header file (under the hxcpp tree) so these should be included *)
+let include_class_header = function
+   | ([],"@Main") -> false
+   | ([],"Math") -> true
+   | path -> not ( is_internal_class path )
+
+
+let is_cpp_class = function
+   | ("cpp"::_ , _)  -> true
+   | ( [] , "EReg" )  -> true
+   | ( ["haxe"] , "Log" )  -> true
+   | _ -> false;;
+
+let is_block exp = match exp.eexpr with | TBlock _ -> true | _ -> false ;;
+
+(* todo - is this how it's done? *)
+let hash_keys hash =
+   let key_list = ref [] in
+   Hashtbl.iter (fun key value -> key_list :=  key :: !key_list ) hash;
+   !key_list;;
+
+let pmap_keys pmap =
+   let key_list = ref [] in
+   PMap.iter (fun key _ -> key_list :=  key :: !key_list ) pmap;
+   !key_list;;
+
+let pmap_values pmap =
+   let value_list = ref [] in
+   PMap.iter (fun _ value -> value_list :=  value :: !value_list ) pmap;
+   !value_list;;
+
+
+
+(* The Hashtbl structure seems a little odd - but here is a helper function *)
+let hash_iterate hash visitor =
+   let result = ref [] in
+   Hashtbl.iter (fun key value -> result :=  (visitor key value) :: !result ) hash;
+   !result
+
+
+
+
+
+let is_internal_member member =
+   member = "toString" || (
+      (String.length member > 1) && (String.sub member 0 2 = "__") &&
+         (match member with
+         | "__ArgCount" | "__ArrayImplRef" | "__CStr" | "__Compare" | "__Create"
+         | "__CreateEmpty" | "__FieldRef" | "__FindArgCount"
+         | "__GetFieldMap" | "__GetHandle" | "__GetItem"
+         | "__GetScriptCallable" | "__GetScriptVTable"
+         | "__Param" | "__Remove" | "__SGetClass"
+         | "__Set" | "__SetItem" | "__TArrayImplRef"
+         | "__ToDouble" | "__ToInt" | "__ToInterface" | "__ToObject"
+         | "__Visit" | "__WCStr" | "__a" | "__blit" | "__boot"
+         | "__boot_all" | "__compare" | "__concat" | "__construct" | "__copy"
+         | "__filter" | "__get_args" | "__hx_dump_stack" | "__hx_field_iter" | "__hxt_gc_new"
+         | "__indexOf" | "__insert" | "__instanceof" | "__int" | "__iterator"
+         | "__join" | "__lastIndexOf" | "__loadprim" | "__mClass" | "__mDynamicFields"
+         | "__map" | "__memcmp" | "__new" | "__pop" | "__prime"
+         | "__push" | "__qsort" | "__unshift" | "__unsafeStringReference" | "__time_stamp"
+         | "__superString" | "__splice" | "__shift" | "__slice" | "__sort"
+         | "__s_id" | "__run" | "__root" | "__register" | "__remove"
+         | "__removeAt" | "__reverse" | "__zero"
+         | "__Field" | "__IField" | "__Run" | "__Is" | "__GetClass" | "__GetType" | "__ToString"
+         | "__s" | "__GetPtr" | "__SetField" | "__length" | "__IsArray" | "__SetThis" | "__Internal"
+         | "__EnumParams" | "__Index" | "__Tag" | "__GetFields" | "__HasField"
+         | "__get" | "__set" | "__unsafe_get" | "__unsafe_set" | "__global__"
+         | "__SetSize" | "__trace" | "__GetRealObject" | "__SetSizeExact" | "__cpp__"
+         | "__URLEncode" | "__URLDecode" | "__IsEnum"
+               -> true
+         | _ -> (String.length member > 4) && (String.sub member 0 4 = "__hx") ) );;
+
+let is_known_member member =
+   match member with
+   | "__meta__" | "__rtti" | "_Compare"
+         -> true
+   | _ -> false;;
+
+(* Convert function names that can't be written in c++ ... *)
+let keyword_remap name =
+   if (is_internal_member name) || (is_known_member name) then
+      name
+   else if (String.length name > 1) && (String.sub name 0 2 = "__") then
+      "_hx_" ^ name
+   else match name with
+   | "int" | "Int" | "Bool" | "super"
+   | "auto" | "char" | "const" | "delete" | "double" | "Float" | "enum"
+   | "extern" | "float" | "friend" | "goto" | "long" | "operator" | "protected"
+   | "register" | "short" | "signed" | "sizeof" | "template" | "typedef"
+   | "union" | "unsigned" | "void" | "volatile" | "or" | "and" | "xor" | "or_eq" | "not"
+   | "and_eq" | "xor_eq" | "typeof" | "stdin" | "stdout" | "stderr" | "system"
+   | "BIG_ENDIAN" | "LITTLE_ENDIAN" | "assert" | "NULL" | "wchar_t" | "EOF"
+   | "bool" | "const_cast" | "dynamic_cast" | "explicit" | "export" | "mutable" | "namespace"
+   | "reinterpret_cast" | "static_cast" | "typeid" | "typename" | "virtual"
+   | "_Complex" | "INFINITY" | "NAN"
+   | "INT_MIN" | "INT_MAX" | "INT8_MIN" | "INT8_MAX" | "UINT8_MAX" | "INT16_MIN"
+   | "INT16_MAX" | "UINT16_MAX" | "INT32_MIN" | "INT32_MAX" | "UINT32_MAX"
+   | "asm" | "near" | "far"
+   | "HX_" | "HXLINE" | "HXDLIN"
+   | "abstract" | "decltype" | "finally" | "nullptr" | "static_assert"
+   | "struct" -> "_hx_" ^ name
+   | x -> x
+;;
+
+let remap_class_path class_path =
+   let path_remap with_keywords name =
+      let len = String.length name in
+      if (len > 3) && (String.sub name 0 3 = " ::") then
+         String.sub name 3 (len-3)
+      else if (len > 2) && (String.sub name 0 2 = "::") then
+         String.sub name 2 (len-2)
+      else if with_keywords then
+         keyword_remap name
+      else
+         name
+   in
+   (List.map (path_remap true) (fst class_path)) , path_remap false (snd class_path)
+;;
+
+let join_class_path_remap path separator =
+   match join_class_path (remap_class_path path) separator with
+   | "Class" -> "hx::Class"
+   | x -> x
+;;
+
+let get_meta_string meta key =
+   let rec loop = function
+      | [] -> ""
+      | (k,[Ast.EConst (Ast.String name),_],_) :: _  when k=key-> name
+      | _ :: l -> loop l
+      in
+   loop meta
+;;
+
+
+
+let get_meta_string_path meta key =
+   let rec loop = function
+      | [] -> ""
+      | (k,[Ast.EConst (Ast.String name),_], pos) :: _  when k=key->
+           (try
+           if (String.sub name 0 2) = "./" then begin
+              let base = if (Filename.is_relative pos.pfile) then
+                 Filename.concat (Sys.getcwd()) pos.pfile
+              else
+                 pos.pfile
+              in
+              Gencommon.normalize (Filename.concat (Filename.dirname base) (String.sub name 2 ((String.length name) -2)  ))
+           end else
+              name
+           with Invalid_argument _ -> name)
+      | _ :: l -> loop l
+      in
+   loop meta
+;;
+
+
+let get_meta_string_full_filename meta key =
+   let rec loop = function
+      | [] -> ""
+      | (k,_, pos) :: _  when k=key->
+           if (Filename.is_relative pos.pfile) then
+              Gencommon.normalize (Filename.concat (Sys.getcwd()) pos.pfile)
+           else
+              pos.pfile
+      | _ :: l -> loop l
+      in
+   loop meta
+;;
+
+let get_meta_string_full_dirname meta key =
+   let name = get_meta_string_full_filename meta key in
+   try
+      Gencommon.normalize (Filename.dirname name)
+   with Invalid_argument _ -> ""
+;;
+
+
+let get_field_access_meta field_access key =
+match field_access with
+   | FInstance(_,_,class_field)
+   | FStatic(_,class_field) -> get_meta_string class_field.cf_meta key
+   | _ -> ""
+;;
+
+let format_code code =
+	String.concat "\n" (ExtString.String.nsplit code "\r\n")
+
+let get_code meta key =
+   let code = get_meta_string meta key in
+   let magic_var = "${GENCPP_SOURCE_DIRECTORY}"  in
+   let code = if ExtString.String.exists code magic_var then begin
+         let source_directory = get_meta_string_full_dirname meta key in
+         let _,code = ExtString.String.replace code magic_var source_directory in
+         code
+      end else
+         code
+      in
+   if (code<>"") then format_code code ^ "\n" else code
+;;
+
+let has_meta_key meta key =
+   List.exists (fun m -> match m with | (k,_,_) when k=key-> true | _ -> false ) meta
+;;
+
+let type_has_meta_key haxe_type key =
+   match follow haxe_type with
+   | TInst (klass,_) -> has_meta_key klass.cl_meta key
+   | TType (type_def,_) -> has_meta_key type_def.t_meta key
+   | TEnum (enum_def,_) -> has_meta_key enum_def.e_meta key
+   | _ -> false
+;;
+
+
+(*
+let dump_meta meta =
+   List.iter (fun m -> match m with | (k,_,_) -> print_endline ((fst (MetaInfo.to_string k)) ^ "=" ^ (get_meta_string meta k) ) | _ -> () ) meta;;
+*)
+
+let get_class_code class_def key = match class_def.cl_kind with
+    | KAbstractImpl abstract_def ->
+       let value = (get_code abstract_def.a_meta key) in
+       value
+    | _ -> get_code class_def.cl_meta key
+;;
+
+
+(* Add include to source code *)
+let add_include writer class_path =
+   writer#add_include class_path;;
+
+let list_num l = string_of_int (List.length l);;
+
+(* This gets the class include order correct.  In the header files, we forward declare
+   the class types so the header file does not have any undefined variables.
+   In the cpp files, we include all the required header files, providing the actual
+   types for everything.  This way there is no problem with circular class references.
+*)
+let gen_forward_decl writer class_path isNative =
+   begin
+      let output = writer#write in
+      match class_path with
+      | (["@verbatim"],file) -> writer#write ("#include <" ^ file ^ ">\n");
+      | _ ->
+         let name = fst (remap_class_path class_path) in
+         output ((if isNative then "HX_DECLARE_NATIVE" else "HX_DECLARE_CLASS") ^ list_num name  ^ "(");
+         List.iter (fun package_part -> output (package_part ^ ",") ) name;
+         output ( (snd class_path) ^ ")\n")
+end;;
+
+let real_interfaces =
+List.filter (function (t,pl) ->
+   match t, pl with
+   | { cl_path = ["cpp";"rtti"],_ },[] -> false
+   | _ -> true
+);;
+
+
+let is_var_field field =
+   match field.cf_kind with
+   | Var _ -> true
+   | Method MethDynamic -> true
+   | _ -> false
+;;
+
+let rec has_rtti_interface c interface =
+   List.exists (function (t,pl) ->
+      (snd t.cl_path) = interface && (match fst t.cl_path with | ["cpp";"rtti"] -> true | _ -> false )
+   ) c.cl_implements ||
+      (match c.cl_super with None -> false | Some (c,_) -> has_rtti_interface c interface);;
+
+let has_field_integer_lookup class_def =
+   has_rtti_interface class_def "FieldIntegerLookup";;
+
+let has_field_integer_numeric_lookup class_def =
+   has_rtti_interface class_def "FieldNumericIntegerLookup";;
+
+(* Output required code to place contents in required namespace *)
+let gen_open_namespace output class_path =
+      List.iter (fun namespace -> output ("namespace " ^ namespace ^ "{\n")) (List.map keyword_remap (fst class_path));;
+
+let gen_close_namespace output class_path =
+      List.iter
+         (fun namespace -> output ( "}" ^ " // end namespace " ^ namespace ^"\n"))
+         (fst class_path);;
+
+(* The basic types can have default values and are passesby value *)
+let is_numeric = function
+   | "Int" | "Bool" | "Float" |  "unsigned char" -> true
+   | "::cpp::UInt8" | "::cpp::Int8" | "::cpp::Char"
+   | "::cpp::UInt16" | "::cpp::Int16"
+   | "::cpp::UInt32" | "::cpp::Int32"
+   | "::cpp::UInt64" | "::cpp::Int64"
+   | "::cpp::Float32" | "::cpp::Float64"
+   | "int" | "bool" | "double" | "float" -> true
+   | _ -> false
+
+
+let rec remove_parens expression =
+      match expression.eexpr with
+      | TParenthesis e -> remove_parens e
+      | TMeta(_,e) -> remove_parens e
+      | _ -> expression
+;;
+
+
+let rec remove_parens_cast expression =
+   match expression.eexpr with
+   | TParenthesis e -> remove_parens_cast e
+   | TMeta(_,e) -> remove_parens_cast e
+   | TCast ( e,None) -> remove_parens_cast e
+   | _ -> expression
+;;
+
+let is_interface_type t =
+   match follow t with
+   | TInst (klass,params) -> klass.cl_interface
+   | _ -> false
+;;
+
+
+let is_cpp_function_instance haxe_type =
+   match follow haxe_type with
+   | TInst (klass,params) ->
+      (match klass.cl_path with
+      | ["cpp"] , "Function" -> true
+      | _ -> false )
+   | _ -> false
+   ;;
+
+
+let is_cpp_function_class haxe_type =
+   match follow haxe_type with
+   | TType (klass,params) ->
+      (match klass.t_path with
+      | ["cpp"] , "Function" -> true
+      | _ -> false )
+   | _ -> false
+   ;;
+
+let is_objc_call field =
+  match field with
+  | FStatic(cl,_) | FInstance(cl,_,_) ->
+      cl.cl_extern && Meta.has Meta.Objc cl.cl_meta
+  | _ -> false
+;;
+
+let is_objc_class klass =
+   klass.cl_extern && Meta.has Meta.Objc klass.cl_meta
+;;
+
+let is_objc_type t = match follow t with
+  | TInst(cl,_) -> cl.cl_extern && Meta.has Meta.Objc cl.cl_meta
+  | _ -> false
+;;
+
+
+let is_lvalue var =
+   match (remove_parens var).eexpr with
+   | TLocal _ -> true
+   | TField (_,FStatic(_,field) ) | TField (_,FInstance(_,_,field) ) -> is_var_field field
+   | _ -> false
+;;
+
+
+
+let is_pointer haxe_type includeRaw =
+   match follow haxe_type with
+   | TInst (klass,params) ->
+      (match klass.cl_path with
+      | ["cpp"] , "Pointer"
+      | ["cpp"] , "ConstPointer"
+      | ["cpp"] , "Function" -> true
+      | ["cpp"] , "RawPointer" when includeRaw -> true
+      | ["cpp"] , "RawConstPointer" when includeRaw -> true
+      | _ -> false )
+   | TType (type_def,params) ->
+      (match type_def.t_path with
+      | ["cpp"] , "Pointer"
+      | ["cpp"] , "ConstPointer"
+      | ["cpp"] , "Function" -> true
+      | ["cpp"] , "RawPointer" when includeRaw -> true
+      | ["cpp"] , "RawConstPointer" when includeRaw -> true
+      | _ -> false )
+   | _ -> false
+   ;;
+
+let is_dynamic_type_param class_kind =
+   match class_kind with
+   | KTypeParameter _ -> true
+   | _ -> false
+;;
+
+
+let is_native_gen_class class_def =
+   (has_meta_key class_def.cl_meta Meta.NativeGen) ||
+      (match class_def.cl_kind with
+       | KAbstractImpl abstract_def -> (has_meta_key abstract_def.a_meta Meta.NativeGen)
+       | _ -> false );
+;;
+
+let is_native_gen_module = function
+   | TClassDecl class_def -> is_native_gen_class class_def
+   | _ -> false
+;;
+
+
+
+(*  Get a string to represent a type.
+   The "suffix" will be nothing or "_obj", depending if we want the name of the
+   pointer class or the pointee (_obj class *)
+let rec class_string klass suffix params remap =
+   let type_string = type_string_remap remap in
+   let join_class_path_remap = if remap then join_class_path_remap else join_class_path in
+   (match klass.cl_path with
+   (* Array class *)
+   |  ([],"Array") when is_dynamic_array_param (List.hd params) ->
+           "cpp::ArrayBase" ^ suffix
+           (*"cpp::VirtualArray" ^ suffix*)
+   |  ([],"Array") -> (snd klass.cl_path) ^ suffix ^ "< " ^ (String.concat ","
+               (List.map array_element_type params) ) ^ " >"
+   (* FastIterator class *)
+   |  (["cpp"],"FastIterator") -> "::cpp::FastIterator" ^ suffix ^ "< " ^ (String.concat ","
+               (List.map type_string  params) ) ^ " >"
+   |  (["cpp"],"Pointer")
+   |  (["cpp"],"ConstPointer") ->
+        "::cpp::Pointer< " ^ (String.concat "," (List.map type_string params) ) ^ " >"
+   |  (["cpp"],"RawPointer") ->
+        " " ^ (String.concat "," (List.map type_string params) ) ^ " * "
+   |  (["cpp"],"RawConstPointer") ->
+        " const " ^ (String.concat "," (List.map type_string params) ) ^ " * "
+   |  (["cpp"],"Function") ->
+        "::cpp::Function< " ^ (cpp_function_signature_params params) ^ " >"
+   | _ when is_dynamic_type_param klass.cl_kind -> "Dynamic"
+   |  ([],"#Int") -> "/* # */int"
+   |  (["cpp"],"UInt8") -> "unsigned char"
+   |  ([],"Class") -> "hx::Class"
+   |  ([],"EnumValue") -> "Dynamic"
+   |  ([],"Null") -> (match params with
+         | [t] ->
+            (match follow t with
+            | TAbstract ({ a_path = [],"Int" },_)
+            | TAbstract ({ a_path = [],"Float" },_)
+            | TAbstract ({ a_path = [],"Bool" },_) -> "Dynamic"
+            | TAbstract ({ a_path = ["cpp"],"UInt8" },_) -> "Dynamic"
+            | t when type_has_meta_key t Meta.NotNull -> "Dynamic"
+            | _ -> "/*NULL*/" ^ (type_string t) )
+         | _ -> assert false);
+   (* Objective-C class *)
+   | path when is_objc_type (TInst(klass,[])) ->
+      let str = join_class_path_remap klass.cl_path "::" in
+      if suffix = "_obj" then
+         str
+      else if klass.cl_interface then
+         "id < " ^ str ^ ">"
+      else
+         str ^ " *"
+   (* Native interface - use pointer *)
+   | _ when klass.cl_interface && is_native_gen_class klass ->
+            (join_class_path_remap klass.cl_path "::") ^ " *"
+   (* Normal class *)
+   | path when klass.cl_extern && (not (is_internal_class path) )->
+            (join_class_path_remap klass.cl_path "::") ^ suffix
+   | _ -> "::" ^ (join_class_path_remap klass.cl_path "::") ^ suffix
+   )
+and type_string_suff suffix haxe_type remap =
+   let type_string = type_string_remap remap in
+   let join_class_path_remap = if remap then join_class_path_remap else join_class_path in
+   (match haxe_type with
+   | TMono r -> (match !r with None -> "Dynamic" ^ suffix | Some t -> type_string_suff suffix t remap)
+   | TAbstract ({ a_path = ([],"Void") },[]) -> "Void"
+   | TAbstract ({ a_path = ([],"Bool") },[]) -> "bool"
+   | TAbstract ({ a_path = ([],"Float") },[]) -> "Float"
+   | TAbstract ({ a_path = ([],"Int") },[]) -> "int"
+   | TAbstract ({ a_path = (["cpp"],"UInt8") },[]) -> "unsigned char"
+   | TAbstract( { a_path = ([], "EnumValue") }, _  ) -> "Dynamic"
+   | TEnum (enum,params) ->  "::" ^ (join_class_path_remap enum.e_path "::") ^ suffix
+   | TInst (klass,params) ->  (class_string klass suffix params remap)
+   | TType (type_def,params) ->
+      (match type_def.t_path with
+      | [] , "Null" ->
+         (match params with
+         | [t] ->
+            (match follow t with
+            | TAbstract ({ a_path = [],"Int" },_)
+            | TAbstract ({ a_path = [],"Float" },_)
+            | TAbstract ({ a_path = [],"Bool" },_) -> "Dynamic" ^ suffix
+            | t when type_has_meta_key t Meta.NotNull -> "Dynamic" ^ suffix
+            | _ -> type_string_suff suffix t remap)
+         | _ -> assert false);
+      | [] , "Array" ->
+         (match params with
+         | [t] when (type_string (follow t) ) = "Dynamic" -> "Dynamic"
+         | [t] -> "Array< " ^ (type_string (follow t) ) ^ " >"
+         | _ -> assert false)
+      | ["cpp"] , "FastIterator" ->
+         (match params with
+         | [t] -> "::cpp::FastIterator< " ^ (type_string (follow t) ) ^ " >"
+         | _ -> assert false)
+      | ["cpp"] , "Pointer"
+      | ["cpp"] , "ConstPointer" ->
+         (match params with
+         | [t] -> "::cpp::Pointer< " ^ (type_string (follow t) ) ^ " >"
+         | _ -> assert false)
+      | ["cpp"] , "RawPointer" ->
+         (match params with
+         | [t] -> " " ^ (type_string (follow t) ) ^ " *"
+         | _ -> assert false)
+      | ["cpp"] , "RawConstPointer" ->
+         (match params with
+         | [t] -> "const " ^ (type_string (follow t) ) ^ " *"
+         | _ -> assert false)
+      | ["cpp"] , "Function" ->
+         "::cpp::Function< " ^ (cpp_function_signature_params params ) ^ " >"
+      | _ ->  type_string_suff suffix (apply_params type_def.t_params params type_def.t_type) remap
+      )
+   | TFun (args,haxe_type) -> "Dynamic" ^ suffix
+   | TAnon a -> "Dynamic"
+      (*
+      (match !(a.a_status) with
+      | Statics c -> type_string_suff suffix (TInst (c,List.map snd c.cl_params))
+      | EnumStatics e -> type_string_suff suffix (TEnum (e,List.map snd e.e_params))
+      | _ -> "Dynamic"  ^ suffix )
+      *)
+   | TDynamic haxe_type -> "Dynamic" ^ suffix
+   | TLazy func -> type_string_suff suffix ((!func)()) remap
+   | TAbstract (abs,pl) when abs.a_impl <> None ->
+      type_string_suff suffix (Abstract.get_underlying_type abs pl) remap
+   | TAbstract (abs,pl) ->
+      "::" ^ (join_class_path_remap abs.a_path "::") ^ suffix
+   )
+
+and type_string_remap remap haxe_type =
+   type_string_suff "" haxe_type remap
+
+and type_string haxe_type =
+   type_string_suff "" haxe_type true
+
+and array_element_type haxe_type =
+   match type_string haxe_type with
+   | x when cant_be_null haxe_type -> x
+   | x when is_interface_type (follow haxe_type) -> x
+   | "::String" -> "::String"
+   | _ -> "::Dynamic"
+
+and is_dynamic_array_param haxe_type =
+   if (type_string (follow haxe_type)) = "Dynamic" then true
+   else (match follow haxe_type with
+   | TInst (klass,params) ->
+         (match klass.cl_path with
+         | ([],"Array") | ([],"Class") | (["cpp"],"FastIterator")
+         | (["cpp"],"RawPointer") |(["cpp"],"ConstRawPointer")
+         | (["cpp"],"Pointer") |(["cpp"],"ConstPointer")|(["cpp"],"Function") -> false
+         | _ -> (match klass.cl_kind with KTypeParameter _ -> true | _ -> false)
+         )
+   | _ -> false
+   )
+and cpp_function_signature tfun abi =
+   match follow tfun with
+   | TFun(args,ret) -> (type_string ret) ^ " " ^ abi ^ "(" ^ (gen_tfun_interface_arg_list args) ^ ")"
+   | _ -> "void *"
+
+and cpp_function_signature_params params = match params with
+   | [t; abi] -> (match follow abi with
+       | TInst (klass,_) -> cpp_function_signature t (get_meta_string klass.cl_meta Meta.Abi)
+       | _ -> print_endline (type_string abi);
+           assert false )
+   | _ ->
+      print_endline ("Params:" ^ (String.concat "," (List.map type_string params) ));
+      assert false;
+
+and gen_interface_arg_type_name name opt typ =
+   let type_str = (type_string typ) in
+   (* type_str may have already converted Null<X> to Dynamic because of NotNull tag ... *)
+   (if (opt && (cant_be_null typ) && type_str<>"Dynamic" ) then
+      "hx::Null< " ^ type_str ^ " > "
+   else
+      type_str ) ^ " " ^ (keyword_remap name)
+
+and gen_tfun_interface_arg_list args =
+   String.concat "," (List.map (fun (name,opt,typ) -> gen_interface_arg_type_name name opt typ) args)
+and cant_be_null haxe_type =
+   is_numeric (type_string haxe_type) || (type_has_meta_key haxe_type Meta.NotNull )
+;;
+
+let is_object type_string =
+   not (is_numeric type_string || type_string="::String");
+;;
+
+
+
+
+let is_array haxe_type =
+   match follow haxe_type with
+   | TInst (klass,params) ->
+      (match klass.cl_path with
+      | [] , "Array" -> not (is_dynamic_array_param (List.hd params))
+      | _ -> false )
+   | TType (type_def,params) ->
+      (match type_def.t_path with
+      | [] , "Array" ->  not (is_dynamic_array_param (List.hd params))
+      | _ -> false )
+   | _ -> false
+   ;;
+
+let is_array_or_dyn_array haxe_type =
+   match follow haxe_type with
+   | TInst (klass,params) ->
+      (match klass.cl_path with | [] , "Array" -> true | _ -> false )
+   | TType (type_def,params) ->
+      (match type_def.t_path with | [] , "Array" -> true | _ -> false )
+   | _ -> false
+   ;;
+
+
+
+let is_array_implementer haxe_type =
+   match follow haxe_type with
+   | TInst (klass,params) ->
+      (match klass.cl_array_access with
+      | Some _ -> true
+      | _ -> false )
+   | _ -> false
+   ;;
+
+
+
+let is_static_access obj =
+   match (remove_parens obj).eexpr with
+   | TTypeExpr _ -> true
+   | _ -> false
+;;
+
+let is_native_with_space func =
+   match (remove_parens func).eexpr with
+   | TField(obj,field) when is_static_access obj ->
+      String.contains (get_field_access_meta field Meta.Native) ' '
+   | _ -> false
+;;
+
+
+let is_native_pointer expr =
+   let t = type_string expr.etype in
+   let l = String.length t in
+   l>1 && (String.sub t (l-1) 1) = "*"
+;;
+
+
+let rec is_cpp_function_member func =
+   match (remove_parens func).eexpr with
+   | TField(obj,field) when is_cpp_function_instance obj.etype -> true
+   | TCall(obj,_) -> is_cpp_function_member obj
+   | _ -> false
+;;
+
+
+
+
+(* Get the type and output it to the stream *)
+(*
+let gen_type ctx haxe_type =
+   ctx.ctx_output (type_string haxe_type)
+;;
+
+let member_type ctx field_object member =
+   let name = (if (is_array field_object.etype) then "::Array"
+            else (type_string field_object.etype)) ^ "." ^ member in
+   try ( Hashtbl.find ctx.ctx_class_member_types name )
+   with Not_found -> "?";;
+
+*)
+let is_interface obj = is_interface_type obj.etype;;
+
+let should_implement_field x = not (is_extern_field x);;
+
+let is_function_member expression =
+   match (follow expression.etype) with | TFun (_,_) -> true | _ -> false;;
+
+let is_extern_class class_def =
+   class_def.cl_extern || (has_meta_key class_def.cl_meta Meta.Extern) ||
+      (match class_def.cl_kind with
+       | KAbstractImpl abstract_def -> (has_meta_key abstract_def.a_meta Meta.Extern)
+       | _ -> false );
+;;
+
+let is_scalar_abstract abstract_def =
+   Meta.has Meta.Scalar abstract_def.a_meta && Meta.has Meta.CoreType abstract_def.a_meta
+;;
+
+
+let real_non_native_interfaces =
+List.filter (function (t,pl) ->
+   match t, pl with
+   | { cl_path = ["cpp";"rtti"],_ },[] -> false
+   | _ -> not (is_native_gen_class t)
+);;
+
+
+
+let is_extern_class_instance obj =
+   match follow obj.etype with
+   | TInst (klass,params) -> klass.cl_extern
+   | _ -> false
+;;
+
+
+let rec is_dynamic_accessor name acc field class_def =
+ ( ( acc ^ "_" ^ field.cf_name) = name ) &&
+   ( not (List.exists (fun f -> f.cf_name=name) class_def.cl_ordered_fields) )
+   && (match class_def.cl_super with None -> true | Some (parent,_) -> is_dynamic_accessor name acc field parent )
+;;
+
+
+(* Check to see if we are the first object in the parent tree to implement a dynamic interface *)
+let implement_dynamic_here class_def =
+   let implements_dynamic c = match c.cl_dynamic with None -> false | _ -> true  in
+   let rec super_implements_dynamic c = match c.cl_super with
+      | None -> false
+      | Some (csup, _) -> if (implements_dynamic csup) then true else
+            super_implements_dynamic csup;
+   in
+   ( (implements_dynamic class_def) && (not (super_implements_dynamic class_def) ) );;
+
+
+let gen_hash32 seed str =
+   let h = ref (Int32.of_int seed) in
+   let cycle = Int32.of_int 223 in
+   for i = 0 to String.length str - 1 do
+      h := Int32.add (Int32.mul !h cycle) (Int32.of_int (int_of_char (String.unsafe_get str i)));
+   done;
+   !h
+;;
+
+let gen_hash seed str =
+   Printf.sprintf "0x%08lx" (gen_hash32 seed str)
+;;
+
+let gen_string_hash str =
+   let h = gen_hash32 0 str in
+   Printf.sprintf "\"\\x%02lx\",\"\\x%02lx\",\"\\x%02lx\",\"\\x%02lx\""
+       (Int32.shift_right_logical (Int32.shift_left h 24) 24)
+       (Int32.shift_right_logical (Int32.shift_left h 16) 24)
+       (Int32.shift_right_logical (Int32.shift_left h 8) 24)
+       (Int32.shift_right_logical h 24)
+;;
+
+let gen_qstring_hash str =
+   let h = gen_hash32 0 str in
+   Printf.sprintf "%02lx,%02lx,%02lx,%02lx"
+       (Int32.shift_right_logical (Int32.shift_left h 24) 24)
+       (Int32.shift_right_logical (Int32.shift_left h 16) 24)
+       (Int32.shift_right_logical (Int32.shift_left h 8) 24)
+       (Int32.shift_right_logical h 24)
+;;
+
+
+
+
+
+(* Make string printable for c++ code *)
+(* Here we know there are no utf8 characters, so use the L"" notation to avoid conversion *)
+let escape_stringw s l =
+   let b = Buffer.create 0 in
+   Buffer.add_char b 'L';
+   Buffer.add_char b '"';
+   let skip = ref 0 in
+   for i = 0 to String.length s - 1 do
+      if (!skip>0) then begin
+         skip := !skip -1;
+         l := !l-1;
+      end else
+      match Char.code (String.unsafe_get s i) with
+      | c when (c>127) ->
+         let encoded =  ((c land 0x3F) lsl 6) lor ( Char.code ((String.unsafe_get s (i+1))) land 0x7F) in
+         skip := 1;
+         Buffer.add_string b (Printf.sprintf "\\x%X\"L\"" encoded)
+      | c when (c < 32) -> Buffer.add_string b (Printf.sprintf "\\x%X\"L\"" c)
+      | c -> Buffer.add_char b (Char.chr c)
+   done;
+   Buffer.add_char b '"';
+   Buffer.contents b;;
+
+let special_to_hex s =
+   let l = String.length s in
+   let b = Buffer.create 0 in
+   for i = 0 to l - 1 do
+      match Char.code (String.unsafe_get s i) with
+      | c when (c>127) || (c<32) ->
+         Buffer.add_string b (Printf.sprintf "\\x%02x\"\"" c)
+      | c -> Buffer.add_char b (Char.chr c)
+   done;
+   Buffer.contents b;;
+
+let escape_extern s =
+   let l = String.length s in
+   let b = Buffer.create 0 in
+   for i = 0 to l - 1 do
+      match Char.code (String.unsafe_get s i) with
+      | c when (c>127) || (c<32) || (c=34) || (c=92) ->
+         Buffer.add_string b (Printf.sprintf "\\x%02x" c)
+      | c -> Buffer.add_char b (Char.chr c)
+   done;
+   Buffer.contents b;;
+
+
+
+let has_utf8_chars s =
+   let result = ref false in
+   for i = 0 to String.length s - 1 do
+      result := !result || ( Char.code (String.unsafe_get s i) > 127 )
+   done;
+   !result;;
+
+let escape_command s =
+   let b = Buffer.create 0 in
+   String.iter (fun ch -> if (ch=='"' || ch=='\\' ) then Buffer.add_string b "\\";  Buffer.add_char b ch ) s;
+   Buffer.contents b;;
+
+let gen_str macro gen s =
+   let rec split s plus =
+      let escaped = Ast.s_escape ~hex:false s in
+      let hexed = (special_to_hex escaped) in
+      if (String.length hexed <= 16000 ) then
+         plus ^ " HX_CSTRING(\"" ^ hexed ^ "\")"
+      else begin
+         let len = String.length s in
+         let half = len lsr 1 in
+         (split (String.sub s 0 half) plus ) ^ (split (String.sub s half (len-half)) "+" )
+      end
+   in
+   let escaped = Ast.s_escape ~hex:false s in
+   let hexed = (special_to_hex escaped) in
+   if (String.length hexed <= 16000 ) then
+      macro ^ "(\"" ^ hexed ^ "\"," ^ (gen s) ^ ")"
+   else
+      "(" ^ (split s "" ) ^ ")"
+;;
+
+let str s = gen_str "HX_HCSTRING" gen_string_hash s;;
+let strq s = gen_str "HX_" gen_qstring_hash s;;
+
+
+
+let const_char_star s =
+   let escaped = Ast.s_escape ~hex:false s in
+   "\"" ^ special_to_hex escaped ^ "\"";
+;;
+
+
+
+
+(* Convert an array to a comma separated list of values *)
+let array_arg_list inList =
+   let i = ref (0-1) in
+   String.concat "," (List.map (fun _ -> incr i; "inArgs[" ^ (string_of_int !i) ^ "]"  ) inList)
+
+
+(* See if there is a haxe break statement that will be swollowed by c++ break *)
+exception BreakFound;;
+
+(* Decide is we should look the field up by name *)
+let dynamic_internal = function | "__Is" -> true | _ -> false
+
+
+let rec is_null expr =
+   match expr.eexpr with
+   | TConst TNull -> true
+   | TParenthesis expr | TMeta (_,expr) -> is_null expr
+   | TCast (e,None) -> is_null e
+   | _ -> false
+;;
+
+
+let is_virtual_array expr = (type_string expr.etype="cpp::VirtualArray") ;;
+
+let rec is_dynamic_in_cpp ctx expr =
+   let expr_type = type_string ( match follow expr.etype with TFun (args,ret) -> ret | _ -> expr.etype) in
+   if ( expr_type="Dynamic" || expr_type="cpp::ArrayBase") then
+      true
+   else begin
+      let result = (
+      match expr.eexpr with
+      | TEnumParameter( obj, _, index ) -> true (* TODO? *)
+      | TField( obj, field ) ->
+            (is_dynamic_member_lookup_in_cpp ctx obj field) ||
+            (is_dynamic_member_return_in_cpp ctx obj field)
+      | TArray (obj,index) -> (is_dynamic_in_cpp ctx obj || is_virtual_array obj)
+      | TTypeExpr _ -> false
+      | TCall(func,args) ->
+               (match follow func.etype with
+               | TFun (args,ret) ->
+                  is_dynamic_in_cpp ctx func
+               | _ -> true
+         );
+      | TParenthesis(expr) | TMeta(_,expr) -> is_dynamic_in_cpp ctx expr
+      | TCast (e,None) -> (type_string expr.etype) = "Dynamic"
+      | TLocal { v_name = "__global__" } -> false
+      | TConst TNull -> true
+      | _ -> false (* others ? *) )
+      in
+      result
+   end
+
+and is_dynamic_member_lookup_in_cpp ctx field_object field =
+   let member = field_name field in
+   if (is_internal_member member) then false else
+   if (is_native_pointer field_object) then false else
+   if (is_pointer field_object.etype true) then false else
+   if (match field_object.eexpr with | TTypeExpr _ -> true | _ -> false) then false else
+   if (is_dynamic_in_cpp ctx field_object) then true else
+   if (is_array field_object.etype) then false else (
+   let tstr = type_string field_object.etype in
+   match tstr with
+      (* Internal classes have no dynamic members *)
+      | "::String" | "Null" | "::hx::Class" | "::Enum" | "::Math" | "::ArrayAccess" -> false
+      | "Dynamic" -> true
+      | name ->
+          let full_name = name ^ "." ^ member in
+          if Hashtbl.mem ctx.ctx_class_member_types full_name  then
+               false
+            else
+               not (is_extern_class_instance field_object)
+   )
+and is_dynamic_member_return_in_cpp ctx field_object field =
+   let member = field_name field in
+   if (is_array field_object.etype) then false else
+   if (is_pointer field_object.etype true) then false else
+   if (is_internal_member member) then false else
+   match field_object.eexpr with
+   | TTypeExpr t ->
+         let full_name = "::" ^ (join_class_path_remap (t_path t) "::" ) ^ "." ^ member in
+         ( try ( let mem_type = (Hashtbl.find ctx.ctx_class_member_types full_name) in
+             mem_type="Dynamic" || mem_type="cpp::ArrayBase" || mem_type="cpp::VirtualArray" )
+         with Not_found -> true )
+   | _ ->
+      let tstr = type_string field_object.etype in
+      (match tstr with
+         (* Internal classes have no dynamic members *)
+         | "::String" | "Null" | "::hx::Class" | "::Enum" | "::Math" | "::ArrayAccess" -> false
+         | "Dynamic" | "cpp::ArrayBase" | "cpp::VirtualArray" -> true
+         | name ->
+               let full_name = name ^ "." ^ member in
+               try ( let mem_type = (Hashtbl.find ctx.ctx_class_member_types full_name) in
+                  mem_type="Dynamic" || mem_type="cpp::ArrayBase" || mem_type="cpp::VirtualArray" )
+               with Not_found -> true )
+;;
+
+let cast_if_required ctx expr to_type =
+   if (is_dynamic_in_cpp ctx expr) then
+      ctx.ctx_output (".Cast< " ^ to_type ^ " >()" )
+;;
+
+
+let is_matching_interface_type t0 t1 =
+    (match (follow t0),(follow t1) with
+    | TInst (k0,_), TInst(k1,_) -> k0==k1
+    | _ -> false
+    )
+;;
+
+
+
+let default_value_string = function
+   | TInt i -> Printf.sprintf "%ld" i
+   | TFloat float_as_string -> "((Float)" ^ float_as_string ^ ")"
+   | TString s -> str s
+   | TBool b -> (if b then "true" else "false")
+   | TNull -> "null()"
+   | _ -> "/* Hmmm */"
+;;
+
+
+
+let get_return_type field =
+   match follow field.cf_type with
+      | TFun (_,return_type) -> return_type
+      | _ -> raise Not_found
+;;
+
+
+let get_nth_type field index =
+   match follow field.ef_type with
+      | TFun (args,_) ->
+         let rec nth l index = match l with
+         | [] -> raise Not_found
+         | (_,_,t)::rest ->
+             if index = 0 then t
+             else nth rest (index-1)
+         in
+         nth args index
+      | _ -> raise Not_found
+;;
+
+
+
+let has_default_values args =
+   List.exists ( fun (_,o) -> match o with
+            | Some TNull -> false
+            | Some _ -> true
+            | _ -> false ) args ;;
+
+exception PathFound of string;;
+
+
+let strip_file ctx file = (match Common.defined ctx Common.Define.AbsolutePath with
+   | true -> file
+   | false -> let flen = String.length file in
+   (* Not quite right - should probably test is file exists *)
+   try
+      List.iter (fun path ->
+         let plen = String.length path in
+         if (flen>plen && path=(String.sub file 0 plen ))
+            then raise (PathFound (String.sub file plen (flen-plen)) ) )
+         (ctx.class_path @ ctx.std_path);
+      file;
+   with PathFound tail ->
+      tail)
+;;
+
+let hx_stack_push ctx output clazz func_name pos =
+   if ctx.ctx_debug_level > 0 then begin
+      let stripped_file = strip_file ctx.ctx_common pos.pfile in
+      let esc_file = (Ast.s_escape stripped_file) in
+      ctx.ctx_file_info := PMap.add stripped_file pos.pfile !(ctx.ctx_file_info);
+      if (ctx.ctx_debug_level>0) then begin
+         let full_name = clazz ^ "." ^ func_name ^ (
+           if (clazz="*") then
+             (" (" ^ esc_file ^ ":" ^ (string_of_int (Lexer.get_error_line pos) ) ^ ")")
+           else "") in
+         let hash_class_func = gen_hash 0 (clazz^"."^func_name) in
+         let hash_file = gen_hash 0 stripped_file in
+         output ("HX_STACK_FRAME(\"" ^ clazz ^ "\",\"" ^ func_name ^ "\"," ^ hash_class_func ^ ",\"" ^
+               full_name ^ "\",\"" ^ esc_file ^ "\"," ^
+               (string_of_int (Lexer.get_error_line pos) ) ^  "," ^ hash_file ^ ")\n")
+      end
+   end
+;;
+
+
+
+(* { *)
+
+type tcpp =
+   | TCppDynamic
+   | TCppObject
+   | TCppVoid
+   | TCppNull
+   | TCppEnum of tenum
+   | TCppScalar of string
+   | TCppString
+   | TCppFastIterator of tcpp
+   | TCppPointer of string * tcpp
+   | TCppRawPointer of string * tcpp
+   | TCppFunction of tcpp list * tcpp * string
+   | TCppReference of tcpp
+   | TCppStar of tcpp
+   | TCppVoidStar
+   | TCppDynamicArray
+   | TCppObjectArray of tcpp
+   | TCppWrapped of tcpp
+   | TCppScalarArray of tcpp
+   | TCppObjC of tclass
+   | TCppNativePointer of tclass
+   | TCppVariant
+   | TCppCode of tcpp
+   | TCppInst of tclass
+   | TCppInterface of tclass
+   | TCppClass
+   | TCppGlobal
+
+
+and tcppexpr = {
+   cppexpr : tcpp_expr_expr;
+   cpptype : tcpp;
+   cpppos : Ast.pos;
+}
+
+
+and tcpp_closure = {
+   close_type : tcpp;
+   close_args : (tvar * tconstant option) list;
+   close_expr : tcppexpr;
+   close_id : int;
+   close_undeclared : (string,tvar) Hashtbl.t;
+   close_this : tcppthis option;
+}
+
+
+and tcpp_block = {
+   block_expr : tcppexpr;
+   block_id : int;
+   block_undeclared : (string,tvar) Hashtbl.t;
+   block_this : bool;
+}
+
+and tcppcrementop =
+   | CppIncrement
+   | CppDecrement
+
+and tcppunop =
+   | CppNeg
+   | CppNegBits
+   | CppNot
+
+and tcppthis =
+   | ThisReal
+   | ThisFake
+   | ThisDynamic
+
+and tcppvarloc =
+   | VarLocal of tvar
+   | VarClosure of tvar
+   | VarThis of tclass_field
+   | VarInstance of tcppexpr * tclass_field * string * string
+   | VarInterface of tcppexpr * tclass_field
+   | VarStatic of tclass * tclass_field
+   | VarInternal of tcppexpr * string * string
+
+and tcppfuncloc =
+   | FuncThis of tclass_field
+   | FuncInstance of tcppexpr * bool * tclass_field
+   | FuncStatic of tclass * bool * tclass_field
+   | FuncTemplate of tclass * tclass_field * path * bool
+   | FuncInterface of tcppexpr * tclass * tclass_field
+   | FuncEnumConstruct of tenum * tenum_field
+   | FuncSuperConstruct
+   | FuncSuper of tcppthis * tclass_field
+   | FuncNew of tcpp
+   | FuncDynamic of tcppexpr
+   | FuncInternal of tcppexpr * string * string
+   | FuncGlobal of string
+   | FuncFromStaticFunction
+
+and tcpparrayloc =
+   | ArrayTyped of tcppexpr * tcppexpr
+   | ArrayPointer of tcppexpr * tcppexpr
+   | ArrayRawPointer of tcppexpr * tcppexpr
+   | ArrayObject of tcppexpr * tcppexpr * tcpp
+   | ArrayVirtual of tcppexpr * tcppexpr
+   | ArrayImplements of tclass * tcppexpr * tcppexpr
+   | ArrayDynamic of tcppexpr * tcppexpr
+
+and tcpplvalue =
+   | CppVarRef of tcppvarloc
+   | CppArrayRef of tcpparrayloc
+   | CppDynamicRef of tcppexpr * string
+   | CppGlobalRef of string
+
+
+and tcpp_expr_expr =
+   | CppInt of int32
+   | CppFloat of string
+   | CppString of string
+   | CppBool of bool
+   | CppNull
+   | CppNullAccess
+   | CppNil
+   | CppThis of tcppthis
+   | CppSuper of tcppthis
+   | CppCode of string * tcppexpr list
+   | CppClosure of tcpp_closure
+   | CppVar of tcppvarloc
+   | CppGlobal of string
+   | CppDynamicField of tcppexpr * string
+   | CppFunction of tcppfuncloc * tcpp
+   | CppEnumIndex of tcppexpr
+   | CppEnumField of tenum * tenum_field
+   | CppCall of tcppfuncloc * tcppexpr list
+   | CppFunctionAddress of tclass * tclass_field
+   | CppArray of tcpparrayloc
+   | CppCrement of  tcppcrementop * Ast.unop_flag * tcpplvalue
+   | CppSet of tcpplvalue * tcppexpr
+   | CppModify of Ast.binop * tcpplvalue * tcppexpr
+   | CppBinop of Ast.binop * tcppexpr * tcppexpr
+   | CppCompare of string * tcppexpr * tcppexpr
+   | CppNullCompare of string * tcppexpr
+   | CppObjectDecl of (string * tcppexpr) list * bool
+   | CppPosition of string * int32 * string * string
+   | CppArrayDecl of tcppexpr list
+   | CppUnop of tcppunop * tcppexpr
+   | CppVarDecl of tvar * tcppexpr option
+   | CppBlock of tcppexpr list * tcpp_closure list
+   | CppFor of tvar * tcppexpr * tcppexpr
+   | CppIf of tcppexpr * tcppexpr * tcppexpr option
+   | CppWhile of tcppexpr * tcppexpr * Ast.while_flag * int
+   | CppIntSwitch of tcppexpr * (Int32.t list * tcppexpr) list * tcppexpr option
+   | CppSwitch of tcppexpr * tcpp * (tcppexpr list * tcppexpr) list * tcppexpr option * int
+   | CppTry of tcppexpr * (tvar * tcppexpr) list
+   | CppBreak
+   | CppContinue
+   | CppClassOf of path * bool
+   | CppGoto of int
+   | CppReturn of tcppexpr option
+   | CppThrow of tcppexpr
+   | CppEnumParameter of tcppexpr * tenum_field * int
+   | CppTCast of tcppexpr * tcpp
+   | CppCast of tcppexpr * tcpp
+   | CppCastStatic of tcppexpr * tcpp
+   | CppCastScalar of tcppexpr * string
+   | CppCastVariant of tcppexpr
+   | CppCastObjC of tcppexpr * tclass
+   | CppCastNative of tcppexpr
+
+let rec s_tcpp = function
+   | CppInt _  -> "CppInt"
+   | CppFloat _ -> "CppFloat"
+   | CppString _ -> "CppString"
+   | CppBool _ -> "CppBool"
+   | CppNull -> "CppNull"
+   | CppNil -> "CppNil"
+   | CppThis _ -> "CppThis"
+   | CppSuper _ -> "CppSuper"
+   | CppCode _ -> "CppCode"
+   | CppClosure _ -> "CppClosure"
+   | CppVar VarLocal(_) -> "CppVarLocal"
+   | CppVar VarClosure(_) -> "CppVarClosure"
+   | CppVar VarThis(_) -> "CppVarThis"
+   | CppVar VarInstance(expr,field,clazz,op) -> "CppVarInstance(" ^ clazz ^ "::" ^ op ^ field.cf_name ^ ")"
+   | CppVar VarInterface(_) -> "CppVarInterface"
+   | CppVar VarStatic(_) -> "CppVarStatic"
+   | CppVar VarInternal(_) -> "CppVarInternal"
+   | CppDynamicField _ -> "CppDynamicField"
+   | CppGlobal _ -> "CppGlobal"
+   | CppFunction _ -> "CppFunction"
+   | CppEnumIndex _ -> "CppEnumIndex"
+   | CppEnumField  _ -> "CppEnumField"
+   | CppNullAccess -> "CppNullAccess"
+
+   | CppCall (FuncThis _,_)  -> "CppCallThis"
+   | CppCall (FuncInstance (obj,objC,field),_) ->
+       (if objC then "CppCallObjCInstance" else "CppCallInstance(") ^ tcpp_to_string obj.cpptype ^ "," ^ field.cf_name ^ ")"
+   | CppCall (FuncInterface  _,_) -> "CppCallInterface"
+   | CppCall (FuncStatic  (_,objC,_),_) -> if objC then "CppCallStaticObjC" else "CppCallStatic"
+   | CppCall (FuncTemplate  _,_) -> "CppCallTemplate"
+   | CppCall (FuncEnumConstruct _,_) -> "CppCallEnumConstruct"
+   | CppCall (FuncSuperConstruct,_) -> "CppCallSuperConstruct"
+   | CppCall (FuncSuper _,_) -> "CppCallSuper"
+   | CppCall (FuncNew _,_) -> "CppCallNew"
+   | CppCall (FuncDynamic _,_) -> "CppCallDynamic"
+   | CppCall (FuncInternal _,_) -> "CppCallInternal"
+   | CppCall (FuncGlobal _,_) -> "CppCallGlobal"
+   | CppCall (FuncFromStaticFunction,_) -> "CppCallFromStaticFunction"
+
+   | CppFunctionAddress  _ -> "CppFunctionAddress"
+   | CppArray  _ -> "CppArray"
+   | CppCrement  _ -> "CppCrement"
+   | CppSet  _ -> "CppSet"
+   | CppModify  _ -> "CppModify"
+   | CppBinop  _ -> "CppBinop"
+   | CppCompare  _ -> "CppCompare"
+   | CppNullCompare  _ -> "CppNullCompare"
+   | CppObjectDecl  _ -> "CppObjectDecl"
+   | CppPosition  _ -> "CppPosition"
+   | CppArrayDecl  _ -> "CppArrayDecl"
+   | CppUnop  _ -> "CppUnop"
+   | CppVarDecl  _ -> "CppVarDecl"
+   | CppBlock  _ -> "CppBlock"
+   | CppFor  _ -> "CppFor"
+   | CppIf  _ -> "CppIf"
+   | CppWhile _ -> "CppWhile"
+   | CppIntSwitch  _ -> "CppIntSwitch"
+   | CppSwitch  _ -> "CppSwitch"
+   | CppTry _ -> "CppTry"
+   | CppBreak -> "CppBreak"
+   | CppContinue -> "CppContinue"
+   | CppClassOf _ -> "CppClassOf"
+   | CppGoto _ -> "CppGoto"
+   | CppReturn _ -> "CppReturn"
+   | CppThrow _ -> "CppThrow"
+   | CppEnumParameter _ -> "CppEnumParameter"
+   | CppTCast _ -> "CppTCast"
+   | CppCast _ -> "CppCast"
+   | CppCastStatic _ -> "CppCastStatic"
+   | CppCastScalar _ -> "CppCastScalar"
+   | CppCastVariant _ -> "CppCastVariant"
+   | CppCastObjC _ -> "CppCastObjC"
+   | CppCastNative _ -> "CppCastNative"
+
+and tcpp_to_string_suffix suffix tcpp = match tcpp with
+   | TCppDynamic -> " ::Dynamic"
+   | TCppObject -> " ::Dynamic"
+   | TCppReference t -> (tcpp_to_string t) ^" &"
+   | TCppStar t -> (tcpp_to_string t) ^" *"
+   | TCppVoid -> "void"
+   | TCppVoidStar -> "void *"
+   | TCppVariant -> "::cpp::Variant"
+   | TCppEnum(enum) -> "::hx::EnumBase" ^ suffix
+   | TCppScalar(scalar) -> scalar
+   | TCppString -> "::String"
+   | TCppFastIterator it -> "::cpp::FastIterator" ^ suffix ^ "< " ^ (tcpp_to_string it) ^ " >";
+   | TCppPointer(ptrType,valueType) -> "::cpp::" ^ ptrType ^ "< " ^ (tcpp_to_string valueType) ^ " >"
+   | TCppRawPointer(constName,valueType) -> constName ^ (tcpp_to_string valueType) ^ "*"
+   | TCppFunction(argTypes,retType,abi) ->
+        let args = (String.concat "," (List.map tcpp_to_string argTypes)) in
+        "::cpp::Function< " ^ abi ^ " " ^ (tcpp_to_string retType) ^ "(" ^ args ^ ") >"
+   | TCppDynamicArray -> "::cpp::VirtualArray" ^ suffix
+   | TCppObjectArray _ -> "::Array" ^ suffix ^ "< ::Dynamic>"
+   | TCppWrapped _ -> " ::Dynamic"
+   | TCppScalarArray(value) -> "::Array" ^ suffix ^ "< " ^ (tcpp_to_string value) ^ " >"
+   | TCppObjC klass ->
+      let path = join_class_path_remap klass.cl_path "::" in
+      if klass.cl_interface then
+         "id < " ^ path ^ ">"
+      else
+         path ^ " *"
+   | TCppNativePointer klass ->
+       let name = (join_class_path_remap klass.cl_path "::") in
+       if suffix="_obj" then
+          name
+       else
+          "hx::Native< " ^ name ^ "* >";
+   | TCppInst klass ->
+        (cpp_class_path_of klass) ^ suffix
+   | TCppInterface klass when suffix="_obj" ->
+        (cpp_class_path_of klass) ^ suffix
+   | TCppInterface _ -> "::Dynamic"
+   | TCppClass -> "hx::Class" ^ suffix;
+   | TCppGlobal -> "";
+   | TCppNull -> " ::Dynamic";
+   | TCppCode _ -> "Code"
+
+and tcpp_to_string tcpp =
+    tcpp_to_string_suffix "" tcpp
+
+and cpp_class_path_of klass =
+      " ::" ^ (join_class_path_remap klass.cl_path "::")
+;;
+
+
+
+let cpp_const_type cval = match cval with
+   | TInt i -> CppInt(i) , TCppScalar("Int")
+   | TBool b -> CppBool(b) , TCppScalar("Bool")
+   | TFloat f -> CppFloat(f) , TCppScalar("Float")
+   | TString s -> CppString(s) , TCppString
+   | _ -> (* TNull, TThis & TSuper should already be handled *)
+      CppNull, TCppNull
+;;
+
+
+let is_cpp_scalar cpp_type =
+   match cpp_type with
+   | TCppScalar(_) -> true
+   | _ -> false
+;;
+
+
+let is_cpp_array_implementer cppType =
+   match cppType with
+   | TCppInst (klass)
+   | TCppInterface (klass) ->
+      (match klass.cl_array_access with
+      | Some _ -> true
+      | _ -> false )
+   | _ -> false
+;;
+
+let rec const_int_of expr =
+   match expr.eexpr with
+   | TConst TInt x -> x
+   | TConst TBool x -> Int32.of_int (if x then 1 else 0)
+   | TParenthesis e -> const_int_of e
+   | _ -> raise Not_found
+;;
+
+let rec const_float_of expr =
+   match expr.eexpr with
+   | TConst TInt x -> Printf.sprintf "%ld" x
+   | TConst TFloat x -> x
+   | TConst TBool x -> if x then "1" else "0"
+   | TParenthesis e -> const_float_of e
+   | _ -> raise Not_found
+;;
+
+
+let rec const_string_of expr =
+   match expr.eexpr with
+   | TConst TString x -> x
+   | TParenthesis e -> const_string_of e
+   | _ -> raise Not_found
+;;
+
+
+let rec cpp_is_struct_access t =
+   match t with
+   | TCppFunction _ -> true
+   | TCppInst (class_def) -> (has_meta_key class_def.cl_meta Meta.StructAccess)
+   | TCppReference (r) -> cpp_is_struct_access r
+   | _ -> false
+;;
+
+
+let cpp_is_dynamic_type = function
+   | TCppDynamic | TCppObject | TCppVariant | TCppWrapped _ | TCppGlobal | TCppNull
+   | TCppInterface _
+      -> true
+   | _ -> false
+;;
+
+
+let rec cpp_type_of ctx haxe_type =
+   (match haxe_type with
+   | TMono r -> (match !r with None -> TCppDynamic | Some t -> cpp_type_of ctx t)
+
+   | TEnum (enum,params) ->  TCppEnum(enum)
+
+   | TInst ({ cl_path=([],"Array"); cl_kind = KTypeParameter _},_)
+      -> TCppObject
+
+   | TInst ({ cl_kind = KTypeParameter _},_)
+      -> TCppDynamic
+
+   | TInst (klass,params) ->
+      cpp_instance_type ctx klass params
+
+   | TAbstract (abs,pl) when not (Meta.has Meta.CoreType abs.a_meta) ->
+       cpp_type_from_path ctx abs.a_path pl (fun () ->
+            cpp_type_of ctx (Abstract.get_underlying_type abs pl) )
+
+   | TAbstract (a,params) ->
+       cpp_type_from_path ctx a.a_path params (fun () ->
+            if is_scalar_abstract a then
+               TCppScalar(join_class_path a.a_path "::")
+            else
+               TCppDynamic)
+
+   | TType (type_def,params) ->
+       cpp_type_from_path ctx type_def.t_path params (fun () ->
+          cpp_type_of ctx (apply_params type_def.t_params params type_def.t_type) )
+
+   | TFun _ -> TCppObject
+   | TAnon _ -> TCppObject
+   | TDynamic _ -> TCppDynamic
+   | TLazy func -> cpp_type_of ctx ((!func)())
+   )
+   and  cpp_type_from_path ctx path params default =
+      match path,params with
+      | ([],"Void"),_ -> TCppVoid
+      | ([],"void"),_ -> TCppVoid (* for old code with @:void *)
+      | ([],"Bool"),_ -> TCppScalar("Bool")
+      | ([],"Float"),_ -> TCppScalar("Float")
+      | ([],"Int"),_ -> TCppScalar("Int")
+      | ([], "EnumValue"),_ -> TCppObject
+      | ([], "Class"),_ -> TCppClass
+      | ([], "Enum"),_  -> TCppClass
+      | (["cpp"], "Char"),_ -> TCppScalar("char")
+      | (["cpp"], "Float32"),_ -> TCppScalar("float")
+      | (["cpp"], "Float64"),_ -> TCppScalar("double")
+      | (["cpp"], "Int8"),_ -> TCppScalar("signed char")
+      | (["cpp"], "Int16"),_ -> TCppScalar("short")
+      | (["cpp"], "Int32"),_ -> TCppScalar("int")
+      | (["cpp"], "Int64"),_ -> TCppScalar("::cpp::Int64")
+      | (["cpp"], "UInt8"),_ -> TCppScalar("unsigned char")
+      | (["cpp"], "UInt16"),_ -> TCppScalar("unsigned short")
+      | (["cpp"], "UInt32"),_ -> TCppScalar("unsigned int")
+      | (["cpp"], "UInt64"),_ -> TCppScalar("::cpp::UInt64")
+
+      | ([],"String"), [] ->
+         TCppString
+
+      (* Things with type parameters hxcpp knows about ... *)
+      | (["cpp"],"FastIterator"), [p] ->
+            TCppFastIterator(cpp_type_of ctx p)
+      | (["cpp"],"Pointer"), [p] ->
+            TCppPointer("Pointer", cpp_type_of ctx p)
+      | (["cpp"],"ConstPointer"), [p] ->
+            TCppPointer("ConstPointer", cpp_type_of ctx p)
+      | (["cpp"],"RawPointer"), [p] ->
+            TCppRawPointer("", cpp_type_of ctx p)
+      | (["cpp"],"RawConstPointer"), [p] ->
+            TCppRawPointer("const ", cpp_type_of ctx p)
+      | (["cpp"],"Function"), [function_type; abi] ->
+            cpp_function_type_of ctx function_type abi;
+      | (["cpp"],"Callable"), [function_type] ->
+            cpp_function_type_of_string ctx function_type "";
+      | (["cpp"],"Reference"), [param] ->
+            TCppReference(cpp_type_of ctx param)
+      | (["cpp"],"Star"), [param] ->
+            TCppStar(cpp_type_of ctx param)
+
+      | ([],"Array"), [p] ->
+         let arrayOf = cpp_type_of ctx p in
+         (match arrayOf with
+            | TCppVoid (* ? *)
+            | TCppDynamic ->
+              TCppDynamicArray
+
+            | TCppObject
+            | TCppReference _
+            | TCppStar _
+            | TCppEnum _
+            | TCppInst _
+            | TCppInterface _
+            | TCppClass
+            | TCppDynamicArray
+            | TCppObjectArray _
+            | TCppScalarArray _
+               -> TCppObjectArray(arrayOf)
+            | _ ->
+              TCppScalarArray(arrayOf)
+         )
+
+      | ([],"Null"), [p] ->
+            cpp_type_of_null ctx p
+
+      | _ -> default ()
+
+   and cpp_type_of_null ctx p =
+     let baseType = cpp_type_of ctx p in
+     if (type_has_meta_key p Meta.NotNull) || (is_cpp_scalar baseType) then
+        TCppObject
+     else
+        baseType
+
+   (* Optional types are Dynamic if they norally could not be null *)
+   and cpp_fun_arg_type_of ctx tvar opt =
+      match opt with
+      | Some _ -> cpp_type_of_null ctx tvar.t_type
+      | _ -> cpp_type_of ctx tvar.t_type
+
+
+   and cpp_function_type_of ctx function_type abi =
+      let abi = (match follow abi with
+                 | TInst (klass1,_) -> get_meta_string klass1.cl_meta Meta.Abi
+                 | _ -> assert false )
+      in
+      cpp_function_type_of_string ctx function_type abi
+   and cpp_function_type_of_string ctx function_type abi_string =
+      match follow function_type with
+      | TFun(args,ret) ->
+          (* Optional types are Dynamic if they norally could not be null *)
+          let  cpp_arg_type_of = fun(_,optional,haxe_type) ->
+             if optional then
+                cpp_type_of_null ctx haxe_type
+             else
+                cpp_type_of ctx haxe_type
+          in
+          TCppFunction(List.map cpp_arg_type_of args, cpp_type_of ctx ret, abi_string)
+      | _ ->  (* ? *)
+          TCppFunction([TCppVoid], TCppVoid, abi_string)
+
+   and cpp_instance_type ctx klass params =
+      cpp_type_from_path ctx klass.cl_path params (fun () ->
+         if is_objc_class klass then
+            TCppObjC(klass)
+         else if klass.cl_interface && is_native_gen_class klass then
+            TCppNativePointer(klass)
+         else if klass.cl_interface then
+            TCppInterface(klass)
+         else if klass.cl_extern && (not (is_internal_class klass.cl_path) ) then
+            TCppInst(klass)
+         else
+            TCppInst(klass)
+       )
+;;
+
+
+let cpp_return_type ctx haxe_type =
+  match haxe_type with
+  | TFun (_,ret) -> cpp_type_of ctx ret
+  | _ -> TCppDynamic
+;;
+
+
+let cpp_member_return_type ctx member =
+   cpp_return_type ctx member.cf_type
+;;
+
+let is_cpp_objc_type cpptype = match cpptype with
+   | TCppObjC(_) -> true;
+   | _ -> false
+;;
+
+
+let cpp_enum_path_of enum =
+   (*
+   let rename = get_meta_string enum.e_meta Meta.Native in
+   if rename <> "" then
+      rename
+   else
+   *)
+      "::" ^ (join_class_path_remap enum.e_path "::")
+;;
+
+
+
+(*
+let rec cpp_object_name = function
+   | TCppString -> "::String"
+   | TCppDynamicArray -> "::cpp::VirtualArray_obj"
+   | TCppObjectArray _ -> "::Array_obj< ::Dynamic>"
+   | TCppScalarArray(value) -> "::Array_obj< " ^ (tcpp_to_string value) ^ " >"
+   | TCppObjC klass ->  (cpp_class_path_of klass) ^ "_obj"
+   | TCppInst klass -> (cpp_class_path_of klass) ^ "_obj"
+   | TCppClass -> "hx::Class_obj";
+   | TCppDynamic -> "Dynamic"
+   | TCppVoid -> "void"
+   | TCppVoidStar -> "void *"
+   | TCppEnum(enum) -> "::hx::EnumBase"
+   | TCppScalar(scalar) -> scalar
+   | TCppFastIterator it -> "::cpp::FastIterator< " ^ (tcpp_to_string it) ^ " >";
+   | TCppPointer(ptrType,valueType) -> "::cpp::" ^ ptrType ^ "< " ^ (tcpp_to_string valueType) ^ " >"
+   | TCppRawPointer(constName,valueType) -> constName ^ (tcpp_to_string valueType) ^ "*"
+   | TCppFunction(argTypes,retType,abi) ->
+        let args = (String.concat "," (List.map tcpp_to_string argTypes)) in
+        "::cpp::Function< " ^ abi ^ " " ^ (tcpp_to_string retType) ^ "(" ^ args ^ ") >"
+   | TCppWrapped _ -> "Dynamic"
+   | TCppNativePointer klass -> (cpp_class_path_of klass) ^ " *"
+   | TCppGlobal -> "";
+   | TCppNull -> "Dynamic";
+   | TCppCode -> "/* code */"
+;;
+*)
+
+let cpp_class_name klass =
+   (*
+   let rename = get_meta_string klass.cl_meta Meta.Native in
+   if rename <> "" then
+      rename ^ "_obj"
+   else
+   *)
+   begin
+      let path = "::" ^ (join_class_path_remap klass.cl_path "::") in
+      if path="::String" then path else path ^ "_obj"
+   end
+;;
+
+
+let cpp_variant_type_of t = match t with
+   | TCppDynamic
+   | TCppObject
+   | TCppReference _
+   | TCppStar _
+   | TCppVoid
+   | TCppFastIterator _
+   | TCppDynamicArray
+   | TCppObjectArray _
+   | TCppScalarArray _
+   | TCppWrapped _
+   | TCppObjC _
+   | TCppInst _
+   | TCppInterface _
+   | TCppCode _
+   | TCppClass
+   | TCppGlobal
+   | TCppNull
+   | TCppEnum _ -> TCppDynamic
+   | TCppString -> TCppString
+   | TCppFunction _
+   | TCppNativePointer _
+   | TCppPointer _
+   | TCppRawPointer _
+   | TCppVoidStar -> TCppVoidStar
+   | TCppScalar "Int"
+   | TCppScalar "Bool"
+   | TCppScalar "Float"  -> t
+   | TCppScalar "double"
+   | TCppScalar "float" -> TCppScalar("Float")
+   | TCppScalar _  -> TCppScalar("Int")
+   | TCppVariant -> TCppVariant
+;;
+
+let cpp_cast_variant_type_of t = match t with
+   | TCppObjectArray _
+   | TCppScalarArray _
+   | TCppDynamicArray
+   | TCppClass
+   | TCppEnum _ 
+   | TCppInst _ -> t
+   | _ -> cpp_variant_type_of t;
+;;
+
+let cpp_base_type_of t =
+   match cpp_variant_type_of t with
+   | TCppDynamic -> "Object"
+   | TCppString -> "String"
+   | TCppVoidStar -> "Pointer"
+   | TCppScalar x  -> x
+   | _  -> "Object"
+;;
+
+let ctx_type_string ctx haxe_type =
+      tcpp_to_string (cpp_type_of ctx haxe_type)
+;;
+
+
+let ctx_cant_be_null ctx haxe_type =
+   match cpp_type_of ctx haxe_type with
+   | TCppScalar _ -> true
+   | _  -> false
+
+let is_complex_compare =  function
+   | TCppScalar _ -> false
+   | TCppString  -> false
+   | _ -> true
+;;
+
+
+
+let ctx_arg_type_name ctx name default_val arg_type prefix =
+   let remap_name = keyword_remap name in
+   let type_str = (ctx_type_string ctx arg_type) in
+   match default_val with
+   | Some TNull  -> (type_str,remap_name)
+   | Some constant when (ctx_cant_be_null ctx arg_type) -> ("hx::Null< " ^ type_str ^ " > ",prefix ^ remap_name)
+   | Some constant  -> (type_str,prefix ^ remap_name)
+   | _ -> (type_str,remap_name);;
+
+
+
+(* Generate prototype text, including allowing default values to be null *)
+let ctx_arg ctx name default_val arg_type prefix =
+   let pair = ctx_arg_type_name ctx name default_val arg_type prefix in
+   (fst pair) ^ " " ^ (snd pair);;
+
+
+(* Generate prototype text, including allowing default values to be null *)
+let ctx_arg_name ctx name default_val arg_type prefix =
+   let pair = ctx_arg_type_name ctx name default_val arg_type prefix in
+   (snd pair);;
+
+
+let ctx_arg_list ctx arg_list prefix =
+   String.concat "," (List.map (fun (v,o) -> (ctx_arg ctx v.v_name o v.v_type prefix) ) arg_list)
+
+let ctx_arg_list_name ctx arg_list prefix =
+   String.concat "," (List.map (fun (v,o) -> (ctx_arg_name ctx v.v_name o v.v_type prefix) ) arg_list)
+
+let cpp_arg_names args =
+   String.concat "," (List.map (fun (name,_,_) -> keyword_remap name) args)
+;;
+
+let rec ctx_tfun_arg_list ctx include_names arg_list =
+   let oType o arg_type =
+      let type_str = (ctx_type_string ctx arg_type) in
+      (* type_str may have already converted Null<X> to Dynamic because of NotNull tag ... *)
+      if o && (ctx_cant_be_null ctx arg_type) && type_str<>"Dynamic" then
+         "hx::Null< " ^ type_str ^ " > "
+      else
+         type_str
+   in
+   match arg_list with
+   | [] -> ""
+   | [(name,o,arg_type)] -> (oType o arg_type) ^ (if include_names then " " ^ (keyword_remap name) else "")
+   | (name,o,arg_type) :: remaining  ->
+      (oType o arg_type) ^ (if include_names then " " ^ (keyword_remap name) else "") ^  "," ^ (ctx_tfun_arg_list ctx include_names remaining)
+
+let cpp_var_type_of ctx var =
+   tcpp_to_string (cpp_type_of ctx var.v_type)
+;;
+
+
+let ctx_function_signature ctx include_names tfun abi =
+   match follow tfun with
+   | TFun(args,ret) -> (ctx_type_string ctx ret) ^ " " ^ abi ^ "(" ^ (ctx_tfun_arg_list ctx include_names args) ^ ")"
+   | _ -> "void *"
+
+
+
+let cpp_var_name_of var =
+   let rename = get_meta_string var.v_meta Meta.Native in
+   if rename <> "" then
+      rename
+   else
+      keyword_remap var.v_name
+;;
+
+let cpp_var_debug_name_of v =
+   let rec loop meta = match meta with
+      | (Meta.RealPath,[EConst (String s),_],_) :: _ -> s
+      | _ :: meta -> loop meta
+      | [] -> v.v_name
+   in
+   loop v.v_meta
+;;
+
+
+let cpp_no_debug_synbol ctx var =
+   (has_meta_key var.v_meta Meta.CompilerGenerated) ||
+      match cpp_type_of ctx var.v_type with
+      | TCppStar _ | TCppReference _ -> true
+      | TCppInst (class_def) when (has_meta_key class_def.cl_meta Meta.StructAccess) -> true
+      | TCppInst (class_def) when (has_meta_key class_def.cl_meta Meta.Unreflective) -> true
+      | _->
+         let name = cpp_var_debug_name_of var in
+         (String.length name) >4 && (String.sub name 0 4) = "_hx_"
+;;
+
+let cpp_debug_name_of var =
+   keyword_remap var.v_name
+;;
+
+let cpp_debug_var_visible ctx var =
+   not (cpp_no_debug_synbol ctx (fst var))
+;;
+
+
+let only_stack_access ctx haxe_type =
+   let tcpp = cpp_type_of ctx haxe_type in
+   match tcpp with
+   | TCppInst(klass) -> has_meta_key klass.cl_meta Meta.StackOnly
+   | _ -> false;
+;;
+
+let cpp_is_real_array obj =
+   match obj.cpptype with
+   | TCppScalarArray _
+   | TCppObjectArray _ -> true
+   | _ -> false
+;;
+
+
+let is_array_splice_call obj member =
+   match obj.cpptype, member.cf_name with
+   | TCppScalarArray _, "splice"
+   | TCppObjectArray _, "splice" -> true
+   | _,_ -> false
+;;
+
+let cpp_can_static_cast funcType inferredType =
+   match funcType with
+   | TCppReference(_) | TCppStar(_) -> false
+   | _ ->
+      (match inferredType with
+      | TCppInst _
+      | TCppClass
+      | TCppEnum _
+         -> (tcpp_to_string funcType) <> (tcpp_to_string inferredType)
+      | _ -> false
+   )
+;;
+
+let cpp_member_name_of member =
+   let rename = get_meta_string member.cf_meta Meta.Native in
+   if rename <> "" then
+      rename
+   else
+      keyword_remap member.cf_name
+;;
+
+let cpp_is_templated_call ctx member =
+   has_meta_key member.cf_meta Meta.TemplatedCall
+;;
+
+let cpp_is_static_extension ctx member =
+   has_meta_key member.cf_meta Meta.NativeStaticExtension
+;;
+
+let cpp_template_param path native =
+   let path = "::" ^ (join_class_path_remap (path) "::" ) in
+   if (native) then
+      path
+   else if (path="::Array") then
+      "hx::ArrayBase"
+   else
+      path
+;;
+
+
+
+let cpp_append_block block expr =
+   match block.cppexpr with
+   | CppBlock(expr_list, closures) ->
+       { block with cppexpr = CppBlock( expr_list @ [expr], closures) }
+   | _ -> error "Internal error appending expression" block.cpppos
+;;
+
+
+
+let cpp_enum_name_of field =
+   let rename = get_meta_string field.ef_meta Meta.Native in
+   if rename <> "" then
+      rename
+   else
+      keyword_remap field.ef_name
+;;
+
+let retype_expression ctx request_type function_args expression_tree forInjection =
+   let rev_closures = ref [] in
+   let closureId = ref 0 in
+   let declarations = ref (Hashtbl.create 0) in
+   let undeclared = ref (Hashtbl.create 0) in
+   let uses_this = ref None in
+   let injection = ref forInjection in
+   let this_real = ref (if ctx.ctx_real_this_ptr then ThisReal else ThisDynamic) in
+   let file_id = ctx.ctx_file_id in
+   let loop_stack = ref [] in
+   let alloc_file_id () =
+      incr file_id;
+      !file_id
+   in
+   let begin_loop () =
+      loop_stack := (alloc_file_id (),ref false) :: !loop_stack;
+      (fun () -> match !loop_stack with
+         | (label_id,used) :: tl ->
+            loop_stack := tl;
+            if !used then label_id else -1
+         | [] ->
+            error "Invalid inernal loop handling" expression_tree.epos
+      )
+   in
+
+   (* '__trace' is at the top-level *)
+   Hashtbl.add !declarations "__trace" ();
+   List.iter (fun arg -> Hashtbl.add !declarations arg.v_name () ) function_args;
+
+   let rec to_lvalue value =
+      match value.cppexpr with
+      | CppVar varloc -> CppVarRef(varloc)
+      | CppArray arrayloc -> CppArrayRef(arrayloc)
+      | CppDynamicField(expr, name) -> CppDynamicRef(expr,name)
+      | CppTCast(cppExpr,_)
+      | CppCast(cppExpr,_)
+      | CppCastStatic(cppExpr,_)
+      | CppCastObjC(cppExpr,_)
+      | CppCastScalar(cppExpr,_) -> to_lvalue cppExpr
+      | CppCastVariant(cppExpr) -> to_lvalue cppExpr
+      | CppGlobal(name) -> CppGlobalRef(name)
+      | _ -> error ("Could not convert expression to l-value (" ^ s_tcpp value.cppexpr ^ ")") value.cpppos
+   in
+
+   let rec retype return_type expr =
+      let cpp_type_of t = cpp_type_of ctx t in
+      let mk_cppexpr newExpr newType = { cppexpr = newExpr; cpptype = newType; cpppos = expr.epos } in
+      let retypedExpr, retypedType =
+         match expr.eexpr with
+         | TEnumParameter( enumObj, enumField, enumIndex  ) ->
+            let retypedObj = retype TCppDynamic enumObj in
+            CppEnumParameter( retypedObj, enumField, enumIndex ), cpp_cast_variant_type_of (cpp_type_of (get_nth_type enumField enumIndex))
+
+         | TConst TThis ->
+            uses_this := Some !this_real;
+            CppThis(!this_real), if !this_real=ThisDynamic then TCppDynamic else cpp_type_of expr.etype
+
+         | TConst TSuper ->
+            uses_this := Some !this_real;
+            CppSuper(!this_real), if !this_real=ThisDynamic then TCppDynamic else cpp_type_of expr.etype
+
+         | TConst TNull when is_objc_type expr.etype ->
+            CppNil, TCppNull
+
+         | TConst x ->
+            cpp_const_type x
+
+         | TLocal { v_name = "__global__" } ->
+            CppClassOf(([],""),false), TCppGlobal
+
+         | TLocal tvar ->
+            let name = tvar.v_name in
+            if (Hashtbl.mem !declarations name) then begin
+               (*print_endline ("Using existing tvar " ^ tvar.v_name);*)
+               CppVar(VarLocal(tvar)), cpp_type_of tvar.v_type
+            end else begin
+               (*print_endline ("Missing tvar " ^ tvar.v_name);*)
+               Hashtbl.replace !undeclared name tvar;
+               CppVar(VarClosure(tvar)), cpp_type_of tvar.v_type
+            end
+
+         | TBreak ->
+            begin match !loop_stack with
+               | [] ->
+                  CppBreak, TCppVoid
+               | (label_id,used) :: _ ->
+                  used := true;
+                  (CppGoto label_id),TCppVoid
+            end
+
+         | TContinue ->
+            CppContinue, TCppVoid
+
+         | TThrow e1 ->
+            CppThrow(retype TCppDynamic e1), TCppVoid
+
+         | TMeta( (Meta.Fixed,_,_),e) ->
+            let cppType = retype return_type e in
+            (match cppType.cppexpr with
+            | CppObjectDecl(def,false) -> CppObjectDecl(def,true), cppType.cpptype
+            | _ -> cppType.cppexpr, cppType.cpptype
+            )
+
+         | TMeta(_,e)
+         | TParenthesis e ->
+            let cppType = retype return_type e in
+            cppType.cppexpr, cppType.cpptype
+
+         | TField( obj, field ) ->
+            (match field with
+            | FInstance (clazz,params,member)
+            | FClosure (Some (clazz,params),member) ->
+               let funcReturn = cpp_member_return_type ctx member in
+               let clazzType = cpp_instance_type ctx clazz params in
+               let retypedObj = retype clazzType obj in
+               let exprType = cpp_type_of member.cf_type in
+               let is_objc = is_cpp_objc_type retypedObj.cpptype in
+
+               if retypedObj.cpptype=TCppNull then
+                  CppNullAccess, TCppDynamic
+               else if retypedObj.cpptype=TCppDynamic && not clazz.cl_interface then begin
+                  if is_internal_member member.cf_name then
+                    CppFunction( FuncInstance(retypedObj,false,member), funcReturn ), exprType
+                  else
+                     CppDynamicField(retypedObj, member.cf_name), TCppVariant
+               end else if cpp_is_struct_access retypedObj.cpptype then begin
+                  match retypedObj.cppexpr with
+                  | CppThis ThisReal ->
+                      CppVar(VarThis(member)), exprType
+                  | _ ->
+                      CppVar( VarInstance(retypedObj,member,tcpp_to_string clazzType, ".") ), exprType
+               end else if is_var_field member then begin
+
+                  let exprType = match retypedObj.cpptype, exprType with
+                       | TCppPointer(_,t), TCppDynamic
+                       | TCppRawPointer(_,t), TCppDynamic (* the 'type parameter' will show up as Dynamic *)
+                          -> t
+                       | _ -> exprType
+                  in
+
+                  match retypedObj.cppexpr with
+                  | CppThis ThisReal ->
+                     CppVar(VarThis(member) ), exprType
+                  | _ ->
+                     (match retypedObj.cpptype, member.cf_name with
+                     (* Special variable remapping ... *)
+                     | TCppDynamicArray, "length" ->
+                        CppCall(FuncInternal(retypedObj,"get_length","->"),[]), exprType
+
+                     | TCppInterface _,_
+                     | TCppDynamic,_ ->
+                        CppDynamicField(retypedObj, member.cf_name), TCppVariant
+
+                     | _ ->
+                        let operator = if cpp_is_struct_access retypedObj.cpptype || retypedObj.cpptype=TCppString then "." else "->" in
+                        CppVar(VarInstance(retypedObj,member,tcpp_to_string clazzType, operator) ), exprType
+                     )
+               end else if (clazz.cl_interface && not is_objc (* Use instance call for objc interfaces *)) then
+                  CppFunction( FuncInterface(retypedObj,clazz,member), funcReturn ), exprType
+               else begin
+                  let isArrayObj = match retypedObj.cpptype with
+                     | TCppDynamicArray
+                     | TCppObjectArray _
+                     | TCppScalarArray _
+                         -> true
+                     | _ -> false in
+                 (* Special array return values *)
+                 let funcReturn =
+                    if isArrayObj then match member.cf_name with
+                       | "map" -> TCppDynamicArray
+                       | "splice"
+                       | "slice"
+                       | "concat"
+                       | "copy"
+                       |  "filter" -> retypedObj.cpptype
+                       | _ -> funcReturn
+                    else match retypedObj.cpptype, funcReturn with
+                       | TCppPointer(_,t), TCppDynamic
+                       | TCppRawPointer(_,t), TCppDynamic (* the 'type parameter' will show up as Dynamic *)
+                          -> t
+                       | _ -> funcReturn
+                 in
+                 (match retypedObj.cppexpr with
+                 | CppThis ThisReal ->
+                    CppFunction( FuncThis(member), funcReturn ), exprType
+                 | CppSuper this ->
+                    CppFunction( FuncSuper(this,member), funcReturn ), exprType
+                 | _ ->
+                    CppFunction( FuncInstance(retypedObj,is_objc,member), funcReturn ), exprType
+                 )
+               end
+
+            | FStatic ( _, ({cf_name="::cpp::Function_obj::fromStaticFunction"} as member) ) ->
+               let funcReturn = cpp_member_return_type ctx member in
+               let exprType = cpp_type_of member.cf_type in
+               CppFunction( FuncFromStaticFunction, funcReturn ), exprType
+
+            | FStatic (clazz,member) ->
+               let funcReturn = cpp_member_return_type ctx member in
+               let exprType = cpp_type_of member.cf_type in
+               let objC = is_objc_class clazz in
+               if is_var_field member then
+                  CppVar(VarStatic(clazz, member)), exprType
+               else
+                  CppFunction( FuncStatic(clazz,objC,member), funcReturn ), exprType
+            | FClosure (None,field)
+            | FAnon field ->
+               let obj = retype TCppDynamic obj in
+               let fieldName = field.cf_name in
+               if obj.cpptype=TCppGlobal then
+                  CppGlobal(fieldName), cpp_type_of expr.etype
+               else if obj.cpptype=TCppNull then
+                  CppNullAccess, TCppDynamic
+               else if is_internal_member fieldName then begin
+                  let cppType = cpp_return_type ctx expr.etype in
+                  if obj.cpptype=TCppString then
+                     CppFunction( FuncInternal(obj,fieldName,"."), cppType), cppType
+                  else
+                     CppFunction( FuncInternal(obj,fieldName,"->"), cppType), cppType
+               end else
+                  CppDynamicField(obj, field.cf_name), TCppVariant
+
+            | FDynamic fieldName ->
+               let obj = retype TCppDynamic obj in
+               if obj.cpptype=TCppNull then
+                  CppNullAccess, TCppDynamic
+               else if fieldName="cca" && obj.cpptype=TCppString then
+                  CppFunction( FuncInternal(obj,"cca","."), TCppScalar("Int")), TCppDynamic
+               else if fieldName="__s" && obj.cpptype=TCppString then
+                  CppVar( VarInternal(obj,".","__s")), TCppPointer("ConstPointer", TCppScalar("char"))
+               else if fieldName="__Index" then
+                  CppEnumIndex(obj), TCppScalar("Int")
+               else if is_internal_member fieldName || cpp_is_real_array obj then begin
+                  let cppType = cpp_return_type ctx expr.etype in
+                  if obj.cpptype=TCppString then
+                     CppFunction( FuncInternal(obj,fieldName,"."), cppType), cppType
+                  else
+                     CppFunction( FuncInternal(obj,fieldName,"->"), cppType), cppType
+               end else if (obj.cpptype=TCppGlobal) then
+                  CppGlobal(fieldName), cpp_type_of expr.etype
+               else if (obj.cpptype=TCppClass) then begin
+                  match obj.cppexpr with
+                  | CppClassOf(path,_) ->
+                     CppGlobal ( (join_class_path_remap path "::" ) ^ "_obj::" ^ fieldName ), cpp_type_of expr.etype
+                  | _ ->
+                     CppVar( VarInternal(obj,"->",fieldName)), cpp_type_of expr.etype
+               end else
+                  CppDynamicField(obj, fieldName), TCppVariant
+
+            | FEnum (enum, enum_field) ->
+                  CppEnumField(enum, enum_field), TCppEnum(enum)
+            )
+
+         | TCall( {eexpr = TLocal { v_name = "__cpp__" }}, arg_list ) ->
+            let  cppExpr = match arg_list with
+            | [{ eexpr = TConst (TString code) }] -> CppCode(code, [])
+            | ({ eexpr = TConst (TString code) }) :: remaining ->
+                  let retypedArgs = List.map (fun arg -> retype (TCppCode(cpp_type_of arg.etype)) arg) remaining in
+                  CppCode(code, retypedArgs)
+            | _ -> error "__cpp__'s first argument must be a string" expr.epos;
+            in
+            cppExpr, TCppCode(cpp_type_of expr.etype)
+
+         | TCall( func, args ) ->
+            let retypedFunc = retype TCppDynamic func in
+            if retypedFunc.cpptype=TCppNull then
+               CppNullAccess, TCppDynamic
+            else begin
+               let cppType = cpp_type_of expr.etype in
+               (*
+               let retypedArgs = List.map2 (fun arg (var,opt) ->
+                   retype (cpp_fun_arg_type_of ctx var opt) arg
+                   ) args, func.tf_args in
+               *)
+               let retypedArgs = List.map (retype TCppDynamic ) args in
+               match retypedFunc.cppexpr with
+               |  CppFunction(FuncFromStaticFunction ,returnType) ->
+                   ( match retypedArgs with
+                   | [ {cppexpr=CppFunction( FuncStatic(clazz,false,member), funcReturn)} ] ->
+                      CppFunctionAddress(clazz,member), funcReturn
+                   | _ -> error "cpp.Function.fromStaticFunction must be called on static function" expr.epos;
+                   )
+               |  CppEnumIndex(_) ->
+                     (* Not actually a TCall...*)
+                     retypedFunc.cppexpr, retypedFunc.cpptype
+               |  CppFunction( FuncInstance(obj, false, member), args ) when return_type=TCppVoid && is_array_splice_call obj member ->
+                     CppCall( FuncInstance(obj, false, {member with cf_name="removeRange"}), retypedArgs), TCppVoid
+               |  CppFunction( FuncStatic(obj, false, member), returnType ) when cpp_is_templated_call ctx member ->
+                     (match retypedArgs with
+                     | {cppexpr = CppClassOf(path,native) }::rest ->
+                         CppCall( FuncTemplate(obj,member,path,native), rest), returnType
+                     | _ -> error "First parameter of template function must be a Class" retypedFunc.cpppos
+                     )
+
+               | CppFunction( FuncInstance(obj,false,member) as func, returnType ) when cpp_can_static_cast returnType cppType ->
+                  let call = mk_cppexpr (CppCall(func,retypedArgs)) returnType in
+                  CppCastStatic(call, cppType), cppType
+                  (*
+                  let error_printer file line = Printf.sprintf "%s:%d:" file line in
+                  let epos = Lexer.get_error_pos error_printer expr.epos in
+                  print_endline ( "fixed override " ^ member.cf_name ^ " @ " ^  epos ^ " " ^ (tcpp_to_string returnType) ^ "->" ^ (ctx_type_string ctx expr.etype) );
+                  CppCall(func,retypedArgs), returnType
+                  *)
+
+               (* Other functions ... *)
+               |  CppFunction(func,returnType) ->
+                     CppCall(func,retypedArgs), returnType
+
+               |  CppEnumField(enum, field) ->
+                     CppCall( FuncEnumConstruct(enum,field),retypedArgs), cppType
+               |  CppSuper(_) ->
+                     CppCall( FuncSuperConstruct ,retypedArgs), TCppVoid
+               |  CppDynamicField(expr,name) ->
+                     (* Special function calls *)
+                     (match expr.cpptype, name with
+                     | TCppGlobal, _  ->
+                        CppCall( FuncGlobal(name),retypedArgs), cppType
+
+                     | TCppString, _  ->
+                        CppCall( FuncInternal(expr,name,"."),retypedArgs), cppType
+
+                     | _, "__Tag"  ->
+                        CppCall( FuncInternal(expr,"_hx_getTag","->"),retypedArgs), cppType
+
+                     | _, name when is_internal_member name ->
+                        CppCall( FuncInternal(expr,name,"->"),retypedArgs), cppType
+
+                     | _ -> (* not special *)
+                        CppCall( FuncDynamic(retypedFunc), retypedArgs), TCppDynamic
+                     )
+               |  CppGlobal(_) ->
+                     CppCall( FuncDynamic(retypedFunc) ,retypedArgs), cppType
+               | _ ->
+                  CppCall( FuncDynamic(retypedFunc), retypedArgs), TCppDynamic
+               end
+
+         | TNew (clazz,params,args) ->
+            (* New DynamicArray ? *)
+            let retypedArgs = List.map (retype TCppDynamic ) args in
+            let created_type = cpp_type_of expr.etype in
+            CppCall( FuncNew(created_type), retypedArgs), created_type
+
+         | TFunction func ->
+            let old_this_real = !this_real in
+            this_real := ThisFake;
+            (* TODO - this_dynamic ? *)
+            let old_undeclared = Hashtbl.copy !undeclared in
+            let old_declarations = Hashtbl.copy !declarations in
+            let old_uses_this = !uses_this in
+            uses_this := None;
+            undeclared := Hashtbl.create 0;
+            declarations := Hashtbl.create 0;
+            List.iter ( fun (tvar,_) ->
+               Hashtbl.add !declarations tvar.v_name () ) func.tf_args;
+            let cppExpr = retype TCppVoid (mk_block func.tf_expr) in
+            let result = { close_expr=cppExpr;
+                           close_id= !closureId;
+                           close_undeclared= !undeclared;
+                           close_type= cpp_type_of func.tf_type;
+                           close_args= func.tf_args;
+                           close_this= !uses_this;
+                         } in
+            incr closureId;
+            declarations := old_declarations;
+            undeclared := old_undeclared;
+            Hashtbl.iter (fun name tvar ->
+               if not (Hashtbl.mem !declarations name) then
+                  Hashtbl.replace !undeclared name tvar;
+            ) result.close_undeclared;
+            this_real := old_this_real;
+            uses_this := if !uses_this != None then Some old_this_real else old_uses_this;
+            rev_closures := result:: !rev_closures;
+            CppClosure(result), TCppDynamic
+
+         | TArray (e1,e2) ->
+            let retypedObj = retype TCppDynamic e1 in
+            let retypedIdx = retype (TCppScalar("Int")) e2 in
+            (match retypedObj.cpptype with
+              | TCppScalarArray scalar ->
+                 CppArray( ArrayTyped(retypedObj,retypedIdx) ), scalar
+              | TCppPointer (_,elem) ->
+                 CppArray( ArrayPointer(retypedObj, retypedIdx) ), elem
+              | TCppRawPointer (_,elem) ->
+                 CppArray( ArrayRawPointer(retypedObj, retypedIdx) ), elem
+              | TCppObjectArray TCppDynamic ->
+                 CppArray( ArrayObject(retypedObj,retypedIdx,TCppDynamic) ), TCppDynamic
+              | TCppObjectArray elem ->
+                 CppArray( ArrayObject(retypedObj,retypedIdx,elem) ), elem
+              | TCppInst({cl_array_access = Some _ } as klass) ->
+                 CppArray( ArrayImplements(klass, retypedObj,retypedIdx) ), cpp_type_of expr.etype
+              | TCppDynamicArray ->
+                 CppArray( ArrayVirtual(retypedObj, retypedIdx) ), TCppDynamic
+              | _ ->
+                 CppArray( ArrayDynamic(retypedObj, retypedIdx) ), TCppDynamic
+            )
+
+         | TTypeExpr module_type ->
+            let path = t_path module_type in
+            CppClassOf(path, is_native_gen_module module_type), TCppClass
+
+         | TBinop (op,e1,e2) ->
+            let objC1 = (is_objc_type e1.etype) in
+            let objC2 = (is_objc_type e2.etype) in
+            let compareObjC = (objC1<>objC2) && (op=OpEq || op=OpNotEq) in
+            let e1 = retype (if compareObjC && objC1 then TCppDynamic else cpp_type_of e1.etype) e1 in
+            let e2 = retype (if compareObjC && objC2 then TCppDynamic else cpp_type_of e2.etype) e2 in
+            let complex = (is_complex_compare e1.cpptype) || (is_complex_compare e2.cpptype) in
+            let e1_null = e1.cpptype=TCppNull in
+            let e2_null = e2.cpptype=TCppNull in
+            let reference = match op with
+               | OpAssign ->
+                  CppSet(to_lvalue e1, e2)
+               | OpAssignOp op ->
+                  CppModify(op, to_lvalue e1, e2)
+               | OpEq when    e1_null && e2_null-> CppBool(true)
+               | OpGte when   e1_null && e2_null-> CppBool(true)
+               | OpLte when   e1_null && e2_null-> CppBool(true)
+               | OpNotEq when e1_null && e2_null-> CppBool(false)
+               | _ when   e1_null && e2_null-> CppBool(false)
+
+               | OpEq when    e1_null -> CppNullCompare("IsNull", e2)
+               | OpGte when   e1_null -> CppNullCompare("IsNull", e2)
+               | OpLte when   e1_null -> CppNullCompare("IsNull", e2)
+               | OpNotEq when e1_null -> CppNullCompare("IsNotNull", e2)
+
+               | OpEq when    e2_null -> CppNullCompare("IsNull", e1)
+               | OpGte when   e2_null -> CppNullCompare("IsNull", e1)
+               | OpLte when   e2_null -> CppNullCompare("IsNull", e1)
+               | OpNotEq when e2_null -> CppNullCompare("IsNotNull", e1)
+
+               | OpEq when complex -> CppCompare("IsEq", e1, e2)
+               | OpNotEq when complex -> CppCompare("IsNotEq", e1, e2)
+               | OpGte when complex -> CppCompare("IsGreaterEq", e1, e2)
+               | OpLte when complex -> CppCompare("IsLessEq", e1, e2)
+               | OpGt when complex -> CppCompare("IsGreater", e1, e2)
+               | OpLt  when complex -> CppCompare("IsLess", e1, e2)
+
+               | _ -> CppBinop(op,e1,e2)
+            in
+            reference, cpp_type_of expr.etype
+
+         | TUnop (op,pre,e1) ->
+            let targetType = match op with
+            | Not -> TCppScalar("Bool")
+            | NegBits -> TCppScalar("Int")
+            | _ -> cpp_type_of e1.etype
+            in
+
+            let e1 = retype targetType e1 in
+            let reference = match op with
+               | Increment -> CppCrement( CppIncrement, pre, to_lvalue e1)
+               | Decrement -> CppCrement( CppDecrement, pre, to_lvalue e1)
+               | Neg -> CppUnop(CppNeg,e1)
+               | Not -> CppUnop(CppNot,e1)
+               | NegBits -> CppUnop(CppNegBits,e1)
+            in reference, cpp_type_of expr.etype
+
+         | TFor (v,init,block) ->
+            let old_declarations = Hashtbl.copy !declarations in
+            Hashtbl.add !declarations v.v_name ();
+            let init = retype (cpp_type_of v.v_type) init in
+            let block = retype TCppVoid (mk_block block) in
+            declarations := old_declarations;
+            CppFor(v,init,block), TCppVoid
+
+         | TWhile (e1,e2,flag) ->
+            let condition = retype (TCppScalar("Bool")) e1 in
+            let close = begin_loop() in
+            let block = retype TCppVoid (mk_block e2) in
+            CppWhile(condition, block, flag, close()), TCppVoid
+
+         | TArrayDecl el ->
+            let retypedEls = List.map (retype TCppDynamic) el in
+            CppArrayDecl(retypedEls), cpp_type_of expr.etype
+
+         | TBlock expr_list ->
+            let inject = !injection in
+            injection := false;
+            if (return_type<>TCppVoid) then
+               print_endline ("Value from a block not handled " ^
+               (expr.epos.pfile ) ^ " " ^  (string_of_int (Lexer.get_error_line expr.epos) ));
+
+            let old_declarations = Hashtbl.copy !declarations in
+            let old_closures = !rev_closures in
+            rev_closures := [];
+            let local_closures = ref [] in
+            let remaining = ref (List.length expr_list) in
+            let cppExprs = List.map ( fun expr ->
+                  let targetType = if inject && (!remaining=1) then cpp_type_of expr.etype else TCppVoid in
+                  decr remaining;
+                  let result = retype targetType expr in
+                  local_closures := !rev_closures @ !local_closures;
+                  rev_closures := [];
+                  result
+               ) expr_list in
+            declarations := old_declarations;
+            rev_closures := old_closures;
+
+            CppBlock(cppExprs, List.rev !local_closures ), TCppVoid
+
+         | TObjectDecl (
+            ("fileName" , { eexpr = (TConst (TString file)) }) ::
+               ("lineNumber" , { eexpr = (TConst (TInt line)) }) ::
+                  ("className" , { eexpr = (TConst (TString class_name)) }) ::
+                     ("methodName", { eexpr = (TConst (TString meth)) }) :: [] ) ->
+              CppPosition(file,line,class_name,meth), TCppDynamic
+
+         | TObjectDecl el ->
+            let retypedEls = List.map ( fun(v,e) -> v, retype TCppDynamic e) el in
+            (match return_type with
+            | TCppVoid -> CppObjectDecl(retypedEls,false), TCppVoid
+            | _ -> CppObjectDecl(retypedEls,false), TCppDynamic
+            )
+
+         | TVar (v,eo) ->
+            let varType = cpp_type_of v.v_type in
+            let init = match eo with None -> None | Some e -> Some (retype varType e) in
+            Hashtbl.add !declarations v.v_name ();
+            CppVarDecl(v, init), varType
+
+         | TIf (ec,e1,e2) ->
+            let ec = retype (TCppScalar("Bool")) ec in
+            let blockify =  if return_type!=TCppVoid then fun e -> e else mk_block in
+            let e1 = retype return_type (blockify e1) in
+            let e2 = match e2 with None->None | Some e -> Some (retype return_type (blockify e))
+            in
+            CppIf(ec, e1, e2), if return_type=TCppVoid then TCppVoid else cpp_type_of expr.etype
+
+          (* Switch internal return - wrap whole thing in block  *)
+         | TSwitch (condition,cases,def) ->
+            if return_type<>TCppVoid then
+               error "Value from a switch not handled" expr.epos;
+
+            let conditionType = cpp_type_of condition.etype in
+            let condition = retype conditionType condition in
+            let cppDef = match def with None -> None | Some e -> Some (retype TCppVoid (mk_block e)) in
+            (try
+               (match conditionType with TCppScalar("Int") | TCppScalar("Bool") -> () | _ -> raise Not_found );
+               let cases = List.map (fun (el,e2) ->
+                  (List.map const_int_of el), (retype TCppVoid (mk_block e2)) ) cases in
+               CppIntSwitch(condition, cases, cppDef), TCppVoid
+            with Not_found ->
+               let label = alloc_file_id () in
+               (* do something better maybe ... *)
+               let cases = List.map (fun (el,e2) ->
+                  let cppBlock = retype TCppVoid (mk_block e2) in
+                  let gotoExpr = { cppexpr = CppGoto(label); cpptype = TCppVoid; cpppos = e2.epos } in
+                  let cppBlock = cpp_append_block cppBlock  gotoExpr in
+                  (List.map (retype conditionType) el), cppBlock ) cases in
+               CppSwitch(condition, conditionType, cases, cppDef, label), TCppVoid
+            )
+
+         | TTry (try_block,catches) ->
+            (* TTry internal return - wrap whole thing in block ? *)
+            if return_type<>TCppVoid then
+               error "Value from a try-block not handled" expr.epos;
+            let cppBlock = retype TCppVoid try_block in
+            let cppCatches = List.map (fun (tvar,catch_block) ->
+                let old_declarations = Hashtbl.copy !declarations in
+                Hashtbl.add !declarations tvar.v_name ();
+                let cppCatchBlock = retype TCppVoid catch_block in
+                declarations := old_declarations;
+                tvar, cppCatchBlock;
+            ) catches in
+            CppTry(cppBlock, cppCatches), TCppVoid
+
+         | TReturn eo ->
+            CppReturn(match eo with None -> None | Some e -> Some (retype (cpp_type_of e.etype) e)), TCppVoid
+         | TCast (base,None) -> (* Use auto-cast rules *)
+            let return_type = cpp_type_of expr.etype in
+            let baseCpp = retype (return_type) base in
+            let baseStr = (tcpp_to_string baseCpp.cpptype) in
+            let returnStr = (tcpp_to_string return_type) in
+            if baseStr=returnStr then
+               baseCpp.cppexpr, baseCpp.cpptype (* nothing to do *)
+            else (match return_type with
+               | TCppObjC(k) -> CppCastObjC(baseCpp,k), return_type
+               | TCppInst(k) -> CppCast(baseCpp,return_type), return_type
+               | TCppCode(t) when baseStr <> (tcpp_to_string t)  ->
+                     CppCast(baseCpp, t),  t
+               | TCppNativePointer(klass) -> CppCastNative(baseCpp), return_type
+               | _ -> baseCpp.cppexpr, baseCpp.cpptype (* use autocasting rules *)
+            )
+
+         | TCast (base,Some t) ->
+            let baseCpp = retype (cpp_type_of base.etype) base in
+            let baseStr = (tcpp_to_string baseCpp.cpptype) in
+            let returnStr = (tcpp_to_string return_type) in
+
+            if baseStr=returnStr then
+               baseCpp.cppexpr, baseCpp.cpptype (* nothing to do *)
+            else (match return_type with
+            | TCppNativePointer(klass) -> CppCastNative(baseCpp), return_type
+            | TCppVoid -> baseCpp.cppexpr, TCppVoid
+            | TCppInterface _
+            | TCppDynamic ->
+                  baseCpp.cppexpr, baseCpp.cpptype
+            | _ ->
+               CppTCast(baseCpp, return_type), return_type
+            )
+      in
+      let cppExpr = mk_cppexpr retypedExpr retypedType in
+
+      (* Auto cast rules... *)
+      if return_type=TCppVoid then
+         mk_cppexpr retypedExpr TCppVoid
+      else if (cppExpr.cpptype=TCppVariant || cppExpr.cpptype=TCppDynamic) then begin
+         match return_type with
+         | TCppObjectArray _
+         | TCppScalarArray _
+         | TCppNativePointer _
+         | TCppDynamicArray
+         | TCppInst _
+             -> mk_cppexpr (CppCast(cppExpr,return_type)) return_type
+
+         | TCppObjC k
+             -> mk_cppexpr (CppCastObjC(cppExpr,k)) return_type
+
+         | TCppScalar(scalar)
+             -> mk_cppexpr (CppCastScalar(cppExpr,scalar)) return_type
+
+         | TCppString
+             -> mk_cppexpr (CppCastScalar(cppExpr,"::String")) return_type
+
+         | TCppInterface _ when cppExpr.cpptype=TCppVariant
+              -> mk_cppexpr (CppCastVariant(cppExpr)) return_type
+
+         | TCppDynamic when cppExpr.cpptype=TCppVariant
+              -> mk_cppexpr (CppCastVariant(cppExpr)) return_type
+
+         | _ -> cppExpr
+      end else match cppExpr.cpptype, return_type with
+         | TCppObjC(k), TCppDynamic
+              -> mk_cppexpr (CppCast(cppExpr,TCppDynamic)) return_type
+         | TCppReference(TCppDynamic), TCppReference(_) -> cppExpr
+         | TCppReference(TCppDynamic),  t ->
+             mk_cppexpr retypedExpr (TCppReference(t))
+         | TCppStar(TCppDynamic), TCppStar(_) -> cppExpr
+         | TCppStar(TCppDynamic),  t ->
+             mk_cppexpr retypedExpr (TCppStar(t))
+         | _ -> cppExpr
+   in
+   retype request_type expression_tree
+;;
+
+type tinject = {
+   inj_prologue : unit -> unit;
+   inj_setvar : string;
+   inj_tail : string;
+}
+
+let mk_injection prologue set_var tail =
+   Some { inj_prologue=prologue; inj_setvar=set_var; inj_tail=tail }
+;;
+
+
+let cpp_arg_type_name ctx tvar default_val prefix =
+   let remap_name = (cpp_var_name_of tvar) in
+   let type_str = (cpp_var_type_of ctx tvar) in
+   match default_val with
+   | Some TNull  -> (tcpp_to_string (cpp_type_of_null ctx tvar.v_type)),remap_name
+   | Some constant -> (tcpp_to_string (cpp_type_of_null ctx tvar.v_type)),prefix ^ remap_name
+   | _ -> type_str,remap_name
+;;
+
+
+let cpp_gen_default_values ctx args prefix =
+   List.iter ( fun (tvar,o) ->
+      match o with
+      | Some TNull -> ()
+      | Some const ->
+         let name = cpp_var_name_of tvar in
+         ctx.ctx_output ((cpp_var_type_of ctx tvar) ^ " " ^ name ^ " = " ^ prefix ^ name ^ ".Default(" ^
+            (default_value_string const) ^ ");\n")
+      | _ -> ()
+   ) args;
+;;
+
+let is_constant_zero expr =
+  match expr.cppexpr with
+  | CppFloat x when (float_of_string x) = 0.0 -> true
+  | CppInt i when i = Int32.of_int 0 -> true
+  | _ -> false
+;;
+
+let cpp_is_const_scalar_array arrayType expressions =
+   List.length expressions>0 && (match arrayType with
+   | TCppScalarArray _ ->
+        List.for_all (fun expr -> match expr.cppexpr with
+            | CppInt _ | CppFloat _ | CppString _ | CppBool _ -> true
+            | _ -> false
+         ) expressions
+   | _ -> false)
+;;
+
+
+
+
+(* Generate prototype text, including allowing default values to be null *)
+let cpp_arg_string ctx tvar default_val prefix =
+   let t,n = cpp_arg_type_name ctx tvar default_val prefix in
+   t ^ " " ^ n
+;;
+
+let cpp_arg_list ctx args prefix =
+    String.concat "," (List.map (fun (v,o) -> (cpp_arg_string ctx v o prefix) ) args)
+;;
+
+
+let ctx_default_values ctx args prefix =
+    cpp_gen_default_values ctx args prefix
+;;
+
+
+let gen_type ctx haxe_type =
+   ctx.ctx_output (ctx_type_string ctx haxe_type)
+;;
+
+
+let gen_cpp_ast_expression_tree ctx class_name func_name function_args injection tree =
+   let writer = ctx.ctx_writer in
+   let out = ctx.ctx_output in
+   let lastLine = ref (-1) in
+   let tempId = ref 0 in
+
+   let spacer = if (ctx.ctx_debug_level>0) then "            \t" else "" in
+   let output_i value = out spacer; writer#write_i value in
+
+   let output_p expr value =
+       if (ctx.ctx_debug_level>0) then begin
+          let line = Lexer.get_error_line expr.cpppos in
+          let lineName = Printf.sprintf "%4d" line in
+          let macro = if (line != !lastLine) then "HXLINE" else "HXDLIN" in
+          out (macro ^ "(" ^ lineName ^ ")\t" );
+          lastLine := line;
+       end;
+       writer#write_i value
+   in
+
+   let forInjection = match injection with Some inject -> inject.inj_setvar<>"" | _ -> false in
+   let cppTree =  retype_expression ctx TCppVoid function_args tree forInjection in
+   let label_name i = Printf.sprintf "_hx_goto_%i" i in
+
+   let rec gen_with_injection injection expr =
+      (match expr.cppexpr with
+      | CppBlock(exprs,closures) ->
+         writer#begin_block;
+         List.iter gen_closure closures;
+         (match injection with Some inject -> inject.inj_prologue () | _ -> () );
+         let remaining = ref (List.length exprs) in
+         lastLine := -1;
+         List.iter (fun e ->
+            output_p e "";
+            if (!remaining=1) then
+               (match injection with Some inject -> out inject.inj_setvar | _ -> () );
+            gen e;
+            decr remaining;
+            writer#terminate_line;
+         ) exprs;
+         (match injection with Some inject -> out inject.inj_tail | _ -> () );
+         out spacer;
+         writer#end_block;
+
+      | CppInt i -> out (Printf.sprintf "(int)%ld" i)
+      | CppFloat float_as_string -> out ("((Float)" ^ float_as_string ^")")
+      | CppString s -> out (strq s)
+      | CppBool b -> out (if b then "true" else "false")
+      | CppNull -> out "null()"
+      | CppNil -> out "nil"
+
+      | CppThis ThisReal -> out "hx::ObjectPtr<OBJ_>(this)"
+      | CppThis _ -> out "__this"
+
+      | CppSuper thiscall ->
+            out ("hx::ObjectPtr<super>(" ^ (if thiscall=ThisReal then "this" else "__this.mPtr") ^ ")")
+
+      | CppBreak -> out "break"
+      | CppContinue -> out "continue"
+      | CppGoto label -> out ("goto " ^ (label_name label));
+
+      | CppVarDecl(var,init) ->
+         let name =  cpp_var_name_of var in
+         if cpp_no_debug_synbol ctx var then
+            out ( (cpp_var_type_of ctx var) ^ " " ^ name )
+         else begin
+            let dbgName =  cpp_var_debug_name_of var in
+            let macro = if init=None then "HX_VAR" else "HX_VARI" in
+            if name<>dbgName then
+               out ( macro ^ "_NAME( " ^ (cpp_var_type_of ctx var) ^ "," ^ name ^ ",\"" ^ dbgName ^ "\")" )
+            else
+               out ( macro ^ "( " ^ (cpp_var_type_of ctx var) ^ "," ^ name ^ ")");
+         end;
+         (match init with Some init -> out " = "; gen init | _ -> () );
+
+      | CppEnumIndex(obj) ->
+         gen obj;
+         (*if cpp_is_dynamic_type obj.cpptype then*)
+            out ".StaticCast< ::hx::EnumBase >()";
+         out "->getIndex()"
+
+      | CppNullAccess -> out ("hx::Throw(" ^ strq "Null access" ^ ")")
+      | CppFunction(func,_) ->
+         (match func with
+         | FuncThis(field) ->
+              out ("this->" ^ (cpp_member_name_of field) ^ "_dyn()");
+         | FuncInstance(expr,objC,field) ->
+              gen expr; out ((if expr.cpptype=TCppString then "." else "->") ^ (cpp_member_name_of field) ^ "_dyn()");
+         | FuncInterface(expr,_,field) ->
+              gen expr;
+              out ("->__Field(" ^ strq field.cf_name ^ ", hx::paccDynamic)")
+         | FuncStatic(clazz,_,field) ->
+              let rename = get_meta_string field.cf_meta Meta.Native in
+              if rename<>"" then
+                 out rename
+              else
+                 (out (cpp_class_name clazz); out ("::" ^ (cpp_member_name_of field) ^ "_dyn()"))
+         | FuncDynamic(expr) ->
+              gen expr;
+         | FuncGlobal(name) ->
+              out ("::" ^ name);
+         | FuncInternal(expr,name,_) ->
+              gen expr; out ("->__Field(" ^ (strq name) ^ ",hx::paccDynamic)")
+         | FuncSuper _ | FuncSuperConstruct -> error "Can't create super closure" expr.cpppos
+         | FuncNew _ -> error "Can't create new closure" expr.cpppos
+         | FuncEnumConstruct _ -> error "Enum constructor outside of CppCall" expr.cpppos
+         | FuncFromStaticFunction -> error "Can't create cpp.Function.fromStaticFunction closure" expr.cpppos
+         | FuncTemplate _ -> error "Can't create template function closure" expr.cpppos
+         );
+      | CppCall( FuncInterface(expr,clazz,field), args) when not (is_native_gen_class clazz)->
+         out ( cpp_class_name clazz ^ "::" ^ cpp_member_name_of field ^ "(");
+         gen expr;
+         List.iter (fun arg -> out ","; gen arg ) args;
+         out ")";
+
+      | CppCall(FuncStatic(_,true,field) as func, arg_list)
+      | CppCall(FuncInstance(_,true,field) as func, arg_list) ->
+         out "[ ";
+         (match func with
+         | FuncStatic(cl,_,_) -> out (join_class_path_remap cl.cl_path "::")
+         | FuncInstance(expr,_,_) -> gen expr
+         | _ ->() );
+
+         let names = ExtString.String.nsplit field.cf_name ":" in
+         let field_name, arg_names = match names with
+           | name :: args -> name, args
+           | _ -> assert false (* per nsplit specs, this should never happen *)
+         in
+         out (" " ^ field_name);
+         (try match arg_list, arg_names with
+         | [], _ -> ()
+         | [single_arg], _ -> out ": "; gen single_arg
+         | first_arg :: args, arg_names ->
+             out ": ";
+             gen first_arg;
+             List.iter2 (fun arg arg_name ->
+               out (" " ^ arg_name ^ ": ");
+               gen arg) args arg_names
+         with | Invalid_argument _ -> (* not all arguments names are known *)
+           error (
+             "The function called here with name " ^ (String.concat ":" names) ^
+             " does not contain the right amount of arguments' names as required" ^
+             " by the objective-c calling / naming convention:" ^
+             " expected " ^ (string_of_int (List.length arg_list)) ^
+             " and found " ^ (string_of_int (List.length arg_names)))
+           expr.cpppos);
+         out " ]"
+
+
+      | CppCall(func, args) ->
+         let closeCall = ref "" in
+         let argsRef = ref args in
+         (match func with
+         | FuncThis(field) ->
+              out ("this->" ^ (cpp_member_name_of field) );
+         | FuncInstance(expr,_,field)
+         | FuncInterface(expr,_,field) ->
+              let operator = if expr.cpptype = TCppString then "." else "->" in
+              gen expr; out (operator ^ (cpp_member_name_of field) );
+         | FuncStatic(clazz,false,field) when cpp_is_static_extension ctx field ->
+            (match args with
+            | fst :: remaining ->
+               argsRef := remaining;
+               gen fst; out ("->" ^ (cpp_member_name_of field) );
+            | _ -> error "Native static extensions must have at least 1 argument" expr.cpppos
+            );
+
+         | FuncStatic(clazz,_,field) ->
+              let rename = get_meta_string field.cf_meta Meta.Native in
+              if rename<>"" then begin
+                 (* This is the case if you use @:native('new foo').  c++ wil group the space undesirably *)
+                 if String.contains rename ' ' then begin
+                    out "(";
+                    closeCall := ")"
+                 end;
+                 out rename
+              end else
+                 (out (cpp_class_name clazz); out ("::" ^ (cpp_member_name_of field) ))
+
+         | FuncTemplate(clazz,field,tpath,native) ->
+              let rename = get_meta_string field.cf_meta Meta.Native in
+              if rename<>"" then begin
+                 (* This is the case if you use @:native('new foo').  c++ wil group the space undesirably *)
+                 if String.contains rename ' ' then begin
+                    out "(";
+                    closeCall := ")"
+                 end;
+                 out rename
+              end else
+                 (out (cpp_class_name clazz); out ("::" ^ (cpp_member_name_of field) ));
+              out ("< " ^ (cpp_template_param tpath native) ^ "  >")
+
+         | FuncFromStaticFunction ->
+              error "Unexpected FuncFromStaticFunction" expr.cpppos
+         | FuncEnumConstruct(enum,field) ->
+            out ((string_of_path enum.e_path) ^ "::" ^ (cpp_enum_name_of field));
+
+         | FuncSuperConstruct -> out "super::__construct"
+
+         | FuncSuper(this,field) ->
+              out ( (if this==ThisReal then "this->" else "__->") ^ "super::" ^ (cpp_member_name_of field) )
+
+         | FuncNew(newType) ->
+            let objName = match newType with
+            | TCppString -> "::String"
+            | TCppDynamicArray -> "::cpp::VirtualArray_obj::__new"
+            | TCppObjectArray _ -> "::Array_obj< ::Dynamic>::__new"
+            | TCppScalarArray(value) -> "::Array_obj< " ^ (tcpp_to_string value) ^ " >::__new"
+            | TCppObjC klass ->  (cpp_class_path_of klass) ^ "_obj::__new"
+            | TCppNativePointer klass -> "new " ^ (cpp_class_path_of klass);
+            | TCppInst klass -> (cpp_class_path_of klass) ^ "_obj::__new"
+            | TCppClass -> "hx::Class_obj::__new";
+            | TCppFunction _ -> tcpp_to_string newType
+            | _ -> error ("Unknown 'new' target " ^ (tcpp_to_string newType)) expr.cpppos
+            in
+            out objName
+
+         | FuncInternal(func,name,join) ->
+            gen func; out (join ^ name);
+
+         | FuncGlobal(name) ->
+              out ("::" ^ name);
+         | FuncDynamic(expr) ->
+              gen expr;
+         );
+         let sep = ref "" in
+         out "(";
+         List.iter (fun arg ->
+            out !sep; sep := ",";
+            gen arg;
+            ) !argsRef;
+         out (")" ^ !closeCall);
+
+      | CppFunctionAddress(klass, member) ->
+         let signature = ctx_function_signature ctx false member.cf_type "" in
+         let name = cpp_member_name_of member in
+         (*let void_cast = has_meta_key field.cf_meta Meta.Void in*)
+         out ("::cpp::Function< " ^ signature ^">(hx::AnyCast(");
+         out ("&::" ^(join_class_path_remap klass.cl_path "::")^ "_obj::" ^ name );
+         out " ))"
+
+      | CppGlobal(name) ->
+         out ("::" ^ name)
+
+      | CppDynamicField(obj,name) ->
+         gen obj;
+         out ("->__Field(" ^ (strq name)  ^ ",hx::paccDynamic)");
+
+      | CppArray(arrayLoc) -> (match arrayLoc with
+         | ArrayTyped(arrayObj,index) ->
+            gen arrayObj; out "->__get("; gen index; out ")"
+
+         | ArrayPointer(arrayObj,index) ->
+            gen arrayObj; out ".ptr["; gen index; out "]"
+
+         | ArrayRawPointer(arrayObj,index) ->
+            gen arrayObj; out "["; gen index; out "]"
+
+         | ArrayObject(arrayObj,index,elem) ->
+            let close = if cpp_is_dynamic_type elem then
+                  ""
+               else if elem=TCppDynamicArray then begin
+                  out (tcpp_to_string elem ^ "( "); ")"
+               end else
+                  ".StaticCast< " ^ tcpp_to_string elem ^ " >()"
+            in
+            gen arrayObj; out "->__get("; gen index; out (")" ^ close);
+
+         | ArrayVirtual(arrayObj,index) ->
+            gen arrayObj; out "->__get("; gen index; out ")";
+
+         | ArrayDynamic(arrayObj,index) ->
+            gen arrayObj; out "->__GetItem("; gen index; out ")"
+
+         | ArrayImplements(_,arrayObj,index) ->
+            gen arrayObj; out "->__get("; gen index; out ")";
+         )
+
+
+      | CppSet(lvalue,rvalue) ->
+         let close = if expr.cpptype=TCppVoid then "" else (out "("; ")" ) in
+         (match lvalue with
+         | CppVarRef varLoc ->
+              gen_val_loc varLoc; out " = "; gen rvalue;
+
+         | CppArrayRef arrayLoc -> (match arrayLoc with
+            | ArrayObject(arrayObj, index, _)
+            | ArrayTyped(arrayObj, index) ->
+               gen arrayObj; out "["; gen index; out "] = "; gen rvalue
+            | ArrayPointer(arrayObj, index) ->
+               gen arrayObj; out ".ptr["; gen index; out "] = "; gen rvalue
+            | ArrayRawPointer(arrayObj, index) ->
+               gen arrayObj; out "["; gen index; out "] = "; gen rvalue
+            | ArrayVirtual(arrayObj, index) ->
+               gen arrayObj; out "->set("; gen index; out ","; gen rvalue; out ")"
+
+            | ArrayDynamic(arrayObj, index) ->
+               gen arrayObj; out "->__SetItem("; gen index; out ","; gen rvalue; out ")"
+
+            | ArrayImplements(_,arrayObj,index) ->
+               gen arrayObj; out "->__set("; gen index; out ","; gen rvalue; out ")"
+            )
+         | CppDynamicRef(expr,name) ->
+            gen expr; out ("->__SetField(" ^ (strq name) ^ ","); gen rvalue; out ",hx::paccDynamic)"
+         | CppGlobalRef(name) -> out ("::" ^ name ^ " = ");
+         );
+         out close;
+
+      | CppCrement(incFlag,preFlag, lvalue) ->
+         let op = if incFlag==CppIncrement then "++" else "--" in
+         if (preFlag==Prefix) then out op;
+         gen_lvalue lvalue;
+         if (preFlag==Postfix) then out op
+
+      | CppModify(op,lvalue,rvalue) ->
+         out (string_of_op_eq op expr.cpppos);
+         out "("; gen_lvalue lvalue; out ","; gen rvalue; out ")"
+
+      | CppPosition(name,line,clazz,func) ->
+         out ("hx::SourceInfo(" ^ strq name ^ "," ^ string_of_int(Int32.to_int line) ^ "," ^ strq clazz ^ "," ^ strq func ^ ")")
+
+      | CppClassOf (path,native) ->
+         let path = "::" ^ (join_class_path_remap (path) "::" ) in
+         if (native) then
+            out "null()"
+         else if (path="::Array") then
+            out "hx::ArrayBase::__mClass"
+         else
+            out ("hx::ClassOf< " ^ path ^ " >()")
+
+      | CppVar(loc) ->
+         gen_val_loc loc;
+
+
+      | CppClosure closure ->
+          out (" ::Dynamic(new _hx_Closure_" ^ (string_of_int(closure.close_id)) ^ "(");
+          let separator = ref "" in
+          (match closure.close_this with
+          | Some this ->
+             out (if this=ThisReal then "this" else "__this");
+             separator := ",";
+          | _ -> () );
+
+          Hashtbl.iter (fun name value ->
+             out !separator; separator := ",";
+             out (keyword_remap name)
+         )  closure.close_undeclared;
+         out "))";
+
+      | CppObjectDecl (values,isStruct) ->
+         let length = List.length values in
+         let lengthStr = string_of_int length in
+         if (expr.cpptype!=TCppVoid) then out " ::Dynamic(";
+         if (isStruct) && length>0 && length<=5 then begin
+            out ("hx::AnonStruct" ^ lengthStr ^"_obj< " ^
+               (String.concat "," (List.map (fun (_,value) ->  tcpp_to_string value.cpptype) values) ) ^
+               " >::Create(" );
+            let sep = ref "" in
+            List.iter (fun (name,value) -> out (!sep ^ (strq name) ^ "," ); sep:=","; gen value ) values;
+            out ")";
+         end else begin
+            out ("hx::Anon_obj::Create(" ^ lengthStr ^")");
+            let sorted = List.sort (fun  (_,_,h0) (_,_,h1) -> Int32.compare h0 h1 )
+                (List.map (fun (name,value) -> name,value,(gen_hash32 0 name ) ) values) in
+            writer#push_indent;
+            ExtList.List.iteri (fun idx (name,value,_) ->
+               out ("\n" ^ spacer); writer#write_i ("->setFixed(" ^ (string_of_int idx) ^ "," ^ (strq name) ^ ","); gen value; out ")";
+            ) sorted;
+         end;
+         if (expr.cpptype!=TCppVoid) then out ")";
+         writer#pop_indent;
+
+      | CppArrayDecl(exprList) when cpp_is_const_scalar_array expr.cpptype exprList ->
+         let arrayType = match expr.cpptype with TCppScalarArray(value) -> value | _ -> assert  false in
+         let typeName = tcpp_to_string arrayType in
+         incr ctx.ctx_file_id;
+         let id = "_hx_array_data_" ^ string_of_int( !(ctx.ctx_file_id) ) in
+
+         let out_top = ctx.ctx_writer#write_h in
+         out_top ("static const " ^ typeName ^ " " ^ id ^ "[] = {\n\t");
+         List.iter (fun expr -> match expr.cppexpr with
+            | CppInt i -> out_top (Printf.sprintf "(%s)%ld," typeName i)
+            | CppFloat f -> out_top ( f ^ "," )
+            | CppString s -> out_top ( (strq s) ^ "," )
+            | CppBool b -> out_top (if b then "1," else "0,")
+            | _ -> assert false
+         ) exprList;
+         out_top ("\n};\n");
+         out ("::Array_obj< " ^ typeName ^ " >::fromData( " ^ id ^ "," ^ list_num exprList ^ ")");
+
+      | CppArrayDecl(exprList) ->
+         let count = List.length exprList in
+         let countStr = string_of_int count in
+         let arrayType,close = match expr.cpptype with
+            | TCppObjectArray _ -> "::Array_obj< ::Dynamic>",""
+            | TCppScalarArray(value) -> "::Array_obj< " ^ (tcpp_to_string value) ^ " >",""
+            | TCppDynamicArray -> "::cpp::VirtualArray_obj",""
+            | _ -> " ::Dynamic( ::cpp::VirtualArray_obj",")"
+         in
+         out (arrayType ^ "::__new(" ^ countStr ^ ")" );
+         ExtList.List.iteri ( fun idx elem -> out ("->init(" ^ (string_of_int idx) ^ ",");
+                     gen elem; out ")" ) exprList;
+         out close;
+
+
+      | CppBinop( Ast.OpUShr, left, right) ->
+         out "hx::UShr("; gen left; out ","; gen right; out ")";
+
+      | CppBinop( Ast.OpMod, left, right) ->
+         if is_constant_zero right then begin
+            out "hx::Mod("; gen left; out ",(double)( "; gen right; out " ))";
+         end else begin
+            out "hx::Mod("; gen left; out ","; gen right; out ")";
+         end
+
+      | CppBinop( Ast.OpDiv, left, right) when is_constant_zero right ->
+         out "hx::DivByZero("; gen left; out ")";
+
+      | CppBinop(op, left, right) ->
+         let op = string_of_op op expr.cpppos in
+         let castOpen, castClose = (match op with
+         | ">>" | "<<" | "&" | "|" | "^"  -> "(int)", ""
+         | "&&" | "||" -> "(bool)", ""
+         | "/" -> "(Float)", ""
+         | _ -> "","") in
+          out "(";
+          out castOpen; gen left; out castClose;
+          out (" " ^ op ^ " ");
+          out castOpen; gen right; out castClose;
+          out ")";
+      | CppCompare(op, left, right) ->
+          out ("hx::" ^ op ^ "( ");
+          gen left;
+          out (",");
+          gen right;
+          out (" )");
+      | CppNullCompare(op, left) ->
+          out ("hx::" ^ op ^ "( "); gen left; out (" )");
+
+      | CppThrow(value) ->
+         out "HX_STACK_DO_THROW("; gen value; out ")";
+
+      | CppReturn None -> out "return";
+      | CppReturn Some value -> out "return "; gen value;
+
+      | CppEnumField(enum,field) ->
+         out ((string_of_path enum.e_path) ^ "::" ^ (cpp_enum_name_of field) ^ "_dyn()" );
+
+      | CppEnumParameter(obj,field,index) ->
+         let valueType = cpp_type_of ctx (get_nth_type field index) in
+         let baseType = cpp_base_type_of valueType in
+         gen obj;
+         if cpp_is_dynamic_type obj.cpptype then
+            out ".StaticCast< ::hx::EnumBase >()";
+         out ( "->_hx_get" ^ baseType ^ "(" ^ (string_of_int index) ^ ")");
+         (match valueType with
+         | TCppObjectArray _
+         | TCppScalarArray _
+         | TCppDynamicArray
+         | TCppClass
+         | TCppEnum _
+         | TCppInst _ -> out (".StaticCast< " ^ (tcpp_to_string valueType ) ^ " >()")
+         | _ ->() 
+         )
+
+      | CppIntSwitch(condition, cases, defVal) ->
+         out "switch((int)("; gen condition; out "))";
+         writer#begin_block;
+         List.iter (fun (values,expr) ->
+            out spacer; writer#write_i "";
+            List.iter (fun value -> out ("case (int)" ^ (Printf.sprintf "%ld" value) ^ ": " ) ) values;
+            gen expr;
+            out spacer; writer#write_i "break;\n";
+         ) cases;
+         (match defVal with
+         | Some expr -> output_i "default:"; gen expr; | _ -> ()  );
+         out spacer;
+         writer#end_block;
+      | CppSwitch(condition, conditionType, cases, optional_default, label) ->
+         let tmp_name = "_hx_switch_" ^ (string_of_int !tempId) in
+         incr tempId;
+         out ( (tcpp_to_string conditionType) ^ " " ^ tmp_name ^ " = " );
+         gen condition;
+         out ";\n";
+         List.iter (fun (cases,expression) ->
+            output_i "if ( ";
+            let or_str = ref "" in
+            List.iter (fun value ->
+               out (!or_str ^ " (" ^ tmp_name ^ "=="); gen value; out ")";
+               or_str := " || ";
+               ) cases;
+            out (" )");
+            gen expression;
+            ) cases;
+         (match optional_default with | None -> ()
+         | Some default ->
+            output_i "/* default */";
+            gen default;
+         );
+         output_i ((label_name label) ^ ":")
+
+      | CppUnop(unop,value) ->
+           out (match unop with
+           | CppNot -> "!"
+           | CppNeg -> "-"
+           | CppNegBits -> "~"
+           );
+           out "(";  gen value; out ")"
+
+      | CppWhile(condition, block, while_flag, loop_id) ->
+          (match while_flag with
+          | NormalWhile ->
+              out "while("; gen condition; out (")");
+              gen block;
+          | DoWhile ->
+              out ("do ");
+              gen block;
+              out "while("; gen condition; out ")"
+          );
+          if loop_id > -1 then output_i ((label_name loop_id) ^ ":");
+
+      | CppIf (condition,block,None) ->
+          out "if ("; gen condition; out (") ");
+          gen block;
+
+      | CppIf (condition,block,Some elze) when expr.cpptype = TCppVoid ->
+          out "if ("; gen condition; out (") ");
+          gen block;
+          output_i ("else ");
+          gen elze;
+
+      | CppIf (condition,block,Some elze) ->
+          gen condition; out " ? "; gen block; out " : "; gen elze;
+
+      | CppFor (tvar, init, loop) ->
+         let varType = cpp_var_type_of ctx tvar in
+         out ("for(::cpp::FastIterator_obj< " ^  varType ^
+               " > *__it = ::cpp::CreateFastIterator< "^ varType ^ " >(");
+         gen init;
+         out (");  __it->hasNext(); )");
+         let prologue = fun () ->
+            output_i ( varType ^ " " ^ (cpp_var_name_of tvar) ^ " = __it->next();\n" );
+         in
+         gen_with_injection (mk_injection prologue "" "") loop;
+
+
+      | CppTry(block,catches) ->
+          let prologue = function () ->
+             ExtList.List.iteri (fun idx (v,_) ->
+                output_i ("HX_STACK_CATCHABLE(" ^ cpp_var_type_of ctx v  ^ ", " ^ string_of_int idx ^ ");\n")
+             ) catches
+          in
+          out ("try ");
+          gen_with_injection (mk_injection prologue "" "" ) block;
+          if (List.length catches > 0 ) then begin
+             output_i "catch( ::Dynamic _hx_e)";
+             writer#begin_block;
+
+             let seen_dynamic = ref false in
+             let else_str = ref "" in
+             List.iter (fun (v,catch) ->
+                let type_name = cpp_var_type_of ctx v in
+                if (type_name="Dynamic") then begin
+                   seen_dynamic := true;
+                   output_i !else_str;
+                end else
+                   output_i (!else_str ^ "if (_hx_e.IsClass< " ^ type_name ^ " >() )");
+                let prologue = function () ->
+                   output_i "HX_STACK_BEGIN_CATCH\n";
+                   output_i (type_name ^ " " ^ (cpp_var_name_of v) ^ " = _hx_e;\n");
+                in
+                gen_with_injection (mk_injection prologue "" "") catch;
+                else_str := "else ";
+                ) catches;
+
+             if (not !seen_dynamic) then begin
+                output_i "else {\n";
+                output_i "\tHX_STACK_DO_THROW(_hx_e);\n";
+                output_i "}\n";
+             end;
+             out spacer;
+             writer#end_block;
+          end
+
+      | CppCode(value, exprs) ->
+         Codegen.interpolate_code ctx.ctx_common (format_code value) exprs out (fun e -> gen e) expr.cpppos
+      | CppTCast(expr,cppType) ->
+         let toType = tcpp_to_string cppType in
+         if toType="Dynamic" then
+            (out " ::Dynamic("; gen expr; out ")")
+         else
+            (out ("hx::TCast< " ^ toType ^ " >::cast("); gen expr; out ")")
+
+      | CppCastStatic(expr,toType) ->
+         let close = match expr.cpptype with
+         | TCppDynamic -> ""
+         | _ -> out "Dynamic( "; ")"
+         in
+         gen expr; out (close ^ ".StaticCast< " ^ tcpp_to_string toType ^" >()")
+
+      | CppCast(expr,toType) ->
+         (match expr.cppexpr with
+         | CppCall( FuncInternal _, _) ->
+            gen expr; out (".StaticCast< " ^ tcpp_to_string toType ^" >()")
+         | _ ->
+            out ("( ("^ tcpp_to_string toType ^")("); gen expr; out (") )")
+         )
+
+      | CppCastScalar(expr,scalar) ->
+         out ("( ("^scalar^")("); gen expr; out (") )");
+
+      | CppCastVariant(expr) ->
+         out " ::Dynamic("; gen expr; out ")";
+
+      | CppCastObjC(expr,klass) ->
+         let path = join_class_path_remap klass.cl_path "::"  in
+         let toType = if klass.cl_interface then "id < " ^ path ^ ">" else path ^ " *" in
+         out ("( (" ^ toType ^ ") (id) ("); gen expr; out ") )"
+
+      | CppCastNative(expr) ->
+         out "("; gen expr; out ").mPtr"
+      );
+      if (ctx.ctx_debug_level > 1) then
+         out ("/* " ^ (s_tcpp expr.cppexpr) ^ ":" ^ tcpp_to_string expr.cpptype ^ " */")
+
+   and gen expr =
+      gen_with_injection None expr
+
+   and gen_lvalue lvalue =
+      match lvalue with
+      | CppVarRef varLoc ->
+           gen_val_loc varLoc
+      | CppArrayRef arrayLoc -> (match arrayLoc with
+         | ArrayObject(arrayObj, index, _)
+         | ArrayTyped(arrayObj, index) ->
+            gen arrayObj; out "["; gen index; out "]";
+         | ArrayPointer(arrayObj, index) ->
+            gen arrayObj; out ".ptr["; gen index; out "]";
+         | ArrayRawPointer(arrayObj, index) ->
+            gen arrayObj; out "["; gen index; out "]";
+         | ArrayVirtual(arrayObj, index)
+         | ArrayDynamic(arrayObj, index) ->
+            out "hx::IndexRef("; gen arrayObj; out ".mPtr,"; gen index; out ")";
+         | ArrayImplements(_,arrayObj,index) ->
+            out "hx::__ArrayImplRef("; gen arrayObj; out ","; gen index; out ")";
+         )
+      | CppGlobalRef(name) -> out ("::" ^ name)
+      | CppDynamicRef(expr,name) ->
+         let objPtr = match expr.cpptype with
+         |  TCppVariant -> "getObject()"
+         | _ -> ".mPtr"
+         in
+         out "hx::FieldRef(("; gen expr ; out (")" ^ objPtr ^ "," ^ strq name ^ ")")
+
+   and gen_val_loc loc =
+      match loc with
+      | VarClosure(var) -> out (cpp_var_name_of var)
+      | VarLocal(local) -> out (cpp_var_name_of local)
+      | VarStatic(clazz,member) ->
+          let rename = get_meta_string member.cf_meta Meta.Native in
+          if rename <> "" then
+             out rename
+          else
+             (out (cpp_class_name clazz ); out ("::" ^ (cpp_member_name_of member)))
+      | VarThis(member) -> out ("this->" ^ (cpp_member_name_of member))
+      | VarInstance(obj,member,_,operator) ->
+         gen obj; out (operator ^ (cpp_member_name_of member))
+      | VarInternal(obj,operator,member) ->
+         gen obj; out (operator ^ member)
+      | VarInterface(obj,member) ->
+         gen obj; out ("->" ^ (cpp_member_name_of member) ^ "_get()" )
+
+   and string_of_op_eq op pos = match op with
+      | OpAdd -> "hx::AddEq"
+      | OpMult -> "hx::MultEq"
+      | OpDiv -> "hx::DivEq"
+      | OpSub -> "hx::SubEq"
+      | OpAnd -> "hx::AndEq"
+      | OpOr  -> "hx::OrEq"
+      | OpXor  -> "hx::XorEq"
+      | OpShl  -> "hx::ShlEq"
+      | OpShr  -> "hx::ShrEq"
+      | OpUShr  -> "hx::UShrEq"
+      | OpMod  -> "hx::ModEq"
+      | _ -> error "Bad assign op" pos
+   and string_of_op op pos = match op with
+      | OpAdd -> "+"
+      | OpMult -> "*"
+      | OpDiv -> "/"
+      | OpSub -> "-"
+      | OpEq -> "=="
+      | OpNotEq -> "!="
+      | OpGt -> ">"
+      | OpGte -> ">="
+      | OpLt -> "<"
+      | OpLte -> "<="
+      | OpAnd -> "&"
+      | OpOr -> "|"
+      | OpXor -> "^"
+      | OpBoolAnd -> "&&"
+      | OpBoolOr -> "||"
+      | OpShl -> "<<"
+      | OpShr -> ">>"
+      | OpUShr -> "<<<"
+      | OpMod -> "%"
+      | OpInterval -> "..."
+      | OpArrow -> "->"
+      | OpAssign | OpAssignOp _ -> error "Unprocessed OpAssign" pos
+   and string_of_path path =
+      "::" ^ (join_class_path_remap path "::") ^ "_obj"
+
+   and gen_closure closure =
+      let argc = Hashtbl.length closure.close_undeclared in
+      let size = string_of_int argc in
+      if argc >= 62 then (* Limited by c++ macro size of 128 args *)
+         error "Too many capture variables" closure.close_expr.cpppos;
+      if argc >= 20 then
+         writer#add_big_closures;
+      let argsCount = list_num closure.close_args in
+      output_i ("HX_BEGIN_LOCAL_FUNC_S" ^ size ^ "(");
+      out (if closure.close_this != None then "hx::LocalThisFunc," else "hx::LocalFunc,");
+      out ("_hx_Closure_" ^ (string_of_int closure.close_id) );
+      Hashtbl.iter (fun name var ->
+         out ("," ^ (cpp_var_type_of ctx var) ^ "," ^ (keyword_remap name));
+      ) closure.close_undeclared;
+      out (") HXARGC(" ^ argsCount ^")\n");
+
+      let func_type = tcpp_to_string closure.close_type in
+      output_i (func_type ^ " _hx_run(" ^ (cpp_arg_list ctx closure.close_args "__o_") ^ ")");
+
+      let prologue = function () ->
+          cpp_gen_default_values ctx closure.close_args "__o_";
+          if (ctx.ctx_debug_level>0) then begin
+             hx_stack_push ctx output_i class_name func_name closure.close_expr.cpppos;
+             if (closure.close_this != None) then
+                output_i ("HX_STACK_THIS(__this.mPtr)\n");
+             List.iter (fun (v,_) -> output_i ("HX_STACK_ARG(" ^ (cpp_var_name_of v) ^ ",\"" ^ (cpp_debug_name_of v) ^"\")\n") )
+                (List.filter (cpp_debug_var_visible ctx) closure.close_args);
+          end
+      in
+
+      gen_with_injection (mk_injection prologue "" "") closure.close_expr;
+
+      let return = match closure.close_type with TCppVoid -> "(void)" | _ -> "return" in
+
+      output_i ("HX_END_LOCAL_FUNC" ^ argsCount ^ "(" ^ return ^ ")\n\n");
+   in
+
+
+   (*out "\t";*)
+
+   gen_with_injection injection cppTree;
+
+;;
+
+(* } *)
+
+
+let gen_cpp_function_body ctx clazz is_static func_name function_def head_code tail_code =
+   let output = ctx.ctx_output in
+   let dot_name = join_class_path clazz.cl_path "." in
+   let prologue = function () ->
+      let spacer = "            \t" in
+      let output_i = fun s -> output (spacer ^ s) in
+      ctx_default_values ctx function_def.tf_args "__o_";
+      if ctx.ctx_debug_level >0 then begin
+         hx_stack_push ctx output_i dot_name func_name function_def.tf_expr.epos;
+         if (not is_static)
+            then output_i ("HX_STACK_THIS(this)\n");
+         List.iter (fun (v,_) -> if not (cpp_no_debug_synbol ctx v) then
+              output_i ("HX_STACK_ARG(" ^ (cpp_var_name_of v) ^ ",\"" ^ v.v_name ^"\")\n") ) function_def.tf_args;
+      end;
+      if (head_code<>"") then
+         output_i (head_code ^ "\n");
+   in
+   let args = List.map fst function_def.tf_args in
+
+   let injection = mk_injection prologue "" tail_code in
+   gen_cpp_ast_expression_tree ctx dot_name func_name args injection (mk_block function_def.tf_expr);
+;;
+
+let gen_cpp_init ctx dot_name func_name var_name expr =
+   let output = ctx.ctx_output in
+   let prologue = function () ->
+      if ctx.ctx_debug_level >0 then begin
+      let spacer = "            \t" in
+      let output_i = fun s -> output (spacer ^ s) in
+         hx_stack_push ctx output_i dot_name func_name expr.epos;
+      end
+   in
+   let injection = mk_injection prologue var_name "" in
+   gen_cpp_ast_expression_tree ctx dot_name func_name [] injection (mk_block expr);
+;;
+
+
+(*
+let is_dynamic_haxe_method f =
+   match follow f.cf_type with
+   | TFun _ when f.cf_expr = None -> true
+   | _ ->
+      (match f.cf_expr with
+      | Some { eexpr = TFunction fd } when f.cf_set = MethodAccess true -> true
+      | Some { eexpr = TFunction fd } when f.cf_set = NormalAccess -> true
+      | _ -> false);;
+*)
+
+let is_dynamic_haxe_method f =
+      (match f.cf_expr, f.cf_kind with
+      | Some { eexpr = TFunction _ }, (Var _ | Method MethDynamic) -> true
+      | _ -> false);;
+
+
+let is_data_member field =
+   match field.cf_expr with
+   | Some { eexpr = TFunction function_def } -> is_dynamic_haxe_method field
+   | _ -> true;;
+
+
+let is_override class_def field =
+   List.exists (fun f -> f.cf_name = field) class_def.cl_overrides
+;;
+
+let all_virtual_functions clazz =
+  let rec all_virtual_functions_rev clazz =
+   (match clazz.cl_super with
+   | Some def -> all_virtual_functions_rev (fst def)
+   | _ -> [] ) @
+   (List.fold_left (fun result elem -> match follow elem.cf_type, elem.cf_kind  with
+      | _, Method MethDynamic -> result
+      | TFun (args,return_type), Method _  when not (is_override clazz elem.cf_name ) -> (elem,args,return_type) :: result
+      | _,_ -> result ) [] clazz.cl_ordered_fields)
+   in
+   List.rev (all_virtual_functions_rev clazz)
+;;
+
+
+let rec unreflective_type t =
+    match follow t with
+       | TInst (klass,_) ->  Meta.has Meta.Unreflective klass.cl_meta
+       | TFun (args,ret) -> 
+           List.fold_left (fun result (_,_,t) -> result || (unreflective_type t)) (unreflective_type ret) args;
+       | _ -> false
+;;
+
+let reflective class_def field = not (
+    (Meta.has Meta.NativeGen class_def.cl_meta) ||
+    (Meta.has Meta.Unreflective class_def.cl_meta) ||
+    (Meta.has Meta.Unreflective field.cf_meta) ||
+    unreflective_type field.cf_type
+   )
+;;
+
+
+
+
+
+let field_arg_count field =
+   match follow field.cf_type, field.cf_kind  with
+      | _, Method MethDynamic -> -1
+      | TFun (args,return_type), Method _  -> List.length args
+      | _,_ -> -1
+;;
+
+let native_field_name_remap is_static field =
+   let remap_name = keyword_remap field.cf_name in
+   if not is_static then
+      remap_name
+   else begin
+      let nativeImpl = get_meta_string field.cf_meta Meta.Native in 
+      if nativeImpl<>"" then begin
+         let r = Str.regexp "^[a-zA-Z_0-9]+$" in
+            if Str.string_match r remap_name 0 then
+               "_hx_" ^ remap_name
+            else
+               "_hx_f" ^ (gen_hash 0 remap_name)
+      end else
+         remap_name
+   end
+;;
+
+
+let gen_field ctx class_def class_name ptr_name dot_name is_static is_interface field =
+   let output = ctx.ctx_output in
+   ctx.ctx_real_this_ptr <- not is_static;
+   let remap_name = keyword_remap field.cf_name in
+   let decl = get_meta_string field.cf_meta Meta.Decl in
+   let has_decl = decl <> "" in
+   if (is_interface) then begin
+      (* Just the dynamic glue  - not even that ... *)
+      ()
+   end else (match  field.cf_expr with
+   (* Function field *)
+   | Some { eexpr = TFunction function_def } ->
+      let return_type = (ctx_type_string ctx function_def.tf_type) in
+      let nargs = string_of_int (List.length function_def.tf_args) in
+      let is_void = (cpp_type_of ctx function_def.tf_type ) = TCppVoid in
+      let ret = if is_void  then "(void)" else "return " in
+      let orig_debug = ctx.ctx_debug_level in
+
+      if (not (is_dynamic_haxe_method field)) then begin
+         (* The actual function definition *)
+         let nativeImpl = get_meta_string field.cf_meta Meta.Native in 
+         let remap_name = native_field_name_remap is_static field in
+         output (if is_void then "void" else return_type );
+         output (" " ^ class_name ^ "::" ^ remap_name ^ "(" );
+         output (ctx_arg_list ctx function_def.tf_args "__o_");
+         output ")";
+         ctx.ctx_real_this_ptr <- true;
+         let code = (get_code field.cf_meta Meta.FunctionCode) in
+         let tail_code = (get_code field.cf_meta Meta.FunctionTailCode) in
+
+         if nativeImpl<>"" && is_static then begin
+            output " {\n";
+            output ("\t" ^ ret ^ "::" ^ nativeImpl ^ "(" ^ (ctx_arg_list_name ctx function_def.tf_args "__o_") ^ ");\n");
+            output "}\n\n";
+         end else
+            gen_cpp_function_body ctx class_def is_static field.cf_name function_def code tail_code;
+
+         output "\n\n";
+         let nonVirtual = has_meta_key field.cf_meta Meta.NonVirtual in
+         let doDynamic =  (nonVirtual || not (is_override class_def field.cf_name ) ) && (reflective class_def field ) in
+         (* generate dynamic version too ... *)
+         if ( doDynamic ) then begin
+            if (is_static) then output "STATIC_";
+            output ("HX_DEFINE_DYNAMIC_FUNC" ^ nargs ^ "(" ^ class_name ^ "," ^
+                     remap_name ^ "," ^ ret ^ ")\n\n");
+         end;
+
+      end else begin
+         ctx.ctx_real_this_ptr <- false;
+         let func_name = "__default_" ^ (remap_name) in
+         output ("HX_BEGIN_DEFAULT_FUNC(" ^ func_name ^ "," ^ class_name ^ ")\n");
+         output return_type;
+         output (" _hx_run(" ^ (ctx_arg_list ctx function_def.tf_args "__o_") ^ ")");
+         gen_cpp_function_body ctx class_def is_static func_name function_def "" "";
+
+         output ("HX_END_LOCAL_FUNC" ^ nargs ^ "(" ^ ret ^ ")\n");
+         output ("HX_END_DEFAULT_FUNC\n\n");
+
+         if (is_static) then
+            output ( "::Dynamic " ^ class_name ^ "::" ^ remap_name ^ ";\n\n");
+      end;
+      ctx.ctx_debug_level <- orig_debug
+
+   (* Data field *)
+   | _ when has_decl ->
+      if is_static then begin
+         output ( class_name ^ "::" ^ remap_name ^ "_decl ");
+         output ( " " ^ class_name ^ "::" ^ remap_name ^ ";\n\n");
+      end
+   | _ ->
+      if is_static && (not (is_extern_field field)) then begin
+         gen_type ctx field.cf_type;
+         output ( " " ^ class_name ^ "::" ^ remap_name ^ ";\n\n");
+      end
+   )
+   ;;
+
+
+
+
+
+let gen_field_init ctx class_def field =
+   let dot_name = join_class_path class_def.cl_path "." in
+   let output = ctx.ctx_output in
+   let remap_name = keyword_remap field.cf_name in
+   match  field.cf_expr with
+   (* Function field *)
+   | Some { eexpr = TFunction function_def } ->
+      if (is_dynamic_haxe_method field) then begin
+         let func_name = "__default_" ^ (remap_name) in
+         output ( "\t" ^ remap_name ^ " = new " ^ func_name ^ ";\n\n" );
+      end
+
+   (* Data field *)
+   | Some expr ->
+      let var_name = ( match remap_name with
+                  | "__meta__" -> "__mClass->__meta__"
+                  | "__rtti" -> "__mClass->__rtti__"
+                  | _ -> remap_name ) in
+
+      gen_cpp_init ctx dot_name "boot" (var_name ^ " = ") expr
+   | _ ->  ()
+;;
+
+
+let cpp_interface_impl_name ctx interface =
+   "_hx_" ^ (join_class_path interface.cl_path "_" )
+;;
+
+
+let cpp_class_hash interface =
+   gen_hash 0 (join_class_path interface.cl_path "::" )
+;;
+
+
+
+let has_field_init field =
+   match field.cf_expr with
+   (* Function field *)
+   | Some { eexpr = TFunction function_def } -> is_dynamic_haxe_method field
+   (* Data field *)
+   | Some _ -> true
+   | _ -> false
+;;
+
+
+let gen_member_def ctx class_def is_static is_interface field =
+   let output = ctx.ctx_output in
+   let remap_name = keyword_remap field.cf_name in
+   let nativeGen = has_meta_key class_def.cl_meta Meta.NativeGen in
+
+   if (is_interface) then begin
+      match follow field.cf_type, field.cf_kind with
+      | _, Method MethDynamic  -> ()
+      | TFun (args,return_type), Method _  ->
+         let gen_args = ctx_tfun_arg_list ctx true in
+         if is_static || nativeGen then begin
+            output ( (if (not is_static) then "		virtual " else "		" ) ^ (ctx_type_string ctx return_type) );
+            output (" " ^ remap_name ^ "( " );
+            output (gen_args args);
+            output (if (not is_static) then ")=0;\n" else ");\n");
+            if (reflective class_def field) then begin
+               if (Common.defined ctx.ctx_common Define.DynamicInterfaceClosures) then
+                  output ("		inline ::Dynamic " ^ remap_name ^ "_dyn() { return __Field( " ^ (str field.cf_name) ^ ", hx::paccDynamic); }\n" )
+               else
+                  output ("		virtual ::Dynamic " ^ remap_name ^ "_dyn()=0;\n" );
+            end
+         end else begin
+            let argList = gen_args args in
+            let returnType = ctx_type_string ctx return_type in
+            let returnStr = if returnType = "void" then "" else "return " in
+            let commaArgList = if argList="" then argList else "," ^ argList in
+            let cast = "static_cast< ::" ^ join_class_path_remap class_def.cl_path "::" ^ "_obj *>" in
+            output ("		" ^ returnType ^ " (hx::Object :: *_hx_" ^ remap_name ^ ")(" ^ argList ^ "); \n");
+            output ("		static inline " ^ returnType ^ " " ^ remap_name ^ "( ::Dynamic _hx_" ^ commaArgList ^ ") {\n");
+            output ("			" ^ returnStr ^ "(_hx_.mPtr->*( " ^ cast ^ "(_hx_.mPtr->_hx_getInterface(" ^ (cpp_class_hash class_def) ^ ")))->_hx_" ^ remap_name ^ ")(" ^ cpp_arg_names args ^ ");\n		}\n" );
+         end
+      | _  ->  ( )
+   end else begin
+      let decl = get_meta_string field.cf_meta Meta.Decl in
+      let has_decl = decl <> "" in
+      if (has_decl) then
+         output ( "      typedef " ^ decl ^ ";\n" );
+      output (if is_static then "\t\tstatic " else "\t\t");
+      (match  field.cf_expr with
+      | Some { eexpr = TFunction function_def } ->
+         let nonVirtual = has_meta_key field.cf_meta Meta.NonVirtual in
+         let doDynamic =  (nonVirtual || not (is_override class_def field.cf_name ) ) && (reflective class_def field ) in
+         if ( is_dynamic_haxe_method field ) then begin
+            if ( doDynamic ) then begin
+               output ("::Dynamic " ^ remap_name ^ ";\n");
+               output (if is_static then "\t\tstatic " else "\t\t");
+               output ("inline ::Dynamic &" ^ remap_name ^ "_dyn() " ^ "{return " ^ remap_name^ "; }\n")
+            end
+         end else begin
+            let return_type = (ctx_type_string ctx function_def.tf_type) in
+            if ( not is_static && not nonVirtual ) then begin
+               let scriptable = Common.defined ctx.ctx_common Define.Scriptable in
+               if (not (is_internal_member field.cf_name) && not scriptable ) then begin
+                  let key = (join_class_path class_def.cl_path ".") ^ "." ^ field.cf_name in
+                  try output (Hashtbl.find ctx.ctx_class_member_types key) with Not_found -> ()
+               end else
+                  output "virtual ";
+            end;
+            output (if return_type="Void" then "void" else return_type );
+
+            let remap_name = native_field_name_remap is_static field in
+            output (" " ^ remap_name ^ "(" );
+            output (ctx_arg_list ctx function_def.tf_args "" );
+            output ");\n";
+            if ( doDynamic ) then begin
+               output (if is_static then "\t\tstatic " else "\t\t");
+               output ("::Dynamic " ^ remap_name ^ "_dyn();\n" )
+            end;
+         end;
+         output "\n";
+      | _ when has_decl ->
+         output ( remap_name ^ "_decl " ^ remap_name ^ ";\n" );
+         (* Variable access *)
+      | _ ->
+         (* Variable access *)
+         let tcpp = cpp_type_of ctx field.cf_type in
+         let tcppStr = tcpp_to_string tcpp in
+         if not is_static && only_stack_access ctx field.cf_type then
+            error ("Variables of type " ^ tcppStr ^ " may not be used as members") field.cf_pos;
+
+         output (tcppStr ^ " " ^ remap_name ^ ";\n" );
+
+         (* Add a "dyn" function for variable to unify variable/function access *)
+         (match follow field.cf_type with
+         | _ when nativeGen  -> ()
+         | TFun (_,_) ->
+            output (if is_static then "\t\tstatic " else "\t\t");
+            gen_type ctx field.cf_type;
+            output (" &" ^ remap_name ^ "_dyn() { return " ^ remap_name ^ ";}\n" )
+         | _ ->  (match field.cf_kind with
+            | Var { v_read = AccCall } when (not is_static) && (is_dynamic_accessor ("get_" ^ field.cf_name) "get" field class_def) ->
+               output ("\t\tDynamic get_" ^ field.cf_name ^ ";\n" )
+            | _ -> ()
+            );
+            (match field.cf_kind with
+            | Var { v_write = AccCall } when (not is_static) &&  (is_dynamic_accessor ("set_" ^ field.cf_name) "set" field class_def) ->
+               output ("\t\tDynamic set_" ^ field.cf_name ^ ";\n" )
+            | _ -> ()
+            )
+         )
+      );
+      end
+   ;;
+
+let path_of_string path =
+   ["@verbatim"], path
+;;
+
+
+(*
+   Get a list of all classes referred to by the class/enum definition
+   These are used for "#include"ing the appropriate header files,
+   or for building the dependencies in the Build.xml file
+*)
+let find_referenced_types_flags ctx obj super_deps constructor_deps header_only for_depends include_super_args =
+   let types = ref PMap.empty in
+   let rec add_type_flag isNative in_path =
+      if ( not (PMap.mem in_path !types)) then begin
+         types := (PMap.add in_path isNative !types);
+         try
+            List.iter (add_type_flag isNative) (Hashtbl.find super_deps in_path);
+         with Not_found -> ()
+      end
+   and add_type in_path =
+      add_type_flag false in_path
+   in
+   let add_extern_type decl =
+      let tinfo = t_infos decl in
+      let include_file = get_meta_string_path tinfo.mt_meta (if for_depends then Meta.Depend else Meta.Include) in
+      if (include_file<>"") then
+         add_type ( path_of_string include_file )
+      else if (not for_depends) && (has_meta_key tinfo.mt_meta Meta.Include) then
+         add_type tinfo.mt_path
+   in
+
+   let add_extern_class klass =
+      add_extern_type (TClassDecl klass)
+   in
+   let add_native_gen_class klass =
+      let include_file = get_meta_string_path klass.cl_meta (if for_depends then Meta.Depend else Meta.Include) in
+      if (include_file<>"") then
+         add_type ( path_of_string include_file )
+      else if for_depends then
+         add_type klass.cl_path
+      else begin
+         let path = klass.cl_path in
+         if not klass.cl_interface then
+            (* Always include native struct headers directly ... *)
+            add_type ( path_of_string ( (join_class_path path "/") ^ ".h") )
+         else begin
+            add_type_flag true klass.cl_path
+         end
+      end
+   in
+   let visited = ref [] in
+   let rec visit_type in_type =
+      if not (List.exists (fun t2 -> Type.fast_eq in_type t2) !visited) then begin
+         visited := in_type :: !visited;
+         begin match follow in_type with
+         | TMono r -> (match !r with None -> () | Some t -> visit_type t)
+         | TEnum (enum,params) -> add_type enum.e_path
+         (* If a class has a template parameter, then we treat it as dynamic - except
+            for the Array, Class, FastIterator or Pointer classes, for which we do a fully typed object *)
+         | TInst (klass,params) ->
+            (match klass.cl_path with
+            | ([],"Array") | ([],"Class") | (["cpp"],"FastIterator")
+            | (["cpp"],"Pointer") | (["cpp"],"ConstPointer") | (["cpp"],"Function")
+            | (["cpp"],"RawPointer") | (["cpp"],"RawConstPointer") -> List.iter visit_type params
+            | _ when is_native_gen_class klass -> add_native_gen_class klass
+            | _ when is_extern_class klass -> add_extern_class klass
+            | _ -> (match klass.cl_kind with KTypeParameter _ -> () | _ -> add_type klass.cl_path);
+            )
+         | TAbstract (a,params) when is_scalar_abstract a ->
+            add_extern_type (TAbstractDecl a)
+         | TFun (args,haxe_type) -> visit_type haxe_type;
+            List.iter (fun (_,_,t) -> visit_type t; ) args;
+         | _ -> ()
+         end;
+         visited := List.tl !visited;
+      end
+   in
+   let rec visit_params expression =
+      begin
+      let rec visit_expression = fun expression ->
+         (* Expand out TTypeExpr (ie, the name of a class, as used for static access etc ... *)
+         (match expression.eexpr with
+            | TTypeExpr type_def -> ( match type_def with
+               | TClassDecl class_def when is_native_gen_class class_def -> add_native_gen_class class_def
+               | TClassDecl class_def when is_extern_class class_def -> add_extern_class class_def
+               | _ -> add_type (t_path type_def)
+               )
+
+            (* Must visit the types, Type.iter will visit the expressions ... *)
+            | TTry (e,catches) ->
+               List.iter (fun (v,_) -> visit_type v.v_type) catches
+            (* Must visit the enum param types, Type.iter will visit the rest ... *)
+(*             | TMatch (_,enum,cases,_) ->
+               add_type (fst enum).e_path;
+               List.iter (fun (case_ids,params,expression) ->
+                  (match params with
+                  | None -> ()
+                  | Some l -> List.iter (function None -> () | Some v -> visit_type v.v_type) l  ) ) cases; *)
+            (* Must visit type too, Type.iter will visit the expressions ... *)
+            | TNew  (klass,params,_) -> begin
+               visit_type (TInst (klass,params));
+               try
+               let construct_type = Hashtbl.find constructor_deps klass.cl_path in
+                  visit_type construct_type.cf_type
+               with Not_found -> ();
+               end
+            (* Must visit type too, Type.iter will visit the expressions ... *)
+            | TVar (v,_) ->
+               visit_type v.v_type
+            (* Must visit enum type too, Type.iter will visit the expressions ... *)
+            | TEnumParameter (_,ef,_) -> visit_type (follow ef.ef_type)
+            (* Must visit args too, Type.iter will visit the expressions ... *)
+            | TFunction func_def ->
+               List.iter (fun (v,_) -> visit_type v.v_type) func_def.tf_args;
+
+            | TField( obj, field ) ->
+               (match field with
+               | FInstance (clazz,params,_)
+               | FClosure (Some (clazz,params),_) ->
+                   visit_type (TInst (clazz,params))
+               | _ -> ()
+               )
+            | TConst TSuper ->
+               (match follow expression.etype with
+               | TInst (klass,params) ->
+                  (try let construct_type = Hashtbl.find constructor_deps klass.cl_path in
+                     visit_type construct_type.cf_type
+                  with Not_found -> () )
+               | _ -> print_endline ("TSuper : Odd etype ?" ^ ( (ctx_type_string ctx expression.etype)) )
+               )
+            | _ -> ()
+         );
+         Type.iter visit_expression expression;
+         visit_type (follow expression.etype)
+      in
+      visit_expression expression
+      end
+   in
+   let visit_field field =
+      (* Add the type of the expression ... *)
+      visit_type field.cf_type;
+      if (not header_only) then
+         (match field.cf_expr with
+         | Some expression -> visit_params expression | _ -> ());
+   in
+   let visit_class class_def =
+      let fields = List.append class_def.cl_ordered_fields class_def.cl_ordered_statics in
+      let fields_and_constructor = List.append fields
+         (match class_def.cl_constructor with | Some expr -> [expr] | _ -> [] ) in
+      List.iter visit_field fields_and_constructor;
+      if (include_super_args) then
+         List.iter visit_field (List.map (fun (a,_,_) -> a ) (all_virtual_functions class_def ));
+
+      (* Add super & interfaces *)
+      if is_native_gen_class class_def then
+         add_native_gen_class class_def
+      else
+         add_type class_def.cl_path;
+   in
+   let visit_enum enum_def =
+      add_type enum_def.e_path;
+      PMap.iter (fun _ constructor ->
+         (match constructor.ef_type with
+         | TFun (args,_) ->
+            List.iter (fun (_,_,t) -> visit_type t; ) args;
+         | _ -> () );
+         ) enum_def.e_constrs;
+      if (not header_only) then begin
+         let meta = Codegen.build_metadata ctx.ctx_common (TEnumDecl enum_def) in
+         match meta with Some expr -> visit_params expr | _ -> ();
+      end;
+   in
+   let inc_cmp i1 i2 =
+      String.compare (join_class_path i1 ".") (join_class_path i2 ".")
+   in
+
+   (* Body of main function *)
+   (match obj with
+   | TClassDecl class_def -> visit_class class_def;
+      (match class_def.cl_init with Some expression -> visit_params expression | _ -> ())
+   | TEnumDecl enum_def -> visit_enum enum_def
+   | TTypeDecl _ | TAbstractDecl _ -> (* These are expanded *) ());
+
+   let deps = List.sort inc_cmp (List.filter (fun path -> (include_class_header path) ) (pmap_keys !types)) in
+   let flags = List.map (fun dep -> PMap.find dep !types) deps in
+   deps, flags
+   ;;
+
+let find_referenced_types ctx obj super_deps constructor_deps header_only for_depends include_super_args =
+  let deps,_ = find_referenced_types_flags ctx obj super_deps constructor_deps header_only for_depends include_super_args in
+  deps
+;;
+
+
+let generate_main_header output_main =
+   output_main "#include <hxcpp.h>\n\n";
+   output_main "#include <stdio.h>\n\n";
+   output_main "extern \"C\" void __hxcpp_main();\n\n";
+   output_main "extern \"C\" void __hxcpp_lib_main();\n\n"
+;;
+
+let generate_main_footer1 output_main =
+   output_main "void __hxcpp_main() {\n";;
+
+let generate_main_footer2 output_main =
+   output_main "	}\n\n";
+   output_main "void __hxcpp_lib_main() {\n";
+   output_main "	HX_TOP_OF_STACK\n";
+   output_main "	hx::Boot();\n";
+   output_main "	__boot_all();\n";
+   output_main "	__hxcpp_main();\n";
+   output_main "	}\n"
+;;
+
+
+let generate_main ctx super_deps class_def =
+   let common_ctx = ctx.ctx_common in
+   (* main routine should be a single static function *)
+   let main_expression =
+      (match class_def.cl_ordered_statics with
+      | [{ cf_expr = Some expression }] -> expression;
+      | _ -> assert false ) in
+   ignore(find_referenced_types ctx (TClassDecl class_def) super_deps (Hashtbl.create 0) false false false);
+   let depend_referenced = find_referenced_types ctx (TClassDecl class_def) super_deps (Hashtbl.create 0) false true false in
+   let generate_startup filename is_main =
+      (*make_class_directories base_dir ( "src" :: []);*)
+      let cpp_file = new_cpp_file common_ctx common_ctx.file ([],filename) in
+      let output_main = (cpp_file#write) in
+
+      generate_main_header (cpp_file#write_h);
+
+      List.iter ( add_include cpp_file ) depend_referenced;
+      output_main "\n\n";
+
+      if is_main then output_main "\n#include <hx/HxcppMain.h>\n\n";
+
+      generate_main_footer1 output_main;
+
+      let ctx = file_context ctx cpp_file 1 in
+      gen_cpp_init ctx "hxcpp" "__hxcpp_main" "" main_expression;
+
+
+      generate_main_footer2 output_main;
+      cpp_file#close;
+   in
+   generate_startup "__main__" true;
+   generate_startup "__lib__" false
+   ;;
+
+let generate_dummy_main common_ctx =
+   let generate_startup filename is_main =
+      let main_file = new_cpp_file common_ctx common_ctx.file ([],filename) in
+      let output_main = (main_file#write) in
+      generate_main_header (main_file#write_h);
+      if is_main then output_main "\n#include <hx/HxcppMain.h>\n\n";
+      generate_main_footer1 output_main;
+      generate_main_footer2 output_main;
+      main_file#close;
+   in
+   generate_startup "__main__" true;
+   generate_startup "__lib__" false
+   ;;
+
+let generate_boot ctx boot_enums boot_classes nonboot_classes init_classes =
+   let common_ctx = ctx.ctx_common in
+   (* Write boot class too ... *)
+   let base_dir = common_ctx.file in
+   let boot_file = new_cpp_file common_ctx base_dir ([],"__boot__") in
+   let output_boot = (boot_file#write) in
+   boot_file#write_h "#include <hxcpp.h>\n\n";
+
+   List.iter ( fun class_path -> boot_file#add_include class_path )
+      (boot_enums @ boot_classes @ nonboot_classes);
+
+   let newScriptable = (Common.defined common_ctx Define.Scriptable) in
+   if newScriptable then begin
+      output_boot "#include <hx/Scriptable.h>\n";
+      let funcs = hash_iterate !(ctx.ctx_interface_slot) (fun name id -> (name,id) ) in
+      let sorted = List.sort (fun (_,id1) (_,id2) -> id1-id2 ) funcs in
+      output_boot "static const char *scriptableInterfaceFuncs[] = {\n\t0,\n\t0,\n";
+      List.iter (fun (name,id) -> output_boot ("\t\"" ^ name ^ "\", //" ^ (string_of_int (-id) ) ^ "\n")) sorted;
+      output_boot "};\n";
+   end;
+
+
+   output_boot "\nvoid __files__boot();\n";
+   output_boot "\nvoid __boot_all()\n{\n";
+   output_boot "__files__boot();\n";
+   output_boot "hx::RegisterResources( hx::GetResources() );\n";
+   if newScriptable then
+      output_boot ("hx::ScriptableRegisterNameSlots(scriptableInterfaceFuncs," ^ (string_of_int !(ctx.ctx_interface_slot_count) ) ^ ");\n");
+
+   List.iter ( fun class_path ->
+      output_boot ("::" ^ ( join_class_path_remap class_path "::" ) ^ "_obj::__register();\n") )
+         (boot_enums @ boot_classes @ nonboot_classes);
+
+   let dump_boot =
+      List.iter ( fun class_path ->
+         output_boot ("::" ^ ( join_class_path_remap class_path "::" ) ^ "_obj::__boot();\n") ) in
+
+   dump_boot boot_enums;
+
+   List.iter ( fun class_path ->
+      output_boot ("::" ^ ( join_class_path_remap class_path "::" ) ^ "_obj::__init__();\n") ) (List.rev init_classes);
+
+   dump_boot (List.filter  (fun path -> is_cpp_class path )  (List.rev boot_classes));
+   dump_boot (List.filter  (fun path -> not (is_cpp_class path) )  (List.rev boot_classes));
+
+   output_boot "}\n\n";
+   boot_file#close;;
+
+
+let generate_files common_ctx file_info =
+   (* Write __files__ class too ... *)
+   let base_dir = common_ctx.file in
+   let files_file = new_cpp_file common_ctx base_dir ([],"__files__") in
+   let output_files = (files_file#write) in
+   let types = common_ctx.types in
+   files_file#write_h "#include <hxcpp.h>\n\n";
+   output_files "namespace hx {\n";
+   output_files "const char *__hxcpp_all_files[] = {\n";
+   output_files "#ifdef HXCPP_DEBUGGER\n";
+   List.iter ( fun file -> output_files ((const_char_star file)^",\n" ) )
+      ( List.sort String.compare ( pmap_keys !file_info) );
+   output_files "#endif\n";
+   output_files " 0 };\n";
+   output_files "\n";
+
+   output_files "const char *__hxcpp_all_files_fullpath[] = {\n";
+   output_files "#ifdef HXCPP_DEBUGGER\n";
+   List.iter ( fun file -> output_files ((const_char_star (
+      Common.get_full_path (try Common.find_file common_ctx file with Not_found -> file)
+      ))^",\n" ) )
+      ( List.sort String.compare ( pmap_keys !file_info) );
+   output_files "#endif\n";
+   output_files " 0 };\n";
+   output_files "\n";
+
+
+   output_files "const char *__hxcpp_all_classes[] = {\n";
+   output_files "#ifdef HXCPP_DEBUGGER\n";
+   List.iter ( fun object_def ->
+   (match object_def with
+      | TClassDecl class_def when is_extern_class class_def -> ( )
+      | TClassDecl class_def when class_def.cl_interface -> ( )
+      | TClassDecl class_def ->
+         output_files ((const_char_star (join_class_path class_def.cl_path "." )) ^ ",\n")
+      | _ -> ( )
+      )
+   ) types;
+   output_files "#endif\n";
+   output_files " 0 };\n";
+
+   output_files "} // namespace hx\n";
+   output_files "void __files__boot() { __hxcpp_set_debugger_info(hx::__hxcpp_all_classes, hx::__hxcpp_all_files_fullpath); }\n";
+
+   files_file#close;;
+
+
+let begin_header_file output_h def_string nativeGen =
+   output_h ("#ifndef INCLUDED_" ^ def_string ^ "\n");
+   output_h ("#define INCLUDED_" ^ def_string ^ "\n\n");
+   output_h "#ifndef HXCPP_H\n";
+   if nativeGen then begin
+      output_h "#ifdef HXCPP_API_LEVEL\n";
+      output_h "#include <hxcpp.h>\n";
+      output_h "#else\n";
+      output_h "#include <hx/Native.h>\n";
+      output_h "#endif\n"
+   end else begin
+      output_h "#include <hxcpp.h>\n"
+   end;
+   output_h "#endif\n\n";;
+
+let end_header_file output_h def_string =
+   output_h ("\n#endif /* INCLUDED_" ^ def_string ^ " */ \n");;
+
+let new_placed_cpp_file common_ctx class_path =
+   let base_dir = common_ctx.file in
+
+   if (Common.defined common_ctx Define.Vcproj ) then begin
+      make_class_directories base_dir ("src"::[]);
+      cached_source_writer common_ctx
+         ( base_dir ^ "/src/" ^ ( String.concat "-" (fst class_path) ) ^ "-" ^
+         (snd class_path) ^ (source_file_extension common_ctx) )
+   end else
+      new_cpp_file common_ctx common_ctx.file class_path;;
+
+
+
+let generate_enum_files baseCtx enum_def super_deps meta =
+   let common_ctx = baseCtx.ctx_common in
+   let class_path = enum_def.e_path in
+   let just_class_name =  (snd class_path) in
+   let class_name =  just_class_name ^ "_obj" in
+   let remap_class_name =  ("::" ^ (join_class_path_remap class_path "::") )  in
+   (*let cpp_file = new_cpp_file common_ctx.file class_path in*)
+   let cpp_file = new_placed_cpp_file common_ctx class_path in
+   let output_cpp = (cpp_file#write) in
+   let debug = if (has_meta_key enum_def.e_meta Meta.NoDebug) || ( Common.defined  common_ctx Define.NoDebug)
+      then 0 else 1 in
+
+   let ctx = file_context baseCtx cpp_file debug in
+
+   if (debug>1) then
+      print_endline ("Found enum definition:" ^ (join_class_path  class_path "::" ));
+
+   cpp_file#write_h "#include <hxcpp.h>\n\n";
+
+   let referenced,flags = find_referenced_types_flags ctx (TEnumDecl enum_def) super_deps (Hashtbl.create 0) false false false in
+   List.iter (add_include cpp_file) referenced;
+
+   gen_open_namespace output_cpp class_path;
+   output_cpp "\n";
+
+   PMap.iter (fun _ constructor ->
+      let name = keyword_remap constructor.ef_name in
+      match constructor.ef_type with
+      | TFun (args,_) ->
+         output_cpp (remap_class_name ^ " " ^ class_name ^ "::" ^ name ^ "(" ^
+            (ctx_tfun_arg_list ctx true args) ^")\n");
+
+         output_cpp ("{\n\treturn hx::CreateEnum< " ^ class_name ^ " >(" ^ (strq name) ^ "," ^
+            (string_of_int constructor.ef_index) ^ "," ^ (string_of_int (List.length args)) ^  ")" );
+          ExtList.List.iteri (fun i (arg,_,_) -> output_cpp ("->_hx_init(" ^ (string_of_int i) ^ "," ^ (keyword_remap arg) ^ ")")) args;
+         output_cpp ";\n}\n\n"
+      | _ ->
+         output_cpp ( remap_class_name ^ " " ^ class_name ^ "::" ^ name ^ ";\n\n" )
+   ) enum_def.e_constrs;
+
+
+   let constructor_arg_count constructor =
+      (match constructor.ef_type with | TFun(args,_) -> List.length args | _ -> 0 )
+   in
+
+   output_cpp ("bool " ^ class_name ^ "::__GetStatic(const ::String &inName, ::Dynamic &outValue, hx::PropertyAccess inCallProp)\n{\n");
+   PMap.iter (fun _ constructor ->
+      let name = constructor.ef_name in
+      let dyn = if constructor_arg_count constructor > 0 then "_dyn()" else "" in
+      output_cpp ("\tif (inName==" ^ strq name ^ ") { outValue = " ^ class_name ^ "::" ^ keyword_remap name ^ dyn ^ "; return true; }\n" );
+   ) enum_def.e_constrs;
+   output_cpp ("\treturn super::__GetStatic(inName, outValue, inCallProp);\n}\n\n");
+
+   output_cpp ("HX_DEFINE_CREATE_ENUM(" ^ class_name ^ ")\n\n");
+
+   output_cpp ("int " ^ class_name ^ "::__FindIndex(::String inName)\n{\n");
+   PMap.iter (fun _ constructor ->
+      let name = constructor.ef_name in
+      let idx = string_of_int constructor.ef_index in
+      output_cpp ("\tif (inName==" ^ (strq name) ^ ") return " ^ idx ^ ";\n") ) enum_def.e_constrs;
+   output_cpp ("\treturn super::__FindIndex(inName);\n");
+   output_cpp ("}\n\n");
+
+   (* Dynamic versions of constructors *)
+   let dump_dynamic_constructor _ constr =
+      let count = constructor_arg_count constr in
+      if (count>0) then begin
+         let nargs = string_of_int count in
+         output_cpp ("STATIC_HX_DEFINE_DYNAMIC_FUNC" ^ nargs ^ "(" ^ class_name ^ "," ^
+               (keyword_remap constr.ef_name) ^ ",return)\n\n");
+      end
+   in
+   PMap.iter dump_dynamic_constructor enum_def.e_constrs;
+
+
+   output_cpp ("int " ^ class_name ^ "::__FindArgCount(::String inName)\n{\n");
+   PMap.iter (fun _ constructor ->
+      let name = constructor.ef_name in
+      let count = string_of_int (constructor_arg_count constructor) in
+      output_cpp ("\tif (inName==" ^ (strq name) ^ ") return " ^ count ^ ";\n") ) enum_def.e_constrs;
+      output_cpp ("\treturn super::__FindArgCount(inName);\n");
+      output_cpp ("}\n\n");
+
+   (* Dynamic "Get" Field function - string version *)
+   output_cpp ("hx::Val " ^ class_name ^ "::__Field(const ::String &inName,hx::PropertyAccess inCallProp)\n{\n");
+   let dump_constructor_test _ constr =
+      output_cpp ("\tif (inName==" ^ (strq constr.ef_name) ^ ") return " ^
+                  (keyword_remap constr.ef_name) );
+      if ( (constructor_arg_count constr) > 0 ) then output_cpp "_dyn()";
+      output_cpp (";\n")
+   in
+   PMap.iter dump_constructor_test enum_def.e_constrs;
+   output_cpp ("\treturn super::__Field(inName,inCallProp);\n}\n\n");
+
+   output_cpp ("static ::String " ^ class_name ^ "_sStaticFields[] = {\n");
+   let sorted =
+      List.sort (fun f1 f2 -> (PMap.find f1 enum_def.e_constrs ).ef_index -
+               (PMap.find f2 enum_def.e_constrs ).ef_index )
+         (pmap_keys enum_def.e_constrs) in
+
+    List.iter (fun name -> output_cpp ("\t" ^ (strq name) ^ ",\n") ) sorted;
+
+   output_cpp "\t::String(null())\n};\n\n";
+
+   (* ENUM - Mark static as used by GC *)
+   output_cpp ("static void " ^ class_name ^ "_sMarkStatics(HX_MARK_PARAMS) {\n");
+   PMap.iter (fun _ constructor ->
+      let name = keyword_remap constructor.ef_name in
+      match constructor.ef_type with
+      | TFun (_,_) -> ()
+      | _ -> output_cpp ("\tHX_MARK_MEMBER_NAME(" ^ class_name ^ "::" ^ name ^ ",\"" ^ name ^ "\");\n") )
+   enum_def.e_constrs;
+   output_cpp "};\n\n";
+
+   (* ENUM - Visit static as used by GC *)
+   output_cpp "#ifdef HXCPP_VISIT_ALLOCS\n";
+   output_cpp ("static void " ^ class_name ^ "_sVisitStatic(HX_VISIT_PARAMS) {\n");
+   output_cpp ("\tHX_VISIT_MEMBER_NAME(" ^ class_name ^ "::__mClass,\"__mClass\");\n");
+   PMap.iter (fun _ constructor ->
+      let name = keyword_remap constructor.ef_name in
+      match constructor.ef_type with
+      | TFun (_,_) -> ()
+      | _ -> output_cpp ("\tHX_VISIT_MEMBER_NAME(" ^ class_name ^ "::" ^ name ^ ",\"" ^ name ^ "\");\n") )
+   enum_def.e_constrs;
+   output_cpp "};\n";
+   output_cpp "#endif\n\n";
+
+   output_cpp ("hx::Class " ^ class_name ^ "::__mClass;\n\n");
+
+   output_cpp ("Dynamic __Create_" ^ class_name ^ "() { return new " ^ class_name ^ "; }\n\n");
+
+   output_cpp ("void " ^ class_name ^ "::__register()\n{\n");
+   let text_name = str (join_class_path class_path ".") in
+   output_cpp ("\nhx::Static(__mClass) = hx::_hx_RegisterClass(" ^ text_name ^
+               ", hx::TCanCast< " ^ class_name ^ " >," ^ class_name ^ "_sStaticFields,0,\n");
+   output_cpp ("\t&__Create_" ^ class_name ^ ", &__Create,\n");
+   output_cpp ("\t&super::__SGetClass(), &Create" ^ class_name ^ ", " ^ class_name ^ "_sMarkStatics\n");
+   output_cpp("#ifdef HXCPP_VISIT_ALLOCS\n    , " ^ class_name ^ "_sVisitStatic\n#endif\n");
+   output_cpp ("#ifdef HXCPP_SCRIPTABLE\n    , 0\n#endif\n");
+      output_cpp (");\n");
+   output_cpp ("\t__mClass->mGetStaticField = &" ^ class_name ^"::__GetStatic;\n");
+   output_cpp "}\n\n";
+
+   output_cpp ("void " ^ class_name ^ "::__boot()\n{\n");
+   (match meta with
+      | Some expr ->
+         let ctx = file_context ctx cpp_file 1 in
+         gen_cpp_init ctx class_name "boot" "__mClass->__meta__ = " expr
+      | _ -> () );
+   PMap.iter (fun _ constructor ->
+      let name = constructor.ef_name in
+      match constructor.ef_type with
+      | TFun (_,_) -> ()
+      | _ ->
+         output_cpp ( (keyword_remap name) ^ " = hx::CreateEnum< " ^ class_name ^ " >(" ^ (str name) ^  "," ^
+            (string_of_int constructor.ef_index) ^ ",0);\n" )
+   ) enum_def.e_constrs;
+   output_cpp ("}\n\n");
+
+
+
+
+   output_cpp "\n";
+   gen_close_namespace output_cpp class_path;
+   cpp_file#close;
+
+   let h_file = new_header_file common_ctx common_ctx.file class_path in
+   let super = "hx::EnumBase_obj" in
+   let output_h = (h_file#write) in
+   let def_string = join_class_path class_path "_"  in
+
+   let ctx = file_context baseCtx h_file debug in
+
+   begin_header_file (h_file#write_h) def_string false;
+
+   List.iter2 (fun r f -> gen_forward_decl h_file r f) referenced flags;
+
+   gen_open_namespace output_h class_path;
+
+   output_h "\n\n";
+   output_h ("class " ^ class_name ^ " : public " ^ super ^ "\n");
+   output_h ("{\n\ttypedef " ^ super ^ " super;\n");
+   output_h ("\t\ttypedef " ^ class_name ^ " OBJ_;\n");
+   output_h "\n\tpublic:\n";
+   output_h ("\t\t" ^ class_name ^ "() {};\n");
+   output_h ("\t\tHX_DO_ENUM_RTTI;\n");
+   output_h ("\t\tstatic void __boot();\n");
+   output_h ("\t\tstatic void __register();\n");
+   output_h ("\t\tstatic bool __GetStatic(const ::String &inName, Dynamic &outValue, hx::PropertyAccess inCallProp);\n");
+   output_h ("\t\t::String GetEnumName( ) const { return " ^ (str (join_class_path class_path "."))  ^ "; }\n" );
+   output_h ("\t\t::String __ToString() const { return " ^ (str (just_class_name ^ ".") )^ " + _hx_tag; }\n\n");
+
+
+   PMap.iter (fun _ constructor ->
+      let name = keyword_remap constructor.ef_name in
+      output_h ( "\t\tstatic " ^  remap_class_name ^ " " ^ name );
+      match constructor.ef_type with
+      | TFun (args,_) ->
+         output_h ( "(" ^ (ctx_tfun_arg_list ctx true args) ^");\n");
+         output_h ( "\t\tstatic ::Dynamic " ^ name ^ "_dyn();\n");
+      | _ ->
+         output_h ";\n";
+         output_h ( "\t\tstatic inline " ^  remap_class_name ^ " " ^ name ^
+                  "_dyn() { return " ^name ^ "; }\n" );
+   ) enum_def.e_constrs;
+
+   output_h "};\n\n";
+
+   gen_close_namespace output_h class_path;
+
+   end_header_file output_h def_string;
+   h_file#close;
+   let depend_referenced = find_referenced_types ctx (TEnumDecl enum_def) super_deps (Hashtbl.create 0) false true false in
+   depend_referenced;;
+
+
+let list_iteri func in_list =
+   let idx = ref 0 in
+   List.iter (fun elem -> func !idx elem; idx := !idx + 1 ) in_list
+;;
+
+let has_new_gc_references ctx class_def =
+   match class_def.cl_dynamic with
+   | Some _ -> true
+   | _ -> (
+      let is_gc_reference field =
+      (should_implement_field field) && (is_data_member field) && not (ctx_cant_be_null ctx field.cf_type)
+      in
+      List.exists is_gc_reference class_def.cl_ordered_fields
+      )
+;;
+
+
+let rec has_gc_references ctx class_def =
+   ( match class_def.cl_super with
+   | Some def when has_gc_references ctx (fst def) -> true
+   | _ -> false )
+   || has_new_gc_references ctx class_def
+;;
+
+let rec find_next_super_iteration ctx class_def =
+   match class_def.cl_super with
+   | Some  (klass,params) when has_new_gc_references ctx klass ->
+        tcpp_to_string_suffix "_obj" (cpp_instance_type ctx klass params)
+   | Some  (klass,_) -> find_next_super_iteration ctx klass
+   | _ -> "";
+;;
+
+let has_init_field class_def =
+   match class_def.cl_init with
+   | Some _ -> true
+   | _ -> false;;
+
+
+let is_abstract_impl class_def = match class_def.cl_kind with
+   | KAbstractImpl _ -> true
+   | _ -> false
+;;
+
+let variable_field field =
+   (match field.cf_expr with
+   | Some { eexpr = TFunction function_def } -> is_dynamic_haxe_method field
+   | _ -> true)
+;;
+
+let is_readable class_def field =
+   (match field.cf_kind with
+   | Var { v_read = AccNever } when (is_extern_field field) -> false
+   | Var { v_read = AccInline } -> false
+   | Var _ when is_abstract_impl class_def -> false
+   | _ -> true)
+;;
+
+let is_writable class_def field =
+   (match field.cf_kind with
+   | Var { v_write = AccNever } when (is_extern_field field) -> false
+   | Var { v_read = AccInline } -> false
+   | Var _ when is_abstract_impl class_def -> false
+   | _ -> true)
+;;
+
+
+let statics_except_meta class_def = (List.filter (fun static -> static.cf_name <> "__meta__" && static.cf_name <> "__rtti") class_def.cl_ordered_statics);;
+
+let has_set_member_field class_def =
+   implement_dynamic_here class_def || (
+      let reflect_fields = List.filter (reflective class_def) (class_def.cl_ordered_fields) in
+      let reflect_writable = List.filter (is_writable class_def) reflect_fields in
+      List.exists variable_field reflect_writable
+   )
+;;
+
+
+let has_set_static_field class_def =
+      let reflect_fields = List.filter (reflective class_def) (statics_except_meta class_def) in
+      let reflect_writable = List.filter (is_writable class_def) reflect_fields in
+      List.exists variable_field reflect_writable
+;;
+
+
+let has_get_fields class_def =
+   implement_dynamic_here class_def || (
+      let is_data_field field = (match follow field.cf_type with | TFun _ -> false | _ -> true) in
+      List.exists is_data_field class_def.cl_ordered_fields
+   )
+;;
+
+let has_get_member_field class_def =
+   implement_dynamic_here class_def || (
+      let reflect_fields = List.filter (reflective class_def) (class_def.cl_ordered_fields) in
+      List.exists (is_readable class_def) reflect_fields
+   )
+;;
+
+
+let has_get_static_field class_def =
+      let reflect_fields = List.filter (reflective class_def) (statics_except_meta class_def) in
+      List.exists (is_readable class_def) reflect_fields
+;;
+
+
+
+let has_boot_field class_def =
+   match class_def.cl_init with
+   | None -> List.exists has_field_init (List.filter should_implement_field class_def.cl_ordered_statics)
+   | _ -> true
+;;
+
+let cpp_tfun_signature ctx include_names args return_type =
+  let argList = ctx_tfun_arg_list ctx include_names args in
+  let returnType = ctx_type_string ctx return_type in
+  ("( " ^ returnType ^ " (hx::Object::*)(" ^ argList ^ "))")
+;;
+
+exception FieldFound of tclass_field;;
+
+let find_class_implementation ctx class_def name interface =
+   let rec find def =
+      List.iter (fun f -> if f.cf_name=name then raise (FieldFound f) ) def.cl_ordered_fields;
+      match def.cl_super with
+      | Some (def,_) -> find def
+      | _ -> ()
+   in
+   try
+     find class_def;
+     error ("Could not find implementation of " ^ name ^ " in " ^
+        (join_class_path class_def.cl_path ".") ^ " required by " ^ (join_class_path interface.cl_path ".")) class_def.cl_pos
+   with FieldFound field ->
+      match follow field.cf_type, field.cf_kind  with
+      | _, Method MethDynamic -> ""
+      | TFun (args,return_type), Method _ ->
+         cpp_tfun_signature ctx false args return_type
+      | _,_ -> ""
+;;
+
+
+let is_macro meta =
+   Meta.has Meta.Macro meta
+;;
+
+
+let cpp_get_interface_slot ctx name =
+   try Hashtbl.find !(ctx.ctx_interface_slot) name
+   with Not_found -> begin
+      let result = !(ctx.ctx_interface_slot_count) in
+      Hashtbl.replace !(ctx.ctx_interface_slot) name result;
+      ctx.ctx_interface_slot_count := !(ctx.ctx_interface_slot_count) + 1;
+      result
+   end
+;;
+
+let access_str a = match a with
+   | AccNormal -> "AccNormal"
+   | AccNo -> "AccNo"
+   | AccNever -> "AccNever"
+   | AccResolve -> "AccResolve"
+   | AccCall -> "AccCall"
+   | AccInline -> "AccInline"
+   | AccRequire(_,_) -> "AccRequire" ;;
+
+
+let script_type t optional = if optional then "Object" else
+   match type_string t with
+   | "bool" -> "Int"
+   | "int" -> "Int"
+   | "Float" -> "Float"
+   | "::String" -> "String"
+   | "Null" -> "Void"
+   | "Void" -> "Void"
+   | _ -> "Object"
+;;
+
+let script_signature t optional = match script_type t optional with
+   | "Bool" -> "b"
+   | "Int" -> "i"
+   | "Float" -> "f"
+   | "String" -> "s"
+   | "Void" -> "v"
+   | "void" -> "v"
+   | _ -> "o"
+;;
+
+let script_size_type t optional = match script_type t optional with
+   | "Object" -> "void *"
+   | x -> x
+;;
+
+
+
+let constructor_arg_var_list class_def ctx =
+   match class_def.cl_constructor with
+   | Some definition ->
+            (match definition.cf_expr with
+               | Some { eexpr = TFunction function_def } ->
+                  List.map (fun (v,o) -> (v.v_name, ctx_arg_type_name ctx v.v_name o v.v_type "__o_"))
+                        function_def.tf_args;
+               | _ ->
+                  (match follow definition.cf_type with
+                     | TFun (args,_) -> List.map (fun (a,_,t) -> (a, (ctx_type_string ctx t, a)) )  args
+                     | _ -> [])
+            )
+   | _ -> []
+;;
+
+
+
+(*
+  Generate class header and cpp files
+
+*)
+
+
+let generate_class_files baseCtx super_deps constructor_deps class_def inScriptable =
+
+   (* Shorcuts *)
+   let common_ctx = baseCtx.ctx_common in
+   let class_path = class_def.cl_path in
+   let nativeGen = has_meta_key class_def.cl_meta Meta.NativeGen in
+   let class_name = (snd class_path) ^ (if nativeGen then "" else "_obj") in
+   let dot_name = join_class_path class_path "." in
+   let smart_class_name =  (snd class_path)  in
+   let class_name_text = join_class_path class_path "." in
+   let ptr_name = "hx::ObjectPtr< " ^ class_name ^ " >" in
+   let debug = if (has_meta_key class_def.cl_meta Meta.NoDebug) || ( Common.defined  baseCtx.ctx_common Define.NoDebug)
+      then 0 else 1 in
+   let scriptable = inScriptable && not class_def.cl_private in
+
+   (* Config *)
+   let implement_dynamic = implement_dynamic_here class_def in
+   let override_iteration = (not nativeGen) && (has_new_gc_references baseCtx class_def) in
+   let dynamic_interface_closures =  (Common.defined baseCtx.ctx_common Define.DynamicInterfaceClosures) in
+
+   (* All interfaces (and sub-interfaces) implemented *)
+   let implemented_hash = Hashtbl.create 0 in
+   let native_implemented = Hashtbl.create 0 in
+   List.iter (fun imp ->
+      let rec descend_interface interface =
+         let intf_def = (fst interface) in
+         let interface_name = cpp_interface_impl_name baseCtx intf_def in
+         let hash = if is_native_gen_class intf_def then native_implemented else implemented_hash in
+         if ( not (Hashtbl.mem hash interface_name) ) then begin
+            Hashtbl.replace hash interface_name intf_def;
+            List.iter descend_interface intf_def.cl_implements;
+         end;
+         match intf_def.cl_super with
+         | Some (interface,params) -> descend_interface (interface,params)
+         | _ -> ()
+      in descend_interface imp
+   ) (real_interfaces class_def.cl_implements);
+   let implemented = hash_keys implemented_hash in
+   let implementsNative = (Hashtbl.length native_implemented) > 0 in
+
+   (* Field groups *)
+   let statics_except_meta = statics_except_meta class_def in
+   let implemented_fields = List.filter should_implement_field statics_except_meta in
+   let implemented_instance_fields = List.filter should_implement_field class_def.cl_ordered_fields in
+
+   let reflect_member_fields = List.filter (reflective class_def) class_def.cl_ordered_fields in
+   let reflect_member_readable = List.filter (is_readable class_def) reflect_member_fields in
+   let reflect_member_writable = List.filter (is_writable class_def) reflect_member_fields in
+   let reflect_write_member_variables = List.filter variable_field reflect_member_writable in
+
+   let reflect_static_fields = List.filter (reflective class_def) (statics_except_meta) in
+   let reflect_static_readable = List.filter (is_readable class_def) reflect_static_fields in
+   let reflect_static_writable = List.filter (is_writable class_def) reflect_static_fields in
+   let reflect_write_static_variables = List.filter variable_field reflect_static_writable in
+
+   let reflective_members = List.filter (reflective class_def) implemented_instance_fields in
+
+   (* native interface glue *)
+   let neededInterfaceFunctions = if not implementsNative then []
+      else begin
+         let have = Hashtbl.create 0 in
+         List.iter (fun field -> Hashtbl.replace have field.cf_name () ) implemented_instance_fields;
+         let want = ref [] in
+         Hashtbl.iter (fun _ intf_def ->
+            List.iter (fun field ->
+               if not (Hashtbl.mem have field.cf_name) then begin
+                  Hashtbl.replace have field.cf_name ();
+                  want := field :: !want;
+               end
+               ) intf_def.cl_ordered_fields;
+          ) native_implemented;
+         !want;
+      end
+   in
+
+   let not_toString = fun (field,args,_) -> field.cf_name<>"toString" || class_def.cl_interface in
+   let functions = List.filter not_toString (all_virtual_functions class_def) in
+
+   (* Constructor definition *)
+   let cargs = (constructor_arg_var_list class_def baseCtx) in
+   let constructor_type_var_list = List.map snd cargs in
+   let constructor_var_list = List.map snd constructor_type_var_list in
+   let constructor_type_args = String.concat ","
+            (List.map (fun (t,a) -> t ^ " " ^ a) constructor_type_var_list) in
+   let constructor_args = String.concat "," constructor_var_list in
+
+   (* State *)
+   let header_glue = ref [] in
+
+ (*
+   Generate cpp code
+ *)
+ let generate_class_cpp () =
+
+   (*let cpp_file = new_cpp_file common_ctx.file class_path in*)
+   let cpp_file = new_placed_cpp_file baseCtx.ctx_common class_path in
+   let output_cpp = (cpp_file#write) in
+   let ctx = file_context baseCtx cpp_file debug in
+
+   let class_super_name = (match class_def.cl_super with
+      | Some (klass, params) -> (tcpp_to_string_suffix "_obj" (cpp_instance_type ctx klass params) )
+      | _ -> "") in
+   if (debug>1) then print_endline ("Found class definition:" ^ (join_class_path class_def.cl_path "::"));
+
+
+   cpp_file#write_h "#include <hxcpp.h>\n\n";
+
+   let all_referenced = find_referenced_types ctx (TClassDecl class_def) super_deps constructor_deps false false scriptable in
+   List.iter ( add_include cpp_file  ) all_referenced;
+
+   if (scriptable) then
+      cpp_file#write_h "#include <hx/Scriptable.h>\n";
+
+   cpp_file#write_h "\n";
+
+   output_cpp ( get_class_code class_def Meta.CppFileCode );
+   let inc = get_meta_string_path class_def.cl_meta Meta.CppInclude in
+   if (inc<>"") then
+      output_cpp ("#include \"" ^ inc ^ "\"\n");
+
+   gen_open_namespace output_cpp class_path;
+   output_cpp "\n";
+
+   output_cpp ( get_class_code class_def Meta.CppNamespaceCode );
+
+   if (not class_def.cl_interface) && not nativeGen then begin
+      output_cpp ("void " ^ class_name ^ "::__construct(" ^ constructor_type_args ^ ")");
+      (match class_def.cl_constructor with
+         | Some ( { cf_expr = Some ( { eexpr = TFunction(function_def) } ) } as definition ) ->
+            let old_debug = ctx.ctx_debug_level in
+            if has_meta_key definition.cf_meta Meta.NoDebug then
+               ctx.ctx_debug_level <- 0;
+
+            gen_cpp_function_body ctx class_def false "new" function_def "" "";
+            output_cpp "\n";
+
+            ctx.ctx_debug_level <- old_debug;
+         | _ ->  output_cpp " { }\n\n"
+      );
+
+      (* Destructor goes in the cpp file so we can "see" the full definition of the member vars *)
+      output_cpp ("Dynamic " ^ class_name ^ "::__CreateEmpty() { return new " ^ class_name ^ "; }\n\n");
+
+      output_cpp (ptr_name ^ " " ^ class_name ^ "::__new(" ^constructor_type_args ^")\n");
+
+      let create_result () =
+         output_cpp ("{\n\t" ^ ptr_name ^ " _hx_result = new " ^ class_name ^ "();\n");
+         in
+      create_result ();
+      output_cpp ("\t_hx_result->__construct(" ^ constructor_args ^ ");\n");
+      output_cpp ("\treturn _hx_result;\n}\n\n");
+
+      output_cpp ("Dynamic " ^ class_name ^ "::__Create(hx::DynamicArray inArgs)\n");
+      create_result ();
+      output_cpp ("\t_hx_result->__construct(" ^ (array_arg_list constructor_var_list) ^ ");\n");
+      output_cpp ("\treturn _hx_result;\n}\n\n");
+
+      if ( List.length implemented) > 0 then begin
+            let alreadyGlued = Hashtbl.create 0 in
+            let cname = "_hx_" ^ (join_class_path class_def.cl_path "_") in
+            let implname = (cpp_class_name class_def) in
+            let cpp_glue = ref [] in
+            List.iter (fun interface_name ->
+               (try let interface = Hashtbl.find implemented_hash interface_name in
+                   output_cpp ("static " ^ cpp_class_name interface ^ " " ^ cname ^ "_" ^ interface_name ^ "= {\n" );
+                   let rec gen_interface_funcs interface =
+                      let gen_field field = (match follow field.cf_type, field.cf_kind  with
+                      | _, Method MethDynamic -> ()
+                      | TFun (args,return_type), Method _ ->
+                         let cast = cpp_tfun_signature ctx false args return_type in
+                         let class_implementation = find_class_implementation ctx class_def field.cf_name interface in
+                         let realName= cpp_member_name_of field in
+                         let castKey = realName ^ "::" ^ cast in
+                         let implementationKey = realName ^ "::" ^ class_implementation in
+                         if castKey <> implementationKey && not (Hashtbl.mem alreadyGlued castKey) then begin
+                            Hashtbl.replace alreadyGlued castKey ();
+                            let glue =  Printf.sprintf "%s_%08lx" field.cf_name (gen_hash32 0 cast) in
+                            let argList = ctx_tfun_arg_list ctx true args in
+                            let returnType = ctx_type_string ctx return_type in
+                            let returnStr = if returnType="void" then "" else "return " in
+                            let cppCode = returnType ^ " " ^ class_name ^ "::" ^ glue ^ "(" ^ argList ^ ") {\n" ^
+                               "\t\t\t" ^ returnStr ^ realName ^ "(" ^ cpp_arg_names args ^ ");\n}\n" in
+                            let headerCode = "\t\t" ^ returnType ^ " " ^ glue ^ "(" ^ argList ^ ");\n" in
+                            header_glue := headerCode :: !header_glue;
+                            cpp_glue := cppCode :: !cpp_glue;
+                            output_cpp ("	" ^ cast ^ "&" ^ implname ^ "::" ^ glue ^ ",\n");
+                         end else
+                            output_cpp ("	" ^ cast ^ "&" ^ implname ^ "::" ^ realName ^ ",\n");
+                      | _ -> () )
+                      in
+                      List.iter gen_field interface.cl_ordered_fields;
+                      match interface.cl_super with
+                      | Some super -> gen_interface_funcs (fst super)
+                      | _ -> ()
+                      in
+                   gen_interface_funcs interface;
+                   output_cpp "};\n\n";
+               with Not_found -> () )
+               ) implemented;
+
+            output_cpp (String.concat "\n" !cpp_glue);
+
+            output_cpp ("void *" ^ class_name ^ "::_hx_getInterface(int inHash) {\n");
+            output_cpp "\tswitch(inHash) {\n";
+            List.iter (fun interface_name ->
+               try let interface = Hashtbl.find implemented_hash interface_name in
+                  output_cpp ("\t\tcase (int)" ^ (cpp_class_hash interface) ^ ": return &" ^ cname ^ "_" ^ interface_name ^ ";\n")
+               with Not_found -> ()
+               ) implemented;
+
+            output_cpp "\t}\n";
+
+            if class_super_name="" then begin
+               output_cpp ("\t#ifdef HXCPP_SCRIPTABLE\n");
+               output_cpp ("\treturn super::_hx_getInterface(inHash);\n");
+               output_cpp ("\t#else\n");
+               output_cpp ("\treturn 0;\n");
+               output_cpp ("\t#endif\n")
+            end else
+               output_cpp ("\treturn super::_hx_getInterface(inHash);\n");
+            output_cpp ("}\n\n");
+      end;
+   end;
+
+   (match class_def.cl_init with
+   | Some expression ->
+      let ctx = file_context baseCtx cpp_file debug in
+      output_cpp ("void " ^ class_name^ "::__init__()");
+      gen_cpp_init ctx (cpp_class_name class_def) "__init__" "" (mk_block expression);
+      output_cpp "\n\n";
+   | _ -> ());
+
+
+   let dump_field_name = (fun field -> output_cpp ("\t" ^  (str field.cf_name) ^ ",\n")) in
+
+   List.iter
+      (gen_field ctx class_def class_name smart_class_name dot_name false class_def.cl_interface)
+      class_def.cl_ordered_fields;
+   List.iter
+      (gen_field ctx class_def class_name smart_class_name dot_name true class_def.cl_interface) statics_except_meta;
+   output_cpp "\n";
+
+   (* Initialise non-static variables *)
+   if ( (not class_def.cl_interface) && (not nativeGen) ) then begin
+      output_cpp (class_name ^ "::" ^ class_name ^  "()\n{\n");
+      if (implement_dynamic) then
+         output_cpp "\tHX_INIT_IMPLEMENT_DYNAMIC;\n";
+      List.iter
+         (fun field -> let remap_name = keyword_remap field.cf_name in
+            match field.cf_expr with
+            | Some { eexpr = TFunction function_def } ->
+                  if (is_dynamic_haxe_method field) then
+                     output_cpp ("\t" ^ remap_name ^ " = new __default_" ^ remap_name ^ "(this);\n")
+            | _ -> ()
+         )
+         class_def.cl_ordered_fields;
+      output_cpp "}\n\n";
+
+
+      let dump_field_iterator macro field =
+         if (is_data_member field) then begin
+            let remap_name = keyword_remap field.cf_name in
+            output_cpp ("\t" ^ macro ^ "(" ^ remap_name ^ ",\"" ^ field.cf_name^ "\");\n");
+
+               (match field.cf_kind with Var { v_read = AccCall } when (is_dynamic_accessor ("get_" ^ field.cf_name) "get" field class_def) ->
+                  let name = "get_" ^ field.cf_name in
+                  output_cpp ("\t" ^ macro ^ "(" ^ name ^ "," ^ "\"" ^ name ^ "\");\n" ) | _ -> ());
+               (match field.cf_kind with Var { v_write = AccCall } when  (is_dynamic_accessor ("set_" ^ field.cf_name) "set" field class_def) ->
+                  let name = "set_" ^ field.cf_name in
+                  output_cpp ("\t" ^ macro ^ "(" ^ name ^ "," ^ "\"" ^ name ^ "\");\n" ) | _ -> ());
+            end
+      in
+
+
+      if (override_iteration) then begin
+         let super_needs_iteration = find_next_super_iteration ctx class_def in
+         (* MARK function - explicitly mark all child pointers *)
+         output_cpp ("void " ^ class_name ^ "::__Mark(HX_MARK_PARAMS)\n{\n");
+         output_cpp ("\tHX_MARK_BEGIN_CLASS(" ^ smart_class_name ^ ");\n");
+         if (implement_dynamic) then
+            output_cpp "\tHX_MARK_DYNAMIC;\n";
+         List.iter (dump_field_iterator "HX_MARK_MEMBER_NAME") implemented_instance_fields;
+         (match super_needs_iteration with
+         | "" -> ()
+         | super -> output_cpp ("\t" ^ super^"::__Mark(HX_MARK_ARG);\n" ) );
+         output_cpp "\tHX_MARK_END_CLASS();\n";
+         output_cpp "}\n\n";
+
+         (* Visit function - explicitly visit all child pointers *)
+         output_cpp ("void " ^ class_name ^ "::__Visit(HX_VISIT_PARAMS)\n{\n");
+         if (implement_dynamic) then
+            output_cpp "\tHX_VISIT_DYNAMIC;\n";
+         List.iter (dump_field_iterator "HX_VISIT_MEMBER_NAME") implemented_instance_fields;
+         (match super_needs_iteration with
+         | "" -> ()
+         | super -> output_cpp ("\t" ^ super ^ "::__Visit(HX_VISIT_ARG);\n") );
+         output_cpp "}\n\n";
+      end;
+
+
+      let dump_quick_field_test fields =
+         if ( (List.length fields) > 0) then begin
+            let len = function (_,l,_) -> l in
+            let sfields = List.sort (fun f1 f2 -> (len f1)-(len f2)) fields in
+            let len_case = ref (-1) in
+            output_cpp "\tswitch(inName.length) {\n";
+            List.iter (fun (field,l,result) ->
+               if (l <> !len_case) then begin
+                  if (!len_case>=0) then output_cpp "\t\tbreak;\n";
+                  output_cpp ("\tcase " ^ (string_of_int l) ^ ":\n");
+                  len_case := l;
+               end;
+               output_cpp ("\t\tif (HX_FIELD_EQ(inName,\"" ^  (Ast.s_escape field)  ^ "\") ) { " ^ result ^ " }\n");
+            ) sfields;
+            output_cpp "\t}\n";
+         end;
+      in
+
+      let checkPropCall field = if ( (has_meta_key class_def.cl_meta Meta.NativeProperty) ||
+                                     (has_meta_key field.cf_meta Meta.NativeProperty) ||
+                                     (Common.defined common_ctx Define.ForceNativeProperty) )
+         then
+            "inCallProp != hx::paccNever"
+         else
+            "inCallProp == hx::paccAlways"
+      in
+
+
+
+      if (has_get_member_field class_def) then begin
+         (* Dynamic "Get" Field function - string version *)
+         output_cpp ("hx::Val " ^ class_name ^ "::__Field(const ::String &inName,hx::PropertyAccess inCallProp)\n{\n");
+         let get_field_dat = List.map (fun f ->
+            (f.cf_name, String.length f.cf_name,
+               (match f.cf_kind with
+               | Var { v_read = AccCall } when is_extern_field f -> "if (" ^ (checkPropCall f) ^ ") return hx::Val(" ^(keyword_remap ("get_" ^ f.cf_name)) ^ "());"
+               | Var { v_read = AccCall } -> "return hx::Val( " ^ (checkPropCall f) ^ " ? " ^ (keyword_remap ("get_" ^ f.cf_name)) ^ "() : " ^
+                     ((keyword_remap f.cf_name) ^ (if (variable_field f) then "" else "_dyn()")) ^ ");"
+               | _ -> "return hx::Val( " ^ ((keyword_remap f.cf_name) ^ if (variable_field f) then "" else "_dyn()") ^ ");"
+            ) ) )
+         in
+         dump_quick_field_test (get_field_dat reflect_member_readable);
+         if (implement_dynamic) then
+            output_cpp "\tHX_CHECK_DYNAMIC_GET_FIELD(inName);\n";
+         output_cpp ("\treturn super::__Field(inName,inCallProp);\n}\n\n");
+
+      end;
+
+      if (has_get_static_field class_def) then begin
+         output_cpp ("bool " ^ class_name ^ "::__GetStatic(const ::String &inName, Dynamic &outValue, hx::PropertyAccess inCallProp)\n{\n");
+         let get_field_dat = List.map (fun f ->
+            (f.cf_name, String.length f.cf_name,
+               (match f.cf_kind with
+               | Var { v_read = AccCall } when is_extern_field f -> "if (" ^ (checkPropCall f) ^ ") { outValue = " ^(keyword_remap ("get_" ^ f.cf_name)) ^ "(); return true; }"
+               | Var { v_read = AccCall } -> "outValue = " ^ (checkPropCall f) ^ " ? " ^ (keyword_remap ("get_" ^ f.cf_name)) ^ "() : " ^
+                     ((keyword_remap f.cf_name) ^ if (variable_field f) then "" else "_dyn()") ^ "; return true;";
+               | _ when variable_field f -> "outValue = " ^ (keyword_remap f.cf_name) ^ "; return true;"
+               | _ -> "outValue = " ^ ((native_field_name_remap true f) ^ "_dyn(); return true;")
+               )
+            ) )
+         in
+         dump_quick_field_test (get_field_dat reflect_static_readable);
+         output_cpp ("\treturn false;\n}\n\n");
+      end;
+
+      (* Dynamic "Set" Field function *)
+      if (has_set_member_field class_def) then begin
+
+         output_cpp ("hx::Val " ^ class_name ^ "::__SetField(const ::String &inName,const hx::Val &inValue,hx::PropertyAccess inCallProp)\n{\n");
+
+         let set_field_dat = List.map (fun f ->
+            let default_action =
+               (keyword_remap f.cf_name) ^ "=inValue.Cast< " ^ (ctx_type_string ctx f.cf_type) ^ " >();" ^
+                  " return inValue;" in
+            (f.cf_name, String.length f.cf_name,
+               (match f.cf_kind with
+               | Var { v_write = AccCall } -> "if (" ^ (checkPropCall f) ^ ") return hx::Val( " ^ (keyword_remap ("set_" ^ f.cf_name)) ^ "(inValue) );"
+                  ^ ( if is_extern_field f then "" else default_action )
+               | _ -> default_action
+               )
+            )
+         ) in
+
+         dump_quick_field_test (set_field_dat reflect_write_member_variables);
+         if (implement_dynamic) then begin
+            output_cpp ("\ttry { return super::__SetField(inName,inValue,inCallProp); }\n");
+            output_cpp ("\tcatch(Dynamic e) { HX_DYNAMIC_SET_FIELD(inName,inValue); }\n");
+            output_cpp "\treturn inValue;\n}\n\n";
+         end else
+            output_cpp ("\treturn super::__SetField(inName,inValue,inCallProp);\n}\n\n");
+      end;
+
+      if (has_set_static_field class_def) then begin
+
+         output_cpp ("bool " ^ class_name ^ "::__SetStatic(const ::String &inName,Dynamic &ioValue,hx::PropertyAccess inCallProp)\n{\n");
+
+         let set_field_dat = List.map (fun f ->
+            let default_action =
+               (keyword_remap f.cf_name) ^ "=ioValue.Cast< " ^ (ctx_type_string ctx f.cf_type) ^ " >(); return true;" in
+            (f.cf_name, String.length f.cf_name,
+               (match f.cf_kind with
+               | Var { v_write = AccCall } -> "if (" ^ (checkPropCall f) ^ ")  ioValue = " ^ (keyword_remap ("set_" ^ f.cf_name)) ^ "(ioValue);"
+                  ^ ( if is_extern_field f then "" else " else " ^ default_action )
+               | _ -> default_action
+               )
+            )
+         ) in
+
+         dump_quick_field_test (set_field_dat reflect_write_static_variables);
+         output_cpp ("\treturn false;\n}\n\n");
+      end;
+
+
+
+
+      (* For getting a list of data members (eg, for serialization) *)
+      if (has_get_fields class_def) then begin
+         let append_field =
+            (fun field -> output_cpp ("\toutFields->push(" ^( str field.cf_name )^ ");\n")) in
+         let is_data_field field = (match follow field.cf_type with | TFun _ -> false | _ -> true) in
+
+         output_cpp ("void " ^ class_name ^ "::__GetFields(Array< ::String> &outFields)\n{\n");
+         List.iter append_field (List.filter is_data_field class_def.cl_ordered_fields);
+         if (implement_dynamic) then
+            output_cpp "\tHX_APPEND_DYNAMIC_FIELDS(outFields);\n";
+         output_cpp "\tsuper::__GetFields(outFields);\n";
+         output_cpp "};\n\n";
+      end;
+
+      let storage field = match type_string field.cf_type with
+         | "bool" -> "hx::fsBool"
+         | "int" -> "hx::fsInt"
+         | "Float" -> "hx::fsFloat"
+         | "::String" -> "hx::fsString"
+         | str -> "hx::fsObject" ^ " /*" ^ str ^ "*/ "
+         in
+      let dump_member_storage = (fun field ->
+         output_cpp ("\t{" ^ (storage field) ^ ",(int)offsetof(" ^ class_name ^"," ^ (keyword_remap field.cf_name) ^")," ^
+            (str field.cf_name) ^ "},\n")
+         )
+      in
+      let dump_static_storage = (fun field ->
+         output_cpp ("\t{" ^ (storage field) ^ ",(void *) &" ^ class_name ^"::" ^ (keyword_remap field.cf_name) ^"," ^
+            (str field.cf_name) ^ "},\n")
+         )
+      in
+
+      output_cpp "#if HXCPP_SCRIPTABLE\n";
+
+      let stored_fields = List.filter is_data_member implemented_instance_fields in
+      if ( (List.length stored_fields) > 0) then begin
+         output_cpp ("static hx::StorageInfo " ^ class_name ^ "_sMemberStorageInfo[] = {\n");
+         List.iter dump_member_storage stored_fields;
+         output_cpp "\t{ hx::fsUnknown, 0, null()}\n};\n";
+      end else
+         output_cpp ("static hx::StorageInfo *" ^ class_name ^ "_sMemberStorageInfo = 0;\n");
+
+      let stored_statics = List.filter is_data_member implemented_fields in
+      if ( (List.length stored_statics) > 0) then begin
+         output_cpp ("static hx::StaticInfo " ^ class_name ^ "_sStaticStorageInfo[] = {\n");
+         List.iter dump_static_storage stored_statics;
+         output_cpp "\t{ hx::fsUnknown, 0, null()}\n};\n";
+      end else
+         output_cpp ("static hx::StaticInfo *" ^ class_name ^ "_sStaticStorageInfo = 0;\n");
+
+      output_cpp "#endif\n\n";
+   end; (* cl_interface *)
+
+   let sMemberFields = if List.length reflective_members>0 then begin
+      let memberFields = class_name ^ "_sMemberFields" in
+      output_cpp ("static ::String " ^ memberFields ^ "[] = {\n");
+      List.iter dump_field_name  reflective_members;
+      output_cpp "\t::String(null()) };\n\n";
+      memberFields
+   end else
+      "0 /* sMemberFields */";
+   in
+
+   if (not nativeGen) then begin
+      (* Mark static variables as used *)
+      output_cpp ("static void " ^ class_name ^ "_sMarkStatics(HX_MARK_PARAMS) {\n");
+      output_cpp ("\tHX_MARK_MEMBER_NAME(" ^ class_name ^ "::__mClass,\"__mClass\");\n");
+      List.iter (fun field ->
+         if (is_data_member field) then
+            output_cpp ("\tHX_MARK_MEMBER_NAME(" ^ class_name ^ "::" ^ (keyword_remap field.cf_name) ^ ",\"" ^  field.cf_name ^ "\");\n") )
+         implemented_fields;
+      output_cpp "};\n\n";
+
+      (* Visit static variables *)
+      output_cpp "#ifdef HXCPP_VISIT_ALLOCS\n";
+      output_cpp ("static void " ^ class_name ^ "_sVisitStatics(HX_VISIT_PARAMS) {\n");
+      output_cpp ("\tHX_VISIT_MEMBER_NAME(" ^ class_name ^ "::__mClass,\"__mClass\");\n");
+      List.iter (fun field ->
+         if (is_data_member field) then
+            output_cpp ("\tHX_VISIT_MEMBER_NAME(" ^ class_name ^ "::" ^ (keyword_remap field.cf_name) ^ ",\"" ^  field.cf_name ^ "\");\n") )
+         implemented_fields;
+      output_cpp "};\n\n";
+      output_cpp "#endif\n\n";
+   end;
+
+   let generate_script_function isStatic field scriptName callName =
+      match follow field.cf_type  with
+      | TFun (args,return_type) ->
+         output_cpp ("\nstatic void " ^ scriptName ^ "(hx::CppiaCtx *ctx) {\n");
+         let ret = script_signature return_type false in
+         if (ret<>"v") then output_cpp ("ctx->return" ^ (script_type return_type false) ^ "(");
+         if class_def.cl_interface then begin
+            output_cpp (class_name ^ "::" ^ callName ^ "(ctx->getThis()" ^ (if (List.length args) > 0 then "," else ""));
+         end else if isStatic then
+            output_cpp (class_name ^ "::" ^ callName ^ "(")
+         else
+            output_cpp ("((" ^  class_name ^ "*)ctx->getThis())->" ^  callName ^ "(");
+
+         let (signature,_,_) = List.fold_left (fun (signature,sep,size) (_,opt,t) ->
+            output_cpp (sep ^ "ctx->get" ^ (script_type t opt) ^ "(" ^ size ^ ")");
+            (signature ^ (script_signature t opt ), ",", (size^"+sizeof(" ^ (script_size_type t opt) ^ ")") ) ) (ret,"","sizeof(void*)")  args
+         in
+
+         output_cpp ")";
+         if (ret<>"v") then output_cpp (")");
+         output_cpp (";\n}\n");
+         signature;
+      | _ -> ""
+   in
+
+
+   let newInteface = class_def.cl_interface in
+
+   if (scriptable && not nativeGen) then begin
+      let delegate = "this->" in
+      let dump_script_field idx (field,f_args,return_t) =
+         let args = ctx_tfun_arg_list ctx true f_args in
+         let names = List.map (fun (n,_,_) -> keyword_remap n) f_args in
+         let return_type = ctx_type_string ctx return_t in
+         let ret = if (return_type="Void" || return_type="void") then " " else "return " in
+         let name = keyword_remap field.cf_name in
+         let vtable =  "__scriptVTable[" ^ (string_of_int (idx+1) ) ^ "] " in
+         let args_varray = (List.fold_left (fun l n -> l ^ ".Add(" ^ n ^ ")") "Array<Dynamic>()" names) in
+
+         output_cpp ("	" ^ return_type ^ " " ^ name ^ "( " ^ args ^ " ) {\n");
+         if newInteface then begin
+            output_cpp ("\t\thx::CppiaCtx *__ctx = hx::CppiaCtx::getCurrent();\n" );
+            output_cpp ("\t\thx::AutoStack __as(__ctx);\n" );
+            output_cpp ("\t\t__ctx->pushObject(this);\n" );
+            List.iter (fun (name,opt, t ) ->
+               output_cpp ("\t\t__ctx->push" ^ (script_type t opt) ^ "(" ^ (keyword_remap name) ^ ");\n" );
+            ) f_args;
+            let interfaceSlot = string_of_int( -(cpp_get_interface_slot ctx name) ) in
+            output_cpp ("\t\t" ^ ret ^ "__ctx->run" ^ (script_type return_t false) ^ "(__GetScriptVTable()[" ^ interfaceSlot ^ "]);\n" );
+            output_cpp "\t}\n";
+         end else begin
+            output_cpp ("\tif (" ^ vtable ^ ") {\n" );
+            output_cpp ("\t\thx::CppiaCtx *__ctx = hx::CppiaCtx::getCurrent();\n" );
+            output_cpp ("\t\thx::AutoStack __as(__ctx);\n" );
+            output_cpp ("\t\t__ctx->pushObject(" ^ (if class_def.cl_interface then "mDelegate.mPtr" else "this" ) ^");\n" );
+            List.iter (fun (name,opt, t ) ->
+               output_cpp ("\t\t__ctx->push" ^ (script_type t opt) ^ "(" ^ (keyword_remap name) ^ ");\n" );
+            ) f_args;
+            output_cpp ("\t\t" ^ ret ^ "__ctx->run" ^ (script_type return_t false) ^ "(" ^ vtable ^ ");\n" );
+            output_cpp ("\t}  else " ^ ret );
+
+
+            if (class_def.cl_interface) then begin
+               output_cpp (" " ^ delegate ^ "__Field(HX_CSTRING(\"" ^ field.cf_name ^ "\"), hx::paccNever)");
+               if (List.length names <= 5) then
+                  output_cpp ("->__run(" ^ (String.concat "," names) ^ ");")
+               else
+                  output_cpp ("->__Run(" ^ args_varray ^ ");");
+            end else
+               output_cpp (class_name ^ "::" ^ name ^ "(" ^ (String.concat "," names)^ ");");
+            if (return_type<>"void") then
+               output_cpp "return null();";
+            output_cpp "}\n";
+            if (class_def.cl_interface) && not dynamic_interface_closures then begin
+               output_cpp ("	Dynamic " ^ name ^ "_dyn() { return mDelegate->__Field(HX_CSTRING(\"" ^ field.cf_name ^ "\"), hx::paccNever); }\n\n");
+
+            end
+         end
+      in
+
+      let new_sctipt_functions = if newInteface then
+            all_virtual_functions class_def
+         else
+            List.filter (fun (f,_,_) -> (not (is_override class_def f.cf_name)) ) functions
+      in
+      let sctipt_name = class_name ^ "__scriptable" in
+
+      if newInteface then begin
+         output_cpp ("class " ^ sctipt_name ^ " : public hx::Object {\n" );
+         output_cpp "public:\n";
+      end else begin
+         output_cpp ("class " ^ sctipt_name ^ " : public " ^ class_name ^ " {\n" );
+         output_cpp ("   typedef "^sctipt_name ^" __ME;\n");
+         output_cpp ("   typedef "^class_name ^" super;\n");
+         let has_funky_toString = List.exists (fun f -> f.cf_name="toString") class_def.cl_ordered_statics  ||
+                                 List.exists (fun f -> f.cf_name="toString" && field_arg_count f <> 0) class_def.cl_ordered_fields in
+         let super_string = if has_funky_toString then class_name ^ "::super" else class_name in
+         output_cpp ("   typedef "^ super_string ^" __superString;\n");
+         if (class_def.cl_interface) then
+            output_cpp ("   HX_DEFINE_SCRIPTABLE_INTERFACE\n")
+         else begin
+            output_cpp ("   HX_DEFINE_SCRIPTABLE(HX_ARR_LIST" ^ (string_of_int (List.length constructor_var_list) ) ^ ")\n");
+            if (not implement_dynamic) then
+               output_cpp "\tHX_DEFINE_SCRIPTABLE_DYNAMIC;\n";
+         end;
+      end;
+
+      list_iteri dump_script_field functions;
+      output_cpp ("};\n\n");
+
+      if (List.length new_sctipt_functions) > 0 then begin
+         let sigs = Hashtbl.create 0 in
+         List.iter (fun (f,_,_) ->
+            let s = generate_script_function false f ("__s_" ^f.cf_name) (keyword_remap f.cf_name) in
+            Hashtbl.add sigs f.cf_name s
+         ) new_sctipt_functions;
+
+         output_cpp "static hx::ScriptNamedFunction __scriptableFunctions[] = {\n";
+         List.iter (fun (f,_,_) ->
+            let s = try Hashtbl.find sigs f.cf_name with Not_found -> "v" in
+            output_cpp ("  hx::ScriptNamedFunction(\"" ^ f.cf_name ^ "\",__s_" ^ f.cf_name ^ ",\"" ^ s ^ "\"),\n" ) ) new_sctipt_functions;
+         output_cpp "  hx::ScriptNamedFunction(0,0,0) };\n";
+      end else
+         output_cpp "static hx::ScriptNamedFunction *__scriptableFunctions = 0;\n";
+
+      if newInteface then begin
+         output_cpp ("\n\n" ^ class_name ^ " " ^ class_name ^ "_scriptable = {\n");
+         List.iter (fun (f,args,return_type) ->
+            let cast = cpp_tfun_signature ctx true args return_type in
+            output_cpp ("\t" ^ cast ^ "&" ^ sctipt_name ^ "::" ^ (keyword_remap f.cf_name) ^ ",\n")
+         ) new_sctipt_functions;
+         output_cpp ("};\n");
+      end;
+
+   end;
+
+
+   let class_name_text = join_class_path class_path "." in
+
+   (* Initialise static in boot function ... *)
+   if (not class_def.cl_interface && not nativeGen) then begin
+      (* Remap the specialised "extern" classes back to the generic names *)
+      output_cpp ("hx::Class " ^ class_name ^ "::__mClass;\n\n");
+      if (scriptable) then begin
+         (match class_def.cl_constructor with
+            | Some field  ->
+                  let signature = generate_script_function false field "__script_construct_func" "__construct" in
+                  output_cpp ("hx::ScriptFunction " ^ class_name ^ "::__script_construct(__script_construct_func,\"" ^ signature ^ "\");\n");
+            | _ ->
+                  output_cpp ("hx::ScriptFunction " ^ class_name ^ "::__script_construct(0,0);\n");
+         );
+      end;
+
+      let reflective_statics = List.filter (reflective class_def) implemented_fields in
+      let sStaticFields = if List.length reflective_statics > 0 then begin
+         output_cpp ("static ::String " ^ class_name ^ "_sStaticFields[] = {\n");
+         List.iter dump_field_name  reflective_statics;
+         output_cpp "\t::String(null())\n};\n\n";
+         class_name ^ "_sStaticFields";
+      end else
+        "0 /* sStaticFields */"
+      in
+
+      output_cpp ("void " ^ class_name ^ "::__register()\n{\n");
+      output_cpp ("\thx::Static(__mClass) = new hx::Class_obj();\n");
+      output_cpp ("\t__mClass->mName = " ^  (str class_name_text)  ^ ";\n");
+      output_cpp ("\t__mClass->mSuper = &super::__SGetClass();\n");
+      output_cpp ("\t__mClass->mConstructEmpty = &__CreateEmpty;\n");
+      output_cpp ("\t__mClass->mConstructArgs = &__Create;\n");
+      output_cpp ("\t__mClass->mGetStaticField = &" ^ (
+         if (has_get_static_field class_def) then class_name ^ "::__GetStatic;\n" else "hx::Class_obj::GetNoStaticField;\n" ));
+      output_cpp ("\t__mClass->mSetStaticField = &" ^ (
+         if (has_set_static_field class_def) then class_name ^ "::__SetStatic;\n" else "hx::Class_obj::SetNoStaticField;\n" ));
+      output_cpp ("\t__mClass->mMarkFunc = " ^ class_name ^ "_sMarkStatics;\n");
+      output_cpp ("\t__mClass->mStatics = hx::Class_obj::dupFunctions(" ^ sStaticFields ^ ");\n");
+      output_cpp ("\t__mClass->mMembers = hx::Class_obj::dupFunctions(" ^ sMemberFields ^ ");\n");
+      output_cpp ("\t__mClass->mCanCast = hx::TCanCast< " ^ class_name ^ " >;\n");
+      output_cpp ("#ifdef HXCPP_VISIT_ALLOCS\n\t__mClass->mVisitFunc = " ^ class_name ^ "_sVisitStatics;\n#endif\n");
+      output_cpp ("#ifdef HXCPP_SCRIPTABLE\n\t__mClass->mMemberStorageInfo = " ^ class_name ^ "_sMemberStorageInfo;\n#endif\n");
+      output_cpp ("#ifdef HXCPP_SCRIPTABLE\n\t__mClass->mStaticStorageInfo = " ^ class_name ^ "_sStaticStorageInfo;\n#endif\n");
+      output_cpp ("\thx::_hx_RegisterClass(__mClass->mName, __mClass);\n");
+      if (scriptable) then
+         output_cpp ("  HX_SCRIPTABLE_REGISTER_CLASS(\""^class_name_text^"\"," ^ class_name ^ ");\n");
+      Hashtbl.iter (fun _ intf_def ->
+          output_cpp ("\tHX_REGISTER_VTABLE_OFFSET( " ^ class_name ^ "," ^ (join_class_path_remap intf_def.cl_path "::")^ ");\n");
+          ) native_implemented;
+      output_cpp ("}\n\n");
+   end else if not nativeGen then begin
+      output_cpp ("hx::Class " ^ class_name ^ "::__mClass;\n\n");
+
+      output_cpp ("void " ^ class_name ^ "::__register()\n{\n");
+
+      output_cpp ("\thx::Static(__mClass) = new hx::Class_obj();\n");
+      output_cpp ("\t__mClass->mName = " ^  (str class_name_text)  ^ ";\n");
+      output_cpp ("\t__mClass->mSuper = &super::__SGetClass();\n");
+      output_cpp ("\t__mClass->mMarkFunc = " ^ class_name ^ "_sMarkStatics;\n");
+      (*output_cpp ("\t__mClass->mStatics = hx::Class_obj::dupFunctions(" ^ sStaticFields ^ ");\n");*)
+      output_cpp ("\t__mClass->mMembers = hx::Class_obj::dupFunctions(" ^ sMemberFields ^ ");\n");
+      output_cpp ("\t__mClass->mCanCast = hx::TIsInterface< (int)" ^ (cpp_class_hash class_def)  ^ " >;\n");
+      output_cpp ("#ifdef HXCPP_VISIT_ALLOCS\n\t__mClass->mVisitFunc = " ^ class_name ^ "_sVisitStatics;\n#endif\n");
+      output_cpp ("\thx::_hx_RegisterClass(__mClass->mName, __mClass);\n");
+      if (scriptable) then
+         output_cpp ("  HX_SCRIPTABLE_REGISTER_INTERFACE(\""^class_name_text^"\"," ^ class_name ^ ");\n");
+      output_cpp ("}\n\n");
+   end;
+
+   if (has_boot_field class_def) then begin
+      output_cpp ("void " ^ class_name ^ "::__boot()\n{\n");
+
+      List.iter (gen_field_init ctx class_def ) (List.filter should_implement_field class_def.cl_ordered_statics);
+
+      output_cpp ("}\n\n");
+   end;
+
+
+   gen_close_namespace output_cpp class_path;
+
+   cpp_file#close;
+ in
+ (*
+    Header code
+ *)
+ let generate_class_header () =
+   let common_ctx = baseCtx.ctx_common in
+   let class_path = class_def.cl_path in
+   let nativeGen = has_meta_key class_def.cl_meta Meta.NativeGen in
+   let class_name = (snd class_path) ^ (if nativeGen then "" else "_obj") in
+   let smart_class_name =  (snd class_path)  in
+   let scriptable = inScriptable && not class_def.cl_private in
+   (*let cpp_file = new_cpp_file common_ctx.file class_path in*)
+   let debug = if (has_meta_key class_def.cl_meta Meta.NoDebug) || ( Common.defined  baseCtx.ctx_common Define.NoDebug)
+      then 0 else 1 in
+
+   let h_file = new_header_file common_ctx common_ctx.file class_path in
+   let ctx = file_context baseCtx h_file debug in
+
+
+
+   let parent,super = match class_def.cl_super with
+      | Some (klass,params) ->
+            let name = (tcpp_to_string_suffix "_obj" (cpp_instance_type ctx klass params) ) in
+            (if class_def.cl_interface && nativeGen then "virtual " else "" ) ^ name, name
+      | None when nativeGen && class_def.cl_interface  -> "virtual hx::NativeInterface", "hx::NativeInterface"
+      | None when class_def.cl_interface -> "", "hx::Object"
+      | None when nativeGen -> "", ""
+      | None -> "hx::Object", "hx::Object"
+      in
+   let output_h = (h_file#write) in
+   let def_string = join_class_path class_path "_"  in
+
+
+   begin_header_file (h_file#write_h) def_string nativeGen;
+
+   (* Include the real header file for the super class *)
+   (match class_def.cl_super with
+   | Some super ->
+      let super_path = (fst super).cl_path in
+      h_file#add_include super_path
+   | _ -> () );
+
+   (* And any interfaces ... *)
+   List.iter (fun imp-> h_file#add_include (fst imp).cl_path)
+      (real_interfaces class_def.cl_implements);
+
+   (* Only need to foreward-declare classes that are mentioned in the header file
+      (ie, not the implementation)  *)
+   let referenced,flags = find_referenced_types_flags ctx (TClassDecl class_def) super_deps (Hashtbl.create 0) true false scriptable in
+   List.iter2 ( fun r f -> gen_forward_decl h_file r f ) referenced flags;
+   output_h "\n";
+
+   output_h ( get_class_code class_def Meta.HeaderCode );
+   let inc = get_meta_string_path class_def.cl_meta Meta.HeaderInclude in
+   if (inc<>"") then
+      output_h ("#include \"" ^ inc ^ "\"\n");
+
+   gen_open_namespace output_h class_path;
+   output_h "\n\n";
+   output_h ( get_class_code class_def Meta.HeaderNamespaceCode );
+
+   let extern_class =  Common.defined common_ctx Define.DllExport in
+   let attribs = "HXCPP_" ^ (if extern_class then "EXTERN_" else "") ^ "CLASS_ATTRIBUTES" in
+
+   let dump_native_interfaces () =
+      List.iter ( fun(c,params) ->
+         output_h (" , public virtual " ^ (join_class_path c.cl_path "::") )
+      ) (List.filter  (fun (t,_) -> is_native_gen_class t) class_def.cl_implements);
+   in
+
+   if (class_def.cl_interface && not nativeGen) then begin
+      output_h ("class " ^ attribs ^ " " ^ class_name ^ " {\n");
+      output_h "\tpublic:\n";
+      output_h ("\t\ttypedef " ^ super ^ " super;\n");
+   end else if (super="") then begin
+      output_h ("class " ^ attribs ^ " " ^ class_name);
+      dump_native_interfaces();
+      output_h "\n{\n\tpublic:\n";
+   end else begin
+      output_h ("class " ^ attribs ^ " " ^ class_name ^ " : public " ^ parent );
+      dump_native_interfaces();
+      output_h "\n{\n\tpublic:\n";
+      if not nativeGen then begin
+         output_h ("\t\ttypedef " ^ super ^ " super;\n");
+         output_h ("\t\ttypedef " ^ class_name ^ " OBJ_;\n");
+      end
+   end;
+
+
+
+   if (not class_def.cl_interface && not nativeGen) then begin
+      output_h ("\t\t" ^ class_name ^  "();\n");
+      output_h "\n\tpublic:\n";
+      output_h ("\t\tvoid __construct(" ^ constructor_type_args ^ ");\n");
+      let new_arg = if (has_gc_references ctx class_def) then "true" else "false" in
+      let name = const_char_star class_name_text in
+      output_h ("\t\tinline void *operator new(size_t inSize, bool inContainer=" ^ new_arg ^",const char *inName=" ^name^ ")\n" );
+      output_h ("\t\t\t{ return hx::Object::operator new(inSize,inContainer,inName); }\n" );
+      output_h ("\t\tinline void *operator new(size_t inSize, int extra)\n" );
+      output_h ("\t\t\t{ return hx::Object::operator new(inSize+extra," ^ new_arg ^ "," ^ name ^ "); }\n" );
+      output_h ("\t\tstatic " ^ptr_name^ " __new(" ^constructor_type_args ^");\n");
+      output_h ("\t\tstatic Dynamic __CreateEmpty();\n");
+      output_h ("\t\tstatic Dynamic __Create(hx::DynamicArray inArgs);\n");
+      if (scriptable) then
+         output_h ("\t\tstatic hx::ScriptFunction __script_construct;\n");
+      output_h ("\t\t//~" ^ class_name ^ "();\n\n");
+      output_h ("\t\tHX_DO_RTTI_ALL;\n");
+      if (has_get_member_field class_def) then
+         output_h ("\t\thx::Val __Field(const ::String &inString, hx::PropertyAccess inCallProp);\n");
+      if (has_get_static_field class_def) then
+         output_h ("\t\tstatic bool __GetStatic(const ::String &inString, Dynamic &outValue, hx::PropertyAccess inCallProp);\n");
+      if (has_set_member_field class_def) then
+         output_h ("\t\thx::Val __SetField(const ::String &inString,const hx::Val &inValue, hx::PropertyAccess inCallProp);\n");
+      if (has_set_static_field class_def) then
+         output_h ("\t\tstatic bool __SetStatic(const ::String &inString, Dynamic &ioValue, hx::PropertyAccess inCallProp);\n");
+      if (has_get_fields class_def) then
+         output_h ("\t\tvoid __GetFields(Array< ::String> &outFields);\n");
+
+      if (implement_dynamic) then
+         output_h ("\t\tHX_DECLARE_IMPLEMENT_DYNAMIC;\n");
+      output_h ("\t\tstatic void __register();\n");
+      if (override_iteration) then begin
+         output_h ("\t\tvoid __Mark(HX_MARK_PARAMS);\n");
+         output_h ("\t\tvoid __Visit(HX_VISIT_PARAMS);\n");
+      end;
+
+      if (implementsNative) then begin
+         output_h ("\n\t\tHX_NATIVE_IMPLEMENTATION\n");
+         List.iter (fun field ->
+            match follow field.cf_type, field.cf_kind with
+            | _, Method MethDynamic  -> ()
+            | TFun (args,return_type), _  ->
+                let retVal = ctx_type_string ctx return_type in
+                let ret = if retVal="void" then "" else "return " in
+                let name = keyword_remap field.cf_name in
+                let argNames = List.map (fun (name,_,_) -> keyword_remap name ) args in
+                output_h ( "\t\t" ^ retVal ^" " ^ name ^ "( " ^ ctx_tfun_arg_list ctx true args ^ ") {\n");
+                output_h ( "\t\t\t" ^ ret ^ "super::" ^ name ^ "( " ^ (String.concat "," argNames) ^ ");\n\t\t}\n");
+            | _ -> ()
+            ) neededInterfaceFunctions;
+         output_h ("\n");
+      end;
+
+      if ( (List.length implemented) > 0 ) then begin
+         output_h "\t\tvoid *_hx_getInterface(int inHash);\n";
+         output_h (String.concat "\n" !header_glue);
+      end;
+
+
+      if (has_init_field class_def) then
+         output_h "\t\tstatic void __init__();\n\n";
+      output_h ("\t\t::String __ToString() const { return " ^ (str smart_class_name) ^ "; }\n\n");
+   end else if not nativeGen then begin
+      output_h ("\t\tHX_DO_INTERFACE_RTTI;\n\n");
+   end else begin
+      (* native interface *) ( )
+   end;
+
+   if (has_boot_field class_def) then
+      output_h ("\t\tstatic void __boot();\n");
+
+
+   (match class_def.cl_array_access with
+   | Some t -> output_h ("\t\ttypedef " ^ (type_string t) ^ " __array_access;\n")
+   | _ -> ());
+
+
+   List.iter (gen_member_def ctx class_def true class_def.cl_interface) (List.filter should_implement_field class_def.cl_ordered_statics);
+
+   if class_def.cl_interface then begin
+      List.iter (fun (field,_,_) -> gen_member_def ctx class_def false true field) functions;
+   end else begin
+      List.iter (gen_member_def ctx class_def false false) (List.filter should_implement_field class_def.cl_ordered_fields);
+   end;
+
+
+   output_h ( get_class_code class_def Meta.HeaderClassCode );
+   output_h "};\n\n";
+
+   gen_close_namespace output_h class_path;
+
+   end_header_file output_h def_string;
+   h_file#close;
+   let depend_referenced = find_referenced_types ctx (TClassDecl class_def) super_deps constructor_deps false true false in
+   depend_referenced
+
+  in
+
+  (* create header and cpp files *)
+  if not (nativeGen && class_def.cl_interface) then
+     generate_class_cpp ();
+  generate_class_header ()
+;;
+
+
+
+
+
+
+let write_resources common_ctx =
+
+   let idx = ref 0 in
+
+   Hashtbl.iter (fun _ data ->
+      let id = "__res_" ^ (string_of_int !idx) in
+      let resource_file = new_cpp_file common_ctx common_ctx.file (["resources"],id) in
+      resource_file#write "namespace hx {\n";
+      resource_file#write_i ("unsigned char " ^ id ^ "[] = {\n");
+      resource_file#write_i "0xff, 0xff, 0xff, 0xff,\n";
+      for i = 0 to String.length data - 1 do
+      let code = Char.code (String.unsafe_get data i) in
+         resource_file#write  (Printf.sprintf "%d," code);
+         if ( (i mod 10) = 9) then resource_file#write "\n";
+      done;
+      resource_file#write ("0x00 };\n");
+      incr idx;
+      resource_file#write ("}\n");
+      resource_file#close;
+   ) common_ctx.resources;
+
+
+   let resource_file = new_cpp_file common_ctx common_ctx.file ([],"__resources__") in
+   resource_file#write_h "#include <hxcpp.h>\n\n";
+   resource_file#write "namespace hx {\n";
+
+   idx := 0;
+   Hashtbl.iter (fun _ data ->
+      let id = "__res_" ^ (string_of_int !idx) in
+      resource_file#write_i ("extern unsigned char " ^ id ^ "[];\n");
+      incr idx;
+   ) common_ctx.resources;
+
+   resource_file#write "}\n\n";
+
+   idx := 0;
+   resource_file#write "hx::Resource __Resources[] = ";
+   resource_file#begin_block;
+   Hashtbl.iter (fun name data ->
+      let id = "__res_" ^ (string_of_int !idx) in
+      resource_file#write_i
+         ("{ " ^ (str name) ^ "," ^ (string_of_int (String.length data)) ^ "," ^
+            "hx::" ^ id ^ " + 4 },\n");
+      incr idx;
+   ) common_ctx.resources;
+
+   resource_file#write_i "{ ::String(null()),0,0 }\n";
+   resource_file#end_block_line;
+   resource_file#write ";\n\n";
+   resource_file#write "namespace hx { Resource *GetResources() { return __Resources; } }\n";
+   resource_file#close;;
+
+
+
+let write_build_data common_ctx filename classes main_deps boot_deps build_extra extern_src exe_name =
+   let buildfile = open_out filename in
+   let include_prefix = get_include_prefix common_ctx true in
+   let add_class_to_buildfile class_path deps fileXml =
+      let cpp = (join_class_path class_path "/") ^ (source_file_extension common_ctx) in
+      output_string buildfile ( "  <file name=\"src/" ^ cpp ^ "\" ");
+      if fileXml<>"" then output_string buildfile fileXml;
+      output_string buildfile (">\n" );
+      let project_deps = List.filter (fun path -> not (is_internal_class path) ) deps in
+      List.iter (fun path-> output_string buildfile ("   <depend name=\"" ^
+      ( match path with
+         | (["@verbatim"],file) -> file
+         | _ -> "include/" ^ include_prefix ^ (join_class_path path "/") ^ ".h" )
+      ^ "\"/>\n") ) project_deps;
+      output_string buildfile ( "  </file>\n" )
+   in
+   let add_classdef_to_buildfile (class_path, deps, object_def )  =
+      let fileXml = match object_def with
+         | TClassDecl class_def -> get_meta_string class_def.cl_meta Meta.FileXml
+         | TEnumDecl enum_def -> get_meta_string enum_def.e_meta Meta.FileXml
+         | _ -> ""
+      in
+      add_class_to_buildfile class_path deps fileXml
+   in
+
+   output_string buildfile "<xml>\n";
+   output_string buildfile ("<set name=\"HXCPP_API_LEVEL\" value=\"" ^
+            (Common.defined_value common_ctx Define.HxcppApiLevel) ^ "\" />\n");
+   output_string buildfile "<files id=\"haxe\">\n";
+   output_string buildfile "<compilerflag value=\"-Iinclude\"/>\n";
+   List.iter add_classdef_to_buildfile classes;
+   add_class_to_buildfile ( [] , "__boot__")  boot_deps "";
+   add_class_to_buildfile ( [] , "__files__")  [] "if='HXCPP_DEBUGGER'";
+   output_string buildfile ("   <file name=\"${HXCPP}/src/hx/NoFiles.cpp\" unless=\"HXCPP_DEBUGGER\" />\n");
+   add_class_to_buildfile ( [] , "__resources__")  [] "";
+   output_string buildfile "</files>\n";
+   output_string buildfile "<files id=\"__lib__\">\n";
+   output_string buildfile "<compilerflag value=\"-Iinclude\"/>\n";
+   add_class_to_buildfile ( [] , "__lib__") main_deps "";
+   output_string buildfile "</files>\n";
+   output_string buildfile "<files id=\"__main__\">\n";
+   output_string buildfile "<compilerflag value=\"-Iinclude\"/>\n";
+   add_class_to_buildfile  ( [] , "__main__") main_deps "";
+   output_string buildfile "</files>\n";
+   output_string buildfile "<files id=\"__resources__\">\n";
+   let idx = ref 0 in
+   Hashtbl.iter (fun _ data ->
+      let id = "__res_" ^ (string_of_int !idx) in
+      output_string buildfile ("<file name=\"src/resources/" ^ id ^ ".cpp\" tags=\"optim-none\" />\n");
+      incr idx;
+   ) common_ctx.resources;
+   output_string buildfile "</files>\n";
+   output_string buildfile "<files id=\"__externs__\">\n";
+   List.iter (fun src -> output_string buildfile ("<file name=\"" ^src^ "\" />\n") ) extern_src;
+   output_string buildfile "</files>\n";
+   output_string buildfile ("<set name=\"HAXE_OUTPUT\" value=\"" ^ exe_name ^ "\" />\n");
+   output_string buildfile "<include name=\"${HXCPP}/build-tool/BuildCommon.xml\"/>\n";
+   output_string buildfile build_extra;
+   output_string buildfile "</xml>\n";
+   close_out buildfile;;
+
+let write_build_options common_ctx filename defines =
+   let writer = cached_source_writer common_ctx filename in
+   PMap.iter ( fun name value -> match name with
+      | "true" | "sys" | "dce" | "cpp" | "debug" -> ()
+      | _ ->  writer#write (name ^ "="^(escape_command value)^ "\n" ) ) defines;
+   let cmd = Unix.open_process_in "haxelib path hxcpp" in
+   writer#write ("hxcpp=" ^ (Pervasives.input_line cmd));
+   Pervasives.ignore (Unix.close_process_in cmd);
+   writer#close;;
+
+let create_member_types common_ctx =
+   let result = Hashtbl.create 0 in
+      List.iter (fun object_def ->
+         (match object_def with
+         | TClassDecl class_def when not class_def.cl_interface ->
+            let rec add_override to_super =
+               let class_name = (join_class_path to_super.cl_path ".") in
+               List.iter (fun member -> Hashtbl.add result (class_name ^ "." ^ member.cf_name) "virtual " ) class_def.cl_ordered_fields;
+               match to_super.cl_super with
+               | Some super -> add_override (fst super)
+               | _ -> ()
+             in
+             (match  class_def.cl_super with Some super -> add_override (fst super) | _->())
+         | _ -> ()
+         ) ) common_ctx.types;
+   result;;
+
+(* Builds inheritance tree, so header files can include parents defs.  *)
+let create_super_dependencies common_ctx =
+   let result = Hashtbl.create 0 in
+   List.iter (fun object_def ->
+      (match object_def with
+      | TClassDecl class_def when not class_def.cl_extern ->
+         let deps = ref [] in
+         (match class_def.cl_super with Some super ->
+            if not (fst super).cl_extern then
+               deps := ((fst super).cl_path) :: !deps
+         | _ ->() );
+         List.iter (fun imp -> if not (fst imp).cl_extern then deps := (fst imp).cl_path :: !deps) (real_non_native_interfaces class_def.cl_implements);
+         Hashtbl.add result class_def.cl_path !deps;
+      | TEnumDecl enum_def when not enum_def.e_extern ->
+         Hashtbl.add result enum_def.e_path [];
+      | _ -> () );
+      ) common_ctx.types;
+   result;;
+
+let create_constructor_dependencies common_ctx =
+   let result = Hashtbl.create 0 in
+   List.iter (fun object_def ->
+      (match object_def with
+      | TClassDecl class_def when not class_def.cl_extern ->
+         (match class_def.cl_constructor with
+         | Some func_def -> Hashtbl.add result class_def.cl_path func_def
+         | _ -> () )
+      | _ -> () );
+      ) common_ctx.types;
+   result;;
+
+let is_this expression =
+   match (remove_parens expression).eexpr with
+   | TConst TThis -> true
+   | _ -> false
+;;
+
+let is_super expression =
+   match (remove_parens expression).eexpr with
+   | TConst TSuper -> true
+   | _ -> false
+;;
+
+
+let is_assign_op op =
+   match op with
+   | OpAssign
+   | OpAssignOp _ -> true
+   | _ -> false
+;;
+
+let rec script_type_string haxe_type =
+   match haxe_type with
+   | TType ({ t_path = ([],"Null") },[t]) ->
+      (match follow t with
+      | TAbstract ({ a_path = [],"Int" },_)
+      | TAbstract ({ a_path = [],"Float" },_)
+      | TAbstract ({ a_path = [],"Bool" },_) -> "Dynamic"
+      | _ -> script_type_string t)
+   | TInst ({cl_path=[],"Null"},[t]) ->
+      (match follow t with
+      | TAbstract ({ a_path = [],"Int" },_)
+      | TAbstract ({ a_path = [],"Float" },_)
+      | TAbstract ({ a_path = [],"Bool" },_) -> "Dynamic"
+      | _ -> script_type_string t )
+   | _ ->
+      match follow haxe_type with
+      | TType ({t_path = [],"Array"},params) -> "Array"
+      | TInst ({cl_path=[],"Array"},params) ->
+         (match params with
+         | [t] ->
+            (match type_string_suff "" t false with
+            | "int" -> "Array.int"
+            | "Float" -> "Array.Float"
+            | "bool" -> "Array.bool"
+            | "::String" -> "Array.String"
+            | "unsigned char" -> "Array.unsigned char"
+            | "::cpp::UInt8" -> "Array.unsigned char"
+            | "Dynamic" -> "Array.Any"
+            | _ -> "Array.Object"
+            )
+         | _ -> "Array.Object"
+         )
+     | TAbstract (abs,pl) when abs.a_impl <> None ->
+         script_type_string  (Abstract.get_underlying_type abs pl);
+     | _ ->
+         type_string_suff "" haxe_type false
+;;
+
+type array_of =
+   | ArrayInterface of int
+   | ArrayData of string
+   | ArrayObject
+   | ArrayAny
+   | ArrayNone
+;;
+
+let is_template_type t =
+   false
+;;
+
+let rec is_dynamic_in_cppia ctx expr =
+   match expr.eexpr with
+   | TCast(_,None) -> true
+   | _ -> is_dynamic_in_cpp ctx expr
+;;
+
+type cppia_op =
+	| IaFunction
+	| IaVar
+	| IaToInterface
+	| IaToDynArray
+	| IaToDataArray
+	| IaToInterfaceArray
+	| IaFun
+	| IaCast
+	| IaBlock
+	| IaBreak
+	| IaContinue
+	| IaIsNull
+	| IaNotNull
+	| IaSet
+	| IaCall
+	| IaCallGlobal
+	| IaCallStatic
+	| IaCallMember
+	| IaCallSuper
+	| IaCallThis
+	| IaCallSuperNew
+	| IaCreateEnum
+	| IaADef
+	| IaIf
+	| IaIfElse
+	| IaFStatic
+	| IaFName
+	| IaFThisInst
+	| IaFLink
+	| IaFThisName
+	| IaFEnum
+	| IaThrow
+	| IaArrayI
+	| IaPlusPlus
+	| IaPlusPlusPost
+	| IaMinusMinus
+	| IaMinusMinusPost
+	| IaNeg
+	| IaBitNot
+	| IaLogicNot
+	| IaTVars
+	| IaVarDecl
+	| IaVarDeclI
+	| IaNew
+	| IaReturn
+	| IaRetVal
+	| IaPosInfo
+	| IaObjDef
+	| IaClassOf
+	| IaWhile
+	| IaFor
+	| IaEnumI
+	| IaSwitch
+	| IaTry
+	| IaImplDynamic
+   | IaConstInt
+   | IaConstFloat
+   | IaConstString
+   | IaConstFalse
+   | IaConstTrue
+   | IaConstNull
+   | IaConsThis
+   | IaConstSuper
+   | IaCastInt
+   | IaCastBool
+   | IaInterface
+   | IaClass
+   | IaAccessNormal
+   | IaAccessNot
+   | IaAccessResolve
+   | IaAccessCall
+   | IaEnum
+   | IaInline
+   | IaMain
+   | IaNoMain
+   | IaResources
+   | IaReso
+   | IaNoCast
+   | IaAccessCallNative
+
+	| IaBinOp of Ast.binop
+;;
+
+let cppia_op_info = function
+	| IaFunction -> ("FUNCTION", 1)
+	| IaVar      -> ("VAR", 2)
+	| IaToInterface -> ("TOINTERFACE", 3)
+	| IaToDynArray -> ("TODYNARRAY", 4)
+	| IaToDataArray -> ("TODATAARRAY", 5)
+	| IaToInterfaceArray -> ("TOINTERFACEARRAY", 6)
+	| IaFun -> ("FUN", 7)
+	| IaCast -> ("CAST", 8)
+	| IaBlock -> ("BLOCK", 9)
+	| IaBreak -> ("BREAK", 10)
+	| IaContinue -> ("CONTINUE", 11)
+	| IaIsNull -> ("ISNULL", 12)
+	| IaNotNull -> ("NOTNULL", 13)
+	| IaSet -> ("SET", 14)
+	| IaCall -> ("CALL", 15)
+	| IaCallGlobal -> ("CALLGLOBAL", 16)
+	| IaCallStatic -> ("CALLSTATIC", 17)
+	| IaCallMember -> ("CALLMEMBER", 18)
+	| IaCallSuper -> ("CALLSUPER", 19)
+	| IaCallThis -> ("CALLTHIS", 20)
+	| IaCallSuperNew -> ("CALLSUPERNEW", 21)
+	| IaCreateEnum -> ("CREATEENUM", 22)
+	| IaADef -> ("ADEF", 23)
+	| IaIf -> ("IF", 24)
+	| IaIfElse -> ("IFELSE", 25)
+	| IaFName -> ("FNAME", 27)
+	| IaFStatic -> ("FSTATIC", 28)
+	| IaFThisInst -> ("FTHISINST", 29)
+	| IaFLink -> ("FLINK", 30)
+	| IaFThisName -> ("FTHISNAME", 31)
+	| IaFEnum -> ("FENUM", 32)
+	| IaThrow -> ("THROW", 33)
+	| IaArrayI -> ("ARRAYI", 34)
+	| IaPlusPlus -> ("++", 35)
+	| IaPlusPlusPost -> ("+++", 36)
+	| IaMinusMinus -> ("--", 37)
+	| IaMinusMinusPost -> ("---", 38)
+	| IaNeg -> ("NEG", 39)
+	| IaBitNot -> ("~", 40)
+	| IaLogicNot -> ("!", 41)
+	| IaTVars -> ("TVARS", 42)
+	| IaVarDecl -> ("VARDECL", 43)
+	| IaVarDeclI -> ("VARDECLI", 44)
+	| IaNew -> ("NEW", 45)
+	| IaReturn -> ("RETURN", 46)
+	| IaRetVal -> ("RETVAL", 47)
+	| IaPosInfo -> ("POSINFO", 48)
+	| IaObjDef -> ("OBJDEF", 49)
+	| IaClassOf -> ("CLASSOF", 50)
+	| IaWhile -> ("WHILE", 51)
+	| IaFor -> ("FOR", 52)
+	| IaEnumI -> ("ENUMI", 53)
+	| IaSwitch -> ("SWITCH", 54)
+	| IaTry -> ("TRY", 55)
+	| IaImplDynamic -> ("IMPLDYNAMIC", 56)
+   | IaConstInt -> ("i", 57)
+   | IaConstFloat -> ("f", 58)
+   | IaConstString -> ("s", 59)
+   | IaConstFalse -> ("false", 60)
+   | IaConstTrue -> ("true", 61)
+   | IaConstNull -> ("NULL", 62)
+   | IaConsThis -> ("THIS", 63)
+   | IaConstSuper -> ("SUPER", 64)
+   | IaCastInt -> ("CASTINT", 65)
+   | IaCastBool -> ("CASTBOOL", 66)
+   | IaInterface -> ("INTERFACE", 67)
+   | IaClass -> ("CLASS", 68)
+   | IaAccessNormal -> ("N", 69)
+   | IaAccessNot  -> ("n", 70)
+   | IaAccessResolve  -> ("R", 71)
+   | IaAccessCall -> ("C", 72)
+   | IaEnum -> ("ENUM", 73)
+   | IaInline -> ("INLINE", 74)
+   | IaMain -> ("MAIN", 75)
+   | IaNoMain -> ("NOMAIN", 76)
+   | IaResources -> ("RESOURCES", 77)
+   | IaReso -> ("RESO", 78)
+	| IaNoCast -> ("NOCAST", 79)
+   | IaAccessCallNative -> ("V", 80)
+
+	| IaBinOp OpAdd -> ("+", 101)
+	| IaBinOp OpMult -> ("*", 102)
+	| IaBinOp OpDiv -> ("/", 103)
+	| IaBinOp OpSub -> ("-", 104)
+	| IaBinOp OpAssign -> ("=", 105)
+	| IaBinOp OpEq -> ("==", 106)
+	| IaBinOp OpNotEq -> ("!=", 107)
+	| IaBinOp OpGte -> (">=", 108)
+	| IaBinOp OpLte -> ("<=", 109)
+	| IaBinOp OpGt -> (">", 110)
+	| IaBinOp OpLt -> ("<", 111)
+	| IaBinOp OpAnd -> ("&", 112)
+	| IaBinOp OpOr -> ("|", 113)
+	| IaBinOp OpXor -> ("^", 114)
+	| IaBinOp OpBoolAnd -> ("&&", 115)
+	| IaBinOp OpBoolOr -> ("||", 116)
+	| IaBinOp OpShr -> (">>", 117)
+	| IaBinOp OpUShr -> (">>>", 118)
+	| IaBinOp OpShl -> ("<<", 119)
+	| IaBinOp OpMod -> ("%", 120)
+	| IaBinOp OpInterval -> ("...", 121)
+	| IaBinOp OpArrow -> ("=>", 122)
+	| IaBinOp OpAssignOp OpAdd -> ("+=", 201)
+	| IaBinOp OpAssignOp OpMult -> ("*=", 202)
+	| IaBinOp OpAssignOp OpDiv -> ("/=", 203)
+	| IaBinOp OpAssignOp OpSub -> ("-=", 204)
+
+
+	| IaBinOp OpAssignOp OpAnd -> ("&=", 212)
+	| IaBinOp OpAssignOp OpOr -> ("|=", 213)
+	| IaBinOp OpAssignOp OpXor -> ("^=", 214)
+	| IaBinOp OpAssignOp OpBoolAnd -> ("&&=", 215)
+	| IaBinOp OpAssignOp OpBoolOr -> ("||=", 216)
+	| IaBinOp OpAssignOp OpShr -> (">>=", 217)
+	| IaBinOp OpAssignOp OpUShr -> (">>>=", 218)
+	| IaBinOp OpAssignOp OpShl -> ("<<=", 219)
+	| IaBinOp OpAssignOp OpMod -> ("%=", 220)
+
+	| IaBinOp OpAssignOp OpInterval
+	| IaBinOp OpAssignOp OpAssign
+	| IaBinOp OpAssignOp OpEq
+	| IaBinOp OpAssignOp OpNotEq
+	| IaBinOp OpAssignOp OpGte
+	| IaBinOp OpAssignOp OpLte
+	| IaBinOp OpAssignOp OpGt
+	| IaBinOp OpAssignOp OpLt
+	| IaBinOp OpAssignOp OpAssignOp _
+	| IaBinOp OpAssignOp OpArrow -> assert false
+;;
+
+class script_writer ctx filename asciiOut =
+   object(this)
+   val debug = asciiOut
+   val doComment = asciiOut && (Common.defined ctx.ctx_common Define.AnnotateSource)
+   val indent_str = if asciiOut then "\t" else ""
+   val mutable indent = ""
+   val mutable indents = []
+   val mutable just_finished_block = false
+   val mutable classCount = 0
+   val mutable return_type = TMono(ref None)
+   val buffer = Buffer.create 0
+   val identTable = Hashtbl.create 0
+   val fileTable = Hashtbl.create 0
+   val identBuffer = Buffer.create 0
+
+   method stringId name =
+      try ( Hashtbl.find identTable name )
+      with Not_found -> begin
+         let size = Hashtbl.length identTable in
+         Hashtbl.add identTable name size;
+         Buffer.add_string identBuffer ((string_of_int (String.length name)) ^ " " ^ name ^ "\n");
+         size;
+      end
+   method incClasses = classCount <- classCount +1
+
+   method stringText name = (string_of_int (this#stringId name)) ^ " "
+   val typeTable = Hashtbl.create 0
+   val typeBuffer = Buffer.create 0
+   method typeId name =
+      let name = if name="::hx::Class" then "::Class" else name in
+      try ( Hashtbl.find typeTable name )
+      with Not_found -> begin
+         let size = Hashtbl.length typeTable in
+         Hashtbl.add typeTable name size;
+         Buffer.add_string typeBuffer ((string_of_int (String.length name)) ^ " " ^ name ^ "\n");
+         size;
+      end
+   method write str = if asciiOut then
+          Buffer.add_string buffer str
+      else begin
+         let push i = Buffer.add_char buffer (Char.chr i) in
+         let pushI32 i = push (Int32.to_int (Int32.logand i (Int32.of_int 255))) in
+         List.iter (fun i ->
+            if ((Int32.compare i Int32.zero) >= 0) && ((Int32.compare i (Int32.of_int 254)) < 0) then
+               pushI32 i
+            else if ((Int32.compare i Int32.zero) >= 0) && ((Int32.compare i (Int32.of_int 65536)) < 0) then begin
+               push 254;
+               pushI32 i;
+               pushI32 (Int32.shift_right i 8);
+            end else begin
+               push 255;
+               pushI32 i;
+               pushI32 (Int32.shift_right i 8);
+               pushI32 (Int32.shift_right i 16);
+               pushI32 (Int32.shift_right i 24);
+            end
+         ) (List.map Int32.of_string (Str.split (Str.regexp "[\n\t ]+") str) );
+      end;
+      just_finished_block <- false
+   method comment text = if doComment then this#write ("# " ^ text ^ "\n")
+   method commentOf text = if doComment then " # " ^ text else ""
+   method typeTextString typeName = (string_of_int (this#typeId typeName)) ^ " "
+   method typeText typeT = (string_of_int (this#typeId (script_type_string typeT))) ^ " "
+   method writeType typeT = this#write (this#typeText typeT)
+   method boolText value = if value then "1" else "0"
+   method writeBool value = this#write (if value then "1 " else "0 ")
+   method staticText value = if value then "1" else "0"
+   method writeData str = Buffer.add_string buffer str;
+   method wint ival = this#write ((string_of_int ival)^" ")
+   method ident name = this#wint (this#stringId name)
+   method instText clazz = match clazz.cl_path with
+      | ([],"Array") -> string_of_int (this#typeId "Array< ::Dynamic >") ^ " "
+      | _ -> this#typeText (TInst(clazz,[]))
+   method instName clazz = this#write (this#instText clazz)
+   method enumText e = this#typeText (TEnum(e,[]))
+   method enumName e = this#write (this#enumText e)
+   method close =
+      let out_file = open_out_bin filename in
+      output_string out_file (if asciiOut then "CPPIA\n" else "CPPIB\n");
+      let idents =  Buffer.contents identBuffer in
+      output_string out_file ((string_of_int (Hashtbl.length identTable)) ^ "\n");
+      output_string out_file idents;
+      let types =  Buffer.contents typeBuffer in
+      output_string out_file ((string_of_int (Hashtbl.length typeTable)) ^ "\n");
+      output_string out_file types;
+      output_string out_file ( (string_of_int classCount) ^ "\n" );
+      let contents = Buffer.contents buffer in
+      output_string out_file contents;
+      close_out out_file
+   method fileId file =
+      try ( Hashtbl.find fileTable file )
+      with Not_found -> begin
+         let stripped_file = strip_file ctx.ctx_common file in
+         let result = this#stringId stripped_file in
+         Hashtbl.add fileTable file result;
+         result;
+      end
+   method constText c = match c with
+   | TInt i -> (this#op IaConstInt) ^ (Printf.sprintf "%ld " i)
+   | TFloat f -> (this#op IaConstFloat) ^ (this#stringText f)
+   | TString s -> (this#op IaConstString) ^ (this#stringText s)
+   | TBool true -> (this#op IaConstTrue)
+   | TBool false -> (this#op IaConstFalse)
+   | TNull -> (this#op IaConstNull)
+   | TThis ->  (this#op IaConsThis)
+   | TSuper ->  (this#op IaConstSuper)
+
+   method get_array_type t =
+      match follow t with
+      | TInst ({cl_path=[],"Array"},[param]) ->
+            let typeName = type_string_suff "" param false in
+            (match typeName with
+            | "::String"  -> ArrayData "String"
+            | "int" | "Float" | "bool" | "String" | "unsigned char" | "::cpp::UInt8" ->
+               ArrayData typeName
+            | "cpp::ArrayBase" | "cpp::VirtualArray" | "Dynamic" -> ArrayAny
+            | _ when is_interface_type param -> ArrayInterface (this#typeId (script_type_string param))
+            | _ -> ArrayObject
+            )
+      | TAbstract (abs,pl) when abs.a_impl <> None ->
+            this#get_array_type  (Abstract.get_underlying_type abs pl);
+      | _ -> ArrayNone;
+
+   method pushReturn inType =
+      let oldReturnType = return_type in
+      return_type <- inType;
+      fun () -> return_type <- oldReturnType;
+   method fileText file = string_of_int (this#fileId file)
+   method indent_one = this#write indent_str
+   method push_indent = indents <- indent_str::indents; indent <- String.concat "" indents
+   method pop_indent = match indents with
+                     | h::tail -> indents <- tail; indent <- String.concat "" indents
+                     | [] -> indent <- "/*?*/";
+   method write_i x = this#write (indent ^ x)
+   method get_indent = indent
+   method begin_expr = this#push_indent
+   method end_expr = if not just_finished_block then this#write "\n"; this#pop_indent; just_finished_block <- true
+   method op x = match cppia_op_info x with
+      | (name,index) -> (if debug then name else string_of_int index) ^ " "
+   method writeOp o = this#write (this#op o)
+   method writeOpLine o = this#write ((this#op o) ^ "\n")
+   method voidFunc isStatic isDynamic funcName fieldExpression =
+      this#comment funcName;
+      this#write ( (this#op IaFunction) ^ (this#staticText isStatic) ^ " " ^(this#boolText isDynamic) ^ " " ^(this#stringText funcName) ^ " ");
+      this#write ((this#typeTextString "Void") ^ "0\n");
+         this#gen_expression fieldExpression
+   method func isStatic isDynamic funcName ret args isInterface fieldExpression =
+      this#comment funcName;
+      this#write ( (this#op IaFunction) ^ (this#staticText isStatic) ^ " " ^(this#boolText isDynamic) ^ " " ^(this#stringText funcName) ^ " ");
+      this#write ((this#typeText ret) ^ (string_of_int (List.length args)) ^ " ");
+      List.iter (fun (name,opt,typ) -> this#write ( (this#stringText name) ^ (this#boolText opt) ^ " " ^ (this#typeText typ) ^ " " )) args;
+      this#write "\n";
+      if (not isInterface) then begin
+         match fieldExpression with
+         | Some ({ eexpr = TFunction function_def } as e) -> this#gen_expression e
+         | _ -> print_endline ("Missing function body for " ^ funcName );
+      end
+   method var readAcc writeAcc isExtern isStatic name varType varExpr =
+      this#write ( (this#op IaVar) ^ (this#staticText isStatic) ^ " " ^ (this#op readAcc) ^ (this#op writeAcc) ^
+         (this#boolText isExtern) ^ " " ^ (this#stringText name)^ (this#typeText varType) ^
+         (match varExpr with Some _ -> "1" | _ -> "0" )  ^
+         (if doComment then (" # " ^ name ^ "\n") else "\n") );
+      match varExpr with
+      | Some expression -> this#gen_expression expression
+      | _ -> ()
+   method implDynamic = this#writeOpLine IaImplDynamic;
+   method writeVar v =
+      this#ident v.v_name;
+      this#wint v.v_id;
+      this#writeBool v.v_capture;
+      this#writeType v.v_type;
+   method writeList prefix len = this#write (prefix ^" "  ^ (string_of_int (len)) ^ "\n");
+   method writePos expr = if debug then
+      this#write ( (this#fileText expr.epos.pfile) ^ "\t" ^ (string_of_int (Lexer.get_error_line expr.epos) ) ^ indent);
+   method checkCast toType expr forceCast fromGenExpression=
+   let write_cast text =
+      if (not fromGenExpression) then
+         this#writePos expr;
+      this#write (text ^"\n" );
+      this#begin_expr;
+      this#gen_expression expr;
+      this#end_expr;
+      true;
+   in
+   let was_cast =
+      if (is_interface_type toType) then begin
+         if (is_dynamic_in_cppia ctx expr) then begin
+            write_cast ( (this#op IaToInterface) ^ (this#typeText toType) ^ " " ^ (this#typeTextString "Dynamic") )
+         end else if (not (is_matching_interface_type toType expr.etype)) then begin
+            write_cast ( (this#op IaToInterface) ^ (this#typeText toType) ^ " " ^ (this#typeText expr.etype) )
+         end else
+            false
+      end else begin
+        let get_array_expr_type expr =
+            if is_dynamic_in_cppia ctx expr then
+               ArrayNone
+            else
+               this#get_array_type expr.etype
+            in
+         match (this#get_array_type toType), (get_array_expr_type expr) with
+         | ArrayAny, _ -> false
+         | ArrayObject, ArrayData _ -> write_cast (this#op IaToDynArray)
+         | ArrayObject, ArrayObject -> false
+         | ArrayObject, ArrayNone
+         | ArrayObject, ArrayAny -> write_cast ((this#op IaToDataArray) ^ (this#typeTextString ("Array.Object")))
+         | ArrayData t, ArrayNone
+         | ArrayData t, ArrayObject
+         | ArrayData t, ArrayAny -> write_cast ((this#op IaToDataArray)  ^ (this#typeTextString ("Array." ^ t)))
+         | ArrayInterface t, ArrayNone
+         | ArrayInterface t, ArrayAny -> write_cast ((this#op IaToInterfaceArray) ^ (string_of_int t))
+         | _,_ -> (* a0,a1 ->
+               let arrayString a =
+                  match a with
+                  | ArrayNone -> "ArrayNone"
+                  | ArrayAny -> "ArrayAny"
+                  | ArrayObject -> "ArrayObject"
+                  | ArrayData _ -> "ArrayData"
+                  | ArrayInterface _ -> "ArrayInterface"
+            in
+            this#write ("NOCAST " ^ (arrayString a0) ^ "=" ^ (arrayString a1));  *)
+            false
+      end
+   in
+
+   if (not was_cast) then begin
+      if (forceCast) then begin
+         let op =match (type_string expr.etype) with
+         | "int" -> IaCastInt
+         | "bool" -> IaCastBool
+         | _ when is_interface_type toType -> IaNoCast
+         | _ -> IaCast
+         in
+         this#writeOpLine op;
+      end;
+      this#gen_expression expr;
+   end
+   method gen_expression expr =
+   let expression = remove_parens expr in
+   this#begin_expr;
+   (*this#write ( (this#fileText expression.epos.pfile) ^ "\t" ^ (string_of_int (Lexer.get_error_line expression.epos) ) ^ indent);*)
+   this#writePos expression;
+   (match expression.eexpr with
+   | TFunction function_def -> this#write ( (this#op IaFun) ^ (this#typeText function_def.tf_type) ^ (string_of_int (List.length function_def.tf_args)) ^ "\n" );
+         List.iter (fun(arg,init) ->
+            this#write (indent ^ indent_str );
+            this#writeVar arg;
+            match init with
+            | Some const -> this#write ("1 " ^ (this#constText const) ^ "\n")
+            | _ -> this#write "0\n";
+         ) function_def.tf_args;
+         let pop = this#pushReturn function_def.tf_type in
+         this#gen_expression function_def.tf_expr;
+         pop ();
+   | TBlock expr_list -> this#writeList (this#op IaBlock) (List.length expr_list);
+         List.iter this#gen_expression expr_list;
+   | TConst const -> this#write (this#constText const)
+   | TBreak -> this#writeOp IaBreak
+   | TContinue -> this#writeOp IaContinue
+
+   | TBinop (op,e1,e2) when op=OpAssign ->
+      this#writeOpLine IaSet;
+      this#gen_expression e1;
+      this#checkCast e1.etype e2 false false;
+   | TBinop (OpEq ,e1, { eexpr = TConst TNull } ) -> this#writeOpLine IaIsNull;
+      this#gen_expression e1;
+   | TBinop (OpNotEq ,e1, { eexpr = TConst TNull }) -> this#writeOpLine IaNotNull;
+      this#gen_expression e1;
+   | TBinop (OpEq , { eexpr = TConst TNull }, e1) -> this#writeOpLine IaIsNull;
+      this#gen_expression e1;
+   | TBinop (OpNotEq, { eexpr = TConst TNull }, e1) -> this#writeOpLine IaNotNull;
+      this#gen_expression e1;
+   | TBinop (op,e1,e2) -> this#writeOpLine (IaBinOp op);
+      this#gen_expression e1;
+      this#gen_expression e2;
+   | TThrow e -> this#writeOpLine IaThrow;
+      this#gen_expression e;
+   | TArrayDecl expr_list ->
+      this#write ( (this#op IaADef) ^ (this#typeText expression.etype) ^ " " ^(string_of_int (List.length expr_list))^"\n");
+      List.iter this#gen_expression expr_list;
+   | TIf (e,e1,e2) ->
+      (match e2 with
+      | None ->
+         this#writeOpLine IaIf;
+         this#gen_expression e;
+         this#gen_expression e1;
+      | Some elze ->
+         this#writeOpLine IaIfElse;
+         this#gen_expression e;
+         this#gen_expression e1;
+         this#gen_expression elze; )
+   | TCall (func, arg_list) ->
+      let argN = (string_of_int (List.length arg_list)) ^ " " in
+      let is_real_function field =
+         match field.cf_kind with
+         | Method MethNormal | Method MethInline-> true
+         | _ -> false;
+      in
+      let gen_call () =
+         (match (remove_parens_cast func).eexpr with
+         | TField ( { eexpr = TLocal  { v_name = "__global__" }}, field ) ->
+                  this#write ( (this#op IaCallGlobal) ^ (this#stringText (field_name field)) ^ argN ^ (this#commentOf (field_name field)) ^ "\n");
+         | TField (obj,FStatic (class_def,field) ) when is_real_function field ->
+                  this#write ( (this#op IaCallStatic) ^ (this#instText class_def) ^ " " ^ (this#stringText field.cf_name) ^
+                     argN ^ (this#commentOf ( join_class_path class_def.cl_path "." ^ "." ^ field.cf_name) ) ^ "\n");
+         | TField (obj,FInstance (_,_,field) ) when (is_this obj) && (is_real_function field) ->
+                  this#write ( (this#op IaCallThis) ^ (this#typeText obj.etype) ^ " " ^ (this#stringText field.cf_name) ^
+                     argN ^ (this#commentOf field.cf_name) ^ "\n");
+         | TField (obj,FInstance (_,_,field) ) when is_super obj ->
+                  this#write ( (this#op IaCallSuper) ^ (this#typeText obj.etype) ^ " " ^ (this#stringText field.cf_name) ^
+                     argN ^ (this#commentOf field.cf_name) ^ "\n");
+         (* Cppia does not have a "GetEnumIndex" op code - must use IaCallMember hx::EnumBase.__Index *)
+         | TField (obj,FInstance (_,_,field) ) when field.cf_name = "_hx_getIndex" && (script_type_string obj.etype)="hx::EnumBase" ->
+                  this#write ( (this#op IaCallMember) ^ (this#typeTextString "hx::EnumBase") ^ " " ^ (this#stringText "__Index") ^
+                     argN ^ (this#commentOf ("Enum index") ) ^ "\n");
+                  this#gen_expression obj;
+         | TField (obj,FInstance (_,_,field) ) when field.cf_name = "__Index" || (not (is_dynamic_in_cppia ctx obj) && is_real_function field) ->
+                  this#write ( (this#op IaCallMember) ^ (this#typeText obj.etype) ^ " " ^ (this#stringText field.cf_name) ^
+                     argN ^ (this#commentOf field.cf_name) ^ "\n");
+                  this#gen_expression obj;
+         | TField (obj,FDynamic (name) )  when (is_internal_member name || (type_string obj.etype = "::String" && name="cca") ) ->
+                  this#write ( (this#op IaCallMember) ^ (this#typeText obj.etype) ^ " " ^ (this#stringText name) ^
+                     argN ^ (this#commentOf name) ^ "\n");
+                  this#gen_expression obj;
+         | TConst TSuper -> this#write ((this#op IaCallSuperNew) ^ (this#typeText func.etype) ^ " " ^ argN ^ "\n");
+         | TField (_,FEnum (enum,field)) -> this#write ((this#op IaCreateEnum) ^ (this#enumText enum) ^ " " ^ (this#stringText field.ef_name) ^ argN ^
+                   (this#commentOf field.ef_name) ^ "\n");
+         | _ -> this#write ( (this#op IaCall) ^ argN ^ "\n");
+                  this#gen_expression func;
+         );
+         let matched_args = match func.etype with
+            | TFun (args,_) ->
+               ( try (
+                  List.iter2 (fun (_,_,protoT) arg -> this#checkCast protoT arg false false)  args arg_list;
+                  true; )
+               with Invalid_argument _ -> (*print_endline "Bad count?";*) false )
+            | _ -> false
+         in
+         if not matched_args then
+            List.iter this#gen_expression arg_list;
+      in
+      (match (remove_parens_cast func).eexpr with
+         | TField(obj,field) when is_array_or_dyn_array obj.etype && (field_name field)="map" ->
+            (match this#get_array_type expression.etype with
+            | ArrayData t ->
+                this#write ( (this#op IaToDataArray) ^ (this#typeTextString ("Array." ^ t)) ^ "\n");
+                this#begin_expr;
+                this#writePos func;
+                gen_call();
+                this#end_expr;
+            | ArrayInterface t ->
+                this#write ( (this#op IaToInterfaceArray) ^ (string_of_int t) ^ "\n");
+                this#begin_expr;
+                this#writePos func;
+                gen_call();
+                this#end_expr;
+            | _ -> gen_call();
+            )
+         | _ -> gen_call();
+      );
+   | TField (obj, acc) ->
+      let objType = if is_dynamic_in_cppia ctx obj then "Dynamic" else script_type_string obj.etype in
+      let typeText = if is_dynamic_in_cppia ctx obj then this#typeTextString "Dynamic" else this#typeText obj.etype in
+      (match acc with
+      | FDynamic name -> this#write ( (this#op IaFName) ^ typeText ^ " " ^ (this#stringText name) ^  (this#commentOf name) ^ "\n");
+            this#gen_expression obj;
+      | FStatic (class_def,field) -> this#write ( (this#op IaFStatic)  ^ (this#instText class_def) ^ " " ^
+           (this#stringText field.cf_name) ^ (this#commentOf field.cf_name) );
+      | FInstance (_,_,field) when is_this obj -> this#write ( (this#op IaFThisInst) ^ typeText ^ " " ^ (this#stringText field.cf_name)
+                ^ (this#commentOf field.cf_name) );
+      | FInstance (_,_,field) -> this#write ( (this#op IaFLink) ^ typeText ^ " " ^ (this#stringText field.cf_name) ^ (this#commentOf ( objType ^ "." ^ field.cf_name)) ^ "\n");
+            this#gen_expression obj;
+
+      | FClosure (_,field) when is_this obj -> this#write ( (this#op IaFThisName) ^typeText ^ " " ^  (this#stringText field.cf_name) ^ "\n")
+      | FAnon (field) when is_this obj -> this#write ( (this#op IaFThisName) ^typeText ^ " " ^  (this#stringText field.cf_name) ^ (this#commentOf field.cf_name) ^ "\n")
+
+      | FClosure (_,field)
+      | FAnon (field) -> this#write ( (this#op IaFName) ^typeText ^ " " ^  (this#stringText field.cf_name) ^ (this#commentOf field.cf_name) ^ "\n");
+            this#gen_expression obj;
+
+      | FEnum (enum,field) -> this#write ( (this#op IaFEnum)  ^ (this#enumText enum) ^ " " ^ (this#stringText field.ef_name) ^ (this#commentOf field.ef_name)  );
+      )
+   | TArray (e1, e2) -> this#write ((this#op IaArrayI) ^ (this#typeText e1.etype) ^ "\n");
+      this#gen_expression e1;
+      this#gen_expression e2;
+   | TUnop (op, flag, e) ->
+      this#writeOpLine (match op,flag with
+      | Increment, Prefix -> IaPlusPlus
+      | Increment, _ -> IaPlusPlusPost
+      | Decrement, Prefix -> IaMinusMinus
+      | Decrement, _ -> IaMinusMinusPost
+      | Not, _ -> IaLogicNot
+      | Neg, _ -> IaNeg
+      | NegBits, _ -> IaBitNot );
+      this#gen_expression e;
+   (* TODO - lval op-assign local/member/array *)
+   | TLocal var -> this#write ((this#op IaVar) ^ (string_of_int var.v_id) ^ (this#commentOf var.v_name) );
+
+   | TVar (tvar,optional_init) ->
+         this#write ( (this#op IaTVars) ^ (string_of_int (1)) ^ (this#commentOf (tvar.v_name ^ ":" ^ (script_type_string tvar.v_type)) ) ^ "\n");
+            this#write ("\t\t" ^ indent);
+            (match optional_init with
+            | None -> this#writeOp IaVarDecl;
+                     this#writeVar tvar;
+            | Some init ->this#writeOp IaVarDeclI;
+                     let init = remove_parens init in
+                     this#writeVar tvar;
+                     this#write (" " ^ (this#typeText init.etype));
+                     this#write "\n";
+                     this#checkCast tvar.v_type init false false);
+   | TNew (clazz,params,arg_list) ->
+      this#write ((this#op IaNew) ^ (this#typeText (TInst(clazz,params))) ^ (string_of_int (List.length arg_list)) ^ "\n");
+      let rec matched_args clazz = match clazz.cl_constructor, clazz.cl_super with
+         | None, Some super -> matched_args (fst super)
+         | None, _ -> false
+         | Some ctr, _ ->
+            (match ctr.cf_type with
+            | TFun(args,_) ->
+               ( try (
+                  List.iter2 (fun (_,_,protoT) arg -> this#checkCast protoT arg false false)  args arg_list;
+                  true; )
+                 with Invalid_argument _ -> (*print_endline "Bad count?";*) false )
+            | _ -> false
+            )
+      in
+      if not (matched_args clazz) then
+         List.iter this#gen_expression arg_list;
+
+   | TReturn optval -> (match optval with
+         | None -> this#writeOpLine IaReturn;
+         | Some value -> this#write ( (this#op IaRetVal) ^ (this#typeText value.etype) ^ "\n");
+              this#checkCast return_type value false false;
+         )
+   | TObjectDecl (
+      ("fileName" , { eexpr = (TConst (TString file)) }) ::
+         ("lineNumber" , { eexpr = (TConst (TInt line)) }) ::
+            ("className" , { eexpr = (TConst (TString class_name)) }) ::
+               ("methodName", { eexpr = (TConst (TString meth)) }) :: [] ) ->
+            this#write ( (this#op IaPosInfo) ^ (this#stringText file) ^ (Printf.sprintf "%ld" line) ^ " " ^
+                        (this#stringText class_name) ^ " " ^  (this#stringText meth))
+
+   | TObjectDecl values ->this#write ( (this#op IaObjDef) ^ (string_of_int (List.length values)));
+         this#write " ";
+         List.iter (fun (name,_) -> this#write (this#stringText name)  ) values;
+         this#write "\n";
+         List.iter (fun (_,e) -> this#gen_expression e ) values;
+   | TTypeExpr type_expr ->
+         let klass = "::" ^ (join_class_path (t_path type_expr) "::" ) in
+         this#write ((this#op IaClassOf) ^ (string_of_int (this#typeId klass)))
+   | TWhile (e1,e2,flag) -> this#write ( (this#op IaWhile) ^ (if flag=NormalWhile then "1" else "0" ) ^ "\n");
+         this#gen_expression e1;
+         this#gen_expression e2;
+   | TFor (tvar,init,loop) -> this#writeOp IaFor;
+         this#writeVar tvar;
+         this#write "\n";
+         this#gen_expression init;
+         this#gen_expression loop;
+   | TEnumParameter (expr,ef,i) ->
+         let enum = match follow ef.ef_type with
+            | TEnum(en,_) | TFun(_,TEnum(en,_)) -> en
+            | _ -> assert false
+         in
+         this#write ( (this#op IaEnumI) ^ (this#typeText (TEnum(enum,[])) ) ^ (string_of_int i) ^ "\n");
+         this#gen_expression expr;
+   | TSwitch (condition,cases,optional_default)  ->
+         this#write ( (this#op IaSwitch) ^ (string_of_int (List.length cases)) ^ " " ^
+                           (match optional_default with None -> "0" | Some _ -> "1") ^ "\n");
+         this#gen_expression condition;
+         List.iter (fun (cases_list,expression) ->
+            this#writeList ("\t\t\t"^indent) (List.length cases_list);
+            List.iter (fun value -> this#gen_expression value ) cases_list;
+            this#gen_expression expression;
+         ) cases;
+         (match optional_default with None -> () | Some expr -> this#gen_expression expr);
+   | TTry (e,catches)  ->
+         this#writeList (this#op IaTry) (List.length catches);
+         this#gen_expression e;
+         List.iter ( fun (tvar,catch_expr) ->
+            this#write ("\t\t\t"^indent);
+            this#writeVar tvar;
+            this#write "\n";
+            this#gen_expression catch_expr;
+         ) catches;
+   | TCast (cast,None) -> this#checkCast expression.etype cast true true;
+   | TCast (cast,Some _) -> this#checkCast expression.etype cast true true;
+   | TParenthesis _ -> error "Unexpected parens" expression.epos
+   | TMeta(_,_) -> error "Unexpected meta" expression.epos
+   );
+   this#end_expr;
+end;;
+
+let generate_script_class common_ctx script class_def =
+   script#incClasses;
+   let classText = (join_class_path class_def.cl_path ".") in
+   script#comment ("Class " ^ classText);
+   script#writeOp (if class_def.cl_interface then IaInterface else IaClass );
+   script#instName class_def;
+   (match class_def.cl_super with
+      | None -> script#ident ""
+      | Some (c,_) -> script#instName c);
+   script#wint (List.length class_def.cl_implements);
+   List.iter (fun(c,_) -> script#instName c) class_def.cl_implements;
+   script#write "\n";
+   (* Looks like some map impl classes have their bodies discarded - not sure best way to filter *)
+   let non_dodgy_function field =
+      class_def.cl_interface ||
+      match field.cf_kind, field.cf_expr with
+      | Var _, _ -> true
+      | Method MethDynamic, _ -> true
+      | Method _, Some _ -> true
+      | _ -> false
+   in
+   let ordered_statics = List.filter non_dodgy_function class_def.cl_ordered_statics in
+   let ordered_fields = List.filter non_dodgy_function class_def.cl_ordered_fields in
+   script#write ((string_of_int ( (List.length ordered_fields) +
+                                 (List.length ordered_statics) +
+                                 (match class_def.cl_constructor with Some _ -> 1 | _ -> 0 ) +
+                                 (if (implement_dynamic_here class_def) then 1 else 0) +
+                                 (match class_def.cl_init with Some _ -> 1 | _ -> 0 ) ) )
+                                 ^ "\n");
+
+   let generate_field isStatic field =
+      match field.cf_kind, follow field.cf_type with
+      | Var { v_read = AccInline; v_write = AccNever },_ ->
+         script#writeOpLine IaInline;
+      | Var v,_ ->
+         let mode_code mode = match mode with
+         | AccNormal -> IaAccessNormal
+         | AccNo -> IaAccessNot
+         | AccNever -> IaAccessNot
+         | AccResolve -> IaAccessResolve
+         | AccCall -> if ( (has_meta_key class_def.cl_meta Meta.NativeProperty) ||
+                           (has_meta_key field.cf_meta Meta.NativeProperty) ||
+                           (Common.defined common_ctx Define.ForceNativeProperty) )
+                         then IaAccessCallNative else IaAccessCall;
+         | AccInline -> IaAccessNormal
+         | AccRequire (_,_) -> IaAccessNormal
+         in
+         let isExtern = is_extern_field field in
+         script#var (mode_code v.v_read) (mode_code v.v_write) isExtern isStatic field.cf_name field.cf_type field.cf_expr
+      | Method MethDynamic, TFun(args,ret) ->
+         script#func isStatic true field.cf_name ret args class_def.cl_interface field.cf_expr
+      | Method _, TFun(args,ret) when field.cf_name="new" ->
+         script#func true false "new" (TInst(class_def,[])) args false field.cf_expr
+      | Method _, TFun (args,ret) ->
+         script#func isStatic false field.cf_name ret args class_def.cl_interface field.cf_expr
+      | Method _, _ -> print_endline ("Unknown method type " ^ (join_class_path class_def.cl_path "." )
+                     ^ "." ^field.cf_name )
+   in
+   (match class_def.cl_constructor with
+      | Some field  -> generate_field true field
+      | _ -> () );
+   (match class_def.cl_init with
+      | Some expression  -> script#voidFunc true false "__init__" expression
+      | _ -> () );
+
+   List.iter (generate_field false) ordered_fields;
+   List.iter (generate_field true) ordered_statics;
+   if (implement_dynamic_here class_def) then
+      script#implDynamic;
+   script#write "\n";
+;;
+
+let generate_script_enum common_ctx script enum_def meta =
+   script#incClasses;
+   let sorted_items = List.sort (fun f1 f2 -> (f1.ef_index - f2.ef_index ) ) (pmap_values enum_def.e_constrs) in
+   script#writeList ((script#op IaEnum) ^ (script#enumText enum_def)) (List.length sorted_items);
+
+   List.iter (fun constructor ->
+      let name = script#stringText constructor.ef_name in
+      match constructor.ef_type with
+      | TFun (args,_) ->
+         script#write ( name ^ " " ^ (string_of_int (List.length args)) );
+         List.iter (fun (arg,_,t) -> script#write ( " " ^ (script#stringText arg) ^ " " ^ (script#typeText t) ) ) args;
+         script#write "\n";
+      | _ -> script#write ( name ^ " 0\n" )
+   ) sorted_items;
+
+   match meta with
+   | Some expr -> script#write "1\n";
+      script#gen_expression expr
+   | _ -> script#write "0\n";
+   script#write "\n"
+;;
+
+
+let generate_cppia ctx =
+   let common_ctx = ctx.ctx_common in
+   let debug = ctx.ctx_debug_level in
+   let script = new script_writer ctx common_ctx.file common_ctx.debug in
+   ignore (script#stringId "");
+   ignore (script#typeId "");
+
+      List.iter (fun object_def ->
+      (match object_def with
+      | TClassDecl class_def when class_def.cl_extern  ->
+         () (*if (gen_externs) then gen_extern_class common_ctx class_def;*)
+      | TClassDecl class_def ->
+         let is_internal = is_internal_class class_def.cl_path in
+         if (is_internal || (is_macro class_def.cl_meta)) then
+            ( if (debug>1) then print_endline (" internal class " ^ (join_class_path class_def.cl_path ".") ))
+         else begin
+            generate_script_class common_ctx script class_def
+         end
+      | TEnumDecl enum_def when enum_def.e_extern -> ()
+      | TEnumDecl enum_def ->
+         let is_internal = is_internal_class enum_def.e_path in
+         if (is_internal) then
+            (if (debug>1) then print_endline (" internal enum " ^ (join_class_path enum_def.e_path ".") ))
+         else begin
+            let meta = Codegen.build_metadata common_ctx object_def in
+            if (enum_def.e_extern) then
+               (if (debug>1) then print_endline ("external enum " ^  (join_class_path enum_def.e_path ".") ));
+            generate_script_enum common_ctx script enum_def meta
+         end
+      | TTypeDecl _ | TAbstractDecl _ -> (* already done *) ()
+      );
+   ) common_ctx.types;
+
+   (match common_ctx.main with
+   | None -> script#writeOpLine IaNoMain;
+   | Some e -> script#writeOpLine IaMain;
+         script#gen_expression e
+   );
+
+   script#write ( (script#op IaResources) ^ (string_of_int (Hashtbl.length common_ctx.resources)) ^ "\n");
+   Hashtbl.iter (fun name data ->
+      script#write ((script#op IaReso) ^ (script#stringText name) ^  (string_of_int (String.length data)) ^ "\n");
+   ) common_ctx.resources;
+   Hashtbl.iter (fun _ data -> script#writeData data) common_ctx.resources;
+
+   script#close
+;;
+
+
+(*
+ The common_ctx contains the haxe AST in the "types" field and the resources
+*)
+let generate_source ctx =
+   let common_ctx = ctx.ctx_common in
+   let debug = ctx.ctx_debug_level in
+   make_base_directory common_ctx.file;
+   let exe_classes = ref [] in
+   let boot_classes = ref [] in
+   let boot_enums = ref [] in
+   let nonboot_classes = ref [] in
+   let init_classes = ref [] in
+   let class_text path = join_class_path path "::" in
+   let super_deps = create_super_dependencies common_ctx in
+   let constructor_deps = create_constructor_dependencies common_ctx in
+   let main_deps = ref [] in
+   let extern_src = ref [] in
+   let build_xml = ref "" in
+   let scriptable = (Common.defined common_ctx Define.Scriptable) in
+
+   List.iter (fun object_def ->
+      (* check if any @:objc class is referenced while '-D objc' is not defined
+         This will guard all code changes to this flag *)
+      (if not (Common.defined common_ctx Define.Objc) then match object_def with
+         | TClassDecl class_def when Meta.has Meta.Objc class_def.cl_meta ->
+            error "In order to compile '@:objc' classes, please define '-D objc'" class_def.cl_pos
+         | _ -> ());
+      (match object_def with
+      | TClassDecl class_def when is_extern_class class_def ->
+         build_xml := !build_xml ^ (get_class_code class_def Meta.BuildXml);
+         let source = get_meta_string_path class_def.cl_meta Meta.SourceFile in
+         if (source<>"") then
+            extern_src := source :: !extern_src;
+      | TClassDecl class_def ->
+         let name =  class_text class_def.cl_path in
+         let is_internal = is_internal_class class_def.cl_path in
+         if (is_internal || (is_macro class_def.cl_meta)) then
+            ( if (debug>1) then print_endline (" internal class " ^ name ))
+         else begin
+            build_xml := !build_xml ^ (get_class_code class_def Meta.BuildXml);
+            if (has_init_field class_def) then
+               init_classes := class_def.cl_path ::  !init_classes;
+            if (has_boot_field class_def) then
+               boot_classes := class_def.cl_path ::  !boot_classes
+            else if not (has_meta_key class_def.cl_meta Meta.NativeGen) then
+               nonboot_classes := class_def.cl_path ::  !nonboot_classes;
+            let deps = generate_class_files ctx super_deps constructor_deps class_def scriptable in
+            if not (class_def.cl_interface && (is_native_gen_class class_def)) then
+               exe_classes := (class_def.cl_path, deps, object_def)  ::  !exe_classes;
+         end
+      | TEnumDecl enum_def when enum_def.e_extern -> ()
+      | TEnumDecl enum_def ->
+         let name =  class_text enum_def.e_path in
+         let is_internal = is_internal_class enum_def.e_path in
+         if (is_internal) then
+            (if (debug>1) then print_endline (" internal enum " ^ name ))
+         else begin
+            let meta = Codegen.build_metadata common_ctx object_def in
+            if (enum_def.e_extern) then
+               (if (debug>1) then print_endline ("external enum " ^ name ));
+            boot_enums := enum_def.e_path :: !boot_enums;
+            let deps = generate_enum_files ctx enum_def super_deps meta in
+            exe_classes := (enum_def.e_path, deps, object_def) :: !exe_classes;
+         end
+      | TTypeDecl _ | TAbstractDecl _ -> (* already done *) ()
+      );
+   ) common_ctx.types;
+
+
+   (match common_ctx.main with
+   | None -> generate_dummy_main common_ctx
+   | Some e ->
+      let main_field = { cf_name = "__main__"; cf_type = t_dynamic; cf_expr = Some e; cf_pos = e.epos; cf_public = true; cf_meta = []; cf_overloads = []; cf_doc = None; cf_kind = Var { v_read = AccNormal; v_write = AccNormal; }; cf_params = [] } in
+      let class_def = { null_class with cl_path = ([],"@Main"); cl_ordered_statics = [main_field] } in
+      main_deps := find_referenced_types ctx (TClassDecl class_def) super_deps constructor_deps false true false;
+      generate_main ctx super_deps class_def
+   );
+
+   generate_boot ctx !boot_enums !boot_classes !nonboot_classes !init_classes;
+
+   generate_files common_ctx ctx.ctx_file_info;
+
+   write_resources common_ctx;
+
+   (* Output class info if requested *)
+   if (scriptable || (Common.defined common_ctx Define.DllExport) ) then begin
+      let filename =
+         try
+            let value = Common.defined_value common_ctx Define.DllExport in
+            if value="1" then raise Not_found;
+            value
+         with Not_found -> "export_classes.info"
+      in
+      if (filename <> "") then begin
+         let escape s =
+            let b = Buffer.create 0 in
+            for i = 0 to String.length s - 1 do
+               let c = String.unsafe_get s i in
+               match c with
+               | '\\' -> Buffer.add_char b c; Buffer.add_char b c;
+               | ' ' -> Buffer.add_char b '\\'; Buffer.add_char b 's';
+               | '\n' -> Buffer.add_char b '\\'; Buffer.add_char b 'n';
+               | _ -> Buffer.add_char b c;
+            done;
+            Buffer.contents b;
+         in
+
+         let exeClasses = open_out filename in
+         let out = output_string exeClasses in
+         let outline str = output_string exeClasses (str ^ "\n") in
+         let spath path = (join_class_path path ".") in
+         List.iter (fun (name,_,def) ->
+            match def with
+            | TClassDecl class_def ->
+                outline ((if class_def.cl_interface then "interface " else "class ") ^ (spath name) );
+            | TEnumDecl enum_def ->
+                out ("enum " ^ (spath name) ^ "\n");
+            | _ -> ()
+            ) !exe_classes;
+
+         (* Output file info too *)
+         List.iter ( fun file ->
+               let full_path = Common.get_full_path (try Common.find_file common_ctx file with Not_found -> file) in
+               if file <> "?" then
+                  out ("file " ^ (escape file) ^ " " ^ (escape full_path) ^"\n") )
+            ( List.sort String.compare ( pmap_keys !(ctx.ctx_file_info) ) );
+         close_out exeClasses;
+     end;
+   end;
+
+   let output_name = match  common_ctx.main_class with
+   | Some path -> (snd path)
+   | _ -> "output" in
+
+   write_build_data common_ctx (common_ctx.file ^ "/Build.xml") !exe_classes !main_deps (!boot_enums@ !boot_classes) !build_xml !extern_src output_name;
+   let cmd_defines = ref "" in
+   PMap.iter ( fun name value -> match name with
+      | "true" | "sys" | "dce" | "cpp" | "debug" -> ()
+      | _ -> cmd_defines := !cmd_defines ^ " -D" ^ name ^ "=\"" ^ (escape_command value) ^ "\"" ) common_ctx.defines;
+   write_build_options common_ctx (common_ctx.file ^ "/Options.txt") common_ctx.defines;
+   if ( not (Common.defined common_ctx Define.NoCompilation) ) then begin
+      let t = Common.timer "generate cpp - native compilation" in
+      let old_dir = Sys.getcwd() in
+      Sys.chdir common_ctx.file;
+      let cmd = ref "haxelib run hxcpp Build.xml haxe" in
+      if (common_ctx.debug) then cmd := !cmd ^ " -Ddebug";
+      cmd := !cmd ^ !cmd_defines;
+      cmd := List.fold_left (fun cmd path -> cmd ^ " -I\"" ^ (escape_command path) ^ "\"" ) !cmd common_ctx.class_path;
+      common_ctx.print (!cmd ^ "\n");
+      if common_ctx.run_command !cmd <> 0 then failwith "Build failed";
+      Sys.chdir old_dir;
+      t()
+   end
+   ;;
+
+let generate common_ctx =
+   if (Common.defined common_ctx Define.Cppia) then begin
+      let ctx = new_context common_ctx 1 (ref PMap.empty) (Hashtbl.create 0)  in
+      generate_cppia ctx
+   end else begin
+      let ctx = new_context common_ctx 1 (ref PMap.empty) (create_member_types common_ctx) in
+      generate_source ctx
+   end
+;;
+
+

+ 147 - 131
gencs.ml → src/generators/gencs.ml

@@ -1,23 +1,20 @@
 (*
- * Copyright (C)2005-2013 Haxe Foundation
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
+	The 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 Gencommon.ReflectionCFs
@@ -184,6 +181,8 @@ let rec change_md = function
 		change_md (t_to_md a.a_this)
 	| TClassDecl( { cl_kind = KAbstractImpl ({ a_this = TInst(impl,_) } as a) }) when Meta.has Meta.Delegate a.a_meta ->
 		TClassDecl impl
+	| TClassDecl( { cl_kind = KAbstractImpl (a) }) when Meta.has Meta.CoreType a.a_meta ->
+		TAbstractDecl a
 	| md -> md
 
 (* ******************************************* *)
@@ -407,7 +406,6 @@ struct
 			| _ -> assert false
 		in
 		let string_ext = get_cl ( get_type gen (["haxe";"lang"], "StringExt")) in
-		let is_string t = match follow t with | TInst({ cl_path = ([], "String") }, []) -> true | _ -> false in
 		let clstring = match basic.tstring with | TInst(cl,_) -> cl | _ -> assert false in
 		let ti64 = match ( get_type gen (["cs"], "Int64") ) with | TTypeDecl t -> TType(t,[]) | TAbstractDecl a -> TAbstract(a,[]) | _ -> assert false in
 		let boxed_ptr =
@@ -430,6 +428,9 @@ struct
 			| _ -> ""
 		in
 
+		let as_var = alloc_var "__as__" t_dynamic in
+		let fast_cast = Common.defined gen.gcon Define.FastCast in
+
 		let rec run e =
 			match e.eexpr with
 
@@ -482,7 +483,7 @@ struct
 							{ e with eexpr = TNew(boxed_ptr,[],[expr]) }
 						| Some e ->
 							run e)
-				| TCast(expr, _) when is_bool e.etype && not (is_exactly_bool gen expr.etype) ->
+				| TCast(expr, _) when is_bool e.etype && is_dynamic gen expr.etype ->
 					{
 						eexpr = TCall(
 							mk_static_field_access_infer runtime_cl "toBool" expr.epos [],
@@ -491,7 +492,7 @@ struct
 						etype = basic.tbool;
 						epos = e.epos
 					}
-				| TCast(expr, _) when is_int_float gen e.etype && not (is_cs_basic_type (gen.greal_type 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) ) && name() <> "haxe.lang.Runtime" ->
 					let needs_cast = match gen.gfollow#run_f e.etype with
 						| TInst _ -> false
 						| _ -> true
@@ -509,6 +510,23 @@ struct
 					} in
 
 					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" ->
+					{
+						eexpr = TCall(
+							mk_static_field_access_infer runtime_cl "toLong" expr.epos [],
+							[ run expr ]
+						);
+						etype = ti64;
+						epos = expr.epos
+					}
+
+				| TCast(expr, Some(TClassDecl cls)) when fast_cast && cls == null_class ->
+					if is_cs_basic_type (gen.greal_type e.etype) || is_tparam (gen.greal_type e.etype) then
+						{ e with eexpr = TCast(run expr, Some(TClassDecl null_class)) }
+					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" ->
 					{ e with eexpr = TCall( mk_static_field_access_infer runtime_cl "toString" expr.epos [], [run expr] ) }
 				| TBinop( (Ast.OpNotEq as op), e1, e2)
@@ -762,6 +780,7 @@ let configure gen =
 
 	let change_ns_params md params ns = if no_root then match ns with
 			| [] when is_hxgen md -> ["haxe";"root"], params
+			| [s] when (t_infos md).mt_private && is_hxgen md -> ["haxe";"root";s], params
 			| [] -> (match md with
 				| TClassDecl { cl_path = ([],"Std" | [],"Math") } -> ["haxe";"root"], params
 				| TClassDecl { cl_meta = m } when Meta.has Meta.Enum m -> ["haxe";"root"], params
@@ -912,7 +931,7 @@ let configure gen =
 				else
 					(match real_type t with
 						| TInst( { cl_kind = KTypeParameter _ }, _ ) -> TInst(null_t, [t])
-						| _ when is_cs_basic_type t -> TInst(null_t, [t])
+						| t when is_cs_basic_type t -> TInst(null_t, [t])
 						| _ -> real_type t)
 			| TAbstract _
 			| TType _ -> t
@@ -1301,6 +1320,7 @@ let configure gen =
 					(match c with
 						| TInt i32 ->
 							write w (Int32.to_string i32);
+							(* these suffixes won't work because of the cast detector which will set each constant to its expected type *)
 							(*match real_type e.etype with
 								| TType( { t_path = (["haxe";"_Int64"], "NativeInt64") }, [] ) -> write w "L";
 								| _ -> ()
@@ -1324,6 +1344,10 @@ let configure gen =
 						| TNull -> write w "null"
 						| TThis -> write w "this"
 						| TSuper -> write w "base")
+				| TCast({ eexpr = TConst(TNull) }, _) ->
+							write w "default(";
+							write w (t_s e.etype);
+							write w ")"
 				| TLocal { v_name = "__sbreak__" } -> write w "break"
 				| TLocal { v_name = "__undefined__" } ->
 					write w (t_s (TInst(runtime_cl, List.map (fun _ -> t_dynamic) runtime_cl.cl_params)));
@@ -1369,7 +1393,7 @@ let configure gen =
 				| TField (e, s) ->
 					expr_s w e; write w "."; write_field w (field_name s)
 				| TTypeExpr mt ->
-					(match mt with
+					(match change_md mt with
 						| TClassDecl { cl_path = (["haxe"], "Int64") } -> write w ("global::" ^ module_s mt)
 						| TClassDecl { cl_path = (["haxe"], "Int32") } -> write w ("global::" ^ module_s mt)
 						| TClassDecl cl -> write w (t_s (TInst(cl, List.map (fun _ -> t_empty) cl.cl_params)));
@@ -1985,6 +2009,34 @@ let configure gen =
 		end;
 	in
 
+	let needs_unchecked e =
+		let rec loop e = match e.eexpr with
+		(* a non-zero integer constant means that we want unchecked context *)
+		| TConst (TInt i) when i <> Int32.zero ->
+			raise Exit
+
+		(* don't recurse into explicit checked blocks *)
+		| TCall ({ eexpr = TLocal({ v_name = "__checked__" }) }, _) ->
+			()
+
+		(* skip reflection field hashes as they are safe *)
+		| TNew ({ cl_path = (["haxe"; "lang"],"DynamicObject") }, [], [_; e1; _; e2]) ->
+			loop e1;
+			loop e2
+		| TNew ({ cl_path = (["haxe"; "lang"],"Closure") }, [], [eo; _; _]) ->
+			loop eo
+		| TCall ({ eexpr = TField (_, FStatic ({ cl_path = ["haxe"; "lang"],"Runtime" },
+				 { cf_name = "getField" | "setField" | "getField_f" | "setField_f" | "callField" })) },
+				 eo :: _ :: _ :: rest) ->
+			loop eo;
+			List.iter loop rest
+
+		| _ ->
+			Type.iter loop e
+		in
+		try (loop e; false) with Exit -> true
+	in
+
 	let rec gen_class_field w ?(is_overload=false) is_static cl is_final cf =
 		gen_attributes w cf.cf_meta;
 		let is_interface = cl.cl_interface in
@@ -2100,33 +2152,6 @@ let configure gen =
 											| _ -> assert false (* FIXME *)
 								in
 
-								let needs_unchecked e =
-									let rec loop e = match e.eexpr with
-									(* a non-zero integer constant means that we want unchecked context *)
-									| TConst (TInt i) when i <> Int32.zero ->
-										raise Exit
-
-									(* don't recurse into explicit checked blocks *)
-									| TCall ({ eexpr = TLocal({ v_name = "__checked__" }) }, _) ->
-										()
-
-									(* skip reflection field hashes as they are safe *)
-									| TNew ({ cl_path = (["haxe"; "lang"],"DynamicObject") }, [], [_; e1; _; e2]) ->
-										loop e1;
-										loop e2
-									| TNew ({ cl_path = (["haxe"; "lang"],"Closure") }, [], [eo; _; _]) ->
-										loop eo
-									| TCall ({ eexpr = TField (_, FStatic ({ cl_path = ["haxe"; "lang"],"Runtime" },
-											 { cf_name = "getField" | "setField" | "getField_f" | "setField_f" | "callField" })) },
-											 eo :: _ :: _ :: rest) ->
-										loop eo;
-										List.iter loop rest
-
-									| _ ->
-										Type.iter loop e
-									in
-									try (loop e; false) with Exit -> true
-								in
 								let write_method_expr e =
 									match e.eexpr with
 									| TBlock [] ->
@@ -2448,7 +2473,13 @@ let configure gen =
 			| None -> ()
 			| Some init ->
 				print w "static %s() " (snd cl.cl_path);
-				expr_s w (mk_block init);
+				if needs_unchecked init then begin
+					begin_block w;
+					write w "unchecked ";
+					expr_s w (mk_block init);
+					end_block w;
+				end else
+					expr_s w (mk_block init);
 				line_reset_directive w;
 				newline w;
 				newline w
@@ -2602,11 +2633,15 @@ let configure gen =
 	in
 
 	let module_type_gen w md_tp =
+		let file_start = len w = 0 in
+		let requires_root = no_root && file_start in
+		if file_start then
+			Codegen.map_source_header gen.gcon (fun s -> print w "// %s\n" s);
 		reset_temps();
 		match md_tp with
 			| TClassDecl cl ->
 				if not cl.cl_extern then begin
-					(if no_root && len w = 0 then write w "using haxe.root;\n"; newline w;);
+					(if requires_root then write w "using haxe.root;\n"; newline w;);
 					gen_class w cl;
 					newline w;
 					newline w
@@ -2614,7 +2649,7 @@ let configure gen =
 				(not cl.cl_extern)
 			| TEnumDecl e ->
 				if not e.e_extern && not (Meta.has Meta.Class e.e_meta) then begin
-					(if no_root && len w = 0 then write w "using haxe.root;\n"; newline w;);
+					(if requires_root then write w "using haxe.root;\n"; newline w;);
 					gen_enum w e;
 					newline w;
 					newline w
@@ -2718,8 +2753,10 @@ let configure gen =
 
 	if not erase_generics then HardNullableSynf.configure gen (HardNullableSynf.traverse gen
 		(fun e ->
-			match real_type e.etype with
-				| TInst({ cl_path = (["haxe";"lang"], "Null") }, [t]) ->
+			match e.eexpr, real_type e.etype with
+				| TConst TThis, _ when gen.gcurrent_path = (["haxe";"lang"], "Null") ->
+					e
+				| _, TInst({ cl_path = (["haxe";"lang"], "Null") }, [t]) ->
 					let e = { e with eexpr = TParenthesis(e) } in
 					{ (mk_field_access gen e "value" e.epos) with etype = t }
 				| _ ->
@@ -3298,8 +3335,6 @@ let configure gen =
 
 	TypeParams.RenameTypeParameters.run gen;
 
-	let t = Common.timer "code generation" in
-
 	let parts = Str.split_delim (Str.regexp "[\\/]+") gen.gcon.file in
 	mkdir_recursive "" parts;
 	generate_modules gen "cs" "src" module_gen out_files;
@@ -3315,9 +3350,7 @@ let configure gen =
 		print_endline cmd;
 		if gen.gcon.run_command cmd <> 0 then failwith "Build failed";
 		Sys.chdir old_dir;
-	end;
-
-	t()
+	end
 
 (* end of configure function *)
 
@@ -3473,7 +3506,7 @@ let rec convert_signature ctx p = function
 	| LObject ->
 		mk_type_path ctx ([],[],"Dynamic") []
 	| LPointer s | LManagedPointer s ->
-		mk_type_path ctx (["cs"],[],"Pointer") [ TPType (convert_signature ctx p s) ]
+		mk_type_path ctx (["cs"],[],"Pointer") [ TPType (convert_signature ctx p s,null_pos) ]
 	| LTypedReference ->
 		mk_type_path ctx (["cs";"system"],[],"TypedReference") []
 	| LIntPtr ->
@@ -3481,16 +3514,16 @@ let rec convert_signature ctx p = function
 	| LUIntPtr ->
 		mk_type_path ctx (["cs";"system"],[],"UIntPtr") []
 	| LValueType (s,args) | LClass (s,args) ->
-		mk_type_path ctx s (List.map (fun s -> TPType (convert_signature ctx p s)) args)
+		mk_type_path ctx s (List.map (fun s -> TPType (convert_signature ctx p s,null_pos)) args)
 	| LTypeParam i ->
 		mk_type_path ctx ([],[],"T" ^ string_of_int i) []
 	| LMethodTypeParam i ->
 		mk_type_path ctx ([],[],"M" ^ string_of_int i) []
 	| LVector s ->
-		mk_type_path ctx (["cs"],[],"NativeArray") [TPType (convert_signature ctx p s)]
+		mk_type_path ctx (["cs"],[],"NativeArray") [TPType (convert_signature ctx p s,null_pos)]
 	(* | LArray of ilsig_norm * (int option * int option) array *)
 	| LMethod (_,ret,args) ->
-		CTFunction (List.map (convert_signature ctx p) args, convert_signature ctx p ret)
+		CTFunction (List.map (fun v -> convert_signature ctx p v,null_pos) args, (convert_signature ctx p ret,null_pos))
 	| _ -> mk_type_path ctx ([],[], "Dynamic") []
 
 let ilpath_s = function
@@ -3552,14 +3585,14 @@ let convert_ilenum ctx p ?(is_flag=false) ilcls =
 				| _ ->
 					[], Int64.zero
 			in
-			data := ( { ec_name = f.fname; ec_doc = None; ec_meta = meta; ec_args = []; ec_pos = p; ec_params = []; ec_type = None; }, const) :: !data;
+			data := ( { ec_name = f.fname,null_pos; ec_doc = None; ec_meta = meta; ec_args = []; ec_pos = p; ec_params = []; ec_type = None; }, const) :: !data;
 	) ilcls.cfields;
 	let data = List.stable_sort (fun (_,i1) (_,i2) -> Int64.compare i1 i2) (List.rev !data) in
 
 	let _, c = netpath_to_hx ctx.nstd ilcls.cpath in
 	let name = netname_to_hx c in
 	EEnum {
-		d_name = if is_flag then name ^ "_FlagsEnum" else name;
+		d_name = (if is_flag then name ^ "_FlagsEnum" else name),null_pos;
 		d_doc = None;
 		d_params = []; (* enums never have type parameters *)
 		d_meta = !meta;
@@ -3604,9 +3637,10 @@ let convert_ilfield ctx p field =
 		Printf.printf "\t%sfield %s : %s\n" (if List.mem AStatic acc then "static " else "") cff_name (IlMetaDebug.ilsig_s field.fsig.ssig);
 	let kind = match readonly with
 		| true ->
-			FProp ("default", "never", Some (convert_signature ctx p field.fsig.snorm), None)
+			cff_meta := (Meta.ReadOnly, [], cff_pos) :: !cff_meta;
+			FProp ("default", "never", Some (convert_signature ctx p field.fsig.snorm,null_pos), None)
 		| false ->
-			FVar (Some (convert_signature ctx p field.fsig.snorm), None)
+			FVar (Some (convert_signature ctx p field.fsig.snorm,null_pos), None)
 	in
 	let cff_name, cff_meta =
 		if String.get cff_name 0 = '%' then
@@ -3617,7 +3651,7 @@ let convert_ilfield ctx p field =
 			cff_name, !cff_meta
 	in
 	{
-		cff_name = cff_name;
+		cff_name = cff_name,null_pos;
 		cff_doc = cff_doc;
 		cff_pos = cff_pos;
 		cff_meta = cff_meta;
@@ -3628,7 +3662,7 @@ let convert_ilfield ctx p field =
 let convert_ilevent ctx p ev =
 	let p = { p with pfile =	p.pfile ^" (" ^ev.ename ^")" } in
 	let name = ev.ename in
-	let kind = FVar (Some (convert_signature ctx p ev.esig.snorm), None) in
+	let kind = FVar (Some (convert_signature ctx p ev.esig.snorm,null_pos), None) in
 	let meta = [Meta.Event, [], p; Meta.Keep,[],p; Meta.SkipReflection,[],p] in
 	let acc = [APrivate] in
 	let add_m acc m = match m with
@@ -3645,7 +3679,7 @@ let convert_ilevent ctx p ev =
 	let acc = add_m acc ev.eremove in
 	let acc = add_m acc ev.eraise in
 	{
-		cff_name = name;
+		cff_name = name,null_pos;
 		cff_doc = None;
 		cff_pos = p;
 		cff_meta = meta;
@@ -3737,24 +3771,25 @@ let convert_ilmethod ctx p m is_explicit_impl =
 				| LManagedPointer s ->
 					let is_out = List.mem POut flag.pf_io && not (List.mem PIn flag.pf_io) in
 					let name = if is_out then "Out" else "Ref" in
-					mk_type_path ctx (["cs"],[],name) [ TPType (convert_signature ctx p s) ]
+					mk_type_path ctx (["cs"],[],name) [ TPType (convert_signature ctx p s,null_pos) ]
 				| _ ->
 					convert_signature ctx p (change_sig s.snorm)
 			in
-			name,false,Some t,None) m.margs
+			(name,null_pos),false,[],Some (t,null_pos),None) m.margs
 		in
 		let ret = convert_signature ctx p (change_sig ret) in
 		let types = List.map (fun t ->
 			{
-				tp_name = "M" ^ string_of_int t.tnumber;
+				tp_name = "M" ^ string_of_int t.tnumber,null_pos;
 				tp_params = [];
 				tp_constraints = [];
+				tp_meta = [];
 			}
 		) m.mtypes in
 		FFun {
 			f_params = types;
 			f_args = args;
-			f_type = Some ret;
+			f_type = Some (ret,null_pos);
 			f_expr = None;
 		}
 	in
@@ -3778,7 +3813,7 @@ let convert_ilmethod ctx p m is_explicit_impl =
 			| _ -> acc
 	in
 	{
-		cff_name = cff_name;
+		cff_name = cff_name,null_pos;
 		cff_doc = cff_doc;
 		cff_pos = cff_pos;
 		cff_meta = cff_meta;
@@ -3821,7 +3856,7 @@ let convert_ilprop ctx p prop is_explicit_impl =
 			raise Exit (* special (?) getter; not used *)
 		| Some(_,m) when access m <> FAPublic -> (match access m with
 			| FAFamily
-			| FAFamOrAssem -> "null"
+			| FAFamOrAssem -> "never"
 			| _ -> "never");
 		| Some _ -> "set"
 	in
@@ -3839,10 +3874,10 @@ let convert_ilprop ctx p prop is_explicit_impl =
 	in
 
 	let kind =
-		FProp (get, set, Some(convert_signature ctx p ilsig), None)
+		FProp (get, set, Some(convert_signature ctx p ilsig,null_pos), None)
 	in
 	{
-		cff_name = prop.pname;
+		cff_name = prop.pname,null_pos;
 		cff_doc = None;
 		cff_pos = p;
 		cff_meta = meta;
@@ -3876,7 +3911,7 @@ let mk_metas metas p =
 let mk_abstract_fun name p kind metas acc =
 	let metas = mk_metas metas p in
 	{
-		cff_name = name;
+		cff_name = name,null_pos;
 		cff_doc = None;
 		cff_pos = p;
 		cff_meta = metas;
@@ -3886,13 +3921,13 @@ let mk_abstract_fun name p kind metas acc =
 
 let convert_fun_arg ctx p = function
 	| LManagedPointer s ->
-		mk_type_path ctx (["cs"],[],"Ref") [ TPType (convert_signature ctx p s) ]
+		mk_type_path ctx (["cs"],[],"Ref") [ TPType (convert_signature ctx p s,null_pos) ],p
 	| s ->
-		convert_signature ctx p s
+		convert_signature ctx p s,p
 
 let convert_fun ctx p ret args =
 	let args = List.map (convert_fun_arg ctx p) args in
-	CTFunction(args, convert_signature ctx p ret)
+	CTFunction(args, (convert_signature ctx p ret,null_pos))
 
 let get_clsname ctx cpath =
 	match netpath_to_hx ctx.nstd cpath with
@@ -3907,16 +3942,17 @@ let convert_delegate ctx p ilcls =
 	(* - AsDelegate():Super *)
 	(* - @:op(A+B) Add(d:absType) *)
 	(* - @:op(A-B) Remove(d:absType) *)
-	let abs_type = mk_type_path ctx (ilcls.cpath) (List.map (fun t -> TPType (mk_type_path ctx ([],[],"T" ^ string_of_int t.tnumber) [])) ilcls.ctypes) in
+	let abs_type = mk_type_path ctx (ilcls.cpath) (List.map (fun t -> TPType (mk_type_path ctx ([],[],"T" ^ string_of_int t.tnumber) [],null_pos)) ilcls.ctypes) in
 	let invoke = List.find (fun m -> m.mname = "Invoke") ilcls.cmethods in
 	let ret = invoke.mret.snorm in
 	let args = List.map (fun (_,_,s) -> s.snorm) invoke.margs in
 	let haxe_type = convert_fun ctx p ret args in
 	let types = List.map (fun t ->
 		{
-			tp_name = "T" ^ string_of_int t.tnumber;
+			tp_name = ("T" ^ string_of_int t.tnumber),null_pos;
 			tp_params = [];
 			tp_constraints = [];
+			tp_meta = [];
 		}
 	) ilcls.ctypes in
 	let mk_op_fn op name p =
@@ -3927,15 +3963,15 @@ let convert_delegate ctx p ilcls =
 		let expr = (ECall( (EField( (EConst(Ident (clsname)),p), fn_name ),p), [(EConst(Ident"arg1"),p);(EConst(Ident"arg2"),p)]),p) in
 		FFun {
 			f_params = types;
-			f_args = ["arg1",false,Some abs_type,None;"arg2",false,Some abs_type,None];
-			f_type = Some abs_type;
+			f_args = [("arg1",null_pos),false,[],Some (abs_type,null_pos),None;("arg2",null_pos),false,[],Some (abs_type,null_pos),None];
+			f_type = Some (abs_type,null_pos);
 			f_expr = Some ( (EReturn (Some expr), p) );
 		}
 	in
 	let mk_op op name =
 		let p = { p with pfile = p.pfile ^" (op " ^ name ^ ")" } in
 		{
-			cff_name = name;
+			cff_name = name,null_pos;
 			cff_doc = None;
 			cff_pos = p;
 			cff_meta = [ Meta.Extern,[],p ; Meta.Op, [ (EBinop(op, (EConst(Ident"A"),p), (EConst(Ident"B"),p)),p) ], p ];
@@ -3944,7 +3980,7 @@ let convert_delegate ctx p ilcls =
 		}
 	in
 	let params = (List.map (fun s ->
-		TPType (mk_type_path ctx ([],[],s.tp_name) [])
+		TPType (mk_type_path ctx ([],[],fst s.tp_name) [],null_pos)
 	) types) in
 	let underlying_type = match ilcls.cpath with
 		| ns,inner,name ->
@@ -3953,33 +3989,16 @@ let convert_delegate ctx p ilcls =
 
 	let fn_new = FFun {
 		f_params = [];
-		f_args = ["hxfunc",false,Some haxe_type,None];
+		f_args = [("hxfunc",null_pos),false,[],Some (haxe_type,null_pos),None];
 		f_type = None;
 		f_expr = Some ( EBinop(Ast.OpAssign, (EConst(Ident "this"),p), (mk_special_call "__delegate__" p [EConst(Ident "hxfunc"),p]) ), p );
 	} in
 	let fn_from_hx = FFun {
 		f_params = types;
-		f_args = ["hxfunc",false,Some haxe_type,None];
-		f_type = Some( mk_type_path ctx ilcls.cpath params );
+		f_args = [("hxfunc",null_pos),false,[],Some (haxe_type,null_pos),None];
+		f_type = Some( mk_type_path ctx ilcls.cpath params,null_pos );
 		f_expr = Some( EReturn( Some (mk_special_call "__delegate__" p [EConst(Ident "hxfunc"),p] )), p);
 	} in
-	let i = ref 0 in
-	let j = ref 0 in
-	let fn_invoke = FFun {
-		f_params = [];
-		f_args = List.map (fun arg ->
-			incr i;
-			"arg" ^ string_of_int !i, false, Some (convert_fun_arg ctx p arg), None
-		) args;
-		f_type = Some(convert_signature ctx p ret);
-		f_expr = Some(
-			EReturn( Some (
-				mk_this_call "Invoke" p (List.map (fun arg ->
-					incr j; (EConst( Ident ("arg" ^ string_of_int !j) ), p)
-				) args )
-			)), p
-		);
-	} in
 	let fn_asdel = FFun {
 		f_params = [];
 		f_args = [];
@@ -3990,16 +4009,15 @@ let convert_delegate ctx p ilcls =
 	} in
 	let fn_new = mk_abstract_fun "new" p fn_new [Meta.Extern] [APublic;AInline] in
 	let fn_from_hx = mk_abstract_fun "FromHaxeFunction" p fn_from_hx [Meta.Extern;Meta.From] [APublic;AInline;AStatic] in
-	let fn_invoke = mk_abstract_fun "Invoke" p fn_invoke [Meta.Extern] [APublic;AInline] in
 	let fn_asdel = mk_abstract_fun "AsDelegate" p fn_asdel [Meta.Extern] [APublic;AInline] in
 	let _, c = netpath_to_hx ctx.nstd ilcls.cpath in
 	EAbstract {
-		d_name = netname_to_hx c;
+		d_name = netname_to_hx c,null_pos;
 		d_doc = None;
 		d_params = types;
-		d_meta = mk_metas [Meta.Delegate] p;
-		d_flags = [AIsType underlying_type];
-		d_data = [fn_new;fn_from_hx;fn_invoke;fn_asdel;mk_op Ast.OpAdd "Add";mk_op Ast.OpSub "Remove"];
+		d_meta = mk_metas [Meta.Delegate; Meta.Forward] p;
+		d_flags = [AIsType (underlying_type,null_pos)];
+		d_data = [fn_new;fn_from_hx;fn_asdel;mk_op Ast.OpAdd "Add";mk_op Ast.OpSub "Remove"];
 	}
 
 let convert_ilclass ctx p ?(delegate=false) ilcls = match ilcls.csuper with
@@ -4034,12 +4052,12 @@ let convert_ilclass ctx p ?(delegate=false) ilcls = match ilcls.csuper with
 		(match ilcls.csuper with
 			| Some { snorm = LClass ( (["System"],[],"Object"), [] ) } -> ()
 			| Some ({ snorm = LClass ( (["System"],[],"ValueType"), [] ) } as s) ->
-				flags := HExtends (get_type_path ctx (convert_signature ctx p s.snorm)) :: !flags;
+				flags := HExtends (get_type_path ctx (convert_signature ctx p s.snorm),null_pos) :: !flags;
 				meta := (Meta.Struct,[],p) :: !meta
 			| Some { snorm = LClass ( (["haxe";"lang"],[],"HxObject"), [] ) } ->
 				meta := (Meta.HxGen,[],p) :: !meta
 			| Some s ->
-				flags := HExtends (get_type_path ctx (convert_signature ctx p s.snorm)) :: !flags
+				flags := HExtends (get_type_path ctx (convert_signature ctx p s.snorm),null_pos) :: !flags
 			| _ -> ());
 
 			let has_explicit_ifaces = ref false in
@@ -4051,9 +4069,9 @@ let convert_ilclass ctx p ?(delegate=false) ilcls = match ilcls.csuper with
 				| i ->
 					if is_explicit ctx ilcls i then has_explicit_ifaces := true;
 					flags := if !is_interface then
-						HExtends (get_type_path ctx (convert_signature ctx p i)) :: !flags
+						HExtends (get_type_path ctx (convert_signature ctx p i),null_pos) :: !flags
 					else
-						HImplements (get_type_path ctx (convert_signature ctx p i)) :: !flags
+						HImplements (get_type_path ctx (convert_signature ctx p i),null_pos) :: !flags
 			) ilcls.cimplements;
 			(* this is needed because of explicit interfaces. see http://msdn.microsoft.com/en-us/library/aa288461(v=vs.71).aspx *)
 			(* explicit interfaces can't be mapped into Haxe in any way - since their fields can't be accessed directly, but they still implement that interface *)
@@ -4064,9 +4082,9 @@ let convert_ilclass ctx p ?(delegate=false) ilcls = match ilcls.csuper with
 			ignore (List.exists (function
 			| { psig = { snorm = LMethod(_,ret,[v]) } } ->
 				flags := if !is_interface then
-					(HExtends( raw_type_path ctx ([],"ArrayAccess") [ TPType (convert_signature ctx p ret) ]) :: !flags)
+					(HExtends( raw_type_path ctx ([],"ArrayAccess") [ TPType (convert_signature ctx p ret,null_pos) ],null_pos) :: !flags)
 				else
-					(HImplements( raw_type_path ctx ([],"ArrayAccess") [ TPType (convert_signature ctx p ret) ]) :: !flags);
+					(HImplements( raw_type_path ctx ([],"ArrayAccess") [ TPType (convert_signature ctx p ret,null_pos) ],null_pos) :: !flags);
 				true
 			| _ -> false) ilcls.cprops);
 
@@ -4095,27 +4113,28 @@ let convert_ilclass ctx p ?(delegate=false) ilcls = match ilcls.csuper with
 
 			let params = List.map (fun p ->
 				{
-					tp_name = "T" ^ string_of_int p.tnumber;
+					tp_name = "T" ^ string_of_int p.tnumber,null_pos;
 					tp_params = [];
 					tp_constraints = [];
+					tp_meta = [];
 				}) ilcls.ctypes
 			in
 
 			if delegate then begin
 				(* add op_Addition and op_Subtraction *)
 				let path = ilcls.cpath in
-				let thist = mk_type_path ctx path (List.map (fun t -> TPType (mk_type_path ctx ([],[],"T" ^ string_of_int t.tnumber) [])) ilcls.ctypes) in
+				let thist = mk_type_path ctx path (List.map (fun t -> TPType (mk_type_path ctx ([],[],"T" ^ string_of_int t.tnumber) [],null_pos)) ilcls.ctypes) in
 				let op name =
 					{
-						cff_name = name;
+						cff_name = name,null_pos;
 						cff_doc = None;
 						cff_pos = p;
 						cff_meta = [];
 						cff_access = [APublic;AStatic];
 						cff_kind = FFun {
 							f_params = params;
-							f_args = ["arg1",false,Some thist,None;"arg2",false,Some thist,None];
-							f_type = Some thist;
+							f_args = [("arg1",null_pos),false,[],Some (thist,null_pos),None;("arg2",null_pos),false,[],Some (thist,null_pos),None];
+							f_type = Some (thist,null_pos);
 							f_expr = None;
 						};
 					}
@@ -4129,7 +4148,7 @@ let convert_ilclass ctx p ?(delegate=false) ilcls = match ilcls.csuper with
 			in
 			let _, c = netpath_to_hx ctx.nstd path in
 			EClass {
-				d_name = netname_to_hx c;
+				d_name = netname_to_hx c,null_pos;
 				d_doc = None;
 				d_params = params;
 				d_meta = !meta;
@@ -4238,10 +4257,7 @@ let compatible_field f1 f2 = match f1, f2 with
 let get_all_fields cls =
 	let all_fields = List.map (fun f -> IlField f, cls.cpath, f.fname, List.mem CStatic f.fflags.ff_contract) cls.cfields in
 	let all_fields = all_fields @ List.map (fun m -> IlMethod m, cls.cpath, m.mname, List.mem CMStatic m.mflags.mf_contract) cls.cmethods in
-	let all_fields = all_fields @ List.map (function
-		| p ->
-			IlProp p, cls.cpath, p.pname, is_static (IlProp p)
-	) cls.cprops in
+	let all_fields = all_fields @ List.map (fun p -> IlProp p, cls.cpath, p.pname, is_static (IlProp p)) cls.cprops in
 	all_fields
 
 let normalize_ilcls ctx cls =

+ 6974 - 0
src/generators/genhl.ml

@@ -0,0 +1,6974 @@
+(*
+ * Copyright (C)2005-2015 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 Ast
+open Type
+open Common
+
+type reg = int
+type global = int
+type 'a index = int
+type functable
+
+type ttype =
+	| HVoid
+	| HI8
+	| HI16
+	| 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;
+}
+
+and enum_proto = {
+	ename : string;
+	eid : int;
+	mutable eglobal : int;
+	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
+	(* unops *)
+	| 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
+	(* 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
+	| OToDyn of reg * reg
+	| OToSFloat of reg * reg
+	| OToUFloat of reg * reg
+	| OToInt 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 *)
+	| OGetI8 of reg * reg * reg
+	| OGetI16 of reg * reg * reg
+	| OGetI32 of reg * reg * reg
+	| OGetF32 of reg * reg * reg
+	| OGetF64 of reg * reg * reg
+	| OGetArray of reg * reg * reg
+	| OSetI8 of reg * reg * reg
+	| OSetI16 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
+	| OSafeCast of reg * reg
+	| OUnsafeCast of reg * reg
+	| OArraySize of reg * reg
+	| OType of reg * ttype
+	| OGetType of reg * reg
+	| OGetTID of reg * reg
+	| OToVirtual of reg * reg
+	(* dynamic *)
+	| ODynGet of reg * reg * string index
+	| ODynSet of reg * string index * 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 *)
+	| ODump of reg
+
+type fundecl = {
+	name : 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;
+}
+
+(* compiler *)
+
+type ('a,'b) lookup = {
+	arr : 'b DynArray.t;
+	mutable map : ('a, int) PMap.t;
+}
+
+(* not mutable, might be be shared *)
+type method_capture = {
+	c_map : (int, int) PMap.t;
+	c_vars : tvar array;
+	c_type : ttype;
+}
+
+type method_context = {
+	mid : int;
+	mregs : (int, ttype) lookup;
+	mops : opcode DynArray.t;
+	mret : ttype;
+	mdebug : (int * int) DynArray.t;
+	mutable mcaptured : method_capture;
+	mutable mcontinues : (int -> unit) list;
+	mutable mbreaks : (int -> unit) list;
+	mutable mtrys : int;
+	mutable mcaptreg : int;
+	mutable mcurpos : (int * int);
+}
+
+type array_impl = {
+	aall : tclass;
+	abase : tclass;
+	adyn : tclass;
+	aobj : tclass;
+	ai16 : tclass;
+	ai32 : tclass;
+	af32 : tclass;
+	af64 : tclass;
+}
+
+type context = {
+	com : Common.context;
+	cglobals : (string, ttype) lookup;
+	cstrings : (string, string) lookup;
+	cfloats : (float, float) lookup;
+	cints : (int32, int32) lookup;
+	cnatives : (string, (string index * string index * ttype * functable index)) lookup;
+	cfids : (string * path, unit) lookup;
+	cfunctions : fundecl DynArray.t;
+	overrides : (string * path, bool) Hashtbl.t;
+	defined_funs : (int,unit) Hashtbl.t;
+	mutable cached_types : (path, ttype) PMap.t;
+	mutable m : method_context;
+	mutable anons_cache : (tanon * ttype) list;
+	mutable method_wrappers : ((ttype * ttype), int) PMap.t;
+	mutable rec_cache : (Type.t * ttype option ref) list;
+	array_impl : array_impl;
+	base_class : tclass;
+	base_type : tclass;
+	base_enum : tclass;
+	core_type : tclass;
+	core_enum : tclass;
+	cdebug_files : (string, string) lookup;
+}
+
+(* --- *)
+
+type access =
+	| ANone
+	| AGlobal of global
+	| ALocal of reg
+	| AStaticVar of global * ttype * field index
+	| AStaticFun of fundecl index
+	| AInstanceFun of texpr * fundecl index
+	| AInstanceProto of texpr * field index
+	| AInstanceField of texpr * field index
+	| AArray of reg * (ttype * ttype) * reg
+	| AVirtualMethod of texpr * field index
+	| ADynamic of texpr * string index
+	| AEnum of tenum * field index
+	| ACaptured of field index
+
+let null_proto =
+	{
+		pname = "";
+		pid = 0;
+		pclassglobal = None;
+		psuper = None;
+		pvirtuals = [||];
+		pproto = [||];
+		pfields = [||];
+		pnfields = 0;
+		pindex = PMap.empty;
+		pfunctions = PMap.empty;
+	}
+
+let all_types =
+	let vp = { vfields = [||]; vindex = PMap.empty } in
+	let ep = { ename = ""; eid = 0; eglobal = 0; efields = [||] } in
+	[HVoid;HI8;HI16;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 is_number = function
+	| HI8 | HI16 | HI32 | HF32 | HF64 -> true
+	| _ -> false
+
+let is_to_string t =
+	match follow t with
+	| TFun([],r) -> (match follow r with TInst({ cl_path=[],"String" },[]) -> true | _ -> false)
+	| _ -> false
+
+let 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 list_iteri f l =
+	let p = ref 0 in
+	List.iter (fun v -> f !p v; incr p) l
+
+let list_mapi f l =
+	let p = ref (-1) in
+	List.map (fun v -> incr p; f !p v) l
+
+let is_extern_field f =
+	Type.is_extern_field f || (match f.cf_kind with Method MethNormal -> List.exists (fun (m,_,_) -> m = Meta.Custom ":hlNative") f.cf_meta | _ -> false)
+
+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 rec tstr ?(stack=[]) ?(detailed=false) t =
+	match t with
+	| HVoid -> "void"
+	| HI8 -> "i8"
+	| HI16 -> "i16"
+	| 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 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
+
+
+(*
+	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
+	| HI8 | HI16 | HI32 | HF32 | HF64 | HBool | HVoid | HType -> 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 is_array_class name =
+	match name with
+	| "hl.types.ArrayDyn" | "hl.types.ArrayBasic_Int" | "hl.types.ArrayBasic_Float" | "hl.types.ArrayObj" -> true
+	| _ -> false
+
+let is_array_type t =
+	match t with
+	| HObj p -> is_array_class p.pname
+	| _ -> false
+
+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 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 to_utf8 str p =
+	let u8 = try
+		UTF8.validate str;
+		str;
+	with
+		UTF8.Malformed_code ->
+			(* ISO to utf8 *)
+			let b = UTF8.Buf.create 0 in
+			String.iter (fun c -> UTF8.Buf.add_char b (UChar.of_char c)) str;
+			UTF8.Buf.contents b
+	in
+	let ccount = ref 0 in
+	UTF8.iter (fun c ->
+		let c = UChar.code c in
+		if (c >= 0xD800 && c <= 0xDFFF) || c >= 0x110000 then error "Invalid unicode char" p;
+		incr ccount;
+		if c > 0x10000 then incr ccount;
+	) u8;
+	u8, !ccount
+
+let type_size_bits = function
+	| HI8 | HBool -> 0
+	| HI16 -> 1
+	| HI32 | HF32 -> 2
+	| HF64 -> 3
+	| _ -> assert false
+
+let iteri f l =
+	let p = ref (-1) in
+	List.iter (fun v -> incr p; f !p v) l
+
+let new_lookup() =
+	{
+		arr = DynArray.create();
+		map = PMap.empty;
+	}
+
+let null_capture =
+	{
+		c_vars = [||];
+		c_map = PMap.empty;
+		c_type = HVoid;
+	}
+
+let lookup l v fb =
+	try
+		PMap.find v l.map
+	with Not_found ->
+		let id = DynArray.length l.arr in
+		DynArray.add l.arr (Obj.magic 0);
+		l.map <- PMap.add v id l.map;
+		DynArray.set l.arr id (fb());
+		id
+
+let lookup_alloc l v =
+	let id = DynArray.length l.arr in
+	DynArray.add l.arr v;
+	id
+
+let method_context id t captured =
+	{
+		mid = id;
+		mregs = new_lookup();
+		mops = DynArray.create();
+		mret = t;
+		mbreaks = [];
+		mcontinues = [];
+		mcaptured = captured;
+		mtrys = 0;
+		mcaptreg = 0;
+		mdebug = DynArray.create();
+		mcurpos = (0,0);
+	}
+
+let gather_types (code:code) =
+	let types = new_lookup() in
+	let rec get_type t =
+		(match t with HObj { psuper = Some p } -> get_type (HObj p) | _ -> ());
+		ignore(lookup types t (fun() ->
+			(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
+			| _ ->
+				());
+			t
+		));
+	in
+	List.iter (fun t -> get_type t) [HVoid; HI8; HI16; 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;
+	types
+
+let field_name c f =
+	s_type_path c.cl_path ^ ":" ^ f.cf_name
+
+let efield_name e f =
+	s_type_path e.e_path ^ ":" ^ f.ef_name
+
+let underscore_class_name c = match c.cl_path with [],s -> s | p,s -> String.concat "_" p ^ "_" ^ s
+
+let fundecl_name f = if snd f.name = "" then "fun$" ^ (string_of_int f.findex) else (fst f.name) ^ "_" ^ (snd f.name)
+
+let global_type ctx g =
+	DynArray.get ctx.cglobals.arr g
+
+let is_overriden ctx c f =
+	Hashtbl.mem ctx.overrides (f.cf_name,c.cl_path)
+
+let alloc_float ctx f =
+	lookup ctx.cfloats f (fun() -> f)
+
+let alloc_i32 ctx i =
+	lookup ctx.cints i (fun() -> i)
+
+let alloc_string ctx s =
+	lookup ctx.cstrings s (fun() -> s)
+
+let array_class ctx t =
+	match t with
+	| HI32 ->
+		ctx.array_impl.ai32
+	| HI16 ->
+		ctx.array_impl.ai16
+	| HF32 ->
+		ctx.array_impl.af32
+	| HF64 ->
+		ctx.array_impl.af64
+	| HDyn ->
+		ctx.array_impl.adyn
+	| _ ->
+		ctx.array_impl.aobj
+
+let member_fun c t =
+	match follow t with
+	| TFun (args, ret) -> TFun (("this",false,TInst(c,[])) :: args, ret)
+	| _ -> assert false
+
+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 rec unsigned t =
+	match follow t with
+	| TAbstract ({ a_path = ["hl";"types"],("UI32"|"UI16"|"UI8") },_) | TAbstract ({ a_path = [],"UInt" },_) -> true
+	| TAbstract (a,pl) -> unsigned (Abstract.get_underlying_type a pl)
+	| _ -> false
+
+let set_curpos ctx p =
+	let get_relative_path() =
+		match Common.defined ctx.com Common.Define.AbsolutePath with
+		| true -> if (Filename.is_relative p.pfile)
+			then Filename.concat (Sys.getcwd()) p.pfile
+			else p.pfile
+		| false -> try
+			(* lookup relative path *)
+			let len = String.length p.pfile in
+			let base = List.find (fun path ->
+				let l = String.length path in
+				len > l && String.sub p.pfile 0 l = path
+			) ctx.com.Common.class_path in
+			let l = String.length base in
+			String.sub p.pfile l (len - l)
+		with Not_found ->
+			p.pfile
+	in
+	ctx.m.mcurpos <- (lookup ctx.cdebug_files p.pfile get_relative_path,Lexer.get_error_line p)
+
+let rec to_type ?tref ctx t =
+	match t with
+	| TMono r ->
+		(match !r with
+		| None -> HDyn
+		| Some t -> to_type ?tref ctx t)
+	| TType (td,tl) ->
+		let t = (try
+			match !(List.assq t ctx.rec_cache) with
+			| None -> error "Unsupported recursive type" td.t_pos
+			| Some t -> t
+		with Not_found ->
+			let tref = ref None in
+			ctx.rec_cache <- (t,tref) :: ctx.rec_cache;
+			let t = to_type ~tref ctx (apply_params td.t_params tl td.t_type) in
+			ctx.rec_cache <- List.tl ctx.rec_cache;
+			t
+		) in
+		(match td.t_path with
+		| [], "Null" when not (is_nullable t) -> HNull t
+		| _ -> t)
+	| TLazy f ->
+		to_type ?tref ctx (!f())
+	| TFun (args, ret) ->
+		HFun (List.map (fun (_,o,t) -> to_type ctx (if o then ctx.com.basic.tnull t else t)) args, to_type ctx ret)
+	| TAnon a when (match !(a.a_status) with Statics _ | EnumStatics _ -> true | _ -> false) ->
+		(match !(a.a_status) with
+		| Statics c ->
+			class_type ctx c (List.map snd c.cl_params) true
+		| EnumStatics e ->
+			enum_class ctx e
+		| _ -> assert false)
+	| TAnon a ->
+		(try
+			(* can't use physical comparison in PMap since addresses might change in GC compact,
+				maybe add an uid to tanon if too slow ? *)
+			List.assq a ctx.anons_cache
+		with Not_found ->
+			let vp = {
+				vfields = [||];
+				vindex = PMap.empty;
+			} in
+			let t = HVirtual vp in
+			(match tref with
+			| None -> ()
+			| Some r -> r := Some t);
+			ctx.anons_cache <- (a,t) :: ctx.anons_cache;
+			let fields = PMap.fold (fun cf acc ->
+				match cf.cf_kind with
+				| Var _ when (match follow cf.cf_type with TAnon _ | TFun _ -> true | _ -> false) ->
+					(*
+						if it's another virtual or a method, it might not match our own (might be larger, or class)
+					*)
+					acc
+				| Method _ ->
+					acc
+				| _ ->
+					(cf.cf_name,alloc_string ctx cf.cf_name,to_type ctx cf.cf_type) :: acc
+			) a.a_fields [] in
+			if fields = [] then
+				let t = HDyn in
+				ctx.anons_cache <- (a,t) :: List.tl ctx.anons_cache;
+				t
+			else
+				let fields = List.sort (fun (n1,_,_) (n2,_,_) -> compare n1 n2) fields in
+				vp.vfields <- Array.of_list fields;
+				Array.iteri (fun i (n,_,_) -> vp.vindex <- PMap.add n i vp.vindex) vp.vfields;
+				t
+		)
+	| TDynamic _ ->
+		HDyn
+	| TEnum (e,_) ->
+		enum_type ~tref ctx e
+	| TInst ({ cl_path = ["hl";"types"],"NativeAbstract" },[TInst({ cl_kind = KExpr (EConst (String name),_) },_)]) ->
+		HAbstract (name, alloc_string ctx name)
+	| TInst (c,pl) ->
+		(match c.cl_kind with
+		| KTypeParameter tl ->
+			let rec loop = function
+				| [] -> HDyn
+				| t :: tl ->
+					match follow (apply_params c.cl_params pl t) with
+					| TInst ({cl_interface=false},_) as t -> to_type ?tref ctx t
+					| _ -> loop tl
+			in
+			loop tl
+		| _ -> class_type ~tref ctx c pl false)
+	| TAbstract (a,pl) ->
+		if Meta.has Meta.CoreType a.a_meta then
+			(match a.a_path with
+			| [], "Void" -> HVoid
+			| [], "Int" | [], "UInt" -> HI32
+			| [], "Float" -> HF64
+			| [], "Single" -> HF32
+			| [], "Bool" -> HBool
+			| [], "Dynamic" -> HDyn
+			| [], "Class" ->
+				class_type ctx ctx.base_class [] false
+			| [], "Enum" ->
+				class_type ctx ctx.base_type [] false
+			| [], "EnumValue" -> HDyn
+			| ["hl";"types"], "Ref" -> HRef (to_type ctx (List.hd pl))
+			| ["hl";"types"], ("Bytes" | "BytesAccess") -> HBytes
+			| ["hl";"types"], "Type" -> HType
+			| ["hl";"types"], "I16" -> HI16
+			| ["hl";"types"], "NativeArray" -> HArray
+			| _ -> failwith ("Unknown core type " ^ s_type_path a.a_path))
+		else
+			to_type ?tref ctx (Abstract.get_underlying_type a pl)
+
+and resolve_class ctx c pl statics =
+	let not_supported() =
+		failwith ("Extern type not supported : " ^ s_type (print_context()) (TInst (c,pl)))
+	in
+	match c.cl_path, pl with
+	| ([],"Array"), [t] ->
+		if statics then ctx.array_impl.abase else array_class ctx (to_type ctx t)
+	| ([],"Array"), [] ->
+		assert false
+	| _, _ when c.cl_extern ->
+		not_supported()
+	| _ ->
+		c
+
+and field_type ctx f p =
+	match f with
+	| FInstance (c,pl,f) | FClosure (Some (c,pl),f) ->
+		let creal = resolve_class ctx c pl false in
+		let rec loop c =
+			try
+				PMap.find f.cf_name c.cl_fields
+			with Not_found ->
+				match c.cl_super with
+				| Some (csup,_) -> loop csup
+				| None -> error (s_type_path creal.cl_path ^ " is missing field " ^ f.cf_name) p
+		in
+		(loop creal).cf_type
+	| FStatic (_,f) | FAnon f | FClosure (_,f) -> f.cf_type
+	| FDynamic _ -> t_dynamic
+	| FEnum (_,f) -> f.ef_type
+
+and real_type ctx e =
+	let rec loop e =
+		match e.eexpr with
+		| TField (_,f) ->
+			let ft = field_type ctx f e.epos in
+			(*
+				Handle function variance:
+				If we have type parameters which are function types, we need to keep the functions
+				because we might need to insert a cast to coerce Void->Bool to Void->Dynamic for instance.
+			*)
+			(match ft, e.etype with
+			| TFun (args,ret), TFun (args2,_) ->
+				TFun (List.map2 (fun ((_,_,t) as a) ((_,_,t2) as a2) -> match t, t2 with TInst ({cl_kind=KTypeParameter _},_), TFun _ -> a2 | _ -> a) args args2, ret)
+			| _ -> ft)
+		| TLocal v -> v.v_type
+		| TParenthesis e -> loop e
+		| _ -> e.etype
+	in
+	to_type ctx (loop e)
+
+and class_type ?(tref=None) ctx c pl statics =
+	let c = if c.cl_extern then resolve_class ctx c pl statics else c in
+	let key_path = (if statics then fst c.cl_path, "$" ^ snd c.cl_path else c.cl_path) in
+	try
+		PMap.find key_path ctx.cached_types
+	with Not_found when c.cl_interface && not statics ->
+		let vp = {
+			vfields = [||];
+			vindex = PMap.empty;
+		} in
+		let t = HVirtual vp in
+		ctx.cached_types <- PMap.add c.cl_path t ctx.cached_types;
+		let rec loop c =
+			let fields = List.fold_left (fun acc (i,_) -> loop i @ acc) [] c.cl_implements in
+			PMap.fold (fun cf acc -> (cf.cf_name,alloc_string ctx cf.cf_name,to_type ctx cf.cf_type) :: acc) c.cl_fields fields
+		in
+		let fields = loop c in
+		vp.vfields <- Array.of_list fields;
+		Array.iteri (fun i (n,_,_) -> vp.vindex <- PMap.add n i vp.vindex) vp.vfields;
+		t
+	| Not_found ->
+		let pname = s_type_path key_path in
+		let p = {
+			pname = pname;
+			pid = alloc_string ctx pname;
+			psuper = None;
+			pclassglobal = None;
+			pproto = [||];
+			pfields = [||];
+			pindex = PMap.empty;
+			pvirtuals = [||];
+			pfunctions = PMap.empty;
+			pnfields = -1;
+		} in
+		let t = HObj p in
+		(match tref with
+		| None -> ()
+		| Some r -> r := Some t);
+		ctx.cached_types <- PMap.add key_path t ctx.cached_types;
+		if c.cl_path = ([],"Array") then assert false;
+		if c == ctx.base_class then begin
+			if statics then assert false;
+			p.pnfields <- 1;
+		end;
+		let tsup = (match c.cl_super with
+			| Some (csup,pl) when not statics -> Some (class_type ctx csup [] statics)
+			| _ -> if statics then Some (class_type ctx ctx.base_class [] false) else None
+		) in
+		let start_field, virtuals = (match tsup with
+			| None -> 0, [||]
+			| Some (HObj psup) ->
+				if psup.pnfields < 0 then assert false;
+				p.psuper <- Some psup;
+				p.pfunctions <- psup.pfunctions;
+				psup.pnfields, psup.pvirtuals
+			| _ -> assert false
+		) in
+		let fa = DynArray.create() and pa = DynArray.create() and virtuals = DynArray.of_array virtuals in
+		let todo = ref [] in
+		List.iter (fun f ->
+			if is_extern_field f || (statics && f.cf_name = "__meta__") then () else
+			match f.cf_kind with
+			| Method m when m <> MethDynamic && not statics ->
+				let g = alloc_fid ctx c f in
+				p.pfunctions <- PMap.add f.cf_name g p.pfunctions;
+				let virt = if List.exists (fun ff -> ff.cf_name = f.cf_name) c.cl_overrides then
+					let vid = (try -(fst (get_index f.cf_name p))-1 with Not_found -> assert false) in
+					DynArray.set virtuals vid g;
+					Some vid
+				else if is_overriden ctx c f then begin
+					let vid = DynArray.length virtuals in
+					DynArray.add virtuals g;
+					p.pindex <- PMap.add f.cf_name (-vid-1,HVoid) p.pindex;
+					Some vid
+				end else
+					None
+				in
+				DynArray.add pa { fname = f.cf_name; fid = alloc_string ctx f.cf_name; fmethod = g; fvirtual = virt; }
+			| Method MethDynamic when List.exists (fun ff -> ff.cf_name = f.cf_name) c.cl_overrides ->
+				()
+			| _ ->
+				let fid = DynArray.length fa in
+				p.pindex <- PMap.add f.cf_name (fid + start_field, t) p.pindex;
+				DynArray.add fa (f.cf_name, alloc_string ctx f.cf_name, HVoid);
+				todo := (fun() ->
+					let t = to_type ctx f.cf_type in
+					p.pindex <- PMap.add f.cf_name (fid + start_field, t) p.pindex;
+					Array.set p.pfields fid (f.cf_name, alloc_string ctx f.cf_name, t)
+				) :: !todo;
+		) (if statics then c.cl_ordered_statics else c.cl_ordered_fields);
+		if not statics then (try
+			let cf = PMap.find "toString" c.cl_fields in
+			if List.memq cf c.cl_overrides || PMap.mem "__string" c.cl_fields || not (is_to_string cf.cf_type) then raise Not_found;
+			DynArray.add pa { fname = "__string"; fid = alloc_string ctx "__string"; fmethod = alloc_fun_path ctx c.cl_path "__string"; fvirtual = None; }
+		with Not_found ->
+			());
+		p.pnfields <- DynArray.length fa + start_field;
+		p.pfields <- DynArray.to_array fa;
+		p.pproto <- DynArray.to_array pa;
+		p.pvirtuals <- DynArray.to_array virtuals;
+		List.iter (fun f -> f()) !todo;
+		if not statics && c != ctx.core_type && c != ctx.core_enum then p.pclassglobal <- Some (fst (class_global ctx (if statics then ctx.base_class else c)));
+		t
+
+and enum_type ?(tref=None) ctx e =
+	try
+		PMap.find e.e_path ctx.cached_types
+	with Not_found ->
+		let ename = s_type_path e.e_path in
+		let et = {
+			eglobal = 0;
+			ename = ename;
+			eid = alloc_string ctx ename;
+			efields = [||];
+		} in
+		let t = HEnum et in
+		(match tref with
+		| None -> ()
+		| Some r -> r := Some t);
+		ctx.cached_types <- PMap.add e.e_path t ctx.cached_types;
+		et.efields <- Array.of_list (List.map (fun f ->
+			let f = PMap.find f e.e_constrs in
+			let args = (match f.ef_type with
+				| TFun (args,_) -> Array.of_list (List.map (fun (_,_,t) -> to_type ctx t) args)
+				| _ -> [||]
+			) in
+			(f.ef_name, alloc_string ctx f.ef_name, args)
+		) e.e_names);
+		let ct = enum_class ctx e in
+		et.eglobal <- alloc_global ctx (match ct with HObj o -> o.pname | _ -> assert false) ct;
+		t
+
+and enum_class ctx e =
+	let cpath = (fst e.e_path,"$" ^ snd e.e_path) in
+	try
+		PMap.find cpath ctx.cached_types
+	with Not_found ->
+		let pname = s_type_path cpath in
+		let p = {
+			pname = pname;
+			pid = alloc_string ctx pname;
+			psuper = None;
+			pclassglobal = None;
+			pproto = [||];
+			pfields = [||];
+			pindex = PMap.empty;
+			pvirtuals = [||];
+			pfunctions = PMap.empty;
+			pnfields = -1;
+		} in
+		let t = HObj p in
+		ctx.cached_types <- PMap.add cpath t ctx.cached_types;
+		p.psuper <- Some (match class_type ctx ctx.base_enum [] false with HObj o -> o | _ -> assert false);
+		t
+
+and alloc_fun_path ctx path name =
+	lookup ctx.cfids (name, path) (fun() -> ())
+
+and alloc_fid ctx c f =
+	match f.cf_kind with
+	| Var _ -> assert false
+	| _ -> alloc_fun_path ctx c.cl_path f.cf_name
+
+and alloc_eid ctx e f =
+	alloc_fun_path ctx e.e_path f.ef_name
+
+and alloc_function_name ctx f =
+	alloc_fun_path ctx ([],"") f
+
+and alloc_global ctx name t =
+	lookup ctx.cglobals name (fun() -> t)
+
+and class_global ?(resolve=true) ctx c =
+	let static = c != ctx.base_class in
+	let c = if resolve && is_array_type (HObj { null_proto with pname = s_type_path c.cl_path }) then ctx.array_impl.abase else c in
+	let c = resolve_class ctx c (List.map snd c.cl_params) static in
+	let t = class_type ctx c [] static in
+	alloc_global ctx ("$" ^ s_type_path c.cl_path) t, t
+
+let alloc_std ctx name args ret =
+	let lib = "std" in
+	(* different from :hlNative to prevent mismatch *)
+	let nid = lookup ctx.cnatives ("$" ^ name ^ "@" ^ lib) (fun() ->
+		let fid = alloc_fun_path ctx ([],"std") name in
+		Hashtbl.add ctx.defined_funs fid ();
+		(alloc_string ctx lib, alloc_string ctx name,HFun (args,ret),fid)
+	) in
+	let _,_,_,fid = DynArray.get ctx.cnatives.arr nid in
+	fid
+
+let is_int ctx t =
+	match to_type ctx t with
+	| HI8 | HI16 | HI32 -> true
+	| _ -> false
+
+let is_float ctx t =
+	match to_type ctx t with
+	| HF32 | HF64 -> true
+	| _ -> false
+
+let alloc_reg ctx v =
+	lookup ctx.m.mregs v.v_id (fun() -> to_type ctx v.v_type)
+
+let alloc_tmp ctx t =
+	let rid = DynArray.length ctx.m.mregs.arr in
+	DynArray.add ctx.m.mregs.arr t;
+	rid
+
+let current_pos ctx =
+	DynArray.length ctx.m.mops
+
+let rtype ctx r =
+	DynArray.get ctx.m.mregs.arr r
+
+let op ctx o =
+	DynArray.add ctx.m.mdebug ctx.m.mcurpos;
+	DynArray.add ctx.m.mops o
+
+let jump ctx f =
+	let pos = current_pos ctx in
+	op ctx (OJAlways (-1)); (* loop *)
+	(fun() -> DynArray.set ctx.m.mops pos (f (current_pos ctx - pos - 1)))
+
+let jump_back ctx =
+	let pos = current_pos ctx in
+	op ctx (OLabel 0);
+	(fun() -> op ctx (OJAlways (pos - current_pos ctx - 1)))
+
+let reg_int ctx v =
+	let r = alloc_tmp ctx HI32 in
+	op ctx (OInt (r,alloc_i32 ctx (Int32.of_int v)));
+	r
+
+let shl ctx idx v =
+	if v = 0 then idx else
+	let idx2 = alloc_tmp ctx HI32 in
+	op ctx (OShl (idx2, idx, reg_int ctx v));
+	idx2
+
+let set_default ctx r =
+	match rtype ctx r with
+	| HI8 | HI16 | HI32 ->
+		op ctx (OInt (r,alloc_i32 ctx 0l))
+	| HF32 | HF64 ->
+		op ctx (OFloat (r,alloc_float ctx 0.))
+	| HBool ->
+		op ctx (OBool (r, false))
+	| HType ->
+		op ctx (OType (r, HVoid))
+	| _ ->
+		op ctx (ONull r)
+
+let read_mem ctx rdst bytes index t =
+	match t with
+	| HI8 ->
+		op ctx (OGetI8 (rdst,bytes,index))
+	| HI16 ->
+		op ctx (OGetI16 (rdst,bytes,index))
+	| HI32 ->
+		op ctx (OGetI32 (rdst,bytes,index))
+	| HF32 ->
+		op ctx (OGetF32 (rdst,bytes,index))
+	| HF64 ->
+		op ctx (OGetF64 (rdst,bytes,index))
+	| _ ->
+		assert false
+
+let write_mem ctx bytes index t r=
+	match t with
+	| HI8 ->
+		op ctx (OSetI8 (bytes,index,r))
+	| HI16 ->
+		op ctx (OSetI16 (bytes,index,r))
+	| HI32 ->
+		op ctx (OSetI32 (bytes,index,r))
+	| HF32 ->
+		op ctx (OSetF32 (bytes,index,r))
+	| HF64 ->
+		op ctx (OSetF64 (bytes,index,r))
+	| _ ->
+		assert false
+
+let common_type ctx e1 e2 for_eq p =
+	let t1 = to_type ctx e1.etype in
+	let t2 = to_type ctx e2.etype in
+	let rec loop t1 t2 =
+		if t1 == t2 then t1 else
+		match t1, t2 with
+		| HI8, (HI16 | HI32 | HF32 | HF64) -> t2
+		| HI16, (HI32 | HF32 | HF64) -> t2
+		| HI32, HF32 -> t2 (* possible loss of precision *)
+		| (HI32 | HF32), HF64 -> t2
+		| (HI8|HI16|HI32|HF32|HF64), (HI8|HI16|HI32|HF32|HF64) -> t1
+		| (HI8|HI16|HI32|HF32|HF64), (HNull t2) -> if for_eq then HNull (loop t1 t2) else loop t1 t2
+		| (HNull t1), (HI8|HI16|HI32|HF32|HF64) -> if for_eq then HNull (loop t1 t2) else loop t1 t2
+		| (HNull t1), (HNull t2) -> if for_eq then HNull (loop t1 t2) else loop t1 t2
+		| HDyn, (HI8|HI16|HI32|HF32|HF64) -> HF64
+		| (HI8|HI16|HI32|HF32|HF64), HDyn -> HF64
+		| HDyn, _ -> HDyn
+		| _, HDyn -> HDyn
+		| _ when for_eq && safe_cast t1 t2 -> t2
+		| _ when for_eq && safe_cast t2 t1 -> t1
+		| HBool, HNull HBool when for_eq -> t2
+		| HNull HBool, HBool when for_eq -> t1
+		| HObj _, HVirtual _ | HVirtual _, HObj _ -> HDyn
+		| _ ->
+			error ("Don't know how to compare " ^ tstr t1 ^ " and " ^ tstr t2) p
+	in
+	loop t1 t2
+
+let captured_index ctx v =
+	if not v.v_capture then None else try Some (PMap.find v.v_id ctx.m.mcaptured.c_map) with Not_found -> None
+
+let before_return ctx =
+	let rec loop i =
+		if i > 0 then begin
+			op ctx (OEndTrap false);
+			loop (i - 1)
+		end
+	in
+	loop ctx.m.mtrys
+
+let type_value ctx t p =
+	match t with
+	| TClassDecl c ->
+		let g, t = class_global ctx c in
+		let r = alloc_tmp ctx t in
+		op ctx (OGetGlobal (r, g));
+		r
+	| TAbstractDecl a ->
+		let r = alloc_tmp ctx (class_type ctx ctx.base_type [] false) in
+		(match a.a_path with
+		| [], "Int" -> op ctx (OGetGlobal (r, alloc_global ctx "$Int" (rtype ctx r)))
+		| [], "Float" -> op ctx (OGetGlobal (r, alloc_global ctx "$Float" (rtype ctx r)))
+		| [], "Bool" -> op ctx (OGetGlobal (r, alloc_global ctx "$Bool" (rtype ctx r)))
+		| [], "Class" -> op ctx (OGetGlobal (r, fst (class_global ctx ctx.base_class)))
+		| [], "Enum" -> op ctx (OGetGlobal (r, fst (class_global ctx ctx.base_enum)))
+		| [], "Dynamic" -> op ctx (OGetGlobal (r, alloc_global ctx "$Dynamic" (rtype ctx r)))
+		| _ -> error ("Unsupported type value " ^ s_type_path (t_path t)) p);
+		r
+	| TEnumDecl e ->
+		let r = alloc_tmp ctx (enum_class ctx e) in
+		let rt = rtype ctx r in
+		op ctx (OGetGlobal (r, alloc_global ctx (match rt with HObj o -> o.pname | _ -> assert false) rt));
+		r
+	| TTypeDecl _ ->
+		assert false
+
+let rec eval_to ctx e (t:ttype) =
+	let r = eval_expr ctx e in
+	cast_to ctx r t e.epos
+
+and cast_to ?(force=false) ctx (r:reg) (t:ttype) p =
+	let rt = rtype ctx r in
+	if safe_cast rt t then r else
+	match rt, t with
+	| _, HVoid ->
+		alloc_tmp ctx HVoid
+	| HVirtual _, HVirtual _ ->
+		let tmp = alloc_tmp ctx HDyn in
+		op ctx (OMov (tmp,r));
+		cast_to ctx tmp t p
+	| (HI8 | HI16 | HI32 | HF32 | HF64), (HF32 | HF64) ->
+		let tmp = alloc_tmp ctx t in
+		op ctx (OToSFloat (tmp, r));
+		tmp
+	| (HI8 | HI16 | HI32 | HF32 | HF64), (HI8 | HI16 | HI32) ->
+		let tmp = alloc_tmp ctx t in
+		op ctx (OToInt (tmp, r));
+		tmp
+	| (HI8 | HI16 | HI32), HObj { pname = "String" } ->
+		let out = alloc_tmp ctx t in
+		let len = alloc_tmp ctx HI32 in
+		let lref = alloc_tmp ctx (HRef HI32) in
+		let bytes = alloc_tmp ctx HBytes in
+		op ctx (ORef (lref,len));
+		op ctx (OCall2 (bytes,alloc_std ctx "itos" [HI32;HRef HI32] HBytes,cast_to ctx r HI32 p,lref));
+		op ctx (OCall2 (out,alloc_fun_path ctx ([],"String") "__alloc__",bytes,len));
+		out
+	| (HF32 | HF64), HObj { pname = "String" } ->
+		let out = alloc_tmp ctx t in
+		let len = alloc_tmp ctx HI32 in
+		let lref = alloc_tmp ctx (HRef HI32) in
+		let bytes = alloc_tmp ctx HBytes in
+		op ctx (ORef (lref,len));
+		op ctx (OCall2 (bytes,alloc_std ctx "ftos" [HF64;HRef HI32] HBytes,cast_to ctx r HF64 p,lref));
+		op ctx (OCall2 (out,alloc_fun_path ctx ([],"String") "__alloc__",bytes,len));
+		out
+	| _, HObj { pname = "String" } ->
+		let out = alloc_tmp ctx t in
+		let r = cast_to ctx r HDyn p in
+		op ctx (OJNotNull (r,2));
+		op ctx (ONull out);
+		op ctx (OJAlways 1);
+		op ctx (OCall1 (out,alloc_fun_path ctx ([],"Std") "string",r));
+		out
+	| (HObj _ | HDynObj | HDyn) , HVirtual _ ->
+		let out = alloc_tmp ctx t in
+		op ctx (OToVirtual (out,r));
+		out
+	| HDyn, _ ->
+		let out = alloc_tmp ctx t in
+		op ctx (OSafeCast (out, r));
+		out
+	| HNull rt, _ when t = rt ->
+		let out = alloc_tmp ctx t in
+		op ctx (OSafeCast (out, r));
+		out
+	| HVoid, HDyn ->
+		let tmp = alloc_tmp ctx HDyn in
+		op ctx (ONull tmp);
+		tmp
+	| _ , HDyn ->
+		let tmp = alloc_tmp ctx HDyn in
+		op ctx (OToDyn (tmp, r));
+		tmp
+	| _, HNull t when rt == t ->
+		let tmp = alloc_tmp ctx (HNull t) in
+		op ctx (OToDyn (tmp, r));
+		tmp
+	| HNull t1, HNull t2 ->
+		let j = jump ctx (fun n -> OJNull (r,n)) in
+		let rtmp = alloc_tmp ctx t1 in
+		op ctx (OSafeCast (rtmp,r));
+		let out = cast_to ctx rtmp t p in
+		op ctx (OJAlways 1);
+		j();
+		op ctx (ONull out);
+		out
+	| (HI8 | HI16 | HI32 | HF32 | HF64), HNull ((HF32 | HF64) as t) ->
+		let tmp = alloc_tmp ctx t in
+		op ctx (OToSFloat (tmp, r));
+		let r = alloc_tmp ctx (HNull t) in
+		op ctx (OToDyn (r,tmp));
+		r
+	| (HI8 | HI16 | HI32 | HF32 | HF64), HNull ((HI8 | HI16 | HI32) as t) ->
+		let tmp = alloc_tmp ctx t in
+		op ctx (OToInt (tmp, r));
+		let r = alloc_tmp ctx (HNull t) in
+		op ctx (OToDyn (r,tmp));
+		r
+	| HNull ((HI8 | HI16 | HI32) as it), (HF32 | HF64) ->
+		let i = alloc_tmp ctx it in
+		op ctx (OSafeCast (i,r));
+		let tmp = alloc_tmp ctx t in
+		op ctx (OToSFloat (tmp, i));
+		tmp
+	| HNull ((HF32 | HF64) as it), (HI8 | HI16 | HI32) ->
+		let i = alloc_tmp ctx it in
+		op ctx (OSafeCast (i,r));
+		let tmp = alloc_tmp ctx t in
+		op ctx (OToInt (tmp, i));
+		tmp
+	| HFun (args1,ret1), HFun (args2, ret2) when List.length args1 = List.length args2 ->
+		let fid = gen_method_wrapper ctx rt t p in
+		let fr = alloc_tmp ctx t in
+		op ctx (OJNotNull (r,2));
+		op ctx (ONull fr);
+		op ctx (OJAlways 1);
+		op ctx (OInstanceClosure (fr,fid,r));
+		fr
+	| HObj _, HObj _ when is_array_type rt && is_array_type t ->
+		let out = alloc_tmp ctx t in
+		op ctx (OSafeCast (out, r));
+		out
+	| _ ->
+		if force then
+			let out = alloc_tmp ctx t in
+			op ctx (OSafeCast (out, r));
+			out
+		else
+			error ("Don't know how to cast " ^ tstr rt ^ " to " ^ tstr t) p
+
+and unsafe_cast_to ctx (r:reg) (t:ttype) p =
+	let rt = rtype ctx r in
+	if safe_cast rt t then
+		r
+	else
+	match rt with
+	| HFun _ ->
+		cast_to ctx r t p
+	| HDyn when is_array_type t ->
+		cast_to ctx r t p
+	| HDyn when (match t with HVirtual _ -> true | _ -> false) ->
+		cast_to ctx r t p
+	| HObj _ when is_array_type rt && is_array_type t ->
+		cast_to ctx r t p
+	| _ ->
+		if is_dynamic (rtype ctx r) && is_dynamic t then
+			let r2 = alloc_tmp ctx t in
+			op ctx (OUnsafeCast (r2,r));
+			r2
+		else
+			cast_to ~force:true ctx r t p
+
+and object_access ctx eobj t f =
+	match t with
+	| HObj p ->
+		(try
+			let fid = fst (get_index f.cf_name p) in
+			if f.cf_kind = Method MethNormal then
+				AInstanceProto (eobj, -fid-1)
+			else
+				AInstanceField (eobj, fid)
+		with Not_found ->
+			ADynamic (eobj, alloc_string ctx f.cf_name))
+	| HVirtual v ->
+		(try
+			let fid = PMap.find f.cf_name v.vindex in
+			if f.cf_kind = Method MethNormal then
+				AVirtualMethod (eobj, fid)
+			else
+				AInstanceField (eobj, fid)
+		with Not_found ->
+			ADynamic (eobj, alloc_string ctx f.cf_name))
+	| HDyn ->
+		ADynamic (eobj, alloc_string ctx f.cf_name)
+	| _ ->
+		error ("Unsupported field access " ^ tstr t) eobj.epos
+
+and get_access ctx e =
+	match e.eexpr with
+	| TField (ethis, a) ->
+		(match a, follow ethis.etype with
+		| FStatic (c,({ cf_kind = Var _ | Method MethDynamic } as f)), _ ->
+			let g, t = class_global ctx c in
+			AStaticVar (g, t, (match t with HObj o -> (try fst (get_index f.cf_name o) with Not_found -> assert false) | _ -> assert false))
+		| FStatic (c,({ cf_kind = Method _ } as f)), _ ->
+			AStaticFun (alloc_fid ctx c f)
+		| FClosure (Some (cdef,pl), ({ cf_kind = Method m } as f)), TInst (c,_)
+		| FInstance (cdef,pl,({ cf_kind = Method m } as f)), TInst (c,_) when m <> MethDynamic && not (c.cl_interface || (is_overriden ctx c f && ethis.eexpr <> TConst(TSuper))) ->
+			(* cdef is the original definition, we want the last redefinition *)
+			let rec loop c =
+				if PMap.mem f.cf_name c.cl_fields then c else (match c.cl_super with None -> cdef | Some (c,_) -> loop c)
+			in
+			let last_def = loop c in
+			AInstanceFun (ethis, alloc_fid ctx (resolve_class ctx last_def pl false) f)
+		| (FInstance (cdef,pl,f) | FClosure (Some (cdef,pl), f)), _ ->
+			let cdef, pl = if cdef.cl_interface then (match follow ethis.etype with TInst (c,pl) -> c,pl | _ -> assert false) else cdef,pl in
+			object_access ctx ethis (class_type ctx cdef pl false) f
+		| (FAnon f | FClosure(None,f)), _ ->
+			object_access ctx ethis (to_type ctx ethis.etype) f
+		| FDynamic name, _ ->
+			ADynamic (ethis, alloc_string ctx name)
+		| FEnum (e,ef), _ ->
+			(match follow ef.ef_type with
+			| TFun _ -> AEnum (e,ef.ef_index)
+			| t -> AGlobal (alloc_global ctx (efield_name e ef) (to_type ctx t))))
+	| TLocal v ->
+		(match captured_index ctx v with
+		| None -> ALocal (alloc_reg ctx v)
+		| Some idx -> ACaptured idx)
+	| TParenthesis e ->
+		get_access ctx e
+	| TArray (a,i) ->
+		(match follow a.etype with
+		| TInst({ cl_path = [],"Array" },[t]) ->
+			let a = eval_null_check ctx a in
+			let i = eval_to ctx i HI32 in
+			let t = to_type ctx t in
+			AArray (a,(t,t),i)
+		| _ ->
+			let a = eval_to ctx a (class_type ctx ctx.array_impl.adyn [] false) in
+			op ctx (ONullCheck a);
+			let i = eval_to ctx i HI32 in
+			AArray (a,(HDyn,to_type ctx e.etype),i)
+		)
+	| _ ->
+		ANone
+
+and array_read ctx ra (at,vt) ridx p =
+	match at with
+	| HI8 | HI16 | HI32 | HF32 | HF64 ->
+		(* check bounds *)
+		let length = alloc_tmp ctx HI32 in
+		op ctx (OField (length, ra, 0));
+		let r = alloc_tmp ctx (match at with HI8 | HI16 -> HI32 | _ -> at) in
+		let j = jump ctx (fun i -> OJULt (ridx,length,i)) in
+		(match at with
+		| HI8 | HI16 | HI32 ->
+			op ctx (OInt (r,alloc_i32 ctx 0l));
+		| HF32 | HF64 ->
+			op ctx (OFloat (r,alloc_float ctx 0.));
+		| _ ->
+			assert false);
+		let jend = jump ctx (fun i -> OJAlways i) in
+		j();
+		let hbytes = alloc_tmp ctx HBytes in
+		op ctx (OField (hbytes, ra, 1));
+		read_mem ctx r hbytes (shl ctx ridx (type_size_bits at)) at;
+		jend();
+		cast_to ctx r vt p
+	| HDyn ->
+		(* call getDyn *)
+		let r = alloc_tmp ctx HDyn in
+		op ctx (OCallMethod (r,0,[ra;ridx]));
+		unsafe_cast_to ctx r vt p
+	| _ ->
+		(* check bounds *)
+		let length = alloc_tmp ctx HI32 in
+		op ctx (OField (length,ra,0));
+		let r = alloc_tmp ctx vt in
+		let j = jump ctx (fun i -> OJULt (ridx,length,i)) in
+		set_default ctx r;
+		let jend = jump ctx (fun i -> OJAlways i) in
+		j();
+		let tmp = alloc_tmp ctx HDyn in
+		let harr = alloc_tmp ctx HArray in
+		op ctx (OField (harr,ra,1));
+		op ctx (OGetArray (tmp,harr,ridx));
+		op ctx (OMov (r,unsafe_cast_to ctx tmp vt p));
+		jend();
+		r
+
+and jump_expr ctx e jcond =
+	match e.eexpr with
+	| TParenthesis e ->
+		jump_expr ctx e jcond
+	| TUnop (Not,_,e) ->
+		jump_expr ctx e (not jcond)
+	| TBinop (OpEq,{ eexpr = TConst(TNull) },e) | TBinop (OpEq,e,{ eexpr = TConst(TNull) }) ->
+		let r = eval_expr ctx e in
+		jump ctx (fun i -> if jcond then OJNull (r,i) else OJNotNull (r,i))
+	| TBinop (OpNotEq,{ eexpr = TConst(TNull) },e) | TBinop (OpNotEq,e,{ eexpr = TConst(TNull) }) ->
+		let r = eval_expr ctx e in
+		jump ctx (fun i -> if jcond then OJNotNull (r,i) else OJNull (r,i))
+	| TBinop (OpEq | OpNotEq | OpGt | OpGte | OpLt | OpLte as jop, e1, e2) ->
+		let t = common_type ctx e1 e2 (match jop with OpEq | OpNotEq -> true | _ -> false) e.epos in
+		let r1 = eval_to ctx e1 t in
+		let r2 = eval_to ctx e2 t in
+		let unsigned = unsigned e1.etype && unsigned e2.etype in
+		jump ctx (fun i ->
+			let lt a b = if unsigned then OJULt (a,b,i) else OJSLt (a,b,i) in
+			let gte a b = if unsigned then OJUGte (a,b,i) else OJSGte (a,b,i) in
+			match jop with
+			| OpEq -> if jcond then OJEq (r1,r2,i) else OJNotEq (r1,r2,i)
+			| OpNotEq -> if jcond then OJNotEq (r1,r2,i) else OJEq (r1,r2,i)
+			| OpGt -> if jcond then lt r2 r1 else gte r2 r1
+			| OpGte -> if jcond then gte r1 r2 else lt r1 r2
+			| OpLt -> if jcond then lt r1 r2 else gte r1 r2
+			| OpLte -> if jcond then gte r2 r1 else lt r2 r1
+			| _ -> assert false
+		)
+	| TBinop (OpBoolAnd, e1, e2) ->
+		let j = jump_expr ctx e1 false in
+		let j2 = jump_expr ctx e2 jcond in
+		if jcond then j();
+		(fun() -> if not jcond then j(); j2());
+	| TBinop (OpBoolOr, e1, e2) ->
+		let j = jump_expr ctx e1 true in
+		let j2 = jump_expr ctx e2 jcond in
+		if not jcond then j();
+		(fun() -> if jcond then j(); j2());
+	| _ ->
+		let r = eval_to ctx e HBool in
+		jump ctx (fun i -> if jcond then OJTrue (r,i) else OJFalse (r,i))
+
+and eval_args ctx el t p =
+	let rl = List.map2 (fun e t -> eval_to ctx e t) el (match t with HFun (args,_) -> args | HDyn -> List.map (fun _ -> HDyn) el | _ -> assert false) in
+	set_curpos ctx p;
+	rl
+
+and eval_null_check ctx e =
+	let r = eval_expr ctx e in
+	(match e.eexpr with
+	| TConst TThis | TConst TSuper -> ()
+	| _ -> op ctx (ONullCheck r));
+	r
+
+and make_string ctx s p =
+	let str, len = to_utf8 s p in
+	let r = alloc_tmp ctx HBytes in
+	let s = alloc_tmp ctx (to_type ctx ctx.com.basic.tstring) in
+	op ctx (ONew s);
+	op ctx (OString (r,alloc_string ctx str));
+	op ctx (OSetField (s,0,r));
+	op ctx (OSetField (s,1,reg_int ctx len));
+	s
+
+and eval_expr ctx e =
+	set_curpos ctx e.epos;
+	match e.eexpr with
+	| TConst c ->
+		(match c with
+		| TInt i ->
+			let r = alloc_tmp ctx HI32 in
+			op ctx (OInt (r,alloc_i32 ctx i));
+			r
+		| TFloat f ->
+			let r = alloc_tmp ctx HF64 in
+			op ctx (OFloat (r,alloc_float ctx (float_of_string f)));
+			r
+		| TBool b ->
+			let r = alloc_tmp ctx HBool in
+			op ctx (OBool (r,b));
+			r
+		| TString s ->
+			make_string ctx s e.epos
+		| TThis | TSuper ->
+			0 (* first reg *)
+		| _ ->
+			let r = alloc_tmp ctx (to_type ctx e.etype) in
+			op ctx (ONull r);
+			r)
+	| TVar (v,e) ->
+		(match e with
+		| None -> ()
+		| Some e ->
+			match captured_index ctx v with
+			| None ->
+				let r = alloc_reg ctx v in
+				let ri = eval_to ctx e (rtype ctx r) in
+				op ctx (OMov (r,ri))
+			| Some idx ->
+				let ri = eval_to ctx e (to_type ctx v.v_type) in
+				op ctx (OSetEnumField (ctx.m.mcaptreg, idx, ri));
+		);
+		alloc_tmp ctx HVoid
+	| TLocal v ->
+		cast_to ctx (match captured_index ctx v with
+		| None ->
+			(* we need to make a copy for cases such as (a - a++) *)
+			let r = alloc_reg ctx v in
+			let r2 = alloc_tmp ctx (rtype ctx r) in
+			op ctx (OMov (r2, r));
+			r2
+		| Some idx ->
+			let r = alloc_tmp ctx (to_type ctx v.v_type) in
+			op ctx (OEnumField (r, ctx.m.mcaptreg, 0, idx));
+			r) (to_type ctx e.etype) e.epos
+	| TReturn None ->
+		before_return ctx;
+		let r = alloc_tmp ctx HVoid in
+		op ctx (ORet r);
+		r
+	| TReturn (Some e) ->
+		let r = eval_to ctx e ctx.m.mret in
+		before_return ctx;
+		op ctx (ORet r);
+		alloc_tmp ctx HVoid
+	| TParenthesis e ->
+		eval_expr ctx e
+	| TBlock el ->
+		let rec loop = function
+			| [e] -> eval_expr ctx e
+			| [] -> alloc_tmp ctx HVoid
+			| e :: l ->
+				ignore(eval_expr ctx e);
+				loop l
+		in
+		loop el
+	| TCall ({ eexpr = TConst TSuper } as s, el) ->
+		(match follow s.etype with
+		| TInst (csup,_) ->
+			(match csup.cl_constructor with
+			| None -> assert false
+			| Some f ->
+				let r = alloc_tmp ctx HVoid in
+				let el = eval_args ctx el (to_type ctx f.cf_type) e.epos in
+				op ctx (OCallN (r, alloc_fid ctx csup f, 0 :: el));
+				r
+			)
+		| _ -> assert false);
+	| TCall ({ eexpr = TLocal v }, el) when v.v_name.[0] = '$' ->
+		let invalid() = error "Invalid native call" e.epos in
+		(match v.v_name, el with
+		| "$new", [{ eexpr = TTypeExpr (TClassDecl _) }] ->
+			(match follow e.etype with
+			| TInst (c,pl) ->
+				let r = alloc_tmp ctx (class_type ctx c pl false) in
+				op ctx (ONew r);
+				r
+			| _ ->
+				invalid())
+		| "$int", [{ eexpr = TBinop (OpDiv, e1, e2) }] when is_int ctx e1.etype && is_int ctx e2.etype ->
+			let tmp = alloc_tmp ctx HI32 in
+			op ctx (if unsigned e1.etype && unsigned e2.etype then OUDiv (tmp, eval_to ctx e1 HI32, eval_to ctx e2 HI32) else OSDiv (tmp, eval_to ctx e1 HI32, eval_to ctx e2 HI32));
+			tmp
+		| "$int", [e] ->
+			let tmp = alloc_tmp ctx HI32 in
+			op ctx (OToInt (tmp, eval_expr ctx e));
+			tmp
+		| "$bseti8", [b;pos;v] ->
+			let b = eval_to ctx b HBytes in
+			let pos = eval_to ctx pos HI32 in
+			let r = eval_to ctx v HI32 in
+			op ctx (OSetI8 (b, pos, r));
+			r
+		| "$bseti32", [b;pos;v] ->
+			let b = eval_to ctx b HBytes in
+			let pos = eval_to ctx pos HI32 in
+			let r = eval_to ctx v HI32 in
+			op ctx (OSetI32 (b, pos, r));
+			r
+		| "$bsetf32", [b;pos;v] ->
+			let b = eval_to ctx b HBytes in
+			let pos = eval_to ctx pos HI32 in
+			let r = eval_to ctx v HF32 in
+			op ctx (OSetF32 (b, pos, r));
+			r
+		| "$bsetf64", [b;pos;v] ->
+			let b = eval_to ctx b HBytes in
+			let pos = eval_to ctx pos HI32 in
+			let r = eval_to ctx v HF64 in
+			op ctx (OSetF64 (b, pos, r));
+			r
+		| "$bytes_sizebits", [eb] ->
+			(match follow eb.etype with
+			| TAbstract({a_path = ["hl";"types"],"BytesAccess"},[t]) ->
+				reg_int ctx (match to_type ctx t with
+				| HI8 -> 0
+				| HI16 -> 1
+				| HI32 -> 2
+				| HF32 -> 2
+				| HF64 -> 3
+				| t -> error ("Unsupported basic type " ^ tstr t) e.epos)
+			| _ ->
+				error "Invalid BytesAccess" eb.epos);
+		| "$bytes_nullvalue", [eb] ->
+			(match follow eb.etype with
+			| TAbstract({a_path = ["hl";"types"],"BytesAccess"},[t]) ->
+				let t = to_type ctx t in
+				let r = alloc_tmp ctx t in
+				(match t with
+				| HI8 | HI16 | HI32 ->
+					op ctx (OInt (r,alloc_i32 ctx 0l))
+				| HF32 | HF64 ->
+					op ctx (OFloat (r, alloc_float ctx 0.))
+				| t ->
+					error ("Unsupported basic type " ^ tstr t) e.epos);
+				r
+			| _ ->
+				error "Invalid BytesAccess" eb.epos);
+		| "$bget", [eb;pos] ->
+			(match follow eb.etype with
+			| TAbstract({a_path = ["hl";"types"],"BytesAccess"},[t]) ->
+				let b = eval_to ctx eb HBytes in
+				let pos = eval_to ctx pos HI32 in
+				let t = to_type ctx t in
+				(match t with
+				| HI8 ->
+					let r = alloc_tmp ctx HI32 in
+					op ctx (OGetI8 (r, b, pos));
+					r
+				| HI16 ->
+					let r = alloc_tmp ctx HI32 in
+					op ctx (OGetI16 (r, b, shl ctx pos 1));
+					r
+				| HI32 ->
+					let r = alloc_tmp ctx HI32 in
+					op ctx (OGetI32 (r, b, shl ctx pos 2));
+					r
+				| HF32 ->
+					let r = alloc_tmp ctx HF32 in
+					op ctx (OGetF32 (r, b, shl ctx pos 2));
+					r
+				| HF64 ->
+					let r = alloc_tmp ctx HF64 in
+					op ctx (OGetF64 (r, b, shl ctx pos 3));
+					r
+				| _ ->
+					error ("Unsupported basic type " ^ tstr t) e.epos)
+			| _ ->
+				error "Invalid BytesAccess" eb.epos);
+		| "$bset", [eb;pos;value] ->
+			(match follow eb.etype with
+			| TAbstract({a_path = ["hl";"types"],"BytesAccess"},[t]) ->
+				let b = eval_to ctx eb HBytes in
+				let pos = eval_to ctx pos HI32 in
+				let t = to_type ctx t in
+				(match t with
+				| HI8 ->
+					let v = eval_to ctx value HI32 in
+					op ctx (OSetI8 (b, pos, v));
+					v
+				| HI16 ->
+					let v = eval_to ctx value HI32 in
+					op ctx (OSetI16 (b, shl ctx pos 1, v));
+					v
+				| HI32 ->
+					let v = eval_to ctx value HI32 in
+					op ctx (OSetI32 (b, shl ctx pos 2, v));
+					v
+				| HF32 ->
+					let v = eval_to ctx value HF32 in
+					op ctx (OSetF32 (b, shl ctx pos 2, v));
+					v
+				| HF64 ->
+					let v = eval_to ctx value HF64 in
+					op ctx (OSetF64 (b, shl ctx pos 3, v));
+					v
+				| _ ->
+					error ("Unsupported basic type " ^ tstr t) e.epos)
+			| _ ->
+				error "Invalid BytesAccess" eb.epos);
+		| "$bgeti8", [b;pos] ->
+			let b = eval_to ctx b HBytes in
+			let pos = eval_to ctx pos HI32 in
+			let r = alloc_tmp ctx HI32 in
+			op ctx (OGetI8 (r, b, pos));
+			r
+		| "$bgeti32", [b;pos] ->
+			let b = eval_to ctx b HBytes in
+			let pos = eval_to ctx pos HI32 in
+			let r = alloc_tmp ctx HI32 in
+			op ctx (OGetI32 (r, b, pos));
+			r
+		| "$bgetf32", [b;pos] ->
+			let b = eval_to ctx b HBytes in
+			let pos = eval_to ctx pos HI32 in
+			let r = alloc_tmp ctx HF32 in
+			op ctx (OGetF32 (r, b, pos));
+			r
+		| "$bgetf64", [b;pos] ->
+			let b = eval_to ctx b HBytes in
+			let pos = eval_to ctx pos HI32 in
+			let r = alloc_tmp ctx HF64 in
+			op ctx (OGetF64 (r, b, pos));
+			r
+		| "$asize", [e] ->
+			let r = alloc_tmp ctx HI32 in
+			op ctx (OArraySize (r, eval_to ctx e HArray));
+			r
+		| "$aalloc", [esize] ->
+			let et = (match follow e.etype with TAbstract ({ a_path = ["hl";"types"],"NativeArray" },[t]) -> to_type ctx t | _ -> invalid()) in
+			let a = alloc_tmp ctx HArray in
+			let rt = alloc_tmp ctx HType in
+			op ctx (OType (rt,et));
+			let size = eval_to ctx esize HI32 in
+			op ctx (OCall2 (a,alloc_std ctx "alloc_array" [HType;HI32] HArray,rt,size));
+			a
+		| "$aget", [a; pos] ->
+			(*
+				read/write on arrays are unsafe : the type of NativeArray needs to be correcly set.
+			*)
+			let at = (match follow a.etype with TAbstract ({ a_path = ["hl";"types"],"NativeArray" },[t]) -> to_type ctx t | _ -> invalid()) in
+			let arr = eval_to ctx a HArray in
+			let pos = eval_to ctx pos HI32 in
+			let r = alloc_tmp ctx at in
+			op ctx (OGetArray (r, arr, pos));
+			cast_to ctx r (to_type ctx e.etype) e.epos
+		| "$aset", [a; pos; value] ->
+			let et = (match follow a.etype with TAbstract ({ a_path = ["hl";"types"],"NativeArray" },[t]) -> to_type ctx t | _ -> invalid()) in
+			let arr = eval_to ctx a HArray in
+			let pos = eval_to ctx pos HI32 in
+			let r = eval_to ctx value et in
+			op ctx (OSetArray (arr, pos, r));
+			r
+		| "$ref", [v] ->
+			(match v.eexpr with
+			| TLocal v ->
+				let r = alloc_tmp ctx (to_type ctx e.etype) in
+				let rv = (match rtype ctx r with HRef t -> alloc_reg ctx v | _ -> invalid()) in
+				op ctx (ORef (r,rv));
+				r
+			| _ ->
+				error "Ref should be a local variable" v.epos)
+		| "$setref", [e1;e2] ->
+			let rec loop e = match e.eexpr with
+				| TParenthesis e1 | TMeta(_,e1) | TCast(e1,None) -> loop e1
+				| TLocal v -> v
+				| _ -> invalid()
+			in
+			let v = loop e1 in
+			let r = alloc_reg ctx v in
+			let rv = eval_to ctx e2 (match rtype ctx r with HRef t -> t | _ -> invalid()) in
+			op ctx (OSetref (r,rv));
+			r
+		| "$ttype", [v] ->
+			let r = alloc_tmp ctx HType in
+			op ctx (OType (r,to_type ctx v.etype));
+			r
+		| "$tdyntype", [v] ->
+			let r = alloc_tmp ctx HType in
+			op ctx (OGetType (r,eval_to ctx v HDyn));
+			r
+		| "$tkind", [v] ->
+			let r = alloc_tmp ctx HI32 in
+			op ctx (OGetTID (r,eval_to ctx v HType));
+			r
+		| "$dump", [v] ->
+			op ctx (ODump (eval_expr ctx v));
+			alloc_tmp ctx HVoid
+		| "$resources", [] ->
+			let tdef = (try List.find (fun t -> (t_infos t).mt_path = (["haxe";"_Resource"],"ResourceContent")) ctx.com.types with Not_found -> assert false) in
+			let t = class_type ctx (match tdef with TClassDecl c -> c | _ -> assert false) [] false in
+			let arr = alloc_tmp ctx HArray in
+			let rt = alloc_tmp ctx HType in
+			op ctx (OType (rt,t));
+			let res = Hashtbl.fold (fun k v acc -> (k,v) :: acc) ctx.com.resources [] in
+			let size = reg_int ctx (List.length res) in
+			op ctx (OCall2 (arr,alloc_std ctx "alloc_array" [HType;HI32] HArray,rt,size));
+			let ro = alloc_tmp ctx t in
+			let rb = alloc_tmp ctx HBytes in
+			let ridx = reg_int ctx 0 in
+			let has_len = (match t with HObj p -> PMap.mem "dataLen" p.pindex | _ -> assert false) in
+			iteri (fun i (k,v) ->
+				op ctx (ONew ro);
+				op ctx (OString (rb,alloc_string ctx k));
+				op ctx (OSetField (ro,0,rb));
+				op ctx (OBytes (rb,alloc_string ctx (v ^ "\x00"))); (* add a \x00 to prevent clashing with existing string *)
+				op ctx (OSetField (ro,1,rb));
+				if has_len then op ctx (OSetField (ro,2,reg_int ctx (String.length v)));
+				op ctx (OSetArray (arr,ridx,ro));
+				op ctx (OIncr ridx);
+			) res;
+			arr
+		| "$rethrow", [v] ->
+			let r = alloc_tmp ctx HVoid in
+			op ctx (ORethrow (eval_to ctx v HDyn));
+			r
+		| "$allTypes", [] ->
+			let r = alloc_tmp ctx (to_type ctx e.etype) in
+			op ctx (OGetGlobal (r, alloc_global ctx "__types__" (rtype ctx r)));
+			r
+		| "$allTypes", [v] ->
+			let v = eval_expr ctx v in
+			op ctx (OSetGlobal (alloc_global ctx "__types__" (rtype ctx v), v));
+			v
+		| "$hash", [v] ->
+			(match v.eexpr with
+			| TConst (TString str) ->
+				let r = alloc_tmp ctx HI32 in
+				op ctx (OInt (r,alloc_i32 ctx (hash str)));
+				r
+			| _ -> error "Constant string required" v.epos)
+		| "$enumIndex", [v] ->
+			let r = alloc_tmp ctx HI32 in
+			let re = eval_expr ctx v in
+			op ctx (ONullCheck re);
+			op ctx (OEnumIndex (r,re));
+			r
+		| _ ->
+			error ("Unknown native call " ^ v.v_name) e.epos)
+	| TCall (ec,args) ->
+		let tfun = real_type ctx ec in
+		let el() = eval_args ctx args tfun e.epos in
+		let ret = alloc_tmp ctx (match tfun with HFun (_,r) -> r | _ -> HDyn) in
+		let def_ret = ref None in
+		(match get_access ctx ec with
+		| AStaticFun f ->
+			(match el() with
+			| [] -> op ctx (OCall0 (ret, f))
+			| [a] -> op ctx (OCall1 (ret, f, a))
+			| [a;b] -> op ctx (OCall2 (ret, f, a, b))
+			| [a;b;c] -> op ctx (OCall3 (ret, f, a, b, c))
+			| [a;b;c;d] -> op ctx (OCall4 (ret, f, a, b, c, d))
+			| el -> op ctx (OCallN (ret, f, el)));
+		| AInstanceFun (ethis, f) ->
+			let el = eval_null_check ctx ethis :: el() in
+			(match el with
+			| [a] -> op ctx (OCall1 (ret, f, a))
+			| [a;b] -> op ctx (OCall2 (ret, f, a, b))
+			| [a;b;c] -> op ctx (OCall3 (ret, f, a, b, c))
+			| [a;b;c;d] -> op ctx (OCall4 (ret, f, a, b, c, d))
+			| _ -> op ctx (OCallN (ret, f, el)));
+		| AInstanceProto ({ eexpr = TConst TThis }, fid) ->
+			op ctx (OCallThis (ret, fid, el()))
+		| AInstanceProto (ethis, fid) | AVirtualMethod (ethis, fid) ->
+			let el = eval_null_check ctx ethis :: el() in
+			op ctx (OCallMethod (ret, fid, el))
+		| AEnum (_,index) ->
+			op ctx (OMakeEnum (ret, index, el()))
+		| AArray (a,t,idx) ->
+			let r = array_read ctx a t idx ec.epos in
+			op ctx (ONullCheck r);
+			op ctx (OCallClosure (ret, r, el())); (* if it's a value, it's a closure *)
+		| _ ->
+			let r = eval_null_check ctx ec in
+			(* don't use real_type here *)
+			let tfun = to_type ctx ec.etype in
+			let el() = eval_args ctx args tfun e.epos in
+			let ret = alloc_tmp ctx (match tfun with HFun (_,r) -> r | _ -> HDyn) in
+			op ctx (OCallClosure (ret, r, el())); (* if it's a value, it's a closure *)
+			def_ret := Some (unsafe_cast_to ctx ret (to_type ctx e.etype) e.epos);
+		);
+		(match !def_ret with None -> unsafe_cast_to ctx ret (to_type ctx e.etype) e.epos | Some r -> r)
+	| TField (ec,FInstance({ cl_path = [],"Array" },[t],{ cf_name = "length" })) when to_type ctx t = HDyn ->
+		let r = alloc_tmp ctx HI32 in
+		op ctx (OCall1 (r,alloc_fun_path ctx (["hl";"types"],"ArrayDyn") "get_length", eval_null_check ctx ec));
+		r
+	| TField (ec,a) ->
+		let r = alloc_tmp ctx (to_type ctx (field_type ctx a e.epos)) in
+		(match get_access ctx e with
+		| AGlobal g ->
+			op ctx (OGetGlobal (r,g));
+		| AStaticVar (g,t,fid) ->
+			let o = alloc_tmp ctx t in
+			op ctx (OGetGlobal (o,g));
+			op ctx (OField (r,o,fid));
+		| AStaticFun f ->
+			op ctx (OStaticClosure (r,f));
+		| AInstanceFun (ethis, f) ->
+			op ctx (OInstanceClosure (r, f, eval_null_check ctx ethis))
+		| AInstanceField (ethis,fid) ->
+			let robj = eval_null_check ctx ethis in
+			op ctx (match ethis.eexpr with TConst TThis -> OGetThis (r,fid) | _ -> OField (r,robj,fid));
+		| AInstanceProto (ethis,fid) | AVirtualMethod (ethis, fid) ->
+			let robj = eval_null_check ctx ethis in
+			op ctx (OVirtualClosure (r,robj,fid));
+		| ADynamic (ethis, f) ->
+			let robj = eval_null_check ctx ethis in
+			op ctx (ODynGet (r,robj,f))
+		| AEnum (en,index) ->
+			let cur_fid = DynArray.length ctx.cfids.arr in
+			let name = List.nth en.e_names index in
+			let fid = alloc_fun_path ctx en.e_path name in
+			if fid = cur_fid then begin
+				let ef = PMap.find name en.e_constrs in
+				let eargs, et = (match follow ef.ef_type with TFun (args,ret) -> args, ret | _ -> assert false) in
+				let ct = ctx.com.basic in
+				let p = ef.ef_pos in
+				let eargs = List.map (fun (n,o,t) -> alloc_var n t en.e_pos, if o then Some TNull else None) eargs in
+				let ecall = mk (TCall (e,List.map (fun (v,_) -> mk (TLocal v) v.v_type p) eargs)) et p in
+				let f = {
+					tf_args = eargs;
+					tf_type = et;
+					tf_expr = mk (TReturn (Some ecall)) ct.tvoid p;
+				} in
+				ignore(make_fun ctx ("","") fid f None None);
+			end;
+			op ctx (OStaticClosure (r,fid));
+		| ANone | ALocal _ | AArray _ | ACaptured _ ->
+			error "Invalid access" e.epos);
+		unsafe_cast_to ctx r (to_type ctx e.etype) e.epos
+	| TObjectDecl fl ->
+		(match to_type ctx e.etype with
+		| HVirtual vp as t when Array.length vp.vfields = List.length fl && not (List.exists (fun (s,e) -> s = "toString" && is_to_string e.etype) fl)  ->
+			let r = alloc_tmp ctx t in
+			op ctx (ONew r);
+			List.iter (fun (s,ev) ->
+				let fidx = (try PMap.find s vp.vindex with Not_found -> assert false) in
+				let _, _, ft = vp.vfields.(fidx) in
+				let v = eval_to ctx ev ft in
+				op ctx (OSetField (r,fidx,v));
+			) fl;
+			r
+		| _ ->
+			let r = alloc_tmp ctx HDynObj in
+			op ctx (ONew r);
+			let a = (match follow e.etype with TAnon a -> Some a | t -> if t == t_dynamic then None else assert false) in
+			List.iter (fun (s,ev) ->
+				let ft = (try (match a with None -> raise Not_found | Some a -> PMap.find s a.a_fields).cf_type with Not_found -> ev.etype) in
+				let v = eval_to ctx ev (to_type ctx ft) in
+				op ctx (ODynSet (r,alloc_string ctx s,v));
+				if s = "toString" && is_to_string ev.etype then begin
+					let f = alloc_tmp ctx (HFun ([],HBytes)) in
+					op ctx (OInstanceClosure (f, alloc_fun_path ctx ([],"String") "call_toString", r));
+					op ctx (ODynSet (r,alloc_string ctx "__string",f));
+				end;
+			) fl;
+			cast_to ctx r (to_type ctx e.etype) e.epos)
+	| TNew (c,pl,el) ->
+		let c = resolve_class ctx c pl false in
+		let r = alloc_tmp ctx (class_type ctx c pl false) in
+		op ctx (ONew r);
+		(match c.cl_constructor with
+		| None -> ()
+		| Some { cf_expr = None } -> error (s_type_path c.cl_path ^ " does not have a constructor") e.epos
+		| Some ({ cf_expr = Some cexpr } as constr) ->
+			let rl = eval_args ctx el (to_type ctx cexpr.etype) e.epos in
+			let ret = alloc_tmp ctx HVoid in
+			let g = alloc_fid ctx c constr in
+			op ctx (match rl with
+			| [] -> OCall1 (ret,g,r)
+			| [a] -> OCall2 (ret,g,r,a)
+			| [a;b] -> OCall3 (ret,g,r,a,b)
+			| [a;b;c] -> OCall4 (ret,g,r,a,b,c)
+			| _ -> OCallN (ret,g,r :: rl));
+		);
+		r
+	| TIf (cond,eif,eelse) ->
+		let t = to_type ctx e.etype in
+		let out = alloc_tmp ctx t in
+		let j = jump_expr ctx cond false in
+		if t = HVoid then ignore(eval_expr ctx eif) else op ctx (OMov (out,eval_to ctx eif t));
+		(match eelse with
+		| None -> j()
+		| Some e ->
+			let jexit = jump ctx (fun i -> OJAlways i) in
+			j();
+			if t = HVoid then ignore(eval_expr ctx e) else op ctx (OMov (out,eval_to ctx e t));
+			jexit());
+		out
+	| TBinop (bop, e1, e2) ->
+		let is_unsigned() = unsigned e1.etype && unsigned e2.etype in
+		let boolop r f =
+			let j = jump ctx f in
+			op ctx (OBool (r,false));
+			op ctx (OJAlways 1);
+			j();
+			op ctx (OBool (r, true));
+		in
+		let binop r a b =
+			let rec loop bop =
+				match bop with
+				| OpLte -> boolop r (fun d -> if is_unsigned() then OJUGte (b,a,d) else OJSLte (a,b,d))
+				| OpGt -> boolop r (fun d -> if is_unsigned() then OJULt (b,a,d) else OJSGt (a,b,d))
+				| OpGte -> boolop r (fun d -> if is_unsigned() then OJUGte (a,b,d) else OJSGte (a,b,d))
+				| OpLt -> boolop r (fun d -> if is_unsigned() then OJULt (a,b,d) else OJSLt (a,b,d))
+				| OpEq -> boolop r (fun d -> OJEq (a,b,d))
+				| OpNotEq -> boolop r (fun d -> OJNotEq (a,b,d))
+				| OpAdd ->
+					(match rtype ctx r with
+					| HI8 | HI16 | HI32 | HF32 | HF64 ->
+						op ctx (OAdd (r,a,b))
+					| HObj { pname = "String" } ->
+						op ctx (OCall2 (r,alloc_fun_path ctx ([],"String") "__add__",a,b))
+					| HDyn ->
+						op ctx (OCall2 (r,alloc_fun_path ctx ([],"Std") "__add__",a,b))
+					| t ->
+						error ("Cannot add " ^ tstr t) e.epos)
+				| OpSub | OpMult | OpMod | OpDiv ->
+					(match rtype ctx r with
+					| HI8 | HI16 | HI32 | HF32 | HF64 ->
+						(match bop with
+						| OpSub -> op ctx (OSub (r,a,b))
+						| OpMult -> op ctx (OMul (r,a,b))
+						| OpMod -> op ctx (if is_unsigned() then OUMod (r,a,b) else OSMod (r,a,b))
+						| OpDiv -> op ctx (OSDiv (r,a,b)) (* don't use UDiv since both operands are float already *)
+						| _ -> assert false)
+					| _ ->
+						assert false)
+				| OpShl | OpShr | OpUShr | OpAnd | OpOr | OpXor ->
+					(match rtype ctx r with
+					| HI8 | HI16 | HI32 ->
+						(match bop with
+						| OpShl -> op ctx (OShl (r,a,b))
+						| OpShr -> op ctx (if unsigned e1.etype then OUShr (r,a,b) else OSShr (r,a,b))
+						| OpUShr -> op ctx (OUShr (r,a,b))
+						| OpAnd -> op ctx (OAnd (r,a,b))
+						| OpOr -> op ctx (OOr (r,a,b))
+						| OpXor -> op ctx (OXor (r,a,b))
+						| _ -> ())
+					| _ ->
+						assert false)
+				| OpAssignOp bop ->
+					loop bop
+				| _ ->
+					assert false
+			in
+			loop bop
+		in
+		(match bop with
+		| OpLte | OpGt | OpGte | OpLt ->
+			let r = alloc_tmp ctx HBool in
+			let t = common_type ctx e1 e2 false e.epos in
+			let a = eval_to ctx e1 t in
+			let b = eval_to ctx e2 t in
+			binop r a b;
+			r
+		| OpEq | OpNotEq ->
+			let r = alloc_tmp ctx HBool in
+			let t = common_type ctx e1 e2 true e.epos in
+			let a = eval_to ctx e1 t in
+			let b = eval_to ctx e2 t in
+			binop r a b;
+			r
+		| OpAdd | OpSub | OpMult | OpDiv | OpMod | OpShl | OpShr | OpUShr | OpAnd | OpOr | OpXor ->
+			let t = (match to_type ctx e.etype with HNull t -> t | t -> t) in
+			let r = alloc_tmp ctx t in
+			let a = eval_to ctx e1 t in
+			let b = eval_to ctx e2 t in
+			binop r a b;
+			r
+		| OpAssign ->
+			let value() = eval_to ctx e2 (real_type ctx e1) in
+			(match get_access ctx e1 with
+			| AGlobal g ->
+				let r = value() in
+				op ctx (OSetGlobal (g,r));
+				r
+			| AStaticVar (g,t,fid) ->
+				let r = value() in
+				let o = alloc_tmp ctx t in
+				op ctx (OGetGlobal (o, g));
+				op ctx (OSetField (o, fid, r));
+				r
+			| AInstanceField ({ eexpr = TConst TThis }, fid) ->
+				let r = value() in
+				op ctx (OSetThis (fid,r));
+				r
+			| AInstanceField (ethis, fid) ->
+				let rthis = eval_null_check ctx ethis in
+				let r = value() in
+				op ctx (OSetField (rthis, fid, r));
+				r
+			| ALocal l ->
+				let r = value() in
+				op ctx (OMov (l, r));
+				r
+			| AArray (ra,(at,vt),ridx) ->
+				let v = cast_to ctx (value()) (match at with HI16 | HI8 -> HI32 | _ -> at) e.epos in
+				(* bounds check against length *)
+				(match at with
+				| HDyn ->
+					(* call setDyn() *)
+					op ctx (OCallMethod (alloc_tmp ctx HVoid,1,[ra;ridx;cast_to ctx v (if is_dynamic at then at else HDyn) e.epos]));
+				| _ ->
+					let len = alloc_tmp ctx HI32 in
+					op ctx (OField (len,ra,0)); (* length *)
+					let j = jump ctx (fun i -> OJULt (ridx,len,i)) in
+					op ctx (OCall2 (alloc_tmp ctx HVoid, alloc_fun_path ctx (array_class ctx at).cl_path "__expand", ra, ridx));
+					j();
+					match at with
+					| HI32 | HF64 | HI16 | HF32 ->
+						let b = alloc_tmp ctx HBytes in
+						op ctx (OField (b,ra,1));
+						write_mem ctx b (shl ctx ridx (type_size_bits at)) at v
+					| _ ->
+						let arr = alloc_tmp ctx HArray in
+						op ctx (OField (arr,ra,1));
+						op ctx (OSetArray (arr,ridx,cast_to ctx v (if is_dynamic at then at else HDyn) e.epos))
+				);
+				v
+			| ADynamic (ethis,f) ->
+				let obj = eval_null_check ctx ethis in
+				let r = eval_expr ctx e2 in
+				op ctx (ODynSet (obj,f,r));
+				r
+			| ACaptured index ->
+				let r = value() in
+				op ctx (OSetEnumField (ctx.m.mcaptreg,index,r));
+				r
+			| AEnum _ | ANone | AInstanceFun _ | AInstanceProto _ | AStaticFun _ | AVirtualMethod _ ->
+				assert false)
+		| OpBoolOr ->
+			let r = alloc_tmp ctx HBool in
+			let j = jump_expr ctx e1 true in
+			let j2 = jump_expr ctx e2 true in
+			op ctx (OBool (r,false));
+			let jend = jump ctx (fun b -> OJAlways b) in
+			j();
+			j2();
+			op ctx (OBool (r,true));
+			jend();
+			r
+		| OpBoolAnd ->
+			let r = alloc_tmp ctx HBool in
+			let j = jump_expr ctx e1 false in
+			let j2 = jump_expr ctx e2 false in
+			op ctx (OBool (r,true));
+			let jend = jump ctx (fun b -> OJAlways b) in
+			j();
+			j2();
+			op ctx (OBool (r,false));
+			jend();
+			r
+		| OpAssignOp bop ->
+			(match get_access ctx e1 with
+			| ALocal l ->
+				let r = eval_to ctx { e with eexpr = TBinop (bop,e1,e2) } (to_type ctx e1.etype) in
+				op ctx (OMov (l, r));
+				r
+			| acc ->
+				gen_assign_op ctx acc e1 (fun r ->
+					let b = eval_to ctx e2 (rtype ctx r) in
+					binop r r b;
+					r))
+		| OpInterval | OpArrow ->
+			assert false)
+	| TUnop (Not,_,v) ->
+		let tmp = alloc_tmp ctx HBool in
+		let r = eval_to ctx v HBool in
+		op ctx (ONot (tmp,r));
+		tmp
+	| TUnop (Neg,_,v) ->
+		let t = to_type ctx e.etype in
+		let tmp = alloc_tmp ctx t in
+		let r = eval_to ctx v t in
+		op ctx (ONeg (tmp,r));
+		tmp
+	| TUnop (NegBits,_,v) ->
+		let t = to_type ctx e.etype in
+		let tmp = alloc_tmp ctx t in
+		let r = eval_to ctx v t in
+		let mask = (match t with
+			| HI8 -> 0xFFl
+			| HI16 -> 0xFFFFl
+			| HI32 -> 0xFFFFFFFFl
+			| _ -> error (tstr t) e.epos
+		) in
+		let r2 = alloc_tmp ctx t in
+		op ctx (OInt (r2,alloc_i32 ctx mask));
+		op ctx (OXor (tmp,r,r2));
+		tmp
+	| TUnop (Increment|Decrement as uop,fix,v) ->
+		let rec unop r =
+			match rtype ctx r with
+			| HI8 | HI16 | HI32 ->
+				if uop = Increment then op ctx (OIncr r) else op ctx (ODecr r)
+			| HF32 | HF64 as t ->
+				let tmp = alloc_tmp ctx t in
+				op ctx (OFloat (tmp,alloc_float ctx 1.));
+				if uop = Increment then op ctx (OAdd (r,r,tmp)) else op ctx (OSub (r,r,tmp))
+			| HNull (HI8 | HI16 | HI32 | HF32 | HF64 as t) ->
+				let tmp = alloc_tmp ctx t in
+				op ctx (OSafeCast (tmp,r));
+				unop tmp;
+				op ctx (OToDyn (r,tmp));
+			| _ ->
+				assert false
+		in
+		(match get_access ctx v, fix with
+		| ALocal r, Prefix ->
+			unop r;
+			r
+		| ALocal r, Postfix ->
+			let r2 = alloc_tmp ctx (rtype ctx r) in
+			op ctx (OMov (r2,r));
+			unop r;
+			r2
+		| acc, _ ->
+			let ret = ref 0 in
+			ignore(gen_assign_op ctx acc v (fun r ->
+				if fix = Prefix then ret := r else begin
+					let tmp = alloc_tmp ctx (rtype ctx r) in
+					op ctx (OMov (tmp, r));
+					ret := tmp;
+				end;
+				unop r;
+				r)
+			);
+			!ret)
+	| TFunction f ->
+		let fid = alloc_function_name ctx ("function#" ^ string_of_int (DynArray.length ctx.cfids.arr)) in
+		let capt = make_fun ctx ("","") fid f None (Some ctx.m.mcaptured) in
+		let r = alloc_tmp ctx (to_type ctx e.etype) in
+		if capt == ctx.m.mcaptured then
+			op ctx (OInstanceClosure (r, fid, ctx.m.mcaptreg))
+		else if Array.length capt.c_vars > 0 then
+			let env = alloc_tmp ctx capt.c_type in
+			op ctx (OEnumAlloc (env,0));
+			Array.iteri (fun i v ->
+				let r = (match captured_index ctx v with
+				| None -> alloc_reg ctx v
+				| Some idx ->
+					let r = alloc_tmp ctx (to_type ctx v.v_type) in
+					op ctx (OEnumField (r,ctx.m.mcaptreg,0,idx));
+					r
+				) in
+				op ctx (OSetEnumField (env,i,r));
+			) capt.c_vars;
+			op ctx (OInstanceClosure (r, fid, env))
+		else
+			op ctx (OStaticClosure (r, fid));
+		r
+	| TThrow v ->
+		op ctx (OThrow (eval_to ctx v HDyn));
+		alloc_tmp ctx HVoid
+	| TWhile (cond,eloop,NormalWhile) ->
+		let oldb = ctx.m.mbreaks and oldc = ctx.m.mcontinues in
+		ctx.m.mbreaks <- [];
+		ctx.m.mcontinues <- [];
+		let continue_pos = current_pos ctx in
+		let ret = jump_back ctx in
+		let j = jump_expr ctx cond false in
+		ignore(eval_expr ctx eloop);
+		ret();
+		j();
+		List.iter (fun f -> f (current_pos ctx)) ctx.m.mbreaks;
+		List.iter (fun f -> f continue_pos) ctx.m.mcontinues;
+		ctx.m.mbreaks <- oldb;
+		ctx.m.mcontinues <- oldc;
+		alloc_tmp ctx HVoid
+	| TWhile (cond,eloop,DoWhile) ->
+		let oldb = ctx.m.mbreaks and oldc = ctx.m.mcontinues in
+		ctx.m.mbreaks <- [];
+		ctx.m.mcontinues <- [];
+		let start = jump ctx (fun p -> OJAlways p) in
+		let continue_pos = current_pos ctx in
+		let ret = jump_back ctx in
+		let j = jump_expr ctx cond false in
+		start();
+		ignore(eval_expr ctx eloop);
+		ret();
+		j();
+		List.iter (fun f -> f (current_pos ctx)) ctx.m.mbreaks;
+		List.iter (fun f -> f continue_pos) ctx.m.mcontinues;
+		ctx.m.mbreaks <- oldb;
+		ctx.m.mcontinues <- oldc;
+		alloc_tmp ctx HVoid
+	| TCast (v,None) ->
+		let t = to_type ctx e.etype in
+		let rv = eval_expr ctx v in
+		(match t with
+		| HF32 | HF64 when unsigned v.etype ->
+			let r = alloc_tmp ctx t in
+			op ctx (OToUFloat (r,rv));
+			r
+		| _ ->
+			cast_to ~force:true ctx rv t e.epos)
+	| TArrayDecl el ->
+		let r = alloc_tmp ctx (to_type ctx e.etype) in
+		let et = (match follow e.etype with TInst (_,[t]) -> to_type ctx t | _ -> assert false) in
+		(match et with
+		| HI32 ->
+			let b = alloc_tmp ctx HBytes in
+			let size = reg_int ctx ((List.length el) * 4) in
+			op ctx (OCall1 (b,alloc_std ctx "alloc_bytes" [HI32] HBytes,size));
+			list_iteri (fun i e ->
+				let r = eval_to ctx e HI32 in
+				op ctx (OSetI32 (b,reg_int ctx (i * 4),r));
+			) el;
+			op ctx (OCall2 (r, alloc_fun_path ctx (["hl";"types"],"ArrayBase") "allocI32", b, reg_int ctx (List.length el)));
+		| HI16 ->
+			let b = alloc_tmp ctx HBytes in
+			let size = reg_int ctx ((List.length el) * 2) in
+			op ctx (OCall1 (b,alloc_std ctx "alloc_bytes" [HI32] HBytes,size));
+			list_iteri (fun i e ->
+				let r = eval_to ctx e HI32 in
+				op ctx (OSetI16 (b,reg_int ctx (i * 2),r));
+			) el;
+			op ctx (OCall2 (r, alloc_fun_path ctx (["hl";"types"],"ArrayBase") "allocI16", b, reg_int ctx (List.length el)));
+		| HF32 ->
+			let b = alloc_tmp ctx HBytes in
+			let size = reg_int ctx ((List.length el) * 4) in
+			op ctx (OCall1 (b,alloc_std ctx "alloc_bytes" [HI32] HBytes,size));
+			list_iteri (fun i e ->
+				let r = eval_to ctx e HF32 in
+				op ctx (OSetF32 (b,reg_int ctx (i * 4),r));
+			) el;
+			op ctx (OCall2 (r, alloc_fun_path ctx (["hl";"types"],"ArrayBase") "allocF32", b, reg_int ctx (List.length el)));
+		| HF64 ->
+			let b = alloc_tmp ctx HBytes in
+			let size = reg_int ctx ((List.length el) * 8) in
+			op ctx (OCall1 (b,alloc_std ctx "alloc_bytes" [HI32] HBytes,size));
+			list_iteri (fun i e ->
+				let r = eval_to ctx e HF64 in
+				op ctx (OSetF64 (b,reg_int ctx (i * 8),r));
+			) el;
+			op ctx (OCall2 (r, alloc_fun_path ctx (["hl";"types"],"ArrayBase") "allocF64", b, reg_int ctx (List.length el)));
+		| _ ->
+			let at = if is_dynamic et then et else HDyn in
+			let a = alloc_tmp ctx HArray in
+			let rt = alloc_tmp ctx HType in
+			op ctx (OType (rt,at));
+			let size = reg_int ctx (List.length el) in
+			op ctx (OCall2 (a,alloc_std ctx "alloc_array" [HType;HI32] HArray,rt,size));
+			list_iteri (fun i e ->
+				let r = eval_to ctx e at in
+				op ctx (OSetArray (a,reg_int ctx i,r));
+			) el;
+			let tmp = if et = HDyn then alloc_tmp ctx (class_type ctx ctx.array_impl.aobj [] false) else r in
+			op ctx (OCall1 (tmp, alloc_fun_path ctx (["hl";"types"],"ArrayObj") "alloc", a));
+			if tmp <> r then begin
+				let re = alloc_tmp ctx HBool in
+				op ctx (OBool (re,true));
+				let ren = alloc_tmp ctx (HNull HBool) in
+				op ctx (OToDyn (ren, re));
+				op ctx (OCall2 (r, alloc_fun_path ctx (["hl";"types"],"ArrayDyn") "alloc", tmp, ren));
+			end;
+		);
+		r
+	| TArray _ ->
+		(match get_access ctx e with
+		| AArray (a,at,idx) ->
+			array_read ctx a at idx e.epos
+		| _ ->
+			assert false)
+	| TMeta (_,e) ->
+		eval_expr ctx e
+	| TFor _ ->
+		assert false (* eliminated by analyzer *)
+	| TSwitch (en,cases,def) ->
+		let rt = to_type ctx e.etype in
+		let r = alloc_tmp ctx rt in
+		(try
+			let max = ref (-1) in
+			let rec get_int e =
+				match e.eexpr with
+				| TConst (TInt i) ->
+					let v = Int32.to_int i in
+					if Int32.of_int v <> i then raise Exit;
+					v
+				| _ ->
+					raise Exit
+			in
+			List.iter (fun (values,_) ->
+				List.iter (fun v ->
+					let i = get_int v in
+					if i < 0 then raise Exit;
+					if i > !max then max := i;
+				) values;
+			) cases;
+			if !max > 255 || cases = [] then raise Exit;
+			let ridx = eval_to ctx en HI32 in
+			let indexes = Array.make (!max + 1) 0 in
+			op ctx (OSwitch (ridx,indexes,0));
+			let switch_pos = current_pos ctx in
+			(match def with
+			| None ->
+				if rt <> HVoid then op ctx (ONull r);
+			| Some e ->
+				let re = eval_to ctx e rt in
+				if rt <> HVoid then op ctx (OMov (r,re)));
+			let jends = ref [jump ctx (fun i -> OJAlways i)] in
+			List.iter (fun (values,ecase) ->
+				List.iter (fun v ->
+					Array.set indexes (get_int v) (current_pos ctx - switch_pos)
+				) values;
+				let re = eval_to ctx ecase rt in
+				op ctx (OMov (r,re));
+				jends := jump ctx (fun i -> OJAlways i) :: !jends
+			) cases;
+			DynArray.set ctx.m.mops (switch_pos - 1) (OSwitch (ridx,indexes,current_pos ctx - switch_pos));
+			List.iter (fun j -> j()) (!jends);
+		with Exit ->
+			let jends = ref [] in
+			let rvalue = eval_expr ctx en in
+			let loop (cases,e) =
+				let ok = List.map (fun c ->
+					let r = eval_to ctx c (common_type ctx en c true c.epos) in
+					jump ctx (fun n -> OJEq (r,rvalue,n))
+				) cases in
+				(fun() ->
+					List.iter (fun f -> f()) ok;
+					let re = eval_to ctx e rt in
+					if rt <> HVoid then op ctx (OMov (r,re));
+					jends := jump ctx (fun n -> OJAlways n) :: !jends)
+			in
+			let all = List.map loop cases in
+			(match def with
+			| None ->
+				if rt <> HVoid then op ctx (ONull r)
+			| Some e ->
+				let rdef = eval_to ctx e rt in
+				if rt <> HVoid then op ctx (OMov (r,rdef)));
+			jends := jump ctx (fun n -> OJAlways n) :: !jends;
+			List.iter (fun f -> f()) all;
+			List.iter (fun j -> j()) (!jends);
+		);
+		r
+	| TEnumParameter (ec,f,index) ->
+		let r = alloc_tmp ctx (match to_type ctx ec.etype with HEnum e -> let _,_,args = e.efields.(f.ef_index) in args.(index) | _ -> assert false) in
+		op ctx (OEnumField (r,eval_expr ctx ec,f.ef_index,index));
+		cast_to ctx r (to_type ctx e.etype) e.epos
+	| TContinue ->
+		let pos = current_pos ctx in
+		op ctx (OJAlways (-1)); (* loop *)
+		ctx.m.mcontinues <- (fun target -> DynArray.set ctx.m.mops pos (OJAlways (target - (pos + 1)))) :: ctx.m.mcontinues;
+		alloc_tmp ctx HVoid
+	| TBreak ->
+		let pos = current_pos ctx in
+		op ctx (OJAlways (-1)); (* loop *)
+		ctx.m.mbreaks <- (fun target -> DynArray.set ctx.m.mops pos (OJAlways (target - (pos + 1)))) :: ctx.m.mbreaks;
+		alloc_tmp ctx HVoid
+	| TTry (etry,catches) ->
+		let pos = current_pos ctx in
+		let rtrap = alloc_tmp ctx HDyn in
+		op ctx (OTrap (rtrap,-1)); (* loop *)
+		ctx.m.mtrys <- ctx.m.mtrys + 1;
+		let tret = to_type ctx e.etype in
+		let result = alloc_tmp ctx tret in
+		let r = eval_expr ctx etry in
+		if tret <> HVoid then op ctx (OMov (result,cast_to ctx r tret etry.epos));
+		ctx.m.mtrys <- ctx.m.mtrys - 1;
+		op ctx (OEndTrap true);
+		let j = jump ctx (fun n -> OJAlways n) in
+		DynArray.set ctx.m.mops pos (OTrap (rtrap, current_pos ctx - (pos + 1)));
+		let rec loop l =
+			match l with
+			| [] ->
+				op ctx (ORethrow rtrap);
+				[]
+			| (v,ec) :: next ->
+				let rv = alloc_reg ctx v in
+				let jnext = if v.v_type == t_dynamic then begin
+					op ctx (OMov (rv, rtrap));
+					(fun() -> ())
+				end else
+					let ct = (match follow v.v_type with
+					| TInst (c,_) -> TClassDecl c
+					| TAbstract (a,_) -> TAbstractDecl a
+					| TEnum (e,_) -> TEnumDecl e
+					| _ -> assert false
+					) in
+					let r = type_value ctx ct ec.epos in
+					let rb = alloc_tmp ctx HBool in
+					op ctx (OCall2 (rb, alloc_fun_path ctx (["hl";"types"],"BaseType") "check",r,rtrap));
+					let jnext = jump ctx (fun n -> OJFalse (rb,n)) in
+					op ctx (OMov (rv, unsafe_cast_to ctx rtrap (to_type ctx v.v_type) ec.epos));
+					jnext
+				in
+				let r = eval_expr ctx ec in
+				if tret <> HVoid then op ctx (OMov (result,cast_to ctx r tret ec.epos));
+				if v.v_type == t_dynamic then [] else
+				let jend = jump ctx (fun n -> OJAlways n) in
+				jnext();
+				jend :: loop next
+		in
+		List.iter (fun j -> j()) (loop catches);
+		j();
+		result
+	| TTypeExpr t ->
+		type_value ctx t e.epos
+	| TCast (ev,Some _) ->
+		let t = to_type ctx e.etype in
+		let re = eval_expr ctx ev in
+		let r = alloc_tmp ctx t in
+		if safe_cast (rtype ctx re) t then
+			op ctx (OMov (r,re))
+		else
+			op ctx (OSafeCast (r,re));
+		r
+
+and gen_assign_op ctx acc e1 f =
+	let f r =
+		match rtype ctx r with
+		| HNull t ->
+			let r2 = alloc_tmp ctx t in
+			op ctx (OSafeCast (r2,r));
+			let r3 = alloc_tmp ctx (HNull t) in
+			op ctx (OToDyn (r3,f r2));
+			r3
+		| _ ->
+			f r
+	in
+	match acc with
+	| AInstanceField (eobj, findex) ->
+		let robj = eval_null_check ctx eobj in
+		let t = real_type ctx e1 in
+		let r = alloc_tmp ctx t in
+		op ctx (OField (r,robj,findex));
+		let r = cast_to ctx r (to_type ctx e1.etype) e1.epos in
+		let r = f r in
+		op ctx (OSetField (robj,findex,cast_to ctx r t e1.epos));
+		r
+	| AStaticVar (g,t,fid) ->
+		let o = alloc_tmp ctx t in
+		op ctx (OGetGlobal (o,g));
+		let r = alloc_tmp ctx (to_type ctx e1.etype) in
+		op ctx (OField (r,o,fid));
+		let r = f r in
+		op ctx (OSetField (o,fid,r));
+		r
+	| AGlobal g ->
+		let r = alloc_tmp ctx (to_type ctx e1.etype) in
+		op ctx (OGetGlobal (r,g));
+		let r = f r in
+		op ctx (OSetGlobal (g,r));
+		r
+	| ACaptured idx ->
+		let r = alloc_tmp ctx (to_type ctx e1.etype) in
+		op ctx (OEnumField (r, ctx.m.mcaptreg, 0, idx));
+		let r = f r in
+		op ctx (OSetEnumField (ctx.m.mcaptreg,idx,r));
+		r
+	| AArray (ra,(at,_),ridx) ->
+		(match at with
+		| HDyn ->
+			(* call getDyn() *)
+			let r = alloc_tmp ctx HDyn in
+			op ctx (OCallMethod (r,0,[ra;ridx]));
+			let r = f r in
+			(* call setDyn() *)
+			op ctx (OCallMethod (alloc_tmp ctx HVoid,1,[ra;ridx;r]));
+			r
+		| _ ->
+			(* bounds check against length *)
+			let len = alloc_tmp ctx HI32 in
+			op ctx (OField (len,ra,0)); (* length *)
+			let j = jump ctx (fun i -> OJULt (ridx,len,i)) in
+			op ctx (OCall2 (alloc_tmp ctx HVoid, alloc_fun_path ctx (array_class ctx at).cl_path "__expand", ra, ridx));
+			j();
+			match at with
+			| HI32 | HF64 ->
+				let hbytes = alloc_tmp ctx HBytes in
+				op ctx (OField (hbytes, ra, 1));
+				let ridx = shl ctx ridx (type_size_bits at) in
+				let r = alloc_tmp ctx at in
+				read_mem ctx r hbytes ridx at;
+				let r = f r in
+				write_mem ctx hbytes ridx at r;
+				r
+			| _ ->
+				let arr = alloc_tmp ctx HArray in
+				op ctx (OField (arr,ra,1));
+				let r = alloc_tmp ctx at in
+				op ctx (OGetArray (r,arr,ridx));
+				let r = f r in
+				op ctx (OSetArray (arr,ridx,r));
+				r
+		)
+	| ADynamic (eobj, fid) ->
+		let robj = eval_null_check ctx eobj in
+		let t = real_type ctx e1 in
+		let r = alloc_tmp ctx t in
+		op ctx (ODynGet (r,robj,fid));
+		let r = cast_to ctx r (to_type ctx e1.etype) e1.epos in
+		let r = f r in
+		op ctx (ODynSet (robj,fid,cast_to ctx r t e1.epos));
+		r
+	| ANone | ALocal _ | AStaticFun _ | AInstanceFun _ | AInstanceProto _ | AVirtualMethod _ | AEnum _ ->
+		assert false
+
+and build_capture_vars ctx f =
+	let ignored_vars = ref PMap.empty in
+	let used_vars = ref PMap.empty in
+	(* get all captured vars in scope, ignore vars that are declared *)
+	let decl_var v =
+		if v.v_capture then ignored_vars := PMap.add v.v_id () !ignored_vars
+	in
+	let use_var v =
+		if v.v_capture then used_vars := PMap.add v.v_id v !used_vars
+	in
+	let rec loop e =
+		(match e.eexpr with
+		| TLocal v ->
+			use_var v;
+		| TVar (v,_) ->
+			decl_var v
+		| TTry (_,catches) ->
+			List.iter (fun (v,_) -> decl_var v) catches
+		| TFunction f ->
+			List.iter (fun (v,_) -> decl_var v) f.tf_args;
+		| _ ->
+			()
+		);
+		Type.iter loop e
+	in
+	List.iter (fun (v,_) -> decl_var v) f.tf_args;
+	loop f.tf_expr;
+	let cvars = Array.of_list (PMap.fold (fun v acc -> if PMap.mem v.v_id !ignored_vars then acc else v :: acc) !used_vars []) in
+	Array.sort (fun v1 v2 -> v1.v_id - v2.v_id) cvars;
+	let indexes = ref PMap.empty in
+	Array.iteri (fun i v -> indexes := PMap.add v.v_id i !indexes) cvars;
+	{
+		c_map = !indexes;
+		c_vars = cvars;
+		c_type = HEnum {
+			eglobal = 0;
+			ename = "";
+			eid = 0;
+			efields = [|"",0,Array.map (fun v -> to_type ctx v.v_type) cvars|];
+		};
+	}
+
+and gen_method_wrapper ctx rt t p =
+	try
+		PMap.find (rt,t) ctx.method_wrappers
+	with Not_found ->
+		let fid = lookup_alloc ctx.cfids () in
+		ctx.method_wrappers <- PMap.add (rt,t) fid ctx.method_wrappers;
+		let old = ctx.m in
+		let targs, tret = (match t with HFun (args, ret) -> args, ret | _ -> assert false) in
+		let iargs, iret = (match rt with HFun (args, ret) -> args, ret | _ -> assert false) in
+		ctx.m <- method_context fid HDyn null_capture;
+		let rfun = alloc_tmp ctx rt in
+		let rargs = List.map (alloc_tmp ctx) targs in
+		let rret = alloc_tmp ctx iret in
+		op ctx (OCallClosure (rret,rfun,List.map2 (fun r t -> cast_to ctx r t p) rargs iargs));
+		op ctx (ORet (cast_to ctx rret tret p));
+		let f = {
+			name = "","";
+			findex = fid;
+			ftype = HFun (rt :: targs, tret);
+			regs = DynArray.to_array ctx.m.mregs.arr;
+			code = DynArray.to_array ctx.m.mops;
+			debug = DynArray.to_array ctx.m.mdebug;
+		} in
+		ctx.m <- old;
+		DynArray.add ctx.cfunctions f;
+		fid
+
+and make_fun ?gen_content ctx name fidx f cthis cparent =
+	let old = ctx.m in
+	let capt = build_capture_vars ctx f in
+	let has_captured_vars = Array.length capt.c_vars > 0 in
+	let capt, use_parent_capture = (match cparent with
+		| Some cparent when has_captured_vars && List.for_all (fun v -> PMap.mem v.v_id cparent.c_map) (Array.to_list capt.c_vars) -> cparent, true
+		| _ -> capt, false
+	) in
+
+	ctx.m <- method_context fidx (to_type ctx f.tf_type) capt;
+
+	set_curpos ctx f.tf_expr.epos;
+
+	let tthis = (match cthis with
+	| None -> None
+	| Some c ->
+		let t = to_type ctx (TInst (c,[])) in
+		ignore(alloc_tmp ctx t); (* index 0 *)
+		Some t
+	) in
+
+	let rcapt = if has_captured_vars && cparent <> None then Some (alloc_tmp ctx capt.c_type) else None in
+
+	let args = List.map (fun (v,o) ->
+		let r = alloc_reg ctx (if o = None then v else { v with v_type = ctx.com.basic.tnull v.v_type }) in
+		rtype ctx r
+	) f.tf_args in
+
+	if has_captured_vars then ctx.m.mcaptreg <- (match rcapt with
+		| None ->
+			let r = alloc_tmp ctx capt.c_type in
+			op ctx (OEnumAlloc (r,0));
+			r
+		| Some r -> r
+	);
+
+	List.iter (fun (v, o) ->
+		let r = alloc_reg ctx v in
+		(match o with
+		| None | Some TNull -> ()
+		| Some c ->
+			let j = jump ctx (fun n -> OJNotNull (r,n)) in
+			(match c with
+			| TNull | TThis | TSuper -> assert false
+			| TInt i when (match to_type ctx (follow v.v_type) with HI8 | HI16 | HI32 | HDyn -> true | _ -> false) ->
+				let tmp = alloc_tmp ctx HI32 in
+				op ctx (OInt (tmp, alloc_i32 ctx i));
+				op ctx (OToDyn (r, tmp));
+			| TFloat s when (match to_type ctx (follow v.v_type) with HI8 | HI16 | HI32 -> true | _ -> false) ->
+				let tmp = alloc_tmp ctx HI32 in
+				op ctx (OInt (tmp, alloc_i32 ctx (Int32.of_float (float_of_string s))));
+				op ctx (OToDyn (r, tmp));
+			| TInt i ->
+				let tmp = alloc_tmp ctx HF64 in
+				op ctx (OFloat (tmp, alloc_float ctx (Int32.to_float i)));
+				op ctx (OToDyn (r, tmp));
+			| TFloat s ->
+				let tmp = alloc_tmp ctx HF64 in
+				op ctx (OFloat (tmp, alloc_float ctx (float_of_string s)));
+				op ctx (OToDyn (r, tmp));
+			| TBool b ->
+				let tmp = alloc_tmp ctx HBool in
+				op ctx (OBool (tmp, b));
+				op ctx (OToDyn (r, tmp));
+			| TString s ->
+				let str, len = to_utf8 s f.tf_expr.epos in
+				let rb = alloc_tmp ctx HBytes in
+				op ctx (ONew r);
+				op ctx (OString (rb,alloc_string ctx str));
+				op ctx (OSetField (r,0,rb));
+				op ctx (OSetField (r,1,reg_int ctx len));
+			);
+			j();
+			(* if optional but not null, turn into a not nullable here *)
+			let vt = to_type ctx v.v_type in
+			if not (is_nullable vt) then begin
+				let t = alloc_tmp ctx vt in
+				ctx.m.mregs.map <- PMap.add v.v_id t ctx.m.mregs.map;
+				op ctx (OSafeCast (t,r));
+			end;
+		);
+		(match captured_index ctx v with
+		| None -> ()
+		| Some index ->
+			op ctx (OSetEnumField (ctx.m.mcaptreg, index, alloc_reg ctx v)));
+	) f.tf_args;
+
+	(match gen_content with
+	| None -> ()
+	| Some f -> f());
+
+	ignore(eval_expr ctx f.tf_expr);
+	let tret = to_type ctx f.tf_type in
+	let rec has_final_jump e =
+		(* prevents a jump outside function bounds error *)
+		match e.eexpr with
+		| TBlock el -> (match List.rev el with e :: _ -> has_final_jump e | [] -> false)
+		| TParenthesis e -> has_final_jump e
+		| TReturn _ -> false
+		| _ -> true
+	in
+	if tret = HVoid then
+		op ctx (ORet (alloc_tmp ctx HVoid))
+	else if has_final_jump f.tf_expr then begin
+		let r = alloc_tmp ctx tret in
+		(match tret with
+		| HI32 | HI8 | HI16 -> op ctx (OInt (r,alloc_i32 ctx 0l))
+		| HF32 | HF64 -> op ctx (OFloat (r,alloc_float ctx 0.))
+		| HBool -> op ctx (OBool (r,false))
+		| _ -> op ctx (ONull r));
+		op ctx (ORet r)
+	end;
+	let fargs = (match tthis with None -> [] | Some t -> [t]) @ (match rcapt with None -> [] | Some r -> [rtype ctx r]) @ args in
+	let f = {
+		name = name;
+		findex = fidx;
+		ftype = HFun (fargs, tret);
+		regs = DynArray.to_array ctx.m.mregs.arr;
+		code = DynArray.to_array ctx.m.mops;
+		debug = DynArray.to_array ctx.m.mdebug;
+	} in
+	ctx.m <- old;
+	Hashtbl.add ctx.defined_funs fidx ();
+	DynArray.add ctx.cfunctions f;
+	capt
+
+let generate_static ctx c f =
+	match f.cf_kind with
+	| Var _ ->
+		()
+	| Method m ->
+		let add_native lib name =
+			ignore(lookup ctx.cnatives (name ^ "@" ^ lib) (fun() ->
+				let fid = alloc_fid ctx c f in
+				Hashtbl.add ctx.defined_funs fid ();
+				(alloc_string ctx lib, alloc_string ctx name,to_type ctx f.cf_type,fid)
+			));
+		in
+		let rec loop = function
+			| (Meta.Custom ":hlNative",[(EConst(String(lib)),_);(EConst(String(name)),_)] ,_ ) :: _ ->
+				add_native lib name
+			| (Meta.Custom ":hlNative",[(EConst(String(lib)),_)] ,_ ) :: _ ->
+				add_native lib f.cf_name
+			| (Meta.Custom ":hlNative",[] ,_ ) :: _ ->
+				add_native "std" f.cf_name
+			| (Meta.Custom ":hlNative",_ ,p) :: _ ->
+				error "Invalid @:hlNative decl" p
+			| [] ->
+				ignore(make_fun ctx ((underscore_class_name c),f.cf_name) (alloc_fid ctx c f) (match f.cf_expr with Some { eexpr = TFunction f } -> f | _ -> assert false) None None)
+			| _ :: l ->
+				loop l
+		in
+		loop f.cf_meta
+
+
+let rec generate_member ctx c f =
+	match f.cf_kind with
+	| Var _ -> ()
+	| Method m ->
+		let gen_content = if f.cf_name <> "new" then None else Some (fun() ->
+			(*
+				init dynamic functions
+			*)
+			List.iter (fun f ->
+				match f.cf_kind with
+				| Method MethDynamic ->
+					let r = alloc_tmp ctx (to_type ctx f.cf_type) in
+					let fid = (match class_type ctx c (List.map snd c.cl_params) false with
+						| HObj o -> (try fst (get_index f.cf_name o) with Not_found -> assert false)
+						| _ -> assert false
+					) in
+					op ctx (OGetThis (r,fid));
+					op ctx (OJNotNull (r,2));
+					op ctx (OInstanceClosure (r,alloc_fid ctx c f,0));
+					op ctx (OSetThis (fid,r));
+				| _ -> ()
+			) c.cl_ordered_fields;
+		) in
+		ignore(make_fun ?gen_content ctx (underscore_class_name c,f.cf_name) (alloc_fid ctx c f) (match f.cf_expr with Some { eexpr = TFunction f } -> f | _ -> error "Missing function body" f.cf_pos) (Some c) None);
+		if f.cf_name = "toString" && not (List.memq f c.cl_overrides) && not (PMap.mem "__string" c.cl_fields) && is_to_string f.cf_type then begin
+			let p = f.cf_pos in
+			(* function __string() return this.toString().bytes *)
+			let ethis = mk (TConst TThis) (TInst (c,List.map snd c.cl_params)) p in
+			let tstr = mk (TCall (mk (TField (ethis,FInstance(c,List.map snd c.cl_params,f))) f.cf_type p,[])) ctx.com.basic.tstring p in
+			let cstr, cf_bytes = (try (match ctx.com.basic.tstring with TInst(c,_) -> c, PMap.find "bytes" c.cl_fields | _ -> assert false) with Not_found -> assert false) in
+			let estr = mk (TReturn (Some (mk (TField (tstr,FInstance (cstr,[],cf_bytes))) cf_bytes.cf_type p))) ctx.com.basic.tvoid p in
+			ignore(make_fun ctx (underscore_class_name c,"__string") (alloc_fun_path ctx c.cl_path "__string") { tf_expr = estr; tf_args = []; tf_type = cf_bytes.cf_type; } (Some c) None)
+		end
+
+let generate_enum ctx e =
+	()
+
+let generate_type ctx t =
+	match t with
+	| TClassDecl { cl_interface = true }->
+		()
+	| TClassDecl c when c.cl_extern ->
+		List.iter (fun f ->
+			List.iter (fun (name,args,pos) ->
+				match name with
+				| Meta.Custom ":hlNative" -> generate_static ctx c f
+				| _ -> ()
+			) f.cf_meta
+		) c.cl_ordered_statics
+	| TClassDecl c ->
+		List.iter (generate_static ctx c) c.cl_ordered_statics;
+		(match c.cl_constructor with
+		| None -> ()
+		| Some f -> generate_member ctx c f);
+		List.iter (generate_member ctx c) c.cl_ordered_fields;
+	| TTypeDecl _ | TAbstractDecl _ ->
+		()
+	| TEnumDecl e  ->
+		generate_enum ctx e
+
+let generate_static_init ctx =
+	let exprs = ref [] in
+	let t_void = ctx.com.basic.tvoid in
+
+	let gen_content() =
+
+		op ctx (OCall0 (alloc_tmp ctx HVoid, alloc_fun_path ctx ([],"Type") "init"));
+
+		(* init class values *)
+		List.iter (fun t ->
+			match t with
+			| TClassDecl c when not c.cl_extern && not (is_array_class (s_type_path c.cl_path) && snd c.cl_path <> "ArrayDyn") && c != ctx.core_type && c != ctx.core_enum ->
+
+				let path = if c == ctx.array_impl.abase then [],"Array" else if c == ctx.base_class then [],"Class" else c.cl_path in
+
+				let g, ct = class_global ~resolve:false ctx c in
+
+				let index name =
+					match ct with
+					| HObj o ->
+						fst (try get_index name o with Not_found -> assert false)
+					| _ ->
+						assert false
+				in
+
+				let rc = alloc_tmp ctx ct in
+				op ctx (ONew rc);
+				op ctx (OSetGlobal (g,rc));
+
+				let rt = alloc_tmp ctx HType in
+				let ctype = if c == ctx.array_impl.abase then ctx.array_impl.aall else c in
+				op ctx (OType (rt, class_type ctx ctype (List.map snd ctype.cl_params) false));
+				op ctx (OSetField (rc,index "__type__",rt));
+				op ctx (OSetField (rc,index "__name__",eval_expr ctx { eexpr = TConst (TString (s_type_path path)); epos = c.cl_pos; etype = ctx.com.basic.tstring }));
+
+				let rname = alloc_tmp ctx HBytes in
+				op ctx (OString (rname, alloc_string ctx (s_type_path path)));
+				op ctx (OCall2 (alloc_tmp ctx HVoid, alloc_fun_path ctx ([],"Type") "register",rname,rc));
+
+				(match c.cl_constructor with
+				| None -> ()
+				| Some f ->
+					(* set __constructor__ *)
+					let r = alloc_tmp ctx (match to_type ctx f.cf_type with
+						| HFun (args,ret) -> HFun (class_type ctx c (List.map snd c.cl_params) false :: args, ret)
+						| _ -> assert false
+					) in
+					op ctx (OStaticClosure (r, alloc_fid ctx c f));
+					op ctx (OSetField (rc,index "__constructor__",r)));
+
+				let gather_implements() =
+					let classes = ref [] in
+					let rec lookup cv =
+						List.exists (fun (i,_) -> i == c || lookup i) cv.cl_implements
+					in
+					let check = function
+						| TClassDecl c when c.cl_interface = false && not c.cl_extern -> if lookup c then classes := c :: !classes
+						| _ -> ()
+					in
+					List.iter check ctx.com.types;
+					!classes
+				in
+				if c.cl_interface then begin
+					let l = gather_implements() in
+					let ra = alloc_tmp ctx HArray in
+					let rt = alloc_tmp ctx HType in
+					op ctx (OType (rt, HType));
+					op ctx (OCall2 (ra, alloc_std ctx "alloc_array" [HType;HI32] HArray, rt, reg_int ctx (List.length l)));
+					iteri (fun i intf ->
+						op ctx (OType (rt, to_type ctx (TInst (intf,[]))));
+						op ctx (OSetArray (ra, reg_int ctx i, rt));
+					) l;
+					op ctx (OSetField (rc,index "__implementedBy__",ra));
+				end;
+
+				(* register static funs *)
+
+				List.iter (fun f ->
+					match f.cf_kind with
+					| Method _ when not (is_extern_field f) ->
+						let cl = alloc_tmp ctx (to_type ctx f.cf_type) in
+						op ctx (OStaticClosure (cl, alloc_fid ctx c f));
+						op ctx (OSetField (rc,index f.cf_name,cl));
+					| _ ->
+						()
+				) c.cl_ordered_statics;
+
+				(match Codegen.build_metadata ctx.com (TClassDecl c) with
+				| None -> ()
+				| Some e ->
+					let r = eval_to ctx e HDyn in
+					op ctx (OSetField (rc,index "__meta__",r)));
+
+			| TEnumDecl e when not e.e_extern ->
+
+				let t = enum_class ctx e in
+				let g = alloc_global ctx (match t with HObj o -> o.pname | _ -> assert false) t in
+
+				let index name =
+					match t with
+					| HObj o ->
+						fst (try get_index name o with Not_found -> assert false)
+					| _ ->
+						assert false
+				in
+				let r = alloc_tmp ctx t in
+				let rt = alloc_tmp ctx HType in
+				op ctx (ONew r);
+
+				let max_val = ref (-1) in
+				PMap.iter (fun _ c ->
+					match follow c.ef_type with
+					| TFun _ -> ()
+					| _ -> if c.ef_index > !max_val then max_val := c.ef_index;
+				) e.e_constrs;
+
+				let avalues = alloc_tmp ctx HArray in
+				op ctx (OType (rt, HDyn));
+				op ctx (OCall2 (avalues, alloc_std ctx "alloc_array" [HType;HI32] HArray, rt, reg_int ctx (!max_val + 1)));
+
+				List.iter (fun n ->
+					let f = PMap.find n e.e_constrs in
+					match follow f.ef_type with
+					| TFun _ -> ()
+					| _ ->
+						let t = to_type ctx f.ef_type in
+						let g = alloc_global ctx (efield_name e f) t in
+						let r = alloc_tmp ctx t in
+						op ctx (OMakeEnum (r,f.ef_index,[]));
+						op ctx (OSetGlobal (g,r));
+						let d = alloc_tmp ctx HDyn in
+						op ctx (OToDyn (d,r));
+						op ctx (OSetArray (avalues, reg_int ctx f.ef_index, d));
+				) e.e_names;
+
+				op ctx (OType (rt, (to_type ctx (TEnum (e,List.map snd e.e_params)))));
+				op ctx (OCall3 (alloc_tmp ctx HVoid, alloc_fun_path ctx (["hl";"types"],"Enum") "new",r,rt,avalues));
+
+				(match Codegen.build_metadata ctx.com (TEnumDecl e) with
+				| None -> ()
+				| Some e -> op ctx (OSetField (r,index "__meta__",eval_to ctx e HDyn)));
+
+				op ctx (OSetGlobal (g,r));
+
+			| TAbstractDecl { a_path = [], name; a_pos = pos } ->
+				(match name with
+				| "Int" | "Float" | "Dynamic" | "Bool" ->
+					let is_bool = name = "Bool" in
+					let t = class_type ctx (if is_bool then ctx.core_enum else ctx.core_type) [] false in
+
+					let index name =
+						match t with
+						| HObj o ->
+							fst (try get_index name o with Not_found -> assert false)
+						| _ ->
+							assert false
+					in
+
+					let g = alloc_global ctx ("$" ^ name) t in
+					let r = alloc_tmp ctx t in
+					let rt = alloc_tmp ctx HType in
+					op ctx (ONew r);
+					op ctx (OType (rt,(match name with "Int" -> HI32 | "Float" -> HF64 | "Dynamic" -> HDyn | "Bool" -> HBool | _ -> assert false)));
+					op ctx (OSetField (r,index "__type__",rt));
+					op ctx (OSetField (r,index (if is_bool then "__ename__" else "__name__"),make_string ctx name pos));
+					op ctx (OSetGlobal (g,r));
+
+					let bytes = alloc_tmp ctx HBytes in
+					op ctx (OString (bytes, alloc_string ctx name));
+					op ctx (OCall2 (alloc_tmp ctx HVoid, alloc_fun_path ctx ([],"Type") "register",bytes,r));
+				| _ ->
+					())
+			| _ ->
+				()
+
+		) ctx.com.types;
+	in
+	(* init class statics *)
+	List.iter (fun t ->
+		match t with
+		| TClassDecl c when not c.cl_extern ->
+			(match c.cl_init with None -> () | Some e -> exprs := e :: !exprs);
+			List.iter (fun f ->
+				match f.cf_kind, f.cf_expr with
+				| Var _, Some e ->
+					let p = e.epos in
+					let e = mk (TBinop (OpAssign,(mk (TField (mk (TTypeExpr t) t_dynamic p,FStatic (c,f))) f.cf_type p), e)) f.cf_type p in
+					exprs := e :: !exprs;
+				| _ ->
+					()
+			) c.cl_ordered_statics;
+		| _ -> ()
+	) ctx.com.types;
+	(* call main() *)
+	(match ctx.com.main_class with
+	| None -> ()
+	| Some m ->
+		let t = (try List.find (fun t -> t_path t = m) ctx.com.types with Not_found -> assert false) in
+		match t with
+		| TClassDecl c ->
+			let f = (try PMap.find "main" c.cl_statics with Not_found -> assert false) in
+			let p = { pfile = "<startup>"; pmin = 0; pmax = 0; } in
+			exprs := mk (TCall (mk (TField (mk (TTypeExpr t) t_dynamic p, FStatic (c,f))) f.cf_type p,[])) t_void p :: !exprs
+		| _ ->
+			assert false
+	);
+	let fid = alloc_function_name ctx "<entry>" in
+	ignore(make_fun ~gen_content ctx ("","") fid { tf_expr = mk (TBlock (List.rev !exprs)) t_void null_pos; tf_args = []; tf_type = t_void } None None);
+	fid
+
+
+(* ------------------------------- CHECK ---------------------------------------------- *)
+
+let check code =
+	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
+			failwith (Printf.sprintf "\n%s:%d: Check failure at %d@%d - %s" code.debugfiles.(dfile) dline f.findex (!pos) msg)
+		in
+		let targs, tret = (match f.ftype with HFun (args,ret) -> args, ret | _ -> assert false) in
+		let rtype i = f.regs.(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
+			| HI8 | HI16 | HI32 | HF32 | HF64 -> ()
+			| _ -> error (reg_inf r ^ " should be numeric")
+		in
+		let int r =
+			match rtype r with
+			| HI8 | HI16 | 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;
+				reg r tret
+			| _ -> 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
+		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; reg r tret
+				| 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; reg r tret
+					| 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; reg r tret
+				| 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) ->
+				reg a (rtype b);
+				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)
+			| 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);
+			| OGetI8 (r,b,p) ->
+				reg r HI32;
+				reg b HBytes;
+				reg p HI32;
+			| OGetI32 (r,b,p) | OGetI16(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;
+			| OSetI8 (r,p,v) ->
+				reg r HBytes;
+				reg p HI32;
+				reg v HI32;
+			| OSetI32 (r,p,v) | OSetI16 (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 -> ()
+				| _ -> 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;
+				can_jump eend
+			| ONullCheck r ->
+				ignore(rtype r)
+			| OTrap (r, idx) ->
+				reg r HDyn;
+				can_jump idx
+			| OEndTrap _ ->
+				()
+			| ODump r ->
+				ignore(rtype r);
+		) 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);
+		ftypes.(fd.findex) <- fd.ftype;
+	) code.functions;
+	Array.iter (fun (_,_,t,idx) ->
+		if idx >= Array.length ftypes then failwith ("Invalid native function index " ^ string_of_int idx);
+		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
+
+(* ------------------------------- INTERP --------------------------------------------- *)
+
+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 value array * int * ttype
+	| VVirtual of vvirtual
+	| VDynObj of vdynobj
+	| VEnum of int * value array
+	| VAbstract of vabstract
+	| VVarArgs of vfunction * value option
+
+and vabstract =
+	| AHashBytes of (string, value) Hashtbl.t
+	| AHashInt of (int32, value) Hashtbl.t
+	| AHashObject of (value * value) list ref
+	| AReg of regexp
+
+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;
+	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
+
+let default t =
+	match t with
+	| HI8 | HI16 | 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 _, (HI8 | HI16 | HI32) -> true
+	| VFloat _, (HF32 | HF64) -> true
+	| VBool _, HBool -> 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
+	| VUndef, HVoid -> true
+	| 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
+
+exception Runtime_error of string
+exception InterpThrow of value
+
+type cast =
+	| CNo
+	| CDyn of ttype
+	| CUnDyn of ttype
+	| CCast of ttype * ttype
+
+let interp code =
+
+	let globals = Array.map default code.globals in
+	let functions = Array.create (Array.length code.functions + Array.length code.natives) (FNativeFun ("",(fun _ -> assert false),HDyn)) in
+	let cached_protos = Hashtbl.create 0 in
+	let func f = Array.unsafe_get functions f in
+
+	let stack = ref [] in
+	let exc_stack = ref [] in
+
+	let rec get_proto p =
+		try
+			Hashtbl.find cached_protos p.pname
+		with Not_found ->
+			let fields = (match p.psuper with None -> [||] | Some p -> snd(get_proto p)) in
+			let meths = Array.map (fun f -> functions.(f)) 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 cached_protos p.pname proto;
+			proto
+	in
+
+	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
+	in
+
+	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 utf16_char buf c =
+		utf16_add buf (int_of_char c)
+	in
+
+	let caml_to_hl str = utf8_to_utf16 str in
+
+	let hl_to_caml str =
+		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
+	in
+
+	let hl_to_caml_sub str pos len =
+		hl_to_caml (String.sub str pos len ^ "\x00\x00")
+	in
+
+	let error msg = raise (Runtime_error msg) in
+	let throw v = exc_stack := []; raise (InterpThrow v) in
+	let throw_msg msg = throw (VDyn (VBytes (caml_to_hl msg),HBytes)) in
+
+	let hash_cache = Hashtbl.create 0 in
+
+	let hash str =
+		let h = hash str in
+		if not (Hashtbl.mem hash_cache h) then Hashtbl.add hash_cache h (String.sub str 0 (try String.index str '\000' with _ -> String.length str));
+		h
+	in
+
+	let null_access() =
+		error "Null value bypass null pointer check"
+	in
+
+	let make_dyn v t =
+		if v = VNull || is_dynamic t then
+			v
+		else
+			VDyn (v,t)
+	in
+
+	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
+	in
+
+	let get_to_string p =
+		match get_method p "__string" with
+		| Some f ->
+			(match func f with
+			| (FFun { ftype = HFun([_],HBytes) } as f) -> Some f
+			| _ -> None)
+		| None ->
+			None
+	in
+
+	let invalid_comparison = 255 in
+
+	let rec vstr_d v =
+		match v with
+		| VNull -> "null"
+		| VInt i -> Int32.to_string i ^ "i"
+		| VFloat f -> float_repres 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 o.oproto.pclass with
+			| Some f -> p ^ ":" ^ vstr_d (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 (regs,i,_) -> "ref(" ^ vstr_d regs.(i) ^ ")"
+		| 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"
+
+	and vstr v t =
+		match v with
+		| VNull -> "null"
+		| VInt i -> Int32.to_string i
+		| VFloat f ->
+			let s = float_repres f in
+			let len = String.length s in
+			if String.unsafe_get s (len - 1) = '.' then String.sub s 0 (len - 1) else s
+		| VBool b -> if b then "true" else "false"
+		| VDyn (v,t) ->
+			vstr v t
+		| VObj o ->
+			(match get_to_string o.oproto.pclass with
+			| None -> "#" ^ o.oproto.pclass.pname
+			| Some f -> vstr (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 (regs,i,t) -> "*" ^ (vstr regs.(i) t)
+		| VVirtual v -> 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 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
+					n ^ "(" ^ String.concat "," (List.map2 vstr (Array.to_list vals) (Array.to_list pl)) ^ ")"
+			| _ ->
+				assert false)
+		| VVarArgs _ -> "varargs"
+
+	and fstr = function
+		| FFun f -> "function@" ^ string_of_int f.findex
+		| FNativeFun (s,_,_) -> "native[" ^ s ^ "]"
+
+	and fcall f args =
+		match f with
+		| FFun f -> call f args
+		| FNativeFun (_,f,_) ->
+			try
+				f args
+			with InterpThrow v ->
+				raise (InterpThrow v)
+			| Failure msg ->
+				throw_msg msg
+			| e ->
+				throw_msg (Printexc.to_string e)
+
+	and rebuild_virtuals d =
+		let old = d.dvirtuals in
+		d.dvirtuals <- [];
+		List.iter (fun v ->
+			let v2 = (match to_virtual (VDynObj d) v.vtype with VVirtual v -> v | _ -> assert false) in
+			v.vindexes <- v2.vindexes;
+			v.vtable <- d.dvalues;
+		) old;
+		d.dvirtuals <- old;
+
+	and dyn_set_field 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 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 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 v vt t
+			with Not_found ->
+				throw_msg (o.oproto.pclass.pname ^ " has no field " ^ field))
+		| VVirtual vp ->
+			dyn_set_field vp.vvalue field v vt
+		| VNull ->
+			null_access()
+		| _ ->
+			throw_msg "Invalid object access"
+
+	and dyn_get_field obj field rt =
+		let get_with v t = dyn_cast 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 (fcall (func f) [obj; VInt (hash field)]) HDyn
+			in
+			let rec loop p =
+				try
+					let fid = PMap.find field p.pfunctions in
+					(match functions.(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 ->
+			dyn_get_field vp.vvalue field rt
+		| VNull ->
+			null_access()
+		| _ ->
+			throw_msg "Invalid object access"
+
+	and dyn_cast v t rt =
+		let invalid() =
+			error ("Can't cast " ^ vstr_d 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
+		| (HI8|HI16|HI32), (HF32|HF64) ->
+			(match v with VInt i -> VFloat (Int32.to_float i) | _ -> assert false)
+		| (HF32|HF64), (HI8|HI16|HI32) ->
+			(match v with VFloat f -> VInt (Int32.of_float f) | _ -> assert false)
+		| (HI8|HI16|HI32|HF32|HF64), HNull ((HI8|HI16|HI32|HF32|HF64) as rt) ->
+			let v = dyn_cast v t rt in
+			VDyn (v,rt)
+		| HBool, HNull HBool ->
+			VDyn (v,HBool)
+		| _, HDyn ->
+			make_dyn 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 v HDyn t
+					| CCast (t1,t2) -> dyn_cast v t1 t2
+				in
+				VClosure (FNativeFun ("~convert",(fun args ->
+					let args = List.map2 convert args conv in
+					let ret = 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 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 (match v with VDyn (v,_) -> v | _ -> v) t rt)
+		| HNull t, _ ->
+			(match v with
+			| VDyn (v,t) -> dyn_cast 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 v vp
+		| HVirtual _, _ ->
+			(match v with
+			| VVirtual v -> dyn_cast 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 = fcall (func 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 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 (Printf.sprintf "Too many arguments (%s) != (%s)" (String.concat "," (List.map (fun (v,_) -> vstr_d v) full_args)) (String.concat "," (List.map tstr full_fargs)))
+				| (v,t) :: args, ft :: fargs -> dyn_cast v t ft :: loop args fargs
+				| [], _ :: fargs -> default ft :: loop args fargs
+			in
+			let vargs = loop args full_fargs in
+			let v = fcall f (match a with None -> vargs | Some a -> a :: vargs) in
+			dyn_cast 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 (VClosure (f,a)) [arr,HArray] tret
+		| _ ->
+			throw_msg (vstr_d v ^ " cannot be called")
+
+	and dyn_compare 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 fcall (func f) [a;b] with VInt i -> Int32.to_int i | _ -> assert false));
+		| VDyn (v,t), _ ->
+			dyn_compare v t b bt
+		| _, VDyn (v,t) ->
+			dyn_compare a at v t
+		| VVirtual v, _ ->
+			dyn_compare v.vvalue HDyn b bt
+		| _, VVirtual v ->
+			dyn_compare a at v.vvalue HDyn
+		| _ ->
+			invalid_comparison
+
+	and alloc_obj t =
+		match t with
+		| HDynObj ->
+			VDynObj { dfields = Hashtbl.create 0; dvalues = [||]; dtypes = [||]; dvirtuals = []; }
+		| HObj p ->
+			let p, fields = get_proto 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
+
+	and set_i32 b p v =
+		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)));
+
+	and 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);
+
+	and to_virtual 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 v ->
+			to_virtual v.vvalue vp
+		| _ ->
+			error ("Invalid ToVirtual " ^ vstr_d v ^ " : " ^ tstr (HVirtual vp))
+
+	and call f args =
+		let regs = Array.create (Array.length f.regs) VUndef in
+		let pos = ref 1 in
+
+		stack := (f,pos) :: !stack;
+		let fret = (match f.ftype with
+			| HFun (fargs,fret) ->
+				if List.length fargs <> List.length args then error (Printf.sprintf "Invalid args: (%s) should be (%s)" (String.concat "," (List.map vstr_d args)) (String.concat "," (List.map tstr fargs)));
+				fret
+			| _ -> assert false
+		) in
+		let rtype i = f.regs.(i) in
+		let check v t id =
+			if not (is_compatible v t) then error (Printf.sprintf "Can't set %s(%s) with %s" (id()) (tstr t) (vstr_d v));
+		in
+		let check_obj v o fid =
+			match o with
+			| VObj o ->
+				let _, fields = get_proto 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 =
+			(*if f.findex = 0 then Printf.eprintf "%d@%d set %d = %s\n" f.findex (!pos - 1) r (vstr_d v);*)
+			check v (rtype r) (fun() -> "register " ^ string_of_int r);
+			Array.unsafe_set regs r v
+		in
+		iteri set args;
+		let get r = Array.unsafe_get regs r in
+		let global g = Array.unsafe_get 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 HI8/16 *)
+			| HI8 | HI16 | HI32 ->
+				(match regs.(a), regs.(b) with
+				| VInt a, VInt b -> VInt (iop a b)
+				| _ -> assert false)
+			| HF32 | HF64 ->
+				(match regs.(a), regs.(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 HI8/16 *)
+			| HI8 | HI16 | HI32 ->
+				(match regs.(a), regs.(b) with
+				| VInt a, VInt b -> VInt (f a b)
+				| _ -> assert false)
+			| _ ->
+				assert false
+		in
+		let iunop iop r =
+			match rtype r with
+			| HI8 | HI16 | HI32 ->
+				(match regs.(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 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 = f.code.(!pos) in
+			incr pos;
+			(match op with
+			| OMov (a,b) -> set a (get b)
+			| OInt (r,i) -> set r (VInt code.ints.(i))
+			| OFloat (r,i) -> set r (VFloat (Array.unsafe_get code.floats i))
+			| OString (r,s) -> set r (VBytes (caml_to_hl code.strings.(s)))
+			| OBytes (r,s) -> set r (VBytes (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 code.globals.(g) (fun() -> "global " ^ string_of_int g);
+				Array.unsafe_set 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 regs.(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 (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 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 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)
+			| 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 (VClosure (functions.(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 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 (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()
+				| _ -> assert false)
+			| OStaticClosure (r, fid) ->
+				let f = functions.(fid) in
+				set r (VClosure (f,None))
+			| OInstanceClosure (r, fid, v) ->
+				let f = functions.(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 (functions.(m), Some obj)
+						with Not_found ->
+							VNull)
+					| _ -> assert false)
+				| _ -> assert false
+				) in
+				set r (if m = VNull then m else dyn_cast m (match get_type m with None -> assert false | Some v -> v) (rtype r))
+			| OThrow r ->
+				throw (get r)
+			| ORethrow r ->
+				stack := List.rev !exc_stack @ !stack;
+				throw (get r)
+			| OGetI8 (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)
+			| OGetI16 (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);
+			| OSetI8 (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)
+			| OSetI16 (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 (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 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
+						| HI8 -> 1
+						| HI16 -> 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 (regs,v,rtype v))
+			| OUnref (v,r) ->
+				set v (match get r with
+				| VRef (regs,i,_) -> Array.unsafe_get regs i
+				| _ -> assert false)
+			| OSetref (r,v) ->
+				(match get r with
+				| VRef (regs,i,t) ->
+					let v = get v in
+					check v t (fun() -> "ref");
+					Array.unsafe_set regs i v
+				| _ -> assert false)
+			| OToVirtual (r,rv) ->
+				set r (to_virtual (get rv) (match rtype r with HVirtual vp -> vp | _ -> assert false))
+			| ODynGet (r,o,f) ->
+				set r (dyn_get_field (get o) code.strings.(f) (rtype r))
+			| ODynSet (o,fid,vr) ->
+				dyn_set_field (get o) 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 "Null access"
+			| OTrap (r,j) ->
+				let target = !pos + j in
+				traps := (r,target) :: !traps
+			| OEndTrap _ ->
+				traps := List.tl !traps
+			| ODump r ->
+				print_endline (vstr_d (get r));
+			);
+			loop()
+		in
+		let rec exec() =
+			try
+				loop()
+			with
+				| Return v ->
+					check v fret (fun() -> "return value");
+					stack := List.tl !stack;
+					v
+				| InterpThrow v ->
+					match !traps with
+					| [] ->
+						exc_stack := List.hd !stack :: !exc_stack;
+						stack := List.tl !stack;
+						raise (InterpThrow v)
+					| (r,target) :: tl ->
+						traps := tl;
+						exc_stack := (f,ref !pos) :: !exc_stack;
+						pos := target;
+						set r v;
+						exec()
+		in
+		pos := 0;
+		exec()
+	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 no_virtual v =
+		match v with
+		| VVirtual v -> v.vvalue
+		| _ -> v
+	in
+	let load_native lib name t =
+		let unresolved() = (fun args -> error ("Unresolved native " ^ lib ^ "@" ^ name)) 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 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 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
+				| _ -> 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 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 (regs,i,_)] ->
+					let str = Int32.to_string v in
+					regs.(i) <- to_int (String.length str);
+					VBytes (caml_to_hl str)
+				| _ -> assert false);
+			| "ftos" ->
+				(function
+				| [VFloat _ as v; VRef (regs,i,_)] ->
+					let str = vstr v HF64 in
+					regs.(i) <- to_int (String.length str);
+					VBytes (caml_to_hl str)
+				| _ -> assert false);
+			| "value_to_string" ->
+				(function
+				| [v; VRef (regs,i,_)] ->
+					let str = caml_to_hl (vstr v HDyn) in
+					regs.(i) <- 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 (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 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 ("Cannot cast " ^ vstr_d 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.time())
+				| _ -> assert false)
+			| "sys_exit" ->
+				(function
+				| [VInt code] -> VUndef
+				| _ -> assert false)
+			| "sys_utf8_path" ->
+				(function
+				| [] -> VBool true
+				| _ -> assert false)
+			| "hash" ->
+				(function
+				| [VBytes str] -> VInt (hash (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 -> globals.(g))
+					| HEnum e -> globals.(e.eglobal)
+					| _ -> 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; VBool r] -> get_fields v r
+				| _ -> 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 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 hash_cache hash with Not_found -> assert false) in
+					(match o with
+					| VObj _ | VDynObj _ | VVirtual _ -> dyn_get_field o f HDyn
+					| _ -> VNull)
+				| _ -> assert false)
+			| "obj_set_field" ->
+				(function
+				| [o;VInt hash;v] ->
+					let f = (try Hashtbl.find hash_cache hash with Not_found -> assert false) in
+					dyn_set_field o f v HDyn;
+					VUndef
+				| _ -> assert false)
+			| "obj_has_field" ->
+				(function
+				| [o;VInt hash] ->
+					let f = (try Hashtbl.find hash_cache 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 hash_cache 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 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 (regs,idx,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
+					regs.(idx) <- to_int (String.length u16 - 2);
+					VBytes u16
+				| _ -> assert false)
+			| "utf16_to_utf8" ->
+				(function
+				| [VBytes s; VInt pos; VRef (regs,idx,HI32)] ->
+					let s = String.sub s (int pos) (String.length s - (int pos)) in
+					let u8 = hl_to_caml s in
+					regs.(idx) <- 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 (regs, idx, 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
+					regs.(idx) <- to_int (String.length str lsr 1 - 1);
+					VBytes str
+				| _ -> assert false)
+			| "url_decode" ->
+				(function
+				| [VBytes s; VRef (regs, idx, 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
+					regs.(idx) <- to_int (UTF8.length str);
+					VBytes (caml_to_hl str)
+				| _ -> assert false)
+			| "call_method" ->
+				(function
+				| [f;VArray (args,HDyn)] -> dyn_call 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)
+			| "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 (regs,pos,HI32) -> regs.(pos) <- 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 (regs,pos,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
+					regs.(pos) <- to_int (String.length str);
+					VBytes (caml_to_hl str)
+				| _ -> assert false)
+			| "random" ->
+				(function
+				| [VInt max] -> VInt (if max <= 0l then 0l else Random.int32 max)
+				| _ -> assert false)
+			| _ ->
+				unresolved())
+		| "regexp" ->
+			(match name with
+			| "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 (regs,rlen,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) -> regs.(rlen) <- to_int (pend - pos); to_int pos)
+				| _ -> assert false)
+			| _ ->
+				unresolved())
+		| _ ->
+			unresolved()
+		) in
+		FNativeFun (lib ^ "@" ^ name, f, t)
+	in
+	Array.iter (fun (lib,name,t,idx) -> functions.(idx) <- load_native code.strings.(lib) code.strings.(name) t) code.natives;
+	Array.iter (fun fd -> functions.(fd.findex) <- FFun fd) code.functions;
+	let get_stack st =
+		String.concat "\n" (List.map (fun (f,pos) ->
+			let pos = !pos - 1 in
+			let file, line = (try let fid, line = f.debug.(pos) in code.debugfiles.(fid), line with _ -> "???", 0) in
+			Printf.sprintf "%s:%d: Called from fun(%d)@%d" file line f.findex pos
+		) st)
+	in
+	match functions.(code.entrypoint) with
+	| FFun f when f.ftype = HFun([],HVoid) ->
+		(try
+			ignore(call f [])
+		with
+			| InterpThrow v -> Common.error ("Uncaught exception " ^ vstr v HDyn ^ "\n" ^ get_stack (List.rev !exc_stack)) Ast.null_pos
+			| Runtime_error msg -> Common.error ("HL Interp error " ^ msg ^ "\n" ^ get_stack !stack) Ast.null_pos
+		)
+	| _ -> assert false
+
+(* --------------------------------------------------------------------------------------------------------------------- *)
+(* WRITE *)
+
+
+(* 	from -500M to +500M
+	0[7] = 0-127
+	10[+/-][5] [8] = -x2000/+x2000
+	11[+/-][5] [24] = -x20000000/+x20000000
+*)
+let write_index_gen b i =
+	if i < 0 then
+		let i = -i in
+		if i < 0x2000 then begin
+			b ((i lsr 8) lor 0xA0);
+			b (i land 0xFF);
+		end else if i >= 0x20000000 then assert false else begin
+			b ((i lsr 24) lor 0xE0);
+			b ((i lsr 16) land 0xFF);
+			b ((i lsr 8) land 0xFF);
+			b (i land 0xFF);
+		end
+	else if i < 0x80 then
+		b i
+	else if i < 0x2000 then begin
+		b ((i lsr 8) lor 0x80);
+		b (i land 0xFF);
+	end else if i >= 0x20000000 then assert false else begin
+		b ((i lsr 24) lor 0xC0);
+		b ((i lsr 16) land 0xFF);
+		b ((i lsr 8) land 0xFF);
+		b (i land 0xFF);
+	end
+
+let write_code ch code =
+
+	let types = gather_types code in
+	let byte = IO.write_byte ch in
+	let write_index = write_index_gen byte in
+
+	let rec write_type t =
+		write_index (lookup types t (fun() -> assert false))
+	in
+
+	let write_op op =
+
+		let o = Obj.repr op in
+		let oid = Obj.tag o in
+
+		match op with
+		| OLabel _ ->
+			byte oid
+		| OCall2 (r,g,a,b) ->
+			byte oid;
+			write_index r;
+			write_index g;
+			write_index a;
+			write_index b;
+		| OCall3 (r,g,a,b,c) ->
+			byte oid;
+			write_index r;
+			write_index g;
+			write_index a;
+			write_index b;
+			write_index c;
+		| OCall4 (r,g,a,b,c,d) ->
+			byte oid;
+			write_index r;
+			write_index g;
+			write_index a;
+			write_index b;
+			write_index c;
+			write_index d;
+		| OCallN (r,f,rl) | OCallClosure (r,f,rl) | OCallMethod (r,f,rl) | OCallThis (r,f,rl) | OMakeEnum (r,f,rl) ->
+			byte oid;
+			write_index r;
+			write_index f;
+			let n = List.length rl in
+			if n > 0xFF then assert false;
+			byte n;
+			List.iter write_index rl
+		| OType (r,t) ->
+			byte oid;
+			write_index r;
+			write_type t
+		| OSwitch (r,pl,eend) ->
+			byte oid;
+			let n = Array.length pl in
+			if n > 0xFF then assert false;
+			byte n;
+			Array.iter write_index pl;
+			write_index eend
+		| OEnumField (r,e,i,idx) ->
+			byte oid;
+			write_index r;
+			write_index e;
+			write_index i;
+			write_index idx;
+		| _ ->
+			let field n = (Obj.magic (Obj.field o n) : int) in
+			match Obj.size o with
+			| 1 ->
+				let a = field 0 in
+				byte oid;
+				write_index a;
+			| 2 ->
+				let a = field 0 in
+				let b = field 1 in
+				byte oid;
+				write_index a;
+				write_index b;
+			| 3 ->
+				let a = field 0 in
+				let b = field 1 in
+				let c = field 2 in
+				byte oid;
+				write_index a;
+				write_index b;
+				write_index c;
+			| _ ->
+				assert false
+	in
+
+	IO.nwrite ch "HLB";
+	IO.write_byte ch code.version;
+
+	write_index (Array.length code.ints);
+	write_index (Array.length code.floats);
+	write_index (Array.length code.strings);
+	write_index (DynArray.length types.arr);
+	write_index (Array.length code.globals);
+	write_index (Array.length code.natives);
+	write_index (Array.length code.functions);
+	write_index code.entrypoint;
+
+	Array.iter (IO.write_real_i32 ch) code.ints;
+	Array.iter (IO.write_double ch) code.floats;
+
+	let str_length = ref 0 in
+	Array.iter (fun str -> str_length := !str_length + String.length str + 1) code.strings;
+	IO.write_i32 ch !str_length;
+	Array.iter (IO.write_string ch) code.strings;
+	Array.iter (fun str -> write_index (String.length str)) code.strings;
+
+	DynArray.iter (fun t ->
+		match t with
+		| HVoid -> byte 0
+		| HI8 -> byte 1
+		| HI16 -> byte 2
+		| HI32 -> byte 3
+		| HF32 -> byte 4
+		| HF64 -> byte 5
+		| HBool -> byte 6
+		| HBytes -> byte 7
+		| HDyn -> byte 8
+		| HFun (args,ret) ->
+			let n = List.length args in
+			if n > 0xFF then assert false;
+			byte 9;
+			byte n;
+			List.iter write_type args;
+			write_type ret
+		| HObj p ->
+			byte 10;
+			write_index p.pid;
+			(match p.psuper with
+			| None -> write_index (-1)
+			| Some t -> write_type (HObj t));
+			write_index (Array.length p.pfields);
+			write_index (Array.length p.pproto);
+			Array.iter (fun (_,n,t) -> write_index n; write_type t) p.pfields;
+			Array.iter (fun f -> write_index f.fid; write_index f.fmethod; write_index (match f.fvirtual with None -> -1 | Some i -> i)) p.pproto;
+		| HArray ->
+			byte 11
+		| HType ->
+			byte 12
+		| HRef t ->
+			byte 13;
+			write_type t
+		| HVirtual v ->
+			byte 14;
+			write_index (Array.length v.vfields);
+			Array.iter (fun (_,sid,t) -> write_index sid; write_type t) v.vfields
+		| HDynObj ->
+			byte 15
+		| HAbstract (_,i) ->
+			byte 16;
+			write_index i
+		| HEnum e ->
+			byte 17;
+			write_index e.eid;
+			write_index (Array.length e.efields);
+			Array.iter (fun (_,n,tl) ->
+				write_index (Array.length tl);
+				Array.iter write_type tl;
+			) e.efields
+		| HNull t ->
+			byte 0x18;
+			write_type t
+	) types.arr;
+
+	Array.iter write_type code.globals;
+	Array.iter (fun (lib_index, name_index,ttype,findex) ->
+		write_index lib_index;
+		write_index name_index;
+		write_type ttype;
+		write_index findex;
+	) code.natives;
+	Array.iter (fun f ->
+		write_type f.ftype;
+		write_index f.findex;
+		write_index (Array.length f.regs);
+		write_index (Array.length f.code);
+		Array.iter write_type f.regs;
+		Array.iter write_op f.code;
+	) code.functions
+
+(* --------------------------------------------------------------------------------------------------------------------- *)
+(* DUMP *)
+
+let ostr 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, f%d()" r g
+	| OCall1 (r,g,a) -> Printf.sprintf "call %d, f%d(%d)" r g a
+	| OCall2 (r,g,a,b) -> Printf.sprintf "call %d, f%d(%d,%d)" r g a b
+	| OCall3 (r,g,a,b,c) -> Printf.sprintf "call %d, f%d(%d,%d,%d)" r g a b c
+	| OCall4 (r,g,a,b,c,d) -> Printf.sprintf "call %d, f%d(%d,%d,%d,%d)" r g a b c d
+	| OCallN (r,g,rl) -> Printf.sprintf "call %d, f%d(%s)" r 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, f%d" r f
+	| OInstanceClosure (r,f,v) -> Printf.sprintf "instanceclosure %d, f%d(%d)" r f v
+	| 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
+	| OGetI8 (r,b,p) -> Printf.sprintf "geti8 %d,%d[%d]" r b p
+	| OGetI16 (r,b,p) -> Printf.sprintf "geti16 %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
+	| OSetI8 (r,p,v) -> Printf.sprintf "seti8 %d,%d,%d" r p v
+	| OSetI16 (r,p,v) -> Printf.sprintf "seti16 %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
+	| ODump r -> Printf.sprintf "dump %d" r
+
+let dump pr code =
+	let all_protos = 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 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 ^ " : " ^ float_repres 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);
+	) code.natives;
+	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));
+		pr (Printf.sprintf "	; %s" (debug_infos f.debug.(0)));
+		Array.iteri (fun i r ->
+			pr ("		r" ^ string_of_int i ^ " " ^ tstr r);
+		) f.regs;
+		Array.iteri (fun i o ->
+			pr (Printf.sprintf "		.%-5d @%d %s" (snd f.debug.(i)) i (ostr 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
+
+(* --------------------------------------------------------------------------------------------------------------------- *)
+(* HLC *)
+
+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";
+(* reserved by HLC *)
+"t"
+]
+
+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 = 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 = String.concat "__" (ExtString.String.nsplit str ".") in
+
+	let is_gc_ptr = function
+		| HVoid | HI8 | HI16 | HI32 | HF32 | HF64 | HBool | HType | HRef _ -> false
+		| HBytes | HDyn | HFun _ | HObj _ | HArray | HVirtual _ | HDynObj | HAbstract _ | HEnum _ | HNull _ -> true
+	in
+
+	let is_ptr = function
+		| HVoid | HI8 | HI16 | HI32 | HF32 | HF64 | HBool -> false
+		| _ -> true
+	in
+
+	let rec ctype_no_ptr = function
+		| HVoid -> "void",0
+		| HI8 -> "char",0
+		| HI16 -> "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"
+		| HI8 -> "HI8"
+		| HI16 -> "HI16"
+		| 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 types = 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 = lookup types (HEnum e) (fun() -> 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
+		| HI8 -> "c"
+		| HI16 -> "s"
+		| 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) ->
+				Hashtbl.replace used_closures fid ()
+			| OBytes (_,sid) ->
+				Hashtbl.replace bytes_strings sid ()
+			| _ ->
+				()
+		) f.code
+	) code.functions;
+
+
+	line "";
+	line "// Types definitions";
+	DynArray.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);
+		| _ ->
+			()
+	) types.arr;
+
+	line "";
+	line "// Types implementation";
+	DynArray.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.iter (fun (n,_,t) ->
+					expr (var_type 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
+		| _ ->
+			()
+	) types.arr;
+
+	line "";
+	line "// Types values declaration";
+	DynArray.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
+		| _ ->
+			()
+	) types.arr;
+
+	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) ->
+			sexpr "static %s %s(%s)" (ctype t) (fundecl_name f) (String.concat "," (List.map ctype args));
+			Array.set tfuns f.findex (args,t);
+			funnames.(f.findex) <- fundecl_name f;
+		| _ ->
+			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 = lookup types t (fun() -> assert false) in
+		"&type$" ^ string_of_int index
+	in
+
+	line "";
+	line "// Types values data";
+	DynArray.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
+				sprintf "{(const uchar*)string$%d, %d, %s, %s, %s}" nid (Array.length tl) tval size 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)
+		| _ ->
+			()
+	) types.arr;
+
+	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 | HI8 | HI16 | HI32 | HF32 | HF64 -> t
+		| HBool -> HI8
+		| HBytes | HDyn | HFun _ | HObj _ | HArray | HType | HRef _ | HVirtual _ | HDynObj | HAbstract _ | HEnum _ | HNull _ -> HDyn
+	in
+	let type_kind_id t =
+		match t with
+		| HVoid -> 0
+		| HI8 -> 1
+		| HI16 -> 2
+		| HI32 -> 3
+		| HF32 -> 4
+		| HF64 -> 5
+		| _ -> 6
+	in
+	Array.iter (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) ()
+	) 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))) all_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"
+		| HI8 | HBool -> "c"
+		| HI16 -> "s"
+		| 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
+			| HI8 | HI16 | 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 "%shlc_dyn_call_args((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 "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)
+					) args));
+					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
+					let fname, fid, _ = vp.vfields.(fid) in
+					sexpr "%shlc_dyn_call_obj(%s->value,%ld/*%s*/,%s,%d)" ret (reg o) (hash fid) fname (if args = [] then "NULL" else "args") (List.length args);
+					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();
+					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) (ident name) (rcast v t)
+			| HVirtual vp ->
+				let name, nid, t = vp.vfields.(fid) in
+				let dset = sprintf "hl_dyn_set%s((vdynamic*)%s,%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) (ident name)
+			| HVirtual v ->
+				let name, nid, t = v.vfields.(fid) in
+				let dget = sprintf "(%s)hl_dyn_get%s((vdynamic*)%s,%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) (fundecl_name f) (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) [] 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;
+
+		Array.iteri (fun i op ->
+			(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);
+			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 op)
+			in
+			let compare_op op a b d =
+				let phys_compare() =
+					sexpr "if( %s %s %s ) goto %s" (reg a) (s_binop 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
+				| (HI8 | HI16 | HI32 | HF32 | HF64 | HBool), (HI8 | HI16 | 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_binop 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_binop op) (reg b) field in
+					if op = OpEq then
+						sexpr "if( %s == %s || (%s && %s && %s) ) goto %s" (reg a) (reg b) (reg a) (reg b) pcompare (label d)
+					else if op = OpNotEq 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 = OpGt || op = OpGte 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_binop op) inv (label d)
+				| HObj oa, HObj _ ->
+					(try
+						let fid = PMap.find "__compare" oa.pfunctions in
+						if op = OpEq 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 = OpNotEq 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_binop op) (label d)
+					with Not_found ->
+						phys_compare())
+				| HEnum _, HEnum _ | HVirtual _, HVirtual _ | HDynObj, HDynObj ->
+					phys_compare()
+				| HVirtual _, HObj _->
+					if op = OpEq then
+						sexpr "if( (void*)%s == (void*)%s || (%s && %s && %s->value == (vdynamic*)%s) ) goto %s" (reg a) (reg b) (reg a) (reg b) (reg a) (reg b) (label d)
+					else if op = OpNotEq then
+						sexpr "if( (void*)%s != (void*)%s && (!%s || !%s || %s->value != (vdynamic*)%s) ) goto %s" (reg a) (reg b) (reg a) (reg b) (reg a) (reg b) (label d)
+					else
+						assert false
+				| HObj _, HVirtual _ ->
+					if op = OpEq then
+						sexpr "if( (void*)%s == (void*)%s || (%s && %s && %s->value == (vdynamic*)%s) ) goto %s" (reg a) (reg b) (reg a) (reg b) (reg b) (reg a) (label d)
+					else if op = OpNotEq then
+						sexpr "if( (void*)%s != (void*)%s && (!%s || !%s || %s->value != (vdynamic*)%s) ) goto %s" (reg a) (reg b) (reg a) (reg b) (reg b) (reg a) (label d)
+					else
+						assert false
+				| 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
+				| HI8 | HI16 | 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
+				| HI8 | HI16 | 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
+			| 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 OpLt a b d
+			| OJSGte (a,b,d) ->
+				compare_op OpGte a b d
+			| OJSGt (a,b,d) ->
+				compare_op OpGt a b d
+			| OJSLte (a,b,d) ->
+				compare_op OpLte 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 OpEq a b d
+			| OJNotEq (a,b,d) ->
+				compare_op OpNotEq 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
+				| HI8 | HI16 | 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)
+			| OGetI8 (r,b,idx) ->
+				sexpr "%s = *(unsigned char*)(%s + %s)" (reg r) (reg b) (reg idx)
+			| OGetI16 (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)
+			| OSetI8 (b,idx,r) ->
+				sexpr "*(unsigned char*)(%s + %s) = (unsigned char)%s" (reg b) (reg idx) (reg r)
+			| OSetI16 (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 t = rtype r in
+				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
+				sexpr "%s = (venum*)hl_gc_alloc%s(sizeof(%s))" (reg r) (if has_ptr then "" else "_noptr") et;
+				sexpr "%s->index = %d" (reg r) cid;
+				let _,_,tl = e.efields.(cid) in
+				iteri (fun i v ->
+					sexpr "((%s*)%s)->p%d = %s" et (reg r) i (rcast v tl.(i))
+				) rl;
+			| 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;
+				output_at2 (i + 1 + eend) [OODecreaseIndent;OODecreaseIndent;OOEndBlock];
+			| ONullCheck r ->
+				sexpr "if( %s == NULL ) hl_null_access()" (reg r)
+			| OTrap (r,d) ->
+				sexpr "hlc_trap(trap$%d,%s,%s)" !trap_depth (reg r) (label d);
+				incr trap_depth
+			| OEndTrap b ->
+				sexpr "hlc_endtrap(trap$%d)" (!trap_depth - 1);
+				if b then decr trap_depth;
+			| ODump r ->
+				todo()
+		) 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;
+	DynArray.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;
+			if e.eglobal <> 0 then sexpr "enum$%d.global_value = (void**)&global$%d" i e.eglobal;
+			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
+		| _ ->
+			()
+	) types.arr;
+	sexpr "%s()" funnames.(code.entrypoint);
+	unblock();
+	line "}";
+	line "";
+	List.iter (fun f -> f()) !end_ch
+
+
+(* --------------------------------------------------------------------------------------------------------------------- *)
+
+let generate com =
+	let get_class name =
+		try
+			match List.find (fun t -> (t_infos t).mt_path = (["hl";"types"],name)) com.types with
+			| TClassDecl c -> c
+			| _ -> assert false
+		with
+			Not_found ->
+				failwith ("hl class " ^ name ^ " not found")
+	in
+	let ctx = {
+		com = com;
+		m = method_context 0 HVoid null_capture;
+		cints = new_lookup();
+		cstrings = new_lookup();
+		cfloats = new_lookup();
+		cglobals = new_lookup();
+		cnatives = new_lookup();
+		cfunctions = DynArray.create();
+		overrides = Hashtbl.create 0;
+		cached_types = PMap.empty;
+		cfids = new_lookup();
+		defined_funs = Hashtbl.create 0;
+		array_impl = {
+			aall = get_class "ArrayAccess";
+			abase = get_class "ArrayBase";
+			adyn = get_class "ArrayDyn";
+			aobj = get_class "ArrayObj";
+			ai16 = get_class "ArrayBasic_hl_types_I16";
+			ai32 = get_class "ArrayBasic_Int";
+			af32 = get_class "ArrayBasic_Single";
+			af64 = get_class "ArrayBasic_Float";
+		};
+		base_class = get_class "Class";
+		base_enum = get_class "Enum";
+		base_type = get_class "BaseType";
+		core_type = get_class "CoreType";
+		core_enum = get_class "CoreEnum";
+		anons_cache = [];
+		rec_cache = [];
+		method_wrappers = PMap.empty;
+		cdebug_files = new_lookup();
+	} in
+	let all_classes = Hashtbl.create 0 in
+	List.iter (fun t ->
+		match t with
+		| TClassDecl ({ cl_path = ["hl";"types"], ("BasicIterator"|"ArrayBasic") } as c) ->
+			c.cl_extern <- true
+		| TClassDecl c ->
+			let rec loop p f =
+				match p with
+				| Some (p,_) when PMap.mem f.cf_name p.cl_fields || loop p.cl_super f ->
+					Hashtbl.replace ctx.overrides (f.cf_name,p.cl_path) true;
+					true
+				| _ ->
+					false
+			in
+			List.iter (fun f -> ignore(loop c.cl_super f)) c.cl_overrides;
+			Hashtbl.add all_classes c.cl_path c;
+			List.iter (fun (m,args,p) ->
+				if m = Meta.Custom ":hlNative" then
+					let lib, prefix = (match args with
+					| [(EConst (String lib),_)] -> lib, ""
+					| [(EConst (String lib),_);(EConst (String p),_)] -> lib, p
+					| _ -> error "hlNative on class requires library name" p
+					) in
+					(* adds :hlNative for all empty methods *)
+					List.iter (fun f ->
+						match f.cf_kind with
+						| Method MethNormal when not (List.exists (fun (m,_,_) -> m = Meta.Custom ":hlNative") f.cf_meta) ->
+							(match f.cf_expr with
+							| Some { eexpr = TFunction { tf_expr = { eexpr = TBlock ([] | [{ eexpr = TReturn (Some { eexpr = TConst _ })}]) } } } ->
+								let name = prefix ^ String.lowercase (Str.global_replace (Str.regexp "[A-Z]+") "_\\0" f.cf_name) in
+								f.cf_meta <- (Meta.Custom ":hlNative", [(EConst (String lib),p);(EConst (String name),p)], p) :: f.cf_meta;
+							| _ -> ())
+						| _ -> ()
+					) c.cl_ordered_statics
+			) c.cl_meta;
+ 		| _ -> ()
+	) com.types;
+	ignore(alloc_string ctx "");
+	ignore(class_type ctx ctx.base_class [] false);
+	List.iter (generate_type ctx) com.types;
+	let ep = generate_static_init ctx in
+	let code = {
+		version = 1;
+		entrypoint = ep;
+		strings = DynArray.to_array ctx.cstrings.arr;
+		ints = DynArray.to_array ctx.cints.arr;
+		floats = DynArray.to_array ctx.cfloats.arr;
+		globals = DynArray.to_array ctx.cglobals.arr;
+		natives = DynArray.to_array ctx.cnatives.arr;
+		functions = DynArray.to_array ctx.cfunctions;
+		debugfiles = DynArray.to_array ctx.cdebug_files.arr;
+	} in
+	Array.sort (fun (lib1,_,_,_) (lib2,_,_,_) -> lib1 - lib2) code.natives;
+	if Common.defined com Define.Dump then begin
+		let ch = open_out_bin "dump/hlcode.txt" in
+		dump (fun s -> output_string ch (s ^ "\n")) code;
+		close_out ch;
+	end;
+	PMap.iter (fun (s,p) fid ->
+		if not (Hashtbl.mem ctx.defined_funs fid) then failwith (Printf.sprintf "Unresolved method %s:%s(@%d)" (s_type_path p) s fid)
+	) ctx.cfids.map;
+	check code;
+	if file_extension com.file = "c" then
+		write_c com.Common.version com.file code
+	else begin
+		let ch = IO.output_string() in
+		write_code ch code;
+		let str = IO.close_out ch in
+		let ch = open_out_bin com.file in
+		output_string ch str;
+		close_out ch;
+	end;
+	if Common.defined com Define.Interp then ignore(interp code)
+

+ 98 - 71
genjava.ml → src/generators/genjava.ml

@@ -1,23 +1,20 @@
 (*
- * Copyright (C)2005-2013 Haxe Foundation
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
+	The 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 JData
@@ -74,6 +71,11 @@ let rec t_has_type_param t = match follow t with
 	| TFun(f,ret) -> t_has_type_param ret || List.exists (fun (_,_,t) -> t_has_type_param t) f
 	| _ -> false
 
+let is_dynamic gen t =
+	match follow (gen.greal_type t) with
+		| TDynamic _ -> true
+		| _ -> false
+
 let is_type_param t = match follow t with
 	| TInst({ cl_kind = KTypeParameter _ }, _) -> true
 	| _ -> false
@@ -299,11 +301,28 @@ struct
 	let priority = solve_deps name [ DAfter ExpressionUnwrap.priority; DAfter ObjectDeclMap.priority; DAfter ArrayDeclSynf.priority; DBefore IntDivisionSynf.priority ]
 
 	let java_hash s =
+		let high_surrogate c = (c lsr 10) + 0xD7C0 in
+		let low_surrogate c = (c land 0x3FF) lor 0xDC00 in
 		let h = ref Int32.zero in
 		let thirtyone = Int32.of_int 31 in
-		for i = 0 to String.length s - 1 do
-			h := Int32.add (Int32.mul thirtyone !h) (Int32.of_int (int_of_char (String.unsafe_get s i)));
-		done;
+		(try
+			UTF8.validate s;
+			UTF8.iter (fun c ->
+				let c = (UChar.code c) in
+				if c > 0xFFFF then
+					(h := Int32.add (Int32.mul thirtyone !h)
+						(Int32.of_int (high_surrogate c));
+					h := Int32.add (Int32.mul thirtyone !h)
+						(Int32.of_int (low_surrogate c)))
+				else
+					h := Int32.add (Int32.mul thirtyone !h)
+						(Int32.of_int c)
+				) s
+		with UTF8.Malformed_code ->
+			String.iter (fun c ->
+				h := Int32.add (Int32.mul thirtyone !h)
+					(Int32.of_int (Char.code c))) s
+		);
 		!h
 
 	let rec is_final_return_expr is_switch e =
@@ -554,7 +573,9 @@ struct
 		(* let tbyte = mt_to_t_dyn ( get_type gen (["java"], "Int8") ) in *)
 		(* let tshort = mt_to_t_dyn ( get_type gen (["java"], "Int16") ) in *)
 		(* let tsingle = mt_to_t_dyn ( get_type gen ([], "Single") ) in *)
+		let ti64 = mt_to_t_dyn ( get_type gen (["java"], "Int64") ) in
 		let string_ext = get_cl ( get_type gen (["haxe";"lang"], "StringExt")) in
+		let fast_cast = Common.defined gen.gcon Define.FastCast in
 
 		let is_string t = match follow t with | TInst({ cl_path = ([], "String") }, []) -> true | _ -> false in
 
@@ -595,7 +616,7 @@ struct
 				(* 	(* let unboxed_type gen t tbyte tshort tchar tfloat = match follow t with *) *)
 				(* 	run { e with etype = unboxed_type gen e.etype tbyte tshort tchar tsingle } *)
 
-				| TCast(expr, _) when is_bool e.etype ->
+				| TCast(expr, _) when is_bool e.etype && is_dynamic gen expr.etype ->
 					{
 						eexpr = TCall(
 							mk_static_field_access_infer runtime_cl "toBool" expr.epos [],
@@ -605,7 +626,7 @@ struct
 						epos = e.epos
 					}
 
-				| TCast(expr, _) when is_int_float gen e.etype && not (is_int_float gen expr.etype) ->
+				| TCast(expr, _) when is_int_float gen e.etype && is_dynamic gen expr.etype ->
 					let needs_cast = match gen.gfollow#run_f e.etype with
 						| TInst _ -> false
 						| _ -> true
@@ -632,8 +653,21 @@ struct
 						| _ -> true
 					in
 					if need_second_cast then { e with eexpr = TCast(mk_cast (follow e.etype) (run expr), c) }  else Type.map_expr run e*)
+				| TCast(expr, _) when like_i64 e.etype && is_dynamic gen expr.etype ->
+					{
+						eexpr = TCall(
+							mk_static_field_access_infer runtime_cl "toLong" expr.epos [],
+							[ run expr ]
+						);
+						etype = ti64;
+						epos = expr.epos
+					}
+
+				| TCast(expr, Some(TClassDecl cls)) when fast_cast && cls == null_class ->
+					{ e with eexpr = TCast(run expr, Some(TClassDecl null_class)) }
+
 				| TBinop( (Ast.OpAssignOp OpAdd as op), e1, e2)
-				| TBinop( (Ast.OpAdd as op), e1, e2) when is_string e.etype || is_string e1.etype || is_string e2.etype ->
+				| TBinop( (Ast.OpAdd as op), e1, e2) when not fast_cast && (is_string e.etype || is_string e1.etype || is_string e2.etype) ->
 						let is_assign = match op with Ast.OpAssignOp _ -> true | _ -> false in
 						let mk_to_string e = { e with eexpr = TCall( mk_static_field_access_infer runtime_cl "toString" e.epos [], [run e] ); etype = gen.gcon.basic.tstring	} in
 						let check_cast e = match gen.greal_type e.etype with
@@ -1239,18 +1273,10 @@ let configure gen =
 					(match c with
 						| TInt i32 ->
 							print w "%ld" i32;
-							(match real_type e.etype with
-								| TType( { t_path = (["java"], "Int64") }, [] ) -> write w "L";
-								| _ -> ()
-							)
 						| TFloat s ->
 							write w s;
 							(* fix for Int notation, which only fit in a Float *)
 							(if not (String.contains s '.' || String.contains s 'e' || String.contains s 'E') then write w ".0");
-							(match real_type e.etype with
-								| TType( { t_path = ([], "Single") }, [] ) -> write w "f"
-								| _ -> ()
-							)
 						| TString s -> print w "\"%s\"" (escape s)
 						| TBool b -> write w (if b then "true" else "false")
 						| TNull ->
@@ -1526,8 +1552,9 @@ let configure gen =
 							write w "case ";
 							in_value := true;
 							(match e.eexpr with
-								| TField(_,FEnum(e,ef)) ->
-									write w ef.ef_name
+								| TField(_, FEnum(e, ef)) ->
+									let changed_name = change_id ef.ef_name in
+									write w changed_name
 								| _ ->
 									expr_s w e);
 							write w ":";
@@ -1994,6 +2021,7 @@ let configure gen =
 	in
 
 	let module_type_gen w md_tp =
+		Codegen.map_source_header gen.gcon (fun s -> print w "// %s\n" s);
 		match md_tp with
 			| TClassDecl cl ->
 				if not cl.cl_extern then begin
@@ -2472,8 +2500,6 @@ let configure gen =
 
 	TypeParams.RenameTypeParameters.run gen;
 
-	let t = Common.timer "code generation" in
-
 	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;
@@ -2490,9 +2516,7 @@ let configure gen =
 		print_endline cmd;
 		if gen.gcon.run_command cmd <> 0 then failwith "Build failed";
 		Sys.chdir old_dir;
-	end;
-
-	t()
+	end
 
 (* end of configure function *)
 
@@ -2634,8 +2658,8 @@ let has_tparam name params = List.exists(fun (n,_,_) -> n = name) params
 
 let rec convert_arg ctx p arg =
 	match arg with
-	| TAny | TType (WSuper, _) -> TPType (mk_type_path ctx ([], "Dynamic") [])
-	| TType (_, jsig) -> TPType (convert_signature ctx p jsig)
+	| TAny | TType (WSuper, _) -> TPType (mk_type_path ctx ([], "Dynamic") [],null_pos)
+	| TType (_, jsig) -> TPType (convert_signature ctx p jsig,null_pos)
 
 and convert_signature ctx p jsig =
 	match jsig with
@@ -2674,7 +2698,7 @@ and convert_signature ctx p jsig =
 			| _ -> assert false in
 			mk_type_path ctx (pack, name ^ "$" ^ String.concat "$" (List.map fst inners)) (List.map (fun param -> convert_arg ctx p param) actual_param)
 	| TObjectInner (pack, inners) -> assert false
-	| TArray (jsig, _) -> mk_type_path ctx (["java"], "NativeArray") [ TPType (convert_signature ctx p jsig) ]
+	| TArray (jsig, _) -> mk_type_path ctx (["java"], "NativeArray") [ TPType (convert_signature ctx p jsig,null_pos) ]
 	| TMethod _ -> JReader.error "TMethod cannot be converted directly into Complex Type"
 	| TTypeParameter s -> (match ctx.jtparams with
 		| cur :: others ->
@@ -2713,9 +2737,10 @@ let convert_param ctx p parent param =
 		in
 		let constraints = List.map (fun s -> if same_sig parent s then (TObject( (["java";"lang"], "Object"), [])) else s) constraints in
 		{
-			tp_name = name;
+			tp_name = name,null_pos;
 			tp_params = [];
-			tp_constraints = List.map (convert_signature ctx p) constraints;
+			tp_constraints = List.map (fun t -> convert_signature ctx p t,null_pos) constraints;
+			tp_meta = [];
 		}
 
 let get_type_path ctx ct = match ct with | CTPath p -> p | _ -> assert false
@@ -2739,12 +2764,12 @@ let convert_java_enum ctx p pe =
 		(* if List.mem JEnum f.jf_flags then *)
 		match f.jf_vmsignature with
 		| TObject( path, [] ) when path = pe.cpath && List.mem JStatic f.jf_flags && List.mem JFinal f.jf_flags ->
-			data := { ec_name = f.jf_name; ec_doc = None; ec_meta = []; ec_args = []; ec_pos = p; ec_params = []; ec_type = None; } :: !data;
+			data := { ec_name = f.jf_name,null_pos; ec_doc = None; ec_meta = []; ec_args = []; ec_pos = p; ec_params = []; ec_type = None; } :: !data;
 		| _ -> ()
 	) pe.cfields;
 
 	EEnum {
-		d_name = jname_to_hx (snd pe.cpath);
+		d_name = jname_to_hx (snd pe.cpath),null_pos;
 		d_doc = None;
 		d_params = []; (* enums never have type parameters *)
 		d_meta = !meta;
@@ -2812,9 +2837,9 @@ let convert_java_enum ctx p pe =
 
 		let kind = match field.jf_kind with
 			| JKField when !readonly ->
-				FProp ("default", "null", Some (convert_signature ctx p field.jf_signature), None)
+				FProp ("default", "null", Some (convert_signature ctx p field.jf_signature,null_pos), None)
 			| JKField ->
-				FVar (Some (convert_signature ctx p field.jf_signature), None)
+				FVar (Some (convert_signature ctx p field.jf_signature,null_pos), None)
 			| JKMethod ->
 				match field.jf_signature with
 				| TMethod (args, ret) ->
@@ -2825,7 +2850,7 @@ let convert_java_enum ctx p pe =
 					let i = ref 0 in
 					let args = List.map (fun s ->
 						incr i;
-						"param" ^ string_of_int !i, false, Some(convert_signature ctx p s), None
+						("param" ^ string_of_int !i,null_pos), false, [], Some(convert_signature ctx p s,null_pos), None
 					) args in
 					let t = Option.map_default (convert_signature ctx p) (mk_type_path ctx ([], "Void") []) ret in
 					cff_meta := (Meta.Overload, [], p) :: !cff_meta;
@@ -2833,15 +2858,17 @@ let convert_java_enum ctx p pe =
 					let types = List.map (function
 						| (name, Some ext, impl) ->
 							{
-								tp_name = name;
+								tp_name = name,null_pos;
 								tp_params = [];
-								tp_constraints = List.map (convert_signature ctx p) (ext :: impl);
+								tp_constraints = List.map (fun t -> convert_signature ctx p t,null_pos) (ext :: impl);
+								tp_meta = [];
 							}
 						| (name, None, impl) ->
 							{
-								tp_name = name;
+								tp_name = name,null_pos;
 								tp_params = [];
-								tp_constraints = List.map (convert_signature ctx p) (impl);
+								tp_constraints = List.map (fun t -> convert_signature ctx p t,null_pos) (impl);
+								tp_meta = [];
 							}
 					) field.jf_types in
 					ctx.jtparams <- old_types;
@@ -2849,7 +2876,7 @@ let convert_java_enum ctx p pe =
 					FFun ({
 						f_params = types;
 						f_args = args;
-						f_type = Some t;
+						f_type = Some (t,null_pos);
 						f_expr = None
 					})
 				| _ -> error "Method signature was expected" p
@@ -2876,7 +2903,7 @@ let convert_java_enum ctx p pe =
 			Printf.printf "\t%s%sfield %s : %s\n" (if List.mem AStatic !cff_access then "static " else "") (if List.mem AOverride !cff_access then "override " else "") cff_name (s_sig field.jf_signature);
 
 		{
-			cff_name = cff_name;
+			cff_name = cff_name,null_pos;
 			cff_doc = cff_doc;
 			cff_pos = cff_pos;
 			cff_meta = cff_meta;
@@ -2938,7 +2965,7 @@ let convert_java_enum ctx p pe =
 			(match jc.csuper with
 				| TObject( (["java";"lang"], "Object"), _ ) -> ()
 				| TObject( (["haxe";"lang"], "HxObject"), _ ) -> meta := (Meta.HxGen,[],p) :: !meta
-				| _ -> flags := HExtends (get_type_path ctx (convert_signature ctx p jc.csuper)) :: !flags
+				| _ -> flags := HExtends (get_type_path ctx (convert_signature ctx p jc.csuper),null_pos) :: !flags
 			);
 
 			List.iter (fun i ->
@@ -2946,9 +2973,9 @@ let convert_java_enum ctx p pe =
 				| TObject ( (["haxe";"lang"], "IHxObject"), _ ) -> meta := (Meta.HxGen,[],p) :: !meta
 				| _ -> flags :=
 					if !is_interface then
-						HExtends (get_type_path ctx (convert_signature ctx p i)) :: !flags
+						HExtends (get_type_path ctx (convert_signature ctx p i),null_pos) :: !flags
 					else
-						HImplements (get_type_path ctx (convert_signature ctx p i)) :: !flags
+						HImplements (get_type_path ctx (convert_signature ctx p i),null_pos) :: !flags
 			) jc.cinterfaces;
 
 			let fields = ref [] in
@@ -2979,7 +3006,7 @@ let convert_java_enum ctx p pe =
 			) jc.cmethods) in
 
 			(EClass {
-				d_name = jname_to_hx (snd jc.cpath);
+				d_name = jname_to_hx (snd jc.cpath),null_pos;
 				d_doc = None;
 				d_params = List.map (convert_param ctx p jc.cpath) jc.ctypes;
 				d_meta = !meta;
@@ -3512,11 +3539,11 @@ let add_java_lib com file std =
 		in
 		match decl with
 			| EClass c ->
-				EClass { c with d_meta = add_meta c.d_name c.d_meta }
+				EClass { c with d_meta = add_meta (fst c.d_name) c.d_meta }
 			| EEnum e ->
-				EEnum { e with d_meta = add_meta e.d_name e.d_meta }
+				EEnum { e with d_meta = add_meta (fst e.d_name) e.d_meta }
 			| EAbstract a ->
-				EAbstract { a with d_meta = add_meta a.d_name a.d_meta }
+				EAbstract { a with d_meta = add_meta (fst a.d_name) a.d_meta }
 			| d -> d
 	in
 	let rec build ctx path p types =
@@ -3577,7 +3604,7 @@ let add_java_lib com file std =
 								let ncls = convert_java_class ctx pos { cls with cmethods = smethods; cfields = sfields; cflags = []; csuper = obj; cinterfaces = []; cinner_types = []; ctypes = [] } in
 								match ncls with
 								| EClass c :: imports ->
-									(EClass { c with d_name = c.d_name ^ "_Statics" }, pos) :: inner @ List.map (fun i -> i,pos) imports
+									(EClass { c with d_name = (fst c.d_name ^ "_Statics"),snd c.d_name }, pos) :: inner @ List.map (fun i -> i,pos) imports
 								| _ -> assert false
 							with | Not_found ->
 								inner
@@ -3586,20 +3613,20 @@ let add_java_lib com file std =
 							List.iter (fun x ->
 								match fst x with
 								| EClass c ->
-									inner_alias := SS.add c.d_name !inner_alias;
+									inner_alias := SS.add (fst c.d_name) !inner_alias;
 								| _ -> ()
 							) inner;
 							let alias_list = ref [] in
 							List.iter (fun x ->
 								match x with
 								| (EClass c, pos) -> begin
-									let parts = String.nsplit c.d_name "_24" in
+									let parts = String.nsplit (fst c.d_name) "_24" in
 									match parts with
 										| _ :: _ ->
 											let alias_name = String.concat "_" parts in
 											if (not (SS.mem alias_name !inner_alias)) && (not (String.exists (snd path) "_24")) then begin
 												let alias_def = ETypedef {
-													d_name = alias_name;
+													d_name = alias_name,null_pos;
 													d_doc = None;
 													d_params = c.d_params;
 													d_meta = [];
@@ -3610,13 +3637,13 @@ let add_java_lib com file std =
 														tparams = List.map (fun tp ->
 															TPType (CTPath {
 																tpackage = [];
-																tname = tp.tp_name;
+																tname = fst tp.tp_name;
 																tparams = [];
 																tsub = None;
-															})
+															},null_pos)
 														) c.d_params;
-														tsub = Some(c.d_name);
-													};
+														tsub = Some(fst c.d_name);
+													},null_pos;
 												} in
 												inner_alias := SS.add alias_name !inner_alias;
 												alias_list := (alias_def, pos) :: !alias_list;

+ 212 - 126
genjs.ml → src/generators/genjs.ml

@@ -1,23 +1,20 @@
 (*
- * Copyright (C)2005-2013 Haxe Foundation
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
+	The 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
@@ -37,15 +34,19 @@ type sourcemap = {
 	mutable print_comma : bool;
 	mutable output_last_col : int;
 	mutable output_current_col : int;
+	mutable current_expr : texpr option;
 }
 
 type ctx = {
 	com : Common.context;
 	buf : Rbuffer.t;
+	chan : out_channel;
 	packages : (string list,unit) Hashtbl.t;
 	smap : sourcemap;
 	js_modern : bool;
 	js_flatten : bool;
+	es_version : int;
+	store_exception_stack : bool;
 	mutable current : tclass;
 	mutable statics : (tclass * string * texpr) list;
 	mutable inits : texpr list;
@@ -65,8 +66,7 @@ type object_store = {
 }
 
 let get_exposed ctx path meta =
-	if not ctx.js_modern then []
-	else try
+	try
 		let (_, args, pos) = Meta.get Meta.Expose meta in
 		(match args with
 			| [ EConst (String s), _ ] -> [s]
@@ -128,44 +128,18 @@ let ident s = if Hashtbl.mem kwds s then "$" ^ s else s
 let check_var_declaration v = if Hashtbl.mem kwds2 v.v_name then v.v_name <- "$" ^ v.v_name
 
 let anon_field s = if Hashtbl.mem kwds s || not (valid_js_ident s) then "'" ^ s ^ "'" else s
-let static_field s =
+let static_field c s =
 	match s with
-	| "length" | "name" -> ".$" ^ s
+	| "length" | "name" when not c.cl_extern || Meta.has Meta.HxGen c.cl_meta-> ".$" ^ s
 	| s -> field s
 
 let has_feature ctx = Common.has_feature ctx.com
 let add_feature ctx = Common.add_feature ctx.com
 
-let handle_newlines ctx str =
-	if ctx.com.debug then
-		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.print_comma <- false;
-				loop next
-			end with Not_found ->
-				ctx.smap.output_current_col <- String.length str - from
-		in
-		loop 0
-	else ()
-
-let spr ctx s =
-	ctx.separator <- false;
-	handle_newlines ctx s;
-	Rbuffer.add_string ctx.buf s
-
-let print ctx =
-	ctx.separator <- false;
-	Printf.kprintf (fun s -> begin
-		handle_newlines ctx s;
-		Rbuffer.add_string ctx.buf s
-	end)
-
 let unsupported p = error "This expression cannot be compiled to Javascript" p
 
-let add_mapping ctx e =
+
+let add_mapping ctx force e =
 	if not ctx.com.debug || e.epos.pmin < 0 then () else
 	let pos = e.epos in
 	let smap = ctx.smap in
@@ -179,8 +153,8 @@ let add_mapping ctx e =
 	in
 	let line, col = Lexer.find_pos pos in
 	let line = line - 1 in
-	let col = col - 1 in
-	if smap.source_last_file != file || smap.source_last_line != line || smap.source_last_col != col then begin
+	if force || smap.source_last_file != file || smap.source_last_line != line || smap.source_last_col != col then begin
+		smap.current_expr <- Some e;
 		if smap.print_comma then
 			Rbuffer.add_char smap.mappings ','
 		else
@@ -227,6 +201,39 @@ let add_mapping ctx e =
 		smap.output_last_col <- smap.output_current_col
 	end
 
+let handle_newlines ctx str =
+	if ctx.com.debug then
+		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;
+				loop next
+			end with Not_found ->
+				ctx.smap.output_current_col <- ctx.smap.output_current_col + (String.length str - from);
+		in
+		loop 0
+	else ()
+
+let flush ctx =
+	Rbuffer.output_buffer ctx.chan ctx.buf;
+	Rbuffer.clear ctx.buf
+
+let spr ctx s =
+	ctx.separator <- false;
+	handle_newlines ctx s;
+	Rbuffer.add_string ctx.buf s
+
+let print ctx =
+	ctx.separator <- false;
+	Printf.kprintf (fun s -> begin
+		handle_newlines ctx s;
+		Rbuffer.add_string ctx.buf s
+	end)
+
 let write_mappings ctx =
 	let basefile = Filename.basename ctx.com.file in
 	print ctx "\n//# sourceMappingURL=%s.map" basefile;
@@ -256,7 +263,7 @@ let write_mappings ctx =
 
 let newline ctx =
 	match Rbuffer.nth ctx.buf (Rbuffer.length ctx.buf - 1) with
-	| '}' | '{' | ':' when not ctx.separator -> print ctx "\n%s" ctx.tabs
+	| '}' | '{' | ':' | ';' when not ctx.separator -> print ctx "\n%s" ctx.tabs
 	| _ -> print ctx ";\n%s" ctx.tabs
 
 let newprop ctx =
@@ -430,6 +437,8 @@ let rec gen_call ctx e el in_value =
 		else match eelse with
 			| [] -> ()
 			| e :: _ -> gen_value ctx e)
+	| TLocal { v_name = "__rethrow__" }, [] ->
+		spr ctx "throw $hx_rethrow";
 	| TLocal { v_name = "__resources__" }, [] ->
 		spr ctx "[";
 		concat ctx "," (fun (name,data) ->
@@ -462,8 +471,8 @@ let rec gen_call ctx e el in_value =
 		spr ctx ")"
 
 and gen_expr ctx e =
-	add_mapping ctx e;
-	match e.eexpr with
+	add_mapping ctx false e;
+	(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) ->
@@ -502,22 +511,26 @@ and gen_expr ctx e =
 			gen_value ctx x;
 			print ctx ",";
 			gen_value ctx x;
-			print ctx "%s)" (field f.cf_name)
+			print ctx "%s)" (if Meta.has Meta.SelfCall f.cf_meta then "" else (field f.cf_name))
 		| _ ->
 			print ctx "($_=";
 			gen_value ctx x;
-			print ctx ",$bind($_,$_%s))" (field f.cf_name))
+			print ctx ",$bind($_,$_%s))" (if Meta.has Meta.SelfCall f.cf_meta then "" else (field f.cf_name)))
 	| TEnumParameter (x,_,i) ->
 		gen_value ctx x;
 		print ctx "[%i]" (i + 2)
-	| TField ({ eexpr = TConst (TInt _ | TFloat _) } as x,f) ->
-		gen_expr ctx { e with eexpr = TField(mk (TParenthesis x) x.etype x.epos,f) }
 	| TField (x, (FInstance(_,_,f) | FStatic(_,f) | FAnon(f))) when Meta.has Meta.SelfCall f.cf_meta ->
 		gen_value ctx x;
 	| 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}
+			| _ -> e
+		in
+		let x = skip x in
 		gen_value ctx x;
 		let name = field_name f in
-		spr ctx (match f with FStatic _ -> static_field name | FEnum _ | FInstance _ | FAnon _ | FDynamic _ | FClosure _ -> field name)
+		spr ctx (match f with FStatic(c,_) -> static_field c name | FEnum _ | FInstance _ | FAnon _ | FDynamic _ | FClosure _ -> field name)
 	| TTypeExpr t ->
 		spr ctx (ctx.type_accessor t)
 	| TParenthesis e ->
@@ -588,7 +601,7 @@ and gen_expr ctx e =
 		spr ctx "if";
 		gen_value ctx cond;
 		spr ctx " ";
-		gen_expr ctx e;
+		gen_expr ctx (mk_block e);
 		(match eelse with
 		| None -> ()
 		| Some e2 ->
@@ -597,7 +610,7 @@ and gen_expr ctx e =
 			| _ -> ());
 			semicolon ctx;
 			spr ctx " else ";
-			gen_expr ctx e2);
+			gen_expr ctx (match e2.eexpr with | TIf _ -> e2 | _ -> mk_block e2));
 	| TUnop (op,Ast.Prefix,e) ->
 		spr ctx (Ast.s_unop op);
 		gen_value ctx e
@@ -622,7 +635,7 @@ and gen_expr ctx e =
 	| TObjectDecl fields ->
 		spr ctx "{ ";
 		concat ctx ", " (fun (f,e) -> (match e.eexpr with
-			| TMeta((Meta.QuotedField,_,_),e) -> print ctx "'%s' : " f;
+			| TMeta((Meta.QuotedField,_,_),e) -> print ctx "'%s' : " (Ast.s_escape f);
 			| _ -> print ctx "%s : " (anon_field f));
 			gen_value ctx e
 		) fields;
@@ -664,14 +677,47 @@ and gen_expr ctx e =
 		let last = ref false in
 		let else_block = ref false in
 
-		if (has_feature ctx "haxe.CallStack.exceptionStack") then begin
+		if ctx.store_exception_stack then begin
 			newline ctx;
 			print ctx "%s.lastException = %s" (ctx.type_accessor (TClassDecl { null_class with cl_path = ["haxe"],"CallStack" })) vname
 		end;
 
+		if (has_feature ctx "js.Lib.rethrow") then begin
+			let has_rethrow (_,e) =
+				let rec loop e = match e.eexpr with
+				| TCall({eexpr = TLocal {v_name = "__rethrow__"}}, []) -> raise Exit
+				| _ -> Type.iter loop e
+				in
+				try (loop e; false) with Exit -> true
+			in
+			if List.exists has_rethrow catchs then begin
+				newline ctx;
+				print ctx "var $hx_rethrow = %s" vname;
+			end
+		end;
+
 		if (has_feature ctx "js.Boot.HaxeError") then begin
-			newline ctx;
-			print ctx "if (%s instanceof %s) %s = %s.val" vname (ctx.type_accessor (TClassDecl { null_class with cl_path = ["js";"_Boot"],"HaxeError" })) vname vname;
+			let catch_var_used =
+				try
+					List.iter (fun (v,e) ->
+						match follow v.v_type with
+						| TDynamic _ -> (* Dynamic catch - unrap if the catch value is used *)
+							let rec loop e = match e.eexpr with
+							| TLocal v2 when v2 == v -> raise Exit
+							| _ -> Type.iter loop e
+							in
+							loop e
+						| _ -> (* not a Dynamic catch - we need to unwrap the error for type-checking *)
+							raise Exit
+					) catchs;
+					false
+				with Exit ->
+					true
+			in
+			if catch_var_used then begin
+				newline ctx;
+				print ctx "if (%s instanceof %s) %s = %s.val" vname (ctx.type_accessor (TClassDecl { null_class with cl_path = ["js";"_Boot"],"HaxeError" })) vname vname;
+			end;
 		end;
 
 		List.iter (fun (v,e) ->
@@ -763,7 +809,8 @@ and gen_expr ctx e =
 		gen_expr ctx e1;
 		spr ctx " , ";
 		spr ctx (ctx.type_accessor t);
-		spr ctx ")"
+		spr ctx ")");
+	ctx.smap.current_expr <- None;
 
 
 and gen_block_element ?(after=false) ctx e =
@@ -787,7 +834,7 @@ and gen_block_element ?(after=false) ctx e =
 		if after then newline ctx
 
 and gen_value ctx e =
-	add_mapping ctx e;
+	add_mapping ctx false e;
 	let assign e =
 		mk (TBinop (Ast.OpAssign,
 			mk (TLocal (match ctx.in_value with None -> assert false | Some v -> v)) t_dynamic e.epos,
@@ -796,7 +843,7 @@ and gen_value ctx e =
 	in
 	let value() =
 		let old = ctx.in_value, ctx.in_loop in
-		let r = alloc_var "$r" t_dynamic in
+		let r = alloc_var "$r" t_dynamic e.epos in
 		ctx.in_value <- Some r;
 		ctx.in_loop <- false;
 		spr ctx "(function($this) ";
@@ -816,7 +863,7 @@ and gen_value ctx e =
 			print ctx "(%s))" (this ctx)
 		)
 	in
-	match e.eexpr with
+	(match e.eexpr with
 	| TConst _
 	| TLocal _
 	| TArray _
@@ -898,7 +945,9 @@ and gen_value ctx e =
 		gen_expr ctx (mk (TTry (block (assign b),
 			List.map (fun (v,e) -> v, block (assign e)) catchs
 		)) e.etype e.epos);
-		v()
+		v());
+	ctx.smap.current_expr <- None
+
 
 let generate_package_create ctx (p,_) =
 	let rec loop acc = function
@@ -933,6 +982,11 @@ let check_field_name c f =
 		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);
 	| _ -> ()
 
+(* convert a.b.c to ["a"]["b"]["c"] *)
+let path_to_brackets path =
+	let parts = ExtString.String.nsplit path "." in
+	"[\"" ^ (String.concat "\"][\"" parts) ^ "\"]"
+
 let gen_class_static_field ctx c f =
 	match f.cf_expr with
 	| None | Some { eexpr = TConst TNull } when not (has_feature ctx "Type.getClassFields") ->
@@ -940,16 +994,16 @@ let gen_class_static_field ctx c f =
 	| None when is_extern_field f ->
 		()
 	| None ->
-		print ctx "%s%s = null" (s_path ctx c.cl_path) (static_field f.cf_name);
+		print ctx "%s%s = null" (s_path ctx c.cl_path) (static_field c f.cf_name);
 		newline ctx
 	| Some e ->
 		match e.eexpr with
 		| TFunction _ ->
-			let path = (s_path ctx c.cl_path) ^ (static_field f.cf_name) in
-			let dot_path = (dot_path c.cl_path) ^ (static_field f.cf_name) in
+			let path = (s_path ctx c.cl_path) ^ (static_field c f.cf_name) in
+			let dot_path = (dot_path c.cl_path) ^ (static_field c f.cf_name) in
 			ctx.id_counter <- 0;
 			print ctx "%s = " path;
-			(match (get_exposed ctx dot_path f.cf_meta) with [s] -> print ctx "$hx_exports.%s = " s | _ -> ());
+			(match (get_exposed ctx dot_path f.cf_meta) with [s] -> print ctx "$hx_exports%s = " (path_to_brackets s) | _ -> ());
 			gen_value ctx e;
 			newline ctx;
 		| _ ->
@@ -1002,7 +1056,7 @@ let generate_class ctx c =
 		print ctx "%s = " p
 	else
 		print ctx "%s = $hxClasses[\"%s\"] = " p (dot_path c.cl_path);
-	(match (get_exposed ctx (dot_path c.cl_path) c.cl_meta) with [s] -> print ctx "$hx_exports.%s = " s | _ -> ());
+	(match (get_exposed ctx (dot_path c.cl_path) c.cl_meta) with [s] -> print ctx "$hx_exports%s = " (path_to_brackets s) | _ -> ());
 	(match c.cl_kind with
 		| KAbstractImpl _ ->
 			(* abstract implementations only contain static members and don't need to have constructor functions *)
@@ -1076,7 +1130,8 @@ let generate_class ctx c =
 		print ctx "\n}";
 		(match c.cl_super with None -> ctx.separator <- true | _ -> print ctx ")");
 		newline ctx
-	end
+	end;
+	flush ctx
 
 let generate_enum ctx e =
 	let p = s_path ctx e.e_path in
@@ -1124,15 +1179,17 @@ let generate_enum ctx e =
 		print ctx "%s.__empty_constructs__ = [%s]" p (String.concat "," (List.map (fun s -> Printf.sprintf "%s.%s" p s) ctors_without_args));
 		newline ctx
 	end;
-	match Codegen.build_metadata ctx.com (TEnumDecl e) with
+	begin match Codegen.build_metadata ctx.com (TEnumDecl e) with
 	| None -> ()
 	| Some e ->
 		print ctx "%s.__meta__ = " p;
 		gen_expr ctx e;
 		newline ctx
+	end;
+	flush ctx
 
 let generate_static ctx (c,f,e) =
-	print ctx "%s%s = " (s_path ctx c.cl_path) (static_field f);
+	print ctx "%s%s = " (s_path ctx c.cl_path) (static_field c f);
 	gen_value ctx e;
 	newline ctx
 
@@ -1188,6 +1245,7 @@ let alloc_ctx com =
 	let ctx = {
 		com = com;
 		buf = Rbuffer.create 16000;
+		chan = open_out_bin com.file;
 		packages = Hashtbl.create 0;
 		smap = {
 			source_last_line = 0;
@@ -1199,9 +1257,12 @@ let alloc_ctx com =
 			sources = DynArray.create();
 			sources_hash = Hashtbl.create 0;
 			mappings = Rbuffer.create 16;
+			current_expr = None;
 		};
 		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);
+		store_exception_stack = if Common.has_dce com then (Common.has_feature com "haxe.CallStack.exceptionStack") else List.exists (function TClassDecl { cl_path=["haxe"],"CallStack" } -> true | _ -> false) com.types;
 		statics = [];
 		inits = [];
 		current = null_class;
@@ -1232,22 +1293,23 @@ let gen_single_expr ctx e expr =
 	str
 
 let generate com =
-	let t = Common.timer "generate js" in
 	(match com.js_gen with
 	| Some g -> g()
 	| None ->
 	let ctx = alloc_ctx com in
-
+	Codegen.map_source_header com (fun s -> print ctx "// %s\n" s);
 	if has_feature ctx "Class" || has_feature ctx "Type.getClassName" then add_feature ctx "js.Boot.isClass";
 	if has_feature ctx "Enum" || has_feature ctx "Type.getEnumName" then add_feature ctx "js.Boot.isEnum";
 
+	let nodejs = Common.raw_defined com "nodejs" in
+
 	let exposed = List.concat (List.map (fun t ->
 		match t with
 			| TClassDecl c ->
 				let path = dot_path c.cl_path in
 				let class_exposed = get_exposed ctx path c.cl_meta in
 				let static_exposed = List.map (fun f ->
-					get_exposed ctx (path ^ static_field f.cf_name) f.cf_meta
+					get_exposed ctx (path ^ static_field c f.cf_name) f.cf_meta
 				) c.cl_ordered_statics in
 				List.concat (class_exposed :: static_exposed)
 			| _ -> []
@@ -1274,6 +1336,26 @@ let generate com =
 		in loop parts "";
 	)) exposed;
 
+	let include_files = List.rev com.include_files in
+
+	List.iter (fun file ->
+		match file with
+		| path, "top" ->
+			let file_content = Std.input_file ~bin:true (fst file) in
+			print ctx "%s\n" file_content;
+			()
+		| _ -> ()
+	) include_files;
+
+	let var_console = (
+		"console",
+		"typeof console != \"undefined\" ? console : {log:function(){}}"
+	) in
+
+	let var_exports = (
+		"$hx_exports",
+		"typeof exports != \"undefined\" ? exports : typeof window != \"undefined\" ? window : typeof self != \"undefined\" ? self : this"
+	) in
 
 	let var_global = (
 		"$global",
@@ -1287,59 +1369,63 @@ let generate com =
 		closureArgs
 	in
 	let closureArgs = if (anyExposed && not (Common.defined com Define.ShallowExpose)) then
-		(
-			"$hx_exports",
-			(* TODO(bruno): Remove runtime branching when standard node haxelib is available *)
-			"typeof window != \"undefined\" ? window : exports"
-		) :: closureArgs
+		var_exports :: closureArgs
 	else
 		closureArgs
 	in
 	(* Provide console for environments that may not have it. *)
-	let closureArgs = if (not (Common.defined com Define.JsEs5)) then
-		(
-			"console",
-			"typeof console != \"undefined\" ? console : {log:function(){}}"
-		) :: closureArgs
+	let closureArgs = if ctx.es_version < 5 then
+		var_console :: closureArgs
 	else
 		closureArgs
 	in
 
-	if Common.raw_defined com "nodejs" then
+	if nodejs then
 		(* Add node globals to pseudo-keywords, so they are not shadowed by local vars *)
 		List.iter (fun s -> Hashtbl.replace kwds2 s ()) [ "global"; "process"; "__filename"; "__dirname"; "module" ];
 
+	if (anyExposed && ((Common.defined com Define.ShallowExpose) || not ctx.js_modern)) then (
+		print ctx "var %s = %s" (fst var_exports) (snd var_exports);
+		ctx.separator <- true;
+		newline ctx
+	);
+
 	if ctx.js_modern then begin
 		(* Additional ES5 strict mode keywords. *)
 		List.iter (fun s -> Hashtbl.replace kwds s ()) [ "arguments"; "eval" ];
 
 		(* Wrap output in a closure *)
-		if (anyExposed && (Common.defined com Define.ShallowExpose)) then (
-			print ctx "var $hx_exports = $hx_exports || {}";
-			ctx.separator <- true;
-			newline ctx
-		);
 		print ctx "(function (%s) { \"use strict\"" (String.concat ", " (List.map fst closureArgs));
 		newline ctx;
-		let rec print_obj f root = (
-			let path = root ^ "." ^ f.os_name in
-			print ctx "%s = %s || {}" path path;
-			ctx.separator <- true;
-			newline ctx;
-			concat ctx ";" (fun g -> print_obj g path) f.os_fields
-		)
-		in
-		List.iter (fun f -> print_obj f "$hx_exports") exposedObject.os_fields;
 	end;
 
+	let rec print_obj f root = (
+		let path = root ^ (path_to_brackets f.os_name) in
+		print ctx "%s = %s || {}" path path;
+		ctx.separator <- true;
+		newline ctx;
+		concat ctx ";" (fun g -> print_obj g path) f.os_fields
+	)
+	in
+	List.iter (fun f -> print_obj f "$hx_exports") exposedObject.os_fields;
+
+	List.iter (fun file ->
+		match file with
+		| path, "closure" ->
+			let file_content = Std.input_file ~bin:true (fst file) in
+			print ctx "%s\n" file_content;
+			()
+		| _ -> ()
+	) include_files;
+
 	(* If ctx.js_modern, console is defined in closureArgs. *)
-	if (not ctx.js_modern) && (not (Common.defined com Define.JsEs5)) then
+	if (not ctx.js_modern) && (ctx.es_version < 5) then
 		add_feature ctx "js.Lib.global"; (* console polyfill will check console from $global *)
 
 	if (not ctx.js_modern) && (has_feature ctx "js.Lib.global") then
 		print ctx "var %s = %s;\n" (fst var_global) (snd var_global);
 
-	if (not ctx.js_modern) && (not (Common.defined com Define.JsEs5)) then
+	if (not ctx.js_modern) && (ctx.es_version < 5) then
 		spr ctx "var console = $global.console || {log:function(){}};\n";
 
 	(* TODO: fix $estr *)
@@ -1400,22 +1486,22 @@ let generate com =
 	if ctx.js_modern then begin
 		print ctx "})(%s)" (String.concat ", " (List.map snd closureArgs));
 		newline ctx;
-		if (anyExposed && (Common.defined com Define.ShallowExpose)) then (
-			List.iter (fun f ->
-				print ctx "var %s = $hx_exports.%s" f.os_name f.os_name;
-				ctx.separator <- true;
-				newline ctx
-			) exposedObject.os_fields;
-			List.iter (fun f ->
-				print ctx "var %s = $hx_exports.%s" f f;
-				ctx.separator <- true;
-				newline ctx
-			) !toplevelExposed
-		);
 	end;
+
+	if (anyExposed && (Common.defined com Define.ShallowExpose)) then (
+		List.iter (fun f ->
+			print ctx "var %s = $hx_exports%s" f.os_name (path_to_brackets f.os_name);
+			ctx.separator <- true;
+			newline ctx
+		) exposedObject.os_fields;
+		List.iter (fun f ->
+			print ctx "var %s = $hx_exports%s" f (path_to_brackets f);
+			ctx.separator <- true;
+			newline ctx
+		) !toplevelExposed
+	);
+
 	if com.debug then write_mappings ctx else (try Sys.remove (com.file ^ ".map") with _ -> ());
-	let ch = open_out_bin com.file in
-	Rbuffer.output_buffer ch ctx.buf;
-	close_out ch);
-	t()
+	flush ctx;
+	close_out ctx.chan)
 

+ 1852 - 0
src/generators/genlua.ml

@@ -0,0 +1,1852 @@
+(*
+ * Copyright (C)2005-2013 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 Ast
+open Type
+open Common
+open ExtList
+
+type pos = Ast.pos
+
+type ctx = {
+	com : Common.context;
+	buf : Buffer.t;
+	packages : (string list,unit) Hashtbl.t;
+	mutable current : tclass;
+	mutable statics : (tclass * string * texpr) list;
+	mutable inits : texpr list;
+	mutable tabs : string;
+	mutable in_value : tvar option;
+	mutable in_loop : bool;
+	mutable iife_assign : bool;
+	mutable handle_break : bool;
+	mutable id_counter : int;
+	mutable type_accessor : module_type -> string;
+	mutable separator : bool;
+	mutable found_expose : bool;
+	mutable lua_jit : bool;
+	mutable lua_ver : float;
+}
+
+type object_store = {
+	os_name : string;
+	mutable os_fields : object_store list;
+}
+
+let debug_expression expression  =
+    " --[[ " ^ Type.s_expr_kind expression  ^ " --]] "
+
+let debug_type t  =
+    " --[[ " ^ Type.s_type_kind t  ^ " --]] ";;
+
+let get_exposed ctx path meta = try
+		let (_, args, pos) = Meta.get Meta.Expose meta in
+		(match args with
+			| [ EConst (String s), _ ] -> [s]
+			| [] -> [path]
+			| _ -> error "Invalid @:expose parameters" pos)
+	with Not_found -> []
+
+let dot_path = Ast.s_type_path
+
+let s_path ctx = dot_path
+
+(* TODO: are all these kwds necessary for field quotes *and* id escapes? *)
+let kwds =
+	let h = Hashtbl.create 0 in
+	List.iter (fun s -> Hashtbl.add h s ()) [
+	    "_G"; ""; "and"; "break"; "do"; "else"; "elseif";
+	    "end"; "false"; "for"; "function"; "if";
+	    "in"; "local"; "nil"; "not"; "or"; "repeat";
+	    "return"; "then"; "true"; "until"; "while";
+	    "goto"
+	];
+	h
+
+let valid_lua_ident s =
+	try
+		for i = 0 to String.length s - 1 do
+		    match String.unsafe_get s i with
+		    | 'a'..'z' | 'A'..'Z' | '_' -> ()
+		    | '0'..'9' when i > 0 -> ()
+		    | _ -> raise Exit
+		done;
+		true
+	with Exit ->
+		false
+
+let field s = if Hashtbl.mem kwds s || not (valid_lua_ident s) then "[\"" ^ s ^ "\"]" else "." ^ s
+let ident s = if Hashtbl.mem kwds s then "_" ^ s else s
+
+let anon_field s = if Hashtbl.mem kwds s || not (valid_lua_ident s) then "['" ^ (Ast.s_escape s) ^ "']" else s
+let static_field c s =
+	match s with
+	| "length" | "name" when not c.cl_extern || Meta.has Meta.HxGen c.cl_meta-> "._hx" ^ s
+	| s -> field s
+
+let has_feature ctx = Common.has_feature ctx.com
+let add_feature ctx = Common.add_feature ctx.com
+
+let temp ctx =
+	ctx.id_counter <- ctx.id_counter + 1;
+	"_hx_" ^ string_of_int (ctx.id_counter)
+
+let spr ctx s =
+	ctx.separator <- false;
+	Buffer.add_string ctx.buf s
+
+let print ctx =
+	ctx.separator <- false;
+	Printf.kprintf (fun s -> begin
+		Buffer.add_string ctx.buf s
+	end)
+
+let newline ctx = print ctx "\n%s" ctx.tabs
+
+(* spr with newline *)
+let sprln ctx s = spr ctx s; newline ctx
+
+(* print with newline *)
+let println ctx =
+	ctx.separator <- false;
+	Printf.kprintf (fun s -> begin
+		Buffer.add_string ctx.buf s;
+		newline ctx
+	end)
+
+let unsupported p = error "This expression cannot be compiled to Lua" p
+
+let basename path =
+	try
+		let idx = String.rindex path '/' in
+		String.sub path (idx + 1) (String.length path - idx - 1)
+	with Not_found -> path
+
+(* TODO : is this necessary any more?*)
+let newprop ctx =
+	match Buffer.nth ctx.buf (Buffer.length ctx.buf - 1) with
+	| '{' -> print ctx "\n%s" ctx.tabs
+	| _ -> print ctx "\n%s" ctx.tabs
+
+let semicolon ctx =
+	match Buffer.nth ctx.buf (Buffer.length ctx.buf - 1) with
+	| '}' when not ctx.separator -> ()
+	| _ -> spr ctx ";"
+
+let rec concat ctx s f = function
+	| [] -> ()
+	| [x] -> f x
+	| x :: l ->
+		f x;
+		spr ctx s;
+		concat ctx s f l
+
+let fun_block ctx f p =
+	let e = List.fold_left (fun e (a,c) ->
+		match c with
+		| None | Some TNull -> e
+		| Some c -> Type.concat (Codegen.set_default ctx.com a c p) e
+	) f.tf_expr f.tf_args in
+	e
+
+let open_block ctx =
+	let oldt = ctx.tabs in
+	ctx.tabs <- "  " ^ ctx.tabs;
+	(fun() -> ctx.tabs <- oldt)
+
+let rec iter_switch_break in_switch e =
+	match e.eexpr with
+	| TFunction _ | TWhile _ | TFor _ -> ()
+	| TSwitch _ when not in_switch -> iter_switch_break true e
+	| TBreak when in_switch -> raise Exit
+	| _ -> iter (iter_switch_break in_switch) e
+
+let handle_break ctx e =
+    (* TODO: This got messy. Find a way to unify the implementation with a try/catch helper at least *)
+	let old = ctx.in_loop, ctx.handle_break in
+	ctx.in_loop <- true;
+	try
+		iter_switch_break false e;
+		ctx.handle_break <- false;
+		(fun() ->
+			ctx.in_loop <- fst old;
+			ctx.handle_break <- snd old;
+		)
+	with
+		Exit ->
+			sprln ctx "local _hx_expected_result = {}";
+			sprln ctx "local _hx_status, _hx_result = pcall(function() ";
+			let b = open_block ctx in
+			newline ctx;
+			ctx.handle_break <- true;
+			(fun() ->
+				b();
+				ctx.in_loop <- fst old;
+				ctx.handle_break <- snd old;
+				newline ctx;
+				sprln ctx "end";
+				sprln ctx " return _hx_expected_result end)";
+				spr ctx " if not _hx_status then ";
+				newline ctx;
+				spr ctx " elseif _hx_result ~= _hx_expected_result then return _hx_result";
+			)
+
+let this ctx = match ctx.in_value with None -> "self" | Some _ -> "self"
+
+let is_dynamic_iterator ctx e =
+	let check x =
+		has_feature ctx "HxOverrides.iter" && (match follow x.etype with
+			| TInst ({ cl_path = [],"Array" },_)
+			| TInst ({ cl_kind = KTypeParameter _}, _)
+			| TAnon _
+			| TDynamic _
+			| TMono _ ->
+				true
+			| _ -> false
+		)
+	in
+	match e.eexpr with
+	| TField (x,f) when field_name f = "iterator" -> check x
+	| _ ->
+		false
+
+(* from genphp *)
+let rec is_uncertain_type t =
+	match follow t with
+	| TInst (c, _) -> c.cl_interface
+	| TMono _ -> true
+	| TAnon a ->
+	  (match !(a.a_status) with
+	  | Statics _
+	  | EnumStatics _ -> false
+	  | _ -> true)
+	| TDynamic _ -> true
+	| _ -> false
+
+let is_uncertain_expr e =
+	is_uncertain_type e.etype
+
+let rec is_anonym_type t =
+	match follow t with
+	| TAnon a ->
+	  (match !(a.a_status) with
+	  | Statics _
+	  | EnumStatics _ -> false
+	  | _ -> true)
+	| TDynamic _ -> true
+	| _ -> false
+
+let is_anonym_expr e = is_anonym_type e.etype
+
+let rec is_unknown_type t =
+	match follow t with
+	| TMono r ->
+		(match !r with
+		| None -> true
+		| Some t -> is_unknown_type t)
+	| _ -> false
+
+let is_unknown_expr e =	is_unknown_type e.etype
+
+let rec is_string_type t =
+	match follow t with
+	| TInst ({cl_path = ([], "String")}, _) -> true
+	| TAnon a ->
+	   (match !(a.a_status) with
+	   | Statics ({cl_path = ([], "String")}) -> true
+	   | _ -> false)
+	| TAbstract (a,pl) -> is_string_type (Abstract.get_underlying_type a pl)
+	| _ -> false
+
+let is_string_expr e = is_string_type e.etype
+(* /from genphp *)
+
+let rec is_int_type ctx t =
+    match follow t with
+	| TInst ({cl_path = ([], "Int")}, _) -> true
+	| TAnon a ->
+		(match !(a.a_status) with
+	    | Statics ({cl_path = ([], "Int")}) -> true
+	    | _ -> false)
+		| TAbstract ({a_path = ([],"Float")}, pl) -> false
+	| TAbstract ({a_path = ([],"Int")}, pl) -> true
+	| TAbstract (a,pl) -> is_int_type ctx (Abstract.get_underlying_type a pl)
+	| _ -> false
+
+let rec should_wrap_int_op ctx op e1 e2 =
+    match op with
+    | Ast.OpAdd | Ast.OpMult | Ast.OpDiv | Ast.OpSub | Ast.OpAnd | Ast.OpOr
+    | Ast.OpXor | Ast.OpShl  | Ast.OpShr | Ast.OpUShr ->
+	    is_int_type ctx e1.etype && is_int_type ctx e2.etype
+    | _ -> false
+
+let gen_constant ctx p = function
+	| TInt i -> print ctx "%ld" i
+	| TFloat s -> spr ctx s
+	| TString s -> begin
+	    add_feature ctx "use.string";
+	    print ctx "\"%s\"" (Ast.s_escape s)
+	end
+	| TBool b -> spr ctx (if b then "true" else "false")
+	| TNull -> spr ctx "nil"
+	| TThis -> spr ctx (this ctx)
+	| TSuper -> assert false
+
+let rec gen_call ctx e el in_value =
+	ctx.iife_assign <- true;
+	(match e.eexpr , el with
+	| TConst TSuper , params ->
+		(match ctx.current.cl_super with
+		| None -> error "Missing api.setCurrentClass" e.epos
+		| Some (c,_) ->
+			print ctx "%s.super(%s" (ctx.type_accessor (TClassDecl c)) (this ctx);
+			List.iter (fun p -> print ctx ","; gen_value ctx p) params;
+			spr ctx ")";
+		);
+	| TField ({ eexpr = TConst TSuper },f) , params ->
+		(match ctx.current.cl_super with
+		| None -> error "Missing api.setCurrentClass" e.epos
+		| Some (c,_) ->
+			let name = field_name f in
+			print ctx "%s.prototype%s(%s" (ctx.type_accessor (TClassDecl c)) (field name) (this ctx);
+			List.iter (fun p -> print ctx ","; gen_value ctx p) params;
+			spr ctx ")";
+		);
+	| TCall (x,_) , el when (match x.eexpr with TLocal { v_name = "__lua__" } -> false | _ -> true) ->
+		spr ctx "(";
+		gen_value ctx e;
+		spr ctx ")";
+		spr ctx "(";
+		concat ctx "," (gen_value ctx) el;
+		spr ctx ")";
+	| TLocal { v_name = "__new__" }, { eexpr = TConst (TString cl) } :: params ->
+		print ctx "%s.new(" cl;
+		concat ctx "," (gen_value ctx) params;
+		spr ctx ")";
+	| TLocal { v_name = "__new__" }, e :: params ->
+		gen_value ctx e;
+		spr ctx ".new(";
+		concat ctx "," (gen_value ctx) params;
+		spr ctx ")";
+	| TLocal { v_name = "__callself__" }, { eexpr = TConst (TString head) } :: { eexpr = TConst (TString tail) } :: el ->
+		print ctx "%s:%s" head tail;
+		spr ctx "(";
+		concat ctx ", " (gen_value ctx) el;
+		spr ctx ")";
+	| TLocal { v_name = "__call__" }, { eexpr = TConst (TString code) } :: el ->
+		spr ctx code;
+		spr ctx "(";
+		concat ctx ", " (gen_value ctx) el;
+		spr ctx ")";
+	| TLocal { v_name = "__lua_table__" }, el ->
+		spr ctx "{";
+		concat ctx ", " (gen_value ctx) el;
+		spr ctx "}";
+	| TLocal { v_name = "__lua__" }, [{ eexpr = TConst (TString code) }] ->
+		spr ctx (String.concat "\n" (ExtString.String.nsplit code "\r\n"))
+	| TLocal { v_name = "__lua__" }, { eexpr = TConst (TString code); epos = p } :: tl ->
+		Codegen.interpolate_code ctx.com code tl (spr ctx) (gen_expr ctx) p
+	| TLocal { v_name = "__type__" },  [o] ->
+		spr ctx "type(";
+		gen_value ctx o;
+		spr ctx ")";
+	| TLocal ({v_name = "__define_feature__"}), [_;e] ->
+		gen_expr ctx e
+	| TLocal { v_name = "__feature__" }, { eexpr = TConst (TString f) } :: eif :: eelse ->
+		(if has_feature ctx f then
+			gen_value ctx eif
+		else match eelse with
+			| [] -> ()
+			| e :: _ -> gen_value ctx e)
+	| TLocal { v_name = "__resources__" }, [] ->
+		(* TODO: Array declaration helper *)
+		spr ctx "_hx_tabArray({";
+		let count = ref 0 in
+		concat ctx "," (fun (name,data) ->
+			if (!count == 0) then spr ctx "[0]=";
+			spr ctx "{ ";
+			spr ctx "name = ";
+			gen_constant ctx e.epos (TString name);
+			spr ctx ", data = ";
+			gen_constant ctx e.epos (TString (Codegen.bytes_serialize data));
+			spr ctx "}";
+			incr count
+		) (Hashtbl.fold (fun name data acc -> (name,data) :: acc) ctx.com.resources []);
+		print ctx "}, %i)" !count;
+	| TLocal { v_name = "`trace" }, [e;infos] ->
+		if has_feature ctx "haxe.Log.trace" then begin
+			let t = (try List.find (fun t -> t_path t = (["haxe"],"Log")) ctx.com.types with _ -> assert false) in
+			spr ctx (ctx.type_accessor t);
+			spr ctx ".trace(";
+			gen_value ctx e;
+			spr ctx ",";
+			gen_value ctx infos;
+			spr ctx ")";
+		end else begin
+			spr ctx "_hx_print(";
+			gen_value ctx e;
+			spr ctx ")";
+		end
+	| TField ( { eexpr = TConst(TInt _ | TFloat _| TString _| TBool _) } as e , ((FInstance _ | FAnon _) as ef)), el ->
+		spr ctx ("(");
+		gen_value ctx e;
+		print ctx ("):%s(") (field_name ef);
+		concat ctx "," (gen_value ctx) el;
+		spr ctx ")";
+	| TField (e, ((FInstance _ | FAnon _ | FDynamic _) as ef)), el ->
+		let s = (field_name ef) in
+		if Hashtbl.mem kwds s || not (valid_lua_ident s) then begin
+		    match e.eexpr with
+		    |TNew _-> (
+			spr ctx "_hx_apply_self(";
+			gen_value ctx e;
+			print ctx ",\"%s\"" (field_name ef);
+			if List.length(el) > 0 then spr ctx ",";
+			concat ctx "," (gen_value ctx) el;
+			spr ctx ")";
+		    );
+		    |_ -> (
+			gen_value ctx e;
+			print ctx "%s(" (anon_field s);
+			concat ctx "," (gen_value ctx) (e::el);
+			spr ctx ")"
+		    )
+		end else begin
+		    gen_value ctx e;
+		    print ctx ":%s(" (field_name ef);
+		    concat ctx "," (gen_value ctx) el;
+		    spr ctx ")"
+		end;
+	| _ ->
+		gen_value ctx e;
+		spr ctx "(";
+		concat ctx "," (gen_value ctx) el;
+		spr ctx ")");
+	ctx.iife_assign <- false;
+
+and gen_expr ?(local=true) ctx e = begin
+	match e.eexpr with
+	 TConst c ->
+		gen_constant ctx e.epos c;
+	| TLocal v when v.v_name = "this" ->
+		spr ctx "self";
+	| TLocal v -> spr ctx (ident v.v_name)
+	| TArray (e1,{ eexpr = TConst (TString s) }) when valid_lua_ident s && (match e1.eexpr with TConst (TInt _|TFloat _) -> false | _ -> true) ->
+		gen_value ctx e1;
+		spr ctx (field s)
+	| TArray (e1,e2) ->
+		gen_value ctx e1;
+		spr ctx "[";
+		gen_value ctx e2;
+		spr ctx "]";
+	| TBinop (op,e1,e2) ->
+		gen_tbinop ctx op e1 e2;
+	| TField (x,f) when field_name f = "iterator" && is_dynamic_iterator ctx e ->
+		add_feature ctx "use._iterator";
+		print ctx "_iterator(";
+		gen_value ctx x;
+		print ctx ")";
+	| TField (x,FClosure (_,f)) ->
+		add_feature ctx "use._hx_bind";
+		(match x.eexpr with
+		| TConst _ | TLocal _ ->
+			print ctx "_hx_bind(";
+			gen_value ctx x;
+			print ctx ",";
+			gen_value ctx x;
+			print ctx "%s)" (if Meta.has Meta.SelfCall f.cf_meta then "" else (field f.cf_name))
+		| _ ->
+			print ctx "(function() local __=";
+			gen_value ctx x;
+			print ctx "; return _hx_bind(__,__%s) end)()" (if Meta.has Meta.SelfCall f.cf_meta then "" else (field f.cf_name)))
+	| TEnumParameter (x,_,i) ->
+		gen_value ctx x;
+		print ctx "[%i]" (i + 2)
+	| TField (x, (FInstance(_,_,f) | FStatic(_,f) | FAnon(f))) when Meta.has Meta.SelfCall f.cf_meta ->
+		gen_value ctx x;
+	| TField ({ eexpr = TConst(TInt _ | TFloat _| TString _| TBool _) } as e , ((FInstance _ | FAnon _) as ef)) ->
+		spr ctx ("(");
+		gen_value ctx e;
+		print ctx (").%s") (field_name ef);
+	| TField ({ eexpr = TConst (TInt _ | TFloat _) } as x,f) ->
+		gen_expr ctx { e with eexpr = TField(mk (TParenthesis x) x.etype x.epos,f) }
+	| TField ({ eexpr = TObjectDecl fields }, ef ) ->
+		spr ctx "(function(x) return x.";
+		print ctx "%s" (field_name ef);
+		spr ctx " end )({";
+		concat ctx ", " (fun (f,e) -> print ctx "%s = " (anon_field f); gen_value ctx e) fields;
+		spr ctx "})";
+	| TField (x,f) ->
+		gen_value ctx x;
+		let name = field_name f in
+		spr ctx (match f with FStatic _ | FEnum _ | FInstance _ | FAnon _ | FDynamic _ | FClosure _ -> field name)
+	| TTypeExpr t ->
+		spr ctx (ctx.type_accessor t)
+	| TParenthesis e ->
+		spr ctx "(";
+		gen_value ctx e;
+		spr ctx ")";
+	| TMeta (_,e) ->
+		gen_expr ctx e
+	| TReturn eo -> gen_return ctx e eo;
+	| TBreak ->
+		if not ctx.in_loop then unsupported e.epos;
+		if ctx.handle_break then spr ctx "_G.error(\"_hx__break__\")" else spr ctx "break" (*todo*)
+	| TContinue ->
+		if not ctx.in_loop then unsupported e.epos;
+		spr ctx "goto _hx_continue";
+	| TBlock el ->
+		let bend = open_block ctx in
+		List.iter (gen_block_element ctx) el;
+		bend();
+		newline ctx;
+	| TFunction f ->
+		let old = ctx.in_value, ctx.in_loop in
+		ctx.in_value <- None;
+		ctx.in_loop <- false;
+		print ctx "function(%s) " (String.concat "," (List.map ident (List.map arg_name f.tf_args)));
+		let fblock = fun_block ctx f e.epos in
+		(match fblock.eexpr with
+		| TBlock el ->
+		    let bend = open_block ctx in
+		    List.iter (gen_block_element ctx) el;
+		    bend();
+		    newline ctx;
+		|_ -> ());
+		spr ctx "end";
+		ctx.in_value <- fst old;
+		ctx.in_loop <- snd old;
+		ctx.separator <- true
+	| TCall (e,el) ->
+		begin
+		    gen_call ctx e el false;
+		end;
+	| TArrayDecl el ->
+		spr ctx "_hx_tabArray({";
+		let count = ref 0 in
+		List.iteri (fun i e ->
+		    incr count;
+		    if (i == 0) then spr ctx "[0]="
+		    else spr ctx ", ";
+		    gen_value ctx e) el;
+		print ctx " }, %i)" !count;
+	| TThrow e ->
+		spr ctx "_G.error(";
+		gen_value ctx e;
+		spr ctx ",0)";
+	| TVar (v,eo) ->
+		begin match eo with
+			| None ->
+				if local then
+				    spr ctx "local ";
+				spr ctx (ident v.v_name);
+			| Some e ->
+				match e.eexpr with
+				| TBinop(OpAssign, e1, e2) ->
+				    gen_tbinop ctx OpAssign e1 e2;
+				    if local then
+					spr ctx " local ";
+				    spr ctx (ident v.v_name);
+				    spr ctx " = ";
+				    gen_value ctx e1;
+				    semicolon ctx;
+
+				| _ ->
+				    if local then
+					spr ctx "local ";
+				    spr ctx (ident v.v_name);
+				    spr ctx " = ";
+				    gen_value ctx e;
+				    semicolon ctx;
+		end
+	| TNew (c,_,el) ->
+		(match c.cl_constructor with
+		| Some cf when Meta.has Meta.SelfCall cf.cf_meta -> ()
+		| _ -> print ctx "%s.new" (ctx.type_accessor (TClassDecl c)));
+		spr ctx "(";
+		concat ctx "," (gen_value ctx) el;
+		spr ctx ")"
+	| TIf (cond,e,eelse) ->
+		ctx.iife_assign <- true;
+		spr ctx "if ";
+		gen_cond ctx cond;
+		spr ctx " then ";
+		let bend = open_block ctx in
+		gen_block_element ctx e;
+		bend();
+		newline ctx;
+		(match eelse with
+		| None -> ();
+		| Some e2 ->
+			(match e.eexpr with
+			| TObjectDecl _ -> ctx.separator <- false
+			| _ ->());
+			spr ctx "else";
+			let bend = open_block ctx in
+			gen_block_element ctx e2;
+			bend();
+			newline ctx);
+		spr ctx "end";
+		ctx.iife_assign <- false;
+	| TUnop ((Increment|Decrement) as op,unop_flag, e) ->
+		(* TODO: Refactor this mess *)
+		sprln ctx "(function() ";
+		(match e.eexpr, unop_flag with
+		    | TArray(e1,e2), _ ->
+			spr ctx "local _hx_idx = "; gen_value ctx e2; semicolon ctx; newline ctx;
+			spr ctx "local _hx_arr ="; gen_value ctx e1; semicolon ctx; newline ctx;
+			(match unop_flag with
+			    | Ast.Postfix ->
+				    spr ctx "local _ = _hx_arr[_hx_idx]"; semicolon ctx; newline ctx;
+			    | _ -> ());
+			spr ctx "_hx_arr[_hx_idx] = _hx_arr[_hx_idx]";
+		    | TField(e1,e2), _ ->
+			spr ctx "local _hx_obj = "; gen_value ctx e1; semicolon ctx; newline ctx;
+			spr ctx "local _hx_fld = ";
+			(match e2 with
+			| FInstance(_,_,fld)
+			| FStatic(_,fld)
+			| FAnon fld
+			| FClosure(_,fld) ->
+				print ctx "'%s'" fld.cf_name;
+			| FDynamic name ->
+				print ctx "'%s'" name;
+			| FEnum(_,efld) ->
+				print ctx "'%s'" efld.ef_name);
+			semicolon ctx; newline ctx;
+			(match unop_flag with
+			    | Ast.Postfix ->
+				    spr ctx "local _ = _hx_obj[_hx_fld]"; semicolon ctx; newline ctx;
+			    | _ -> ());
+			spr ctx "_hx_obj[_hx_fld] = _hx_obj[_hx_fld] ";
+		    | _, Ast.Prefix ->
+			gen_value ctx e;
+			spr ctx " = ";
+			gen_value ctx e;
+		    | _, Ast.Postfix ->
+			spr ctx "local _ = ";
+			gen_value ctx e; semicolon ctx;
+			gen_value ctx e;
+			spr ctx " = ";
+			gen_value ctx e);
+		(match op with
+		    |Increment -> spr ctx " + 1;"
+		    |Decrement -> spr ctx " - 1;"
+		    |_-> print ctx " %s 1;" (Ast.s_unop op));
+		newline ctx;
+		spr ctx " return ";
+		(match unop_flag, e.eexpr with
+		    | Ast.Postfix, _ ->
+			    spr ctx "_";
+		    | _, TArray(e1,e2) ->
+			    spr ctx "_hx_arr[_hx_idx]";
+		    | _, TField(e1,e2) ->
+			    spr ctx "_hx_obj[_hx_fld]";
+		    | _, _ ->
+			    gen_value ctx e;
+		    );
+		semicolon ctx; newline ctx;
+		spr ctx " end)()";
+	| TUnop (Not,unop_flag,e) ->
+		spr ctx "not ";
+		gen_value ctx e;
+	| TUnop (NegBits,unop_flag,e) ->
+		spr ctx "_hx_bit.bnot(";
+		gen_value ctx e;
+		spr ctx ")";
+	| TUnop (op,Ast.Prefix,e) ->
+		spr ctx (Ast.s_unop op);
+		gen_value ctx e
+	| TUnop (op,Ast.Postfix,e) ->
+		gen_value ctx e;
+		spr ctx (Ast.s_unop op)
+	| TWhile (cond,e,Ast.NormalWhile) ->
+		let handle_break = handle_break ctx e in
+		spr ctx "while ";
+		gen_cond ctx cond;
+		spr ctx " do ";
+		let b = open_block ctx in
+		gen_block_element ctx e;
+		b();
+		if has_continue e then begin
+		    newline ctx;
+		    spr ctx "::_hx_continue::";
+		end;
+		newline ctx;
+		handle_break();
+		newline ctx;
+		spr ctx "end";
+	| TWhile (cond,e,Ast.DoWhile) ->
+		let handle_break = handle_break ctx e in
+		spr ctx "while true do ";
+		gen_block_element ctx e;
+		newline ctx;
+		spr ctx " while ";
+		gen_cond ctx cond;
+		spr ctx " do ";
+		gen_block_element ctx e;
+		handle_break();
+		newline ctx;
+		if has_continue e then begin
+		    newline ctx;
+		    spr ctx "::_hx_continue::";
+		end;
+		newline ctx;
+		sprln ctx "end";
+		spr ctx "break end";
+	| TObjectDecl [] ->
+		spr ctx "_hx_empty()";
+		ctx.separator <- true
+	| TObjectDecl fields ->
+		spr ctx "_hx_o({__fields__={";
+		concat ctx "," (fun (f,e) -> print ctx "%s=" (anon_field f); spr ctx "true") fields;
+		spr ctx "},";
+		concat ctx "," (fun (f,e) -> print ctx "%s=" (anon_field f); gen_value ctx e) fields;
+		spr ctx "})";
+		ctx.separator <- true
+	| TFor (v,it,e) ->
+		let handle_break = handle_break ctx e in
+		let it = ident (match it.eexpr with
+			| TLocal v -> v.v_name
+			| _ ->
+				let name = temp ctx in
+				print ctx "local %s = " name;
+				gen_value ctx it;
+				newline ctx;
+				name
+		) in
+		print ctx "while( %s:hasNext() ) do" it;
+		let bend = open_block ctx in
+		newline ctx;
+		print ctx "local %s = %s:next();" (ident v.v_name) it;
+		gen_block_element ctx e;
+		bend();
+		newline ctx;
+		spr ctx "end";
+		handle_break();
+		newline ctx;
+	| TTry (e,catchs) ->
+		(* TODO: add temp variables *)
+		sprln ctx "local _hx_expected_result = {}";
+		spr ctx "local _hx_status, _hx_result = pcall(function() ";
+		gen_expr ctx e;
+		let vname = temp ctx in
+		sprln ctx " return _hx_expected_result end)";
+		spr ctx " if not _hx_status then ";
+		let bend = open_block ctx in
+		newline ctx;
+		print ctx "local %s = _hx_result" vname;
+		let last = ref false in
+		let else_block = ref false in
+		List.iter (fun (v,e) ->
+			if !last then () else
+			let t = (match follow v.v_type with
+			| TEnum (e,_) -> Some (TEnumDecl e)
+			| TInst (c,_) -> Some (TClassDecl c)
+			| TAbstract (a,_) -> Some (TAbstractDecl a)
+			| TFun _
+			| TLazy _
+			| TType _
+			| TAnon _ ->
+				assert false
+			| TMono _
+			| TDynamic _ ->
+				None
+			) in
+			match t with
+			| None ->
+				last := true;
+				if !else_block then print ctx "";
+				if vname <> v.v_name then begin
+					newline ctx;
+					print ctx "local %s = %s" v.v_name vname;
+				end;
+				gen_block_element ctx e;
+				if !else_block then begin
+					newline ctx;
+					print ctx " end ";
+				end
+			| Some t ->
+				if not !else_block then newline ctx;
+				print ctx "if( %s.__instanceof(%s," (ctx.type_accessor (TClassDecl { null_class with cl_path = ["lua"],"Boot" })) vname;
+				gen_value ctx (mk (TTypeExpr t) (mk_mono()) e.epos);
+				spr ctx ") ) then ";
+				let bend = open_block ctx in
+				if vname <> v.v_name then begin
+					newline ctx;
+					print ctx "local %s = %s" v.v_name vname;
+				end;
+				gen_block_element ctx e;
+				bend();
+				newline ctx;
+				spr ctx "else";
+				else_block := true
+		) catchs;
+		if not !last then begin
+		    println ctx " _G.error(%s)" vname;
+		    spr ctx "end";
+		end;
+		bend();
+		newline ctx;
+		spr ctx " elseif _hx_result ~= _hx_expected_result then return _hx_result end";
+	| TSwitch (e,cases,def) ->
+		List.iteri (fun cnt (el,e2) ->
+		    if cnt == 0 then spr ctx "if " else spr ctx "elseif ";
+		    List.iteri (fun ccnt e3 ->
+			if ccnt > 0 then spr ctx " or ";
+			gen_value ctx e;
+			spr ctx " == ";
+			gen_value ctx e3;
+		    ) el;
+		    spr ctx " then ";
+		    let bend = open_block ctx in
+		    gen_block_element ctx e2;
+		    bend();
+		    newline ctx;
+		) cases;
+		(match def with
+		| None -> spr ctx "end"
+		| Some e ->
+			begin
+			if (List.length(cases) > 0) then
+			    spr ctx "else";
+			let bend = open_block ctx in
+			gen_block_element ctx e;
+			bend();
+			newline ctx;
+			if (List.length(cases) > 0) then
+			    spr ctx "end";
+			end;);
+	| TCast (e1,Some t) ->
+		print ctx "%s.__cast(" (ctx.type_accessor (TClassDecl { null_class with cl_path = ["lua"],"Boot" }));
+		gen_expr ctx e1;
+		spr ctx " , ";
+		spr ctx (ctx.type_accessor t);
+		spr ctx ")"
+	| TCast (e1,None) ->
+		gen_value ctx e1;
+end;
+
+and gen__init__hoist ctx e =
+    begin match e.eexpr with
+	| TVar (v,eo) ->(
+		print ctx ", %s" (ident v.v_name);
+	    )
+	| TBlock el ->
+		List.iter (gen__init__hoist ctx) el
+	| TCall (e, el) ->
+		(match e.eexpr , el with
+		    | TLocal { v_name = "__feature__" }, { eexpr = TConst (TString f) } :: eif :: eelse ->
+			    (if has_feature ctx f then
+				    gen__init__hoist ctx eif
+			    else match eelse with
+				    | [] -> ()
+				    | e :: _ -> gen__init__hoist ctx e)
+		    |_->());
+	| _ -> ()
+    end;
+
+and gen__init__impl ctx e =
+    begin match e.eexpr with
+	| TVar (v,eo) ->
+		newline ctx;
+		gen_expr ctx e
+	| TBlock el ->
+		List.iter (gen__init__impl ctx) el
+	| TCall (e, el) ->
+		(match e.eexpr , el with
+		    | TLocal { v_name = "__feature__" }, { eexpr = TConst (TString f) } :: eif :: eelse ->
+			    (if has_feature ctx f then
+				    gen__init__impl ctx eif
+			    else match eelse with
+				    | [] -> ()
+				    | e :: _ -> gen__init__impl ctx e)
+		    |_->
+			begin
+			    newline ctx;
+			    gen_call ctx e el false
+			end;
+			    );
+	| _ -> gen_block_element ctx e;
+    end;
+
+and gen_block_element ?(after=false) ctx e  =
+    newline ctx;
+    ctx.iife_assign <- false;
+    begin match e.eexpr with
+	| TTypeExpr _ -> ()
+	| TCast (ce,_) -> gen_block_element ctx ce
+	| TParenthesis pe -> gen_block_element ctx pe
+	| TArrayDecl el -> concat ctx " " (gen_block_element ctx) el;
+	| TBinop (op,e1,e2) when op <> Ast.OpAssign ->
+		let f () = gen_tbinop ctx op e1 e2 in
+		gen_iife_assign ctx f;
+		semicolon ctx;
+	| TUnop ((Increment|Decrement) as op,_,e) ->
+		newline ctx;
+		gen_expr ctx e;
+		print ctx " = ";
+		gen_expr ctx e;
+		(match op with
+			| Increment -> print ctx " + 1;"
+			| _ -> print ctx " - 1;"
+		)
+	| TArray (e1,e2) ->
+		gen_block_element ctx e1;
+		gen_block_element ctx e2;
+	| TSwitch (e,[],def) ->
+		(match def with
+		| None -> ()
+		| Some e -> gen_block_element ctx e)
+	| TField _ ->
+		let f () = gen_expr ctx e in
+		gen_iife_assign ctx f;
+		semicolon ctx;
+	| TConst _ | TLocal _ -> ()
+	| TBlock el ->
+		List.iter (gen_block_element ~after ctx) el
+	| TCall ({ eexpr = TLocal { v_name = "__feature__" } }, { eexpr = TConst (TString f) } :: eif :: eelse) ->
+		if has_feature ctx f then
+			gen_block_element ~after ctx eif
+		else (match eelse with
+			| [] -> ()
+			| [e] -> gen_block_element ~after ctx e
+			| _ -> assert false)
+	| TFunction _ -> ()
+	| TObjectDecl fl ->
+		List.iter (fun (_,e) -> gen_block_element ~after ctx e) fl
+	| TVar (v,eo) ->
+		gen_expr ctx e; (* these already generate semicolons*)
+	| TMeta (_,e) ->
+		gen_block_element ctx e
+	| _ ->
+		gen_expr ctx e;
+		semicolon ctx;
+		if after then newline ctx;
+    end;
+
+and gen_value ctx e =
+	let assign e =
+		mk (TBinop (Ast.OpAssign,
+			mk (TLocal (match ctx.in_value with None -> assert false | Some v -> v)) t_dynamic e.epos,
+			e
+		)) e.etype e.epos
+	in
+	let value() =
+		let old = ctx.in_value, ctx.in_loop in
+		let r_id = temp ctx in
+		let r = alloc_var r_id t_dynamic e.epos in
+		ctx.in_value <- Some r;
+		ctx.in_loop <- false;
+		spr ctx "(function() ";
+		let b = open_block ctx in
+		newline ctx;
+		sprln ctx ("local " ^ r_id);
+		(fun() ->
+			newline ctx;
+			spr ctx ("return " ^ r_id);
+			b();
+			newline ctx;
+			ctx.in_value <- fst old;
+			ctx.in_loop <- snd old;
+			spr ctx "end )()"
+		)
+	in
+	match e.eexpr with
+	| TBinop (OpAssign, e1, e2) ->
+		spr ctx "(function() ";
+		gen_block_element ctx e;
+		spr ctx " return ";
+		gen_value ctx e1;
+		spr ctx " end)()";
+	| TConst _
+	| TLocal _
+	| TArray _
+	| TBinop _
+	| TField _
+	| TEnumParameter _
+	| TTypeExpr _
+	| TParenthesis _
+	| TObjectDecl _
+	| TArrayDecl _
+	| TNew _
+	| TUnop _
+	| TFunction _ ->
+		gen_expr ctx e
+	| TMeta (_,e1) ->
+		gen_value ctx e1
+	| TCall (e,el) ->
+		gen_call ctx e el true
+	| TReturn _
+	| TBreak
+	| TContinue ->
+		unsupported e.epos
+	(* TODO: this is just a hack because this specific case is a TestReflect unit test. I don't know how to address this properly
+	   at the moment. - Simon *)
+	| TCast ({ eexpr = TTypeExpr mt } as e1, None) when (match mt with TClassDecl {cl_path = ([],"Array")} -> false | _ -> true) ->
+	    spr ctx "_hx_staticToInstance(";
+	    gen_expr ctx e1;
+	    spr ctx ")";
+	| TCast (e1, Some t) ->
+		print ctx "%s.__cast(" (ctx.type_accessor (TClassDecl { null_class with cl_path = ["lua"],"Boot" }));
+		gen_value ctx e1;
+		spr ctx " , ";
+		spr ctx (ctx.type_accessor t);
+		spr ctx ")"
+	| TCast (e1, _) ->
+		gen_value ctx e1
+	| TVar _
+	| TFor _
+	| TWhile _
+	| TThrow _ ->
+		(* value is discarded anyway *)
+		let v = value() in
+		gen_expr ctx e;
+		v()
+	| TBlock [e] ->
+		gen_value ctx e
+	| TBlock el ->
+		let v = value() in
+		let rec loop = function
+			| [] ->
+				spr ctx "return nil";
+			| [e] ->
+				gen_block_element ctx (assign e);
+			| e :: l ->
+				gen_block_element ctx e;
+				newline ctx;
+				loop l
+		in
+		loop el;
+		v();
+	| TIf (cond,e,eo) ->
+		let v = value() in
+		spr ctx "if ";
+		gen_cond ctx cond;
+		spr ctx " then ";
+		gen_block_element ctx (assign e);
+		let rec gen_elseif ctx e =
+		(match e with
+		| None->();
+		| Some e2->
+		    (match e2.eexpr with
+		    | TIf(cond3, e3, eo3) ->
+			spr ctx " elseif ";
+			gen_cond ctx cond3;
+			spr ctx " then ";
+			gen_block_element ctx (assign e3);
+			gen_elseif ctx eo3;
+		    | _ ->
+			spr ctx " else ";
+			gen_expr ctx (assign e2);
+			semicolon ctx;
+		    ));
+		in
+		gen_elseif ctx eo;
+		spr ctx " end";
+		v()
+	| TSwitch (cond,cases,def) ->
+		let v = value() in
+		gen_expr ctx (mk (TSwitch (cond,
+			List.map (fun (e1,e2) -> (e1,assign e2)) cases,
+			match def with None -> None | Some e -> Some (assign e)
+		)) e.etype e.epos);
+		v()
+	| TTry (b,catchs) ->
+		let v = value() in
+		let block e = mk (TBlock [e]) e.etype e.epos in
+		gen_block_element ctx (mk (TTry (block (assign b),
+			List.map (fun (v,e) -> v, block (assign e)) catchs
+		)) e.etype e.epos);
+		v()
+
+and is_function_type ctx t =
+    match t with
+    | TFun _ -> true
+    | TMono r -> (match !r with | Some (TFun _) -> true | _ -> false)
+    | _ -> false;
+
+and gen_tbinop ctx op e1 e2 =
+    (match op, e1.eexpr, e2.eexpr with
+    | Ast.OpAssign, TField(e3, FInstance(_,_,_) ), TFunction f ->
+	    gen_expr ctx e1;
+	    spr ctx " = " ;
+	    print ctx "function(%s) " (String.concat "," ("self" :: List.map ident (List.map arg_name f.tf_args)));
+	    let fblock = fun_block ctx f e1.epos in
+	    (match fblock.eexpr with
+	    | TBlock el ->
+		    let rec loop ctx el = (match el with
+		    | [hd] -> (match hd.eexpr with
+			    | TReturn eo -> begin
+				    newline ctx;
+				    gen_return ctx e1 eo;
+				end;
+			    | _ -> gen_block_element ctx hd);
+		    | hd :: tl ->
+			    gen_block_element ctx hd;
+			    loop ctx tl
+		    |[] ->()
+		    ) in
+		    let bend = open_block ctx in
+		    loop ctx el;
+		    bend();
+		    newline ctx;
+	    | _ -> gen_value ctx e2);
+	    spr ctx " end"
+    | Ast.OpAssign, _, _ ->
+	    let iife_assign = ctx.iife_assign in
+	    if iife_assign then spr ctx "(function() ";
+	    (match e1.eexpr, e2.eexpr with
+	    | _, TBinop(OpAssign as op, e3, e4) ->
+		gen_tbinop ctx op e3 e4;
+		newline ctx;
+		gen_value ctx e1;
+		spr ctx " = ";
+		gen_value ctx e3;
+	    | TField(e3, FInstance _ ), TField(e4, FClosure _ )  ->
+		gen_value ctx e1;
+		print ctx " %s " (Ast.s_binop op);
+		spr ctx "_hx_functionToInstanceFunction(";
+		gen_value ctx e2;
+		spr ctx ")";
+	    | TField(_, FInstance _ ), TLocal t  when (is_function_type ctx t.v_type)   ->
+		gen_value ctx e1;
+		print ctx " %s " (Ast.s_binop op);
+		spr ctx "_hx_functionToInstanceFunction(";
+		gen_value ctx e2;
+		spr ctx ")";
+	    | _,_ ->
+		gen_value ctx e1;
+		print ctx " %s " (Ast.s_binop op);
+		gen_value ctx e2);
+	    if iife_assign then begin
+		spr ctx " return ";
+		gen_value ctx e1;
+		spr ctx " end)()";
+	    end;
+    | Ast.OpAssignOp(op2), TArray(e3,e4), _ ->
+	    (* TODO: Figure out how to rewrite this expression more cleanly *)
+	    sprln ctx "(function() ";
+	    let idx = alloc_var "idx" e4.etype e4.epos in
+	    let idx_var =  mk (TVar( idx , Some(e4))) e4.etype e4.epos in
+	    gen_expr ctx idx_var;
+	    let arr = alloc_var "arr" e3.etype e3.epos in
+	    let arr_var = mk (TVar(arr, Some(e3))) e3.etype e3.epos in
+	    gen_expr ctx arr_var;
+	    newline ctx;
+	    let arr_expr = (mk (TArray(
+	    	(mk ( TLocal(arr)) e3.etype e3.epos),
+	    	(mk ( TLocal(idx)) e4.etype e4.epos)
+	    	)) e3.etype e3.epos) in
+	    spr ctx "arr[idx] = ";
+	    gen_tbinop ctx op2 arr_expr e2; semicolon ctx; newline ctx;
+	    spr ctx "return arr[idx]";
+	    spr ctx " end)()";
+    | Ast.OpAssignOp(op2), TField(e3,e4), _ ->
+	    (* TODO: Figure out how to rewrite this expression more cleanly *)
+	    sprln ctx "(function() ";
+	    let obj = alloc_var "obj" e3.etype e3.epos in
+	    spr ctx "local fld = ";
+	    (match e4 with
+	    | FInstance(_,_,fld)
+	    | FStatic(_,fld)
+	    | FAnon fld
+	    | FClosure(_,fld) ->
+		    print ctx "'%s'" fld.cf_name;
+	    | FDynamic name ->
+		    print ctx "'%s'" name;
+	    | FEnum(_,efld) ->
+		    print ctx "'%s'" efld.ef_name);
+	    semicolon ctx; newline ctx;
+	    let obj_var = mk (TVar(obj, Some(e3))) e3.etype e3.epos in
+	    gen_expr ctx obj_var;
+	    newline ctx;
+	    let obj_expr = (mk (TField(
+	    	(mk ( TLocal(obj)) e3.etype e3.epos),
+		e4
+	    	)) e3.etype e3.epos) in
+	    spr ctx "obj[fld] = ";
+	    gen_tbinop ctx op2 obj_expr e2; semicolon ctx; newline ctx;
+	    spr ctx "return obj[fld]";
+	    spr ctx " end)()";
+    | Ast.OpAssignOp(op2),_,_ ->
+	    (* TODO: Rewrite expression *)
+	    spr ctx "(function() "; gen_value ctx e1;
+	    spr ctx " = "; gen_tbinop ctx op2 e1 e2;
+	    spr ctx " return "; gen_value ctx e1;
+	    spr ctx " end)()";
+    | Ast.OpXor,_,_ | Ast.OpAnd,_,_  | Ast.OpShl,_,_ | Ast.OpShr,_,_ | Ast.OpUShr,_,_ | Ast.OpOr,_,_ ->
+	    gen_bitop ctx op e1 e2;
+    | Ast.OpMod,_,_ ->
+	    spr ctx "_G.math.fmod(";
+	    gen_expr ctx e1;
+	    spr ctx ", ";
+	    gen_expr ctx e2;
+	    spr ctx ")";
+    | Ast.OpAdd,_,_ when (is_string_expr e1 || is_string_expr e2) ->
+	    gen_value ctx e1;
+	    print ctx " .. ";
+	    gen_value ctx e2;
+    | _ -> begin
+	    spr ctx "(";
+	    gen_value ctx e1;
+	    spr ctx ")";
+	    (match op with
+		| Ast.OpNotEq -> print ctx " ~= ";
+		| Ast.OpBoolAnd -> print ctx " and ";
+		| Ast.OpBoolOr -> print ctx " or ";
+		| _ -> print ctx " %s " (Ast.s_binop op));
+	    spr ctx "(";
+	    gen_value ctx e2;
+	    spr ctx ")";
+	    end;
+    );
+
+and gen_wrap_tbinop ctx e=
+    match e.eexpr with
+    | TBinop _  ->
+	    spr ctx "(";
+	    gen_value ctx e;
+	    spr ctx ")";
+    | _ ->
+	    gen_value ctx e
+
+and gen_bitop ctx op e1 e2 =
+    print ctx "_hx_bit.%s(" (match op with
+	| Ast.OpXor  ->  "bxor"
+	| Ast.OpAnd  ->  "band"
+	| Ast.OpShl  ->  "lshift"
+	| Ast.OpShr  ->  "arshift"
+	| Ast.OpUShr ->  "rshift"
+	| Ast.OpOr   ->  "bor"
+	| _ -> "");
+    gen_value ctx e1;
+    spr ctx ",";
+    gen_value ctx e2;
+    spr ctx ")"
+
+and gen_return ctx e eo =
+    if ctx.in_value <> None then unsupported e.epos;
+    (match eo with
+    | None ->
+	    spr ctx "do return end"
+    | Some e ->
+	    (match e.eexpr with
+	    | TBinop(OpAssign, e1, e2) ->
+		gen_expr ctx e;
+		spr ctx " do return ";
+		gen_value ctx e1;
+		spr ctx " end";
+	    | _ ->
+		spr ctx "do return ";
+		gen_value ctx e;
+		spr ctx " end");
+	)
+
+and gen_iife_assign ctx f =
+    spr ctx "(function() return ";
+    f();
+    spr ctx " end)()";
+
+and gen_cond ctx cond =
+    ctx.iife_assign <- true;
+    gen_value ctx cond;
+    ctx.iife_assign <- false;
+
+and has_class ctx c =
+    has_feature ctx "lua.Boot.getClass" && (c.cl_super <> None || c.cl_ordered_fields <> [] || c.cl_constructor <> None)
+
+and has_prototype ctx c =
+    c.cl_super <> None || (has_class ctx c) || List.exists (can_gen_class_field ctx) c.cl_ordered_fields
+
+and can_gen_class_field ctx = function
+	| { cf_expr = (None | Some { eexpr = TConst TNull }) } when not (has_feature ctx "Type.getInstanceFields") ->
+		false
+	| f ->
+		not (is_extern_field f)
+
+and has_continue e =
+    let rec loop e = match e.eexpr with
+	| TContinue -> raise Exit
+	| TWhile(e1,_,_) | TFor(_,e1,_) -> loop e1 (* in theory there could be a continue there. Note that we don't want to recurse into the loop body because we do not care about inner continue expressions *)
+	| _ -> Type.iter loop e
+    in
+    try
+	loop e;
+	false;
+    with Exit ->
+	true
+
+let generate_package_create ctx (p,_) =
+	let rec loop acc = function
+		| [] -> ()
+		| p :: l when Hashtbl.mem ctx.packages (p :: acc) -> loop (p :: acc) l
+		| p :: l ->
+			Hashtbl.add ctx.packages (p :: acc) ();
+			(match acc with
+			| [] -> print ctx "local %s = {}" p
+			| _ ->
+				let p = String.concat "." (List.rev acc) ^ (field p) in
+				print ctx "%s = {}" p
+			);
+			ctx.separator <- true;
+			newline ctx;
+			loop (p :: acc) l
+	in
+	match p with
+	| [] -> print ctx "local "
+	| _ -> loop [] 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 Lua") (match f.cf_expr with None -> c.cl_pos | Some e -> e.epos);
+	| _ -> ()
+
+(* convert a.b.c to ["a"]["b"]["c"] *)
+let path_to_brackets path =
+	let parts = ExtString.String.nsplit path "." in
+	"[\"" ^ (String.concat "\"][\"" parts) ^ "\"]"
+
+let gen_class_static_field ctx c f =
+	match f.cf_expr with
+	| None | Some { eexpr = TConst TNull } when not (has_feature ctx "Type.getClassFields") ->
+		()
+	| None when is_extern_field f ->
+		()
+	| None ->
+		println ctx "%s%s = nil" (s_path ctx c.cl_path) (field f.cf_name);
+	| Some e ->
+		match e.eexpr with
+		| TFunction _ ->
+			let path = (s_path ctx c.cl_path) ^ (field f.cf_name) in
+			let dot_path = (dot_path c.cl_path) ^ (static_field c f.cf_name) in
+			ctx.id_counter <- 0;
+			print ctx "%s = " path;
+			gen_value ctx e;
+			newline ctx;
+			(match (get_exposed ctx dot_path f.cf_meta) with [s] -> (print ctx "_hx_exports%s = %s" (path_to_brackets s) path; newline ctx) | _ -> ());
+		| _ ->
+			ctx.statics <- (c,f.cf_name,e) :: ctx.statics
+
+let gen_class_field ctx c f predelimit =
+	check_field_name c f;
+	if predelimit then sprln ctx ",";
+	match f.cf_expr with
+	| None ->
+		print ctx "'%s', nil" f.cf_name;
+	| Some e ->
+		ctx.id_counter <- 0;
+		(match e.eexpr with
+		| TFunction f2 ->
+		    let old = ctx.in_value, ctx.in_loop in
+		    ctx.in_value <- None;
+		    ctx.in_loop <- false;
+		    print ctx "'%s', function" f.cf_name;
+		    print ctx "(%s) " (String.concat "," ("self" :: List.map ident (List.map arg_name f2.tf_args)));
+		    let fblock = fun_block ctx f2 e.epos in
+		    (match fblock.eexpr with
+		    | TBlock el ->
+			let rec loop ctx el = (match el with
+			    | [hd] -> (match hd.eexpr with
+				    | TReturn eo -> begin
+					    newline ctx;
+					    gen_return ctx e eo;
+					end;
+				    | _ -> gen_block_element ctx hd);
+			    | hd :: tl ->
+				    gen_block_element ctx hd;
+				    loop ctx tl
+			    |[] ->()
+			    ) in
+			let bend = open_block ctx in
+			loop ctx el;
+			bend();
+			newline ctx;
+		    |_ -> ());
+		    spr ctx "end";
+		    ctx.in_value <- fst old;
+		    ctx.in_loop <- snd old;
+		    ctx.separator <- true;
+		| _ -> gen_value ctx e)
+
+let generate_class___name__ ctx c =
+	if has_feature ctx "lua.Boot.isClass" then begin
+		let p = s_path ctx c.cl_path in
+		print ctx "%s.__name__ = " p;
+		if has_feature ctx "Type.getClassName" then
+			println ctx "{%s}" (String.concat "," (List.map (fun s -> Printf.sprintf "\"%s\"" (Ast.s_escape s)) (fst c.cl_path @ [snd c.cl_path])))
+		else
+			println ctx "true";
+	end
+
+let generate_class ctx c =
+	ctx.current <- c;
+	ctx.id_counter <- 0;
+	(match c.cl_path with
+	| [],"Function" -> error "This class redefines a native one" c.cl_pos
+	| _ -> ());
+	let p = s_path ctx c.cl_path in
+	let hxClasses = has_feature ctx "Type.resolveClass" in
+	newline ctx;
+	print ctx "%s.new = " p;
+	(match c.cl_kind with
+		| KAbstractImpl _ ->
+			(* abstract implementations only contain static members and don't need to have constructor functions *)
+			print ctx "{}"; ctx.separator <- true
+		| _ ->
+			(match c.cl_constructor with
+			| Some { cf_expr = Some e } ->
+				(match e.eexpr with
+				| TFunction f ->
+				    let old = ctx.in_value, ctx.in_loop in
+				    ctx.in_value <- None;
+				    ctx.in_loop <- false;
+				    print ctx "function(%s) " (String.concat "," (List.map ident (List.map arg_name f.tf_args)));
+				    let fblock = fun_block ctx f e.epos in
+				    (match fblock.eexpr with
+				    | TBlock el ->
+					let bend = open_block ctx in
+					newline ctx;
+					if not (has_prototype ctx c) then println ctx "local self = _hx_new()" else
+					println ctx "local self = _hx_new(%s.prototype)" p;
+					println ctx "%s.super(%s)" p (String.concat "," ("self" :: (List.map ident (List.map arg_name f.tf_args))));
+					if p = "String" then sprln ctx "self = string";
+					spr ctx "return self";
+					bend(); newline ctx;
+					spr ctx "end"; newline ctx; newline ctx;
+					let bend = open_block ctx in
+					print ctx "%s.super = function(%s) " p (String.concat "," ("self" :: (List.map ident (List.map arg_name f.tf_args))));
+					List.iter (gen_block_element ctx) el;
+					bend();
+					newline ctx;
+					spr ctx "end";
+				    |_ -> ());
+				    ctx.in_value <- fst old;
+				    ctx.in_loop <- snd old;
+				    ctx.separator <- true
+				| _ -> gen_expr ctx e);
+			| _ -> (print ctx "{}"); ctx.separator <- true)
+	);
+	newline ctx;
+
+	(match (get_exposed ctx (dot_path c.cl_path) c.cl_meta) with [s] -> (print ctx "_hx_exports%s = %s" (path_to_brackets s) p; newline ctx) | _ -> ());
+
+	if hxClasses then println ctx "_hxClasses[\"%s\"] = %s" (dot_path c.cl_path) p;
+	generate_class___name__ ctx c;
+	(match c.cl_implements with
+	| [] -> ()
+	| l ->
+		println ctx "%s.__interfaces__ = {%s}" p (String.concat "," (List.map (fun (i,_) -> ctx.type_accessor (TClassDecl i)) l));
+	);
+
+	let gen_props props =
+		String.concat "," (List.map (fun (p,v) -> p ^"=\""^v^"\"") props) in
+	let has_property_reflection =
+		(has_feature ctx "Reflect.getProperty") || (has_feature ctx "Reflect.setProperty") in
+
+	if has_property_reflection then begin
+		(match Codegen.get_properties c.cl_ordered_statics with
+		| [] -> ()
+		| props -> println ctx "%s.__properties__ = {%s}" p (gen_props props);
+		);
+	end;
+
+	List.iter (gen_class_static_field ctx c) c.cl_ordered_statics;
+
+	newline ctx;
+	if (has_prototype ctx c) then begin
+		print ctx "%s.prototype = _hx_anon(" p;
+		let bend = open_block ctx in
+		newline ctx;
+		let count = ref 0 in
+
+		List.iter (fun f -> if can_gen_class_field ctx f then (gen_class_field ctx c f (!count > 0); incr count;) ) c.cl_ordered_fields;
+		if (has_class ctx c) then begin
+			newprop ctx;
+			if !count > 0 then spr ctx ",";
+			print ctx "'__class__',  %s" p;
+			incr count;
+		end;
+
+		if has_property_reflection then begin
+			let props = Codegen.get_properties c.cl_ordered_fields in
+			(match c.cl_super with
+			| _ when props = [] -> ()
+			| _ ->
+				if !count > 0 then spr ctx ",";
+				newprop ctx;
+				print ctx "'__properties__',  {%s}" (gen_props props));
+		end;
+
+		bend(); newline ctx;
+		println ctx ")";
+		(match c.cl_super with
+		| None -> ()
+		| Some (csup,_) ->
+			let psup = ctx.type_accessor (TClassDecl csup) in
+			println ctx "%s.__super__ = %s" p psup;
+			println ctx "setmetatable(%s.prototype,{__index=%s.prototype})" p psup;
+			(* Also use the __properties__  from the super class as the __index metatable *)
+			if has_property_reflection && Codegen.has_properties csup then
+			    println ctx "setmetatable(%s.prototype.__properties__,{__index=%s.prototype.__properties__})" p psup;
+		);
+	end
+
+let generate_enum ctx e =
+	let p = s_path ctx e.e_path in
+	let ename = List.map (fun s -> Printf.sprintf "\"%s\"" (Ast.s_escape s)) (fst e.e_path @ [snd e.e_path]) in
+
+	(* TODO: Unify the _hxClasses declaration *)
+	if has_feature ctx "Type.resolveEnum" then begin
+	    newline ctx;
+	    print ctx "_hxClasses[\"%s\"] = %s" (dot_path e.e_path) p; semicolon ctx; newline ctx;
+	end;
+	if has_feature ctx "lua.Boot.isEnum" then begin
+	    newline ctx;
+	    print ctx "_hxClasses[\"%s\"] = {" (dot_path e.e_path);
+	    if has_feature ctx "lua.Boot.isEnum" then  begin
+		print ctx " __ename__ = %s," (if has_feature ctx "Type.getEnumName" then "{" ^ String.concat "," ename ^ "}" else "true");
+	    end;
+	    (* TODO :  Come up with a helper function for _hx_tabArray declarations *)
+	    spr ctx " __constructs__ = _hx_tabArray({";
+	    if ((List.length e.e_names) > 0) then begin
+		    spr ctx "[0]=";
+		    spr ctx (String.concat "," (List.map (fun s -> Printf.sprintf "\"%s\"" s) e.e_names));
+	    end;
+	    print ctx "},%i)}" (List.length e.e_names);
+	    ctx.separator <- true;
+	    newline ctx;
+	end;
+
+	if has_feature ctx "Type.resolveEnum" || has_feature ctx "lua.Boot.isEnum" then
+	    print ctx "%s = _hxClasses[\"%s\"];" p (dot_path e.e_path);
+
+	newline ctx;
+	List.iter (fun n ->
+		let f = PMap.find n e.e_constrs in
+		print ctx "%s%s = " p (field f.ef_name);
+		(match f.ef_type with
+		| TFun (args,_) ->
+			let count = List.length args in
+			let sargs = String.concat "," (List.map (fun (n,_,_) -> ident n) args) in
+			print ctx "function(%s) local _x = _hx_tabArray({[0]=\"%s\",%d,%s,__enum__=%s}, %i);" sargs f.ef_name f.ef_index sargs p (count + 2);
+			if has_feature ctx "may_print_enum" then
+				(* TODO: better namespacing for _estr *)
+				spr ctx " _x.toString = _estr;";
+			spr ctx " return _x; end ";
+			ctx.separator <- true;
+		| _ ->
+			println ctx "_hx_tabArray({[0]=\"%s\",%d},2)" f.ef_name f.ef_index;
+			if has_feature ctx "may_print_enum" then begin
+				println ctx "%s%s.toString = _estr" p (field f.ef_name);
+			end;
+			print ctx "%s%s.__enum__ = %s" p (field f.ef_name) p;
+		);
+		newline ctx
+	) e.e_names;
+	if has_feature ctx "Type.allEnums" then begin
+		let ctors_without_args = List.filter (fun s ->
+			let ef = PMap.find s e.e_constrs in
+			match follow ef.ef_type with
+				| TFun _ -> false
+				| _ -> true
+		) e.e_names in
+		print ctx "%s.__empty_constructs__ = " p;
+		spr ctx "_hx_tabArray({";
+		if (List.length ctors_without_args)  > 0 then
+		    begin
+			spr ctx "[0] = ";
+			print ctx "%s" (String.concat "," (List.map (fun s -> Printf.sprintf "%s.%s" p s) ctors_without_args));
+		    end;
+		println ctx "}, %i)"  (List.length ctors_without_args);
+	end
+
+let generate_static ctx (c,f,e) =
+	print ctx "%s%s = " (s_path ctx c.cl_path) (field f);
+	gen_value ctx e;
+	newline ctx
+
+let generate_enumMeta_fields ctx = function
+    | TEnumDecl e -> begin
+	    let p = s_path ctx e.e_path in
+	    match Codegen.build_metadata ctx.com (TEnumDecl e) with
+	| None -> ()
+	| Some e ->
+		print ctx "%s.__meta__ = " p;
+		gen_expr ctx e;
+		newline ctx
+	end
+    | _ -> ()
+
+let generate_require ctx path meta =
+	let _, args, mp = Meta.get Meta.LuaRequire meta in
+	let p = (s_path ctx path) in
+
+	generate_package_create ctx path;
+
+	(match args with
+	| [(EConst(String(module_name)),_)] ->
+		print ctx "%s = _G.require(\"%s\")" p module_name
+	| [(EConst(String(module_name)),_) ; (EConst(String(object_path)),_)] ->
+		print ctx "%s = _G.require(\"%s\").%s" p module_name object_path
+	| _ ->
+		error "Unsupported @:luaRequire format" mp);
+
+	newline ctx
+
+let generate_type ctx = function
+	| TClassDecl c ->
+		(match c.cl_init with
+		| None -> ()
+		| Some e ->
+			ctx.inits <- e :: ctx.inits);
+		(* Special case, want to add Math.__name__ only when required, handle here since Math is extern *)
+		let p = s_path ctx c.cl_path in
+		if p = "Math" then generate_class___name__ ctx c;
+		(* Another special case for Std because we do not want to generate it if it's empty. *)
+		if p = "Std" && c.cl_ordered_statics = [] then
+			()
+		else if not c.cl_extern then
+			generate_class ctx c
+		else if Meta.has Meta.LuaRequire c.cl_meta && is_directly_used ctx.com c.cl_meta then
+			generate_require ctx c.cl_path c.cl_meta
+		else if Meta.has Meta.InitPackage c.cl_meta then
+			(match c.cl_path with
+			| ([],_) -> ()
+			| _ -> generate_package_create ctx c.cl_path)
+	| TEnumDecl e when e.e_extern ->
+		if Meta.has Meta.LuaRequire e.e_meta && is_directly_used ctx.com e.e_meta then
+		    generate_require ctx e.e_path e.e_meta;
+	| TEnumDecl e -> generate_enum ctx e
+	| TTypeDecl _ | TAbstractDecl _ -> ()
+
+let generate_type_forward ctx = function
+	| TClassDecl c ->
+		(match c.cl_init with
+		| None -> ()
+		| Some e ->
+			ctx.inits <- e :: ctx.inits);
+		if not c.cl_extern then begin
+		    generate_package_create ctx c.cl_path;
+		    let p = s_path ctx c.cl_path in
+		    print ctx "%s = _hx_empty() " p;
+		end
+	| TEnumDecl e when e.e_extern ->
+		()
+	| TEnumDecl e ->
+		generate_package_create ctx e.e_path;
+		let p = s_path ctx e.e_path in
+		print ctx "%s = _hx_empty() " p;
+	| TTypeDecl _ | TAbstractDecl _ -> ()
+
+let set_current_class ctx c =
+	ctx.current <- c
+
+let alloc_ctx com =
+	let ctx = {
+		com = com;
+		buf = Buffer.create 16000;
+		packages = Hashtbl.create 0;
+		statics = [];
+		inits = [];
+		current = null_class;
+		tabs = "";
+		in_value = None;
+		iife_assign = false;
+		in_loop = false;
+		handle_break = false;
+		id_counter = 0;
+		type_accessor = (fun _ -> assert false);
+		separator = false;
+		found_expose = false;
+		lua_jit = Common.defined com Define.LuaJit;
+		lua_ver = try
+			float_of_string (PMap.find "lua_ver" com.defines)
+		    with | Not_found -> 5.2;
+	} in
+	ctx.type_accessor <- (fun t ->
+		let p = t_path t in
+		match t with
+		| TClassDecl ({ cl_extern = true } as c) when not (Meta.has Meta.LuaRequire c.cl_meta)
+			-> dot_path p
+		| TEnumDecl { e_extern = true }
+			-> dot_path p
+		| _ -> s_path ctx p);
+	ctx
+
+let gen_single_expr ctx e expr =
+	if expr then gen_expr ctx e else gen_value ctx e;
+	let str = Buffer.contents ctx.buf in
+	Buffer.reset ctx.buf;
+	ctx.id_counter <- 0;
+	str
+
+let generate com =
+	let t = Common.timer "generate lua" in
+	let ctx = alloc_ctx com in
+
+	if has_feature ctx "Class" || has_feature ctx "Type.getClassName" then add_feature ctx "lua.Boot.isClass";
+	if has_feature ctx "Enum" || has_feature ctx "Type.getEnumName" then add_feature ctx "lua.Boot.isEnum";
+
+	let include_files = List.rev com.include_files in
+	List.iter (fun file ->
+		match file with
+		| path, "top" ->
+			let file_content = Std.input_file ~bin:true (fst file) in
+			print ctx "%s\n" file_content;
+			()
+		| _ -> ()
+	) include_files;
+
+	let var_exports = (
+		"_hx_exports",
+		"_G"
+	) in
+
+	let exposed = List.concat (List.map (fun t ->
+		match t with
+			| TClassDecl c ->
+				let path = dot_path c.cl_path in
+				let class_exposed = get_exposed ctx path c.cl_meta in
+				let static_exposed = List.map (fun f ->
+					get_exposed ctx (path ^ static_field c f.cf_name) f.cf_meta
+				) c.cl_ordered_statics in
+				List.concat (class_exposed :: static_exposed)
+			| _ -> []
+		) com.types) in
+	let anyExposed = exposed <> [] in
+	let exportMap = ref (PMap.create String.compare) in
+	let exposedObject = { os_name = ""; os_fields = [] } in
+	let toplevelExposed = ref [] in
+	List.iter (fun path -> (
+		let parts = ExtString.String.nsplit path "." in
+		let rec loop p pre = match p with
+			| f :: g :: ls ->
+				let path = match pre with "" -> f | pre -> (pre ^ "." ^ f) in
+				if not (PMap.exists path !exportMap) then (
+					let elts = { os_name = f; os_fields = [] } in
+					exportMap := PMap.add path elts !exportMap;
+					let cobject = match pre with "" -> exposedObject | pre -> PMap.find pre !exportMap in
+					cobject.os_fields <- elts :: cobject.os_fields
+				);
+				loop (g :: ls) path;
+			| f :: [] when pre = "" ->
+				toplevelExposed := f :: !toplevelExposed;
+			| _ -> ()
+		in loop parts "";
+	)) exposed;
+
+
+	if (anyExposed) then (
+		print ctx "local %s = %s" (fst var_exports) (snd var_exports);
+		ctx.separator <- true;
+		newline ctx
+	);
+
+	let rec print_obj f root = (
+		let path = root ^ (path_to_brackets f.os_name) in
+		print ctx "%s = %s or _hx_empty()" path path;
+		ctx.separator <- true;
+		newline ctx;
+		concat ctx ";" (fun g -> print_obj g path) f.os_fields
+	)
+	in
+	List.iter (fun f -> print_obj f "_hx_exports") exposedObject.os_fields;
+
+
+	let vars = [] in
+	(* let vars = (if has_feature ctx "Type.resolveClass" || has_feature ctx "Type.resolveEnum" then ("_hxClasses = " ^ "{}") :: vars else vars) in *)
+	let vars = if has_feature ctx "may_print_enum"
+		then ("_estr = function(self) return " ^ (ctx.type_accessor (TClassDecl { null_class with cl_path = ["lua"],"Boot" })) ^ ".__string_rec(self,''); end") :: vars
+		else vars in
+	(match List.rev vars with
+	| [] -> ()
+	| vl ->
+		print ctx "local %s" (String.concat ";" vl);
+		ctx.separator <- true;
+		newline ctx
+	);
+
+
+	List.iter (generate_type_forward ctx) com.types; newline ctx;
+
+	spr ctx "local _hx_bind";
+	List.iter (gen__init__hoist ctx) (List.rev ctx.inits); newline ctx;
+	ctx.inits <- []; (* reset inits *)
+
+	List.iter (generate_type ctx) com.types;
+
+	(* If we use haxe Strings, patch Lua's string *)
+	if has_feature ctx "use.string" then begin
+	    sprln ctx "local _hx_string_mt = _G.getmetatable('');";
+	    sprln ctx "String.__oldindex = _hx_string_mt.__index;";
+	    sprln ctx "_hx_string_mt.__index = String.__index;";
+	    sprln ctx "_hx_string_mt.__add = function(a,b) return Std.string(a)..Std.string(b) end;";
+	    sprln ctx "_hx_string_mt.__concat = _hx_string_mt.__add";
+	end;
+
+	(* Array is required, always patch it *)
+	sprln ctx "_hx_array_mt.__index = Array.prototype";
+	newline ctx;
+
+	(* Generate statics *)
+	List.iter (generate_static ctx) (List.rev ctx.statics);
+
+	(* Localize init variables inside a do-block *)
+	(* Note: __init__ logic can modify static variables. *)
+	sprln ctx "do";
+	List.iter (gen__init__impl ctx) (List.rev ctx.inits);
+	newline ctx;
+	sprln ctx "end";
+
+	let rec chk_features e =
+		if is_dynamic_iterator ctx e then add_feature ctx "use._iterator";
+		match e.eexpr with
+		| TField (_,FClosure _) ->
+			add_feature ctx "use._hx_bind"
+		| _ ->
+			Type.iter chk_features e
+	in
+
+	List.iter chk_features ctx.inits;
+
+	List.iter (fun (_,_,e) -> chk_features e) ctx.statics;
+	if has_feature ctx "use._iterator" then begin
+		add_feature ctx "use._hx_bind";
+		println ctx "function _hx_iterator(o)  if ( lua.Boot.__instanceof(o, Array) ) then return function() return HxOverrides.iter(o) end elseif (typeof(o.iterator) == 'function') then return  _hx_bind(o,o.iterator) else return  o.iterator end end";
+	end;
+	if has_feature ctx "use._hx_bind" then println ctx "_hx_bind = lua.Boot.bind";
+
+	List.iter (generate_enumMeta_fields ctx) com.types;
+
+	(match com.main with
+	| None -> ()
+	| Some e -> gen_expr ctx e; newline ctx);
+
+	sprln ctx "return _G";
+
+	let ch = open_out_bin com.file in
+	output_string ch (Buffer.contents ctx.buf);
+	close_out ch;
+	t()
+

+ 18 - 22
genneko.ml → src/generators/genneko.ml

@@ -1,23 +1,20 @@
 (*
- * Copyright (C)2005-2013 Haxe Foundation
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
+	The 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
@@ -782,8 +779,8 @@ let build ctx types =
 	packs @ methods @ boot :: names @ inits @ vars
 
 let generate com =
+	Hashtbl.clear files;
 	let ctx = new_context com (if Common.defined com Define.NekoV1 then 1 else 2) false in
-	let t = Common.timer "neko generation" in
 	let libs = (EBlock (generate_libs_init com.neko_libs) , { psource = "<header>"; pline = 1; }) in
 	let el = build ctx com.types in
 	let emain = (match com.main with None -> [] | Some e -> [gen_expr ctx e]) in
@@ -819,5 +816,4 @@ let generate com =
 		if command ("nekoc -p \"" ^ neko_file ^ "\"") <> 0 then failwith "Failed to print neko code";
 		Sys.remove neko_file;
 		Sys.rename ((try Filename.chop_extension com.file with _ -> com.file) ^ "2.neko") neko_file;
-	end;
-	t()
+	end

+ 75 - 44
genphp.ml → src/generators/genphp.ml

@@ -1,23 +1,20 @@
 (*
- * Copyright (C)2005-2013 Haxe Foundation
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
+	The 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
@@ -71,6 +68,8 @@ type context = {
 	mutable lib_path : string;
 }
 
+let follow = Abstract.follow_with_abstracts
+
 let join_class_path path separator =
 	let result = match fst path, snd path with
 	| [], s -> s
@@ -210,7 +209,7 @@ let rec is_string_type t =
 let is_string_expr e = is_string_type e.etype
 
 let to_string ctx e =
-	let v = alloc_var "__call__" t_dynamic in
+	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
 
@@ -223,7 +222,7 @@ let as_string_expr ctx e =
 	| _ -> e
 (* for known String type that could have null value *)
 let to_string_null ctx e =
-	let v = alloc_var "__call__" t_dynamic in
+	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
 
@@ -307,8 +306,11 @@ let is_keyword n =
 	| "include_once" | "isset" | "list" | "namespace" | "print" | "require" | "require_once"
 	| "unset" | "use" | "__function__" | "__class__" | "__method__" | "final"
 	| "php_user_filter" | "protected" | "abstract" | "__set" | "__get" | "__call"
-	| "clone" | "instanceof" | "break" | "case" | "class" | "continue" | "default" | "do" | "else" | "extends" | "for" | "function" | "if" | "new" | "return" | "static" | "switch" | "var" | "while" | "interface" | "implements" | "public" | "private" | "try" | "catch" | "throw" -> true
-	| "goto"
+	| "clone" | "instanceof" | "break" | "case" | "class" | "continue" | "default"
+	| "do" | "else" | "extends" | "for" | "function" | "if" | "new" | "return"
+	| "static" | "switch" | "var" | "while" | "interface" | "implements" | "public"
+	| "private" | "try" | "catch" | "throw" | "goto" | "yield"
+		-> true
 	| _ -> false
 
 let s_ident n =
@@ -361,7 +363,7 @@ let init com cwd path def_type =
 	let ch = open_out (String.concat "/" dir ^ "/" ^ (filename path) ^ (if def_type = 0 then ".class" else if def_type = 1 then ".enum"  else if def_type = 2 then ".interface" else ".extern") ^ ".php") in
 	let imports = Hashtbl.create 0 in
 	Hashtbl.add imports (snd path) [fst path];
-	{
+	let ctx = {
 		com = com;
 		stack = stack_init com false;
 		tabs = "";
@@ -390,7 +392,10 @@ let init com cwd path def_type =
 		inline_index = 0;
 		in_block = false;
 		lib_path = match com.php_lib with None -> "lib" | Some s -> s;
-	}
+	} in
+	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 newline ctx =
@@ -770,21 +775,35 @@ and is_static t =
 		| _ -> false)
 	| _ -> false
 
+and get_constant_prefix meta =
+	let (_, args, pos) = Meta.get Meta.PhpConstants meta in
+	(match args with
+		| [EConst(String prefix), _] -> prefix
+		| [] -> ""
+		| _ -> error "Invalid @:phpConstant parameters" pos)
+
 and gen_member_access ctx isvar e s =
 	match follow e.etype with
 	| TAnon a ->
 		(match !(a.a_status) with
 		| EnumStatics _ ->
 			print ctx "::%s%s" (if isvar then "$" else "") (s_ident s)
-		| Statics _ ->
-			print ctx "::%s%s" (if isvar then "$" else "") (s_ident s)
+		| Statics sta ->
+			let sep = if Meta.has Meta.PhpGlobal sta.cl_meta then "" else "::" in
+			let isconst = Meta.has Meta.PhpConstants sta.cl_meta in
+			let cprefix = if isconst then get_constant_prefix sta.cl_meta else "" in
+			print ctx "%s%s%s" sep (if isvar && not isconst then "$" else cprefix)
+			(if sta.cl_extern && sep = "" then s else s_ident s)
 		| _ -> print ctx "->%s" (if isvar then s_ident_field s else s_ident s))
 	| _ -> print ctx "->%s" (if isvar then s_ident_field s else s_ident s)
 
 and gen_field_access ctx isvar e s =
 	match e.eexpr with
 	| TTypeExpr t ->
-		spr ctx (s_path ctx (t_path t) false e.epos);
+		let isglobal = match t with
+		| TClassDecl(c) -> Meta.has Meta.PhpGlobal c.cl_meta && c.cl_extern
+		| _ -> false in
+		spr ctx (if isglobal then "" else (s_path ctx (t_path t) false e.epos));
 		gen_member_access ctx isvar e s
 	| TLocal _ ->
 		gen_expr ctx e;
@@ -886,25 +905,32 @@ and gen_inline_function ctx f hasthis p =
 	ctx.in_value <- Some "closure";
 
 	let args a = List.map (fun (v,_) -> v.v_name) a in
-	let arguments = ref [] in
 
-	if hasthis then begin arguments := "this" :: !arguments end;
+	let used_locals = ref PMap.empty in
 
-	PMap.iter (fun n _ -> arguments := !arguments @ [n]) old_li;
+	let rec loop e = match e.eexpr with
+		| TLocal v when not (start_with v.v_name "__hx__") && PMap.mem v.v_name old_l ->
+			used_locals := PMap.add v.v_name v.v_name !used_locals
+		| _ ->
+			Type.iter loop e
+	in
+	loop f.tf_expr;
 
 	spr ctx "array(new _hx_lambda(array(";
 
 	let c = ref 0 in
 
-	List.iter (fun a ->
+	let print_arg a =
 		if !c > 0 then spr ctx ", ";
 		incr c;
 		print ctx "&$%s" a;
-	) (remove_internals !arguments);
+	in
+	if hasthis then print_arg "this";
+	PMap.iter (fun _ a -> print_arg a) !used_locals;
 
 	spr ctx "), \"";
 
-	spr ctx (inline_function ctx (args f.tf_args) hasthis (fun_block ctx f p));
+	spr ctx (inline_function ctx (args f.tf_args) hasthis !used_locals (fun_block ctx f p));
 	print ctx "\"), 'execute')";
 
 	ctx.in_value <- old;
@@ -1215,11 +1241,13 @@ and gen_expr ctx e =
 				let tmp = define_local ctx "_t" in
 				print ctx "(is_object($%s = " tmp;
 				gen_field_op ctx e1;
-				print ctx ") && !($%s instanceof Enum) ? $%s%s" tmp tmp s_phop;
+				print ctx ") && ($%s instanceof Enum) ? $%s%s" tmp tmp s_op;
 				gen_field_op ctx e2;
-				print ctx " : $%s%s" tmp s_op;
+				print ctx " : ";
+				if op = Ast.OpNotEq then spr ctx "!";
+				print ctx "_hx_equal($%s, " tmp;
 				gen_field_op ctx e2;
-				spr ctx ")";
+				spr ctx "))";
 			end
 		| Ast.OpGt | Ast.OpGte | Ast.OpLt | Ast.OpLte when is_string_expr e1 ->
 			spr ctx "(strcmp(";
@@ -1544,7 +1572,7 @@ and gen_expr ctx e =
 		let catchall = ref false in
 		let evar = define_local ctx "_ex_" in
 		newline ctx;
-		print ctx "$%s = ($%s instanceof HException) ? $%s->e : $%s" evar ex ex ex;
+		print ctx "$%s = ($%s instanceof HException) && $%s->getCode() == null ? $%s->e : $%s" evar ex ex ex ex;
 		old();
 		List.iter (fun (v,e) ->
 			let ev = define_local ctx v.v_name in
@@ -1680,7 +1708,7 @@ and inline_block ctx e =
 
 		ctx.inline_methods <- ctx.inline_methods @ [block]
 
-and inline_function ctx args hasthis e =
+and inline_function ctx args hasthis used_args e =
 		let index = ctx.inline_index in
 		ctx.inline_index <- ctx.inline_index + 1;
 		let block = {
@@ -1689,9 +1717,9 @@ and inline_function ctx args hasthis e =
 			ihasthis = hasthis; (* param this *)
 			iarguments = args;
 			iexpr = e;
-			ilocals = ctx.locals;
+			ilocals = used_args;
 			iin_block = false;
-			iinv_locals = ctx.inv_locals;
+			iinv_locals = used_args;
 		} in
 
 		ctx.inline_methods <- ctx.inline_methods @ [block];
@@ -1878,13 +1906,13 @@ let generate_field ctx static f =
 				| AccCall, _ ->
 					let m = "get_" ^ f.cf_name in
 					if not (is_method_defined ctx m static) then generate_self_method ctx rights m static false;
-					print ctx "%s $%s" rights (s_ident_field f.cf_name);
+					print ctx "%s $%s" rights (s_ident f.cf_name);
 					gen_assigned_value ctx f.cf_expr;
 					true
 				| _, AccCall ->
 					let m = "set_" ^ f.cf_name in
 					if not (is_method_defined ctx m static) then generate_self_method ctx rights m static true;
-					print ctx "%s $%s" rights (s_ident_field f.cf_name);
+					print ctx "%s $%s" rights (s_ident f.cf_name);
 					gen_assigned_value ctx f.cf_expr;
 					true
 				| _ ->
@@ -2032,6 +2060,7 @@ let generate_class ctx c =
 		} in
 		ctx.constructor_block <- true;
 		generate_field ctx false f;
+		ctx.constructor_block <- false;
 	);
 
 	List.iter (generate_field ctx false) c.cl_ordered_fields;
@@ -2123,11 +2152,13 @@ let createmain com e =
 
 	spr ctx "if(version_compare(PHP_VERSION, '5.1.0', '<')) {
     exit('Your current PHP version is: ' . PHP_VERSION . '. Haxe/PHP generates code for version 5.1.0 or later');
+} else if(version_compare(PHP_VERSION, '5.4.0', '<')) {
+	trigger_error('Your current PHP version is: ' . PHP_VERSION . '. Code generated by Haxe/PHP might not work for versions < 5.4.0', E_USER_WARNING);
 }";
 	newline ctx;
 	newline ctx;
 	spr ctx ("require_once dirname(__FILE__).'/" ^ ctx.lib_path ^ "/php/" ^ (prefix_class com "Boot.class.php';\n\n"));
-	gen_value ctx e;
+	gen_expr ctx e;
 	newline ctx;
 	spr ctx "\n?>";
 	close ctx

+ 176 - 127
genpy.ml → src/generators/genpy.ml

@@ -1,28 +1,26 @@
 (*
- * Copyright (C)2005-2014 Haxe Foundation
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
+	The 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 Type
 open Common
+open Codegen.ExprBuilder
 
 module Utils = struct
 	let class_of_module_type mt = match mt with
@@ -125,12 +123,12 @@ module Transformer = struct
 
 	and debug_expr e =
 		let s_type = Type.s_type (print_context()) in
-		let s = Type.s_expr_pretty "\t" s_type e in
+		let s = Type.s_expr_pretty 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 "\t" s_type e in
+		let es = Type.s_expr_pretty false "    " s_type e in
 		let t = s_type e.etype in
 		Printf.printf "%s : %s\n" es t
 
@@ -174,19 +172,19 @@ module Transformer = struct
 	let lift_expr1 is_value next_id blocks e =
 		lift_expr ~is_value:is_value ~next_id:(Some next_id) ~blocks:blocks e
 
-	let to_tvar ?(capture = false) n t =
-		alloc_var n t
+	let to_tvar ?(capture = false) n t p =
+		alloc_var n t p
 		(* { v_name = n; v_type = t; v_id = 0; v_capture = capture; v_extra = None; v_meta = [] } *)
 
 	let create_non_local n pos =
 		let s = "nonlocal " ^ (KeywordHandler.handle_keywords n) in
 		(* TODO: this is a hack... *)
-		let id = mk (TLocal (to_tvar "python_Syntax._pythonCode" t_dynamic ) ) !t_void pos in
-		let id2 = mk (TLocal( to_tvar s t_dynamic )) !t_void pos in
+		let id = mk (TLocal (to_tvar "python_Syntax._pythonCode" t_dynamic pos) ) !t_void pos in
+		let id2 = mk (TLocal( to_tvar s t_dynamic pos)) !t_void pos in
 		mk (TCall(id, [id2])) t_dynamic pos
 
 	let to_tlocal_expr ?(capture = false) n t p =
-		mk (TLocal (to_tvar ~capture:capture n t)) t p
+		mk (TLocal (to_tvar ~capture:capture n t p)) t p
 
 	let check_unification e t = match follow e.etype,follow t with
 		| TAnon an1, TAnon an2 ->
@@ -197,18 +195,19 @@ module Transformer = struct
 		| _ ->
 			e
 
-	let dynamic_field_read e s =
-		Utils.mk_static_call_2 ((!c_reflect)()) "field" [e;mk (TConst (TString s)) !t_string e.epos] e.epos
+	let dynamic_field_read e s t =
+		let e = Utils.mk_static_call_2 ((!c_reflect)()) "field" [e;mk (TConst (TString s)) !t_string e.epos] e.epos in
+		{ e with etype = t }
 
 	let dynamic_field_write e1 s e2 =
 		Utils.mk_static_call_2 ((!c_reflect)()) "setField" [e1;mk (TConst (TString s)) !t_string e1.epos;e2] e1.epos
 
-	let dynamic_field_read_write next_id e1 s op e2 =
+	let dynamic_field_read_write next_id e1 s op e2 t =
 		let id = next_id() in
-		let temp_var = to_tvar id e1.etype in
+		let temp_var = to_tvar id e1.etype e1.epos in
 		let temp_var_def = mk (TVar(temp_var,Some e1)) e1.etype e1.epos in
 		let temp_local = mk (TLocal temp_var) e1.etype e1.epos in
-		let e_field = dynamic_field_read temp_local s in
+		let e_field = dynamic_field_read temp_local s t in
 		let e_op = mk (TBinop(op,e_field,e2)) e_field.etype e_field.epos in
 		let e_set_field = dynamic_field_write temp_local s e_op in
 		mk (TBlock [
@@ -301,7 +300,7 @@ module Transformer = struct
 		let fn = add_non_locals_to_func fn in
 		if is_value then begin
 			let new_name = ae.a_next_id() in
-			let new_var = alloc_var new_name tf.tf_type in
+			let new_var = alloc_var new_name tf.tf_type p in
 			let new_local = mk (TLocal new_var) fn.etype p in
 			let def = mk (TVar(new_var,Some fn)) fn.etype p in
 			lift_expr1 false ae.a_next_id [def] new_local
@@ -405,7 +404,7 @@ module Transformer = struct
 			List.iter (fun es ->
 				match es.eexpr with
 				| TConst (TString s) ->
-					let l = String.length s in
+					let l = UTF8.length s in
 					let sl = try
 						Hashtbl.find length_map l
 					with Not_found ->
@@ -435,7 +434,7 @@ module Transformer = struct
 			let c_string = match !t_string with TInst(c,_) -> c | _ -> assert false in
 			let cf_length = PMap.find "length" c_string.cl_fields in
 			let ef = mk (TField(e1,FInstance(c_string,[],cf_length))) !t_int e1.epos in
-			let res_var = alloc_var (ae.a_next_id()) ef.etype in
+			let res_var = alloc_var (ae.a_next_id()) ef.etype ef.epos in
 			let res_local = {ef with eexpr = TLocal res_var} in
 			let var_expr = {ef with eexpr = TVar(res_var,Some ef)} in
 			let e = mk (TBlock [
@@ -448,7 +447,7 @@ module Transformer = struct
 		let e1_ = transform_expr e1 ~is_value:true ~next_id:(Some ae.a_next_id) in
 		let handle_as_local temp_local =
 			let ex = ae.a_expr in
-			let res_var = alloc_var (ae.a_next_id()) ex.etype in
+			let res_var = alloc_var (ae.a_next_id()) ex.etype ex.epos in
 			let res_local = {ex with eexpr = TLocal res_var} in
 			let plus = {ex with eexpr = TBinop(op,temp_local,one)} in
 			let var_expr = {ex with eexpr = TVar(res_var,Some temp_local)} in
@@ -475,17 +474,17 @@ module Transformer = struct
 				handle_as_local e1_.a_expr
 			| TArray(e1,e2) ->
 				let id = ae.a_next_id() in
-				let temp_var_l = alloc_var id e1.etype in
+				let temp_var_l = alloc_var id e1.etype e1.epos in
 				let temp_local_l = {e1 with eexpr = TLocal temp_var_l} in
 				let temp_var_l = {e1 with eexpr = TVar(temp_var_l,Some e1)} in
 
 				let id = ae.a_next_id() in
-				let temp_var_r = alloc_var id e2.etype in
+				let temp_var_r = alloc_var id e2.etype e2.epos in
 				let temp_local_r = {e2 with eexpr = TLocal temp_var_r} in
 				let temp_var_r = {e2 with eexpr = TVar(temp_var_r,Some e2)} in
 
 				let id = ae.a_next_id() in
-				let temp_var = alloc_var id e1_.a_expr.etype in
+				let temp_var = alloc_var id e1_.a_expr.etype e1_.a_expr.epos in
 				let temp_local = {e1_.a_expr with eexpr = TLocal temp_var} in
 				let temp_var_expr = {e1_.a_expr with eexpr = TArray(temp_local_l,temp_local_r)} in
 				let temp_var = {e1_.a_expr with eexpr = TVar(temp_var,Some temp_var_expr)} in
@@ -499,11 +498,11 @@ module Transformer = struct
 				end else
 					transform_exprs_to_block block ae.a_expr.etype false ae.a_expr.epos ae.a_next_id
 			| TField(e1,fa) ->
-				let temp_var_l = alloc_var (ae.a_next_id()) e1.etype in
+				let temp_var_l = alloc_var (ae.a_next_id()) e1.etype e1.epos in
 				let temp_local_l = {e1 with eexpr = TLocal temp_var_l} in
 				let temp_var_l = {e1 with eexpr = TVar(temp_var_l,Some e1)} in
 
-				let temp_var = alloc_var (ae.a_next_id()) e1_.a_expr.etype in
+				let temp_var = alloc_var (ae.a_next_id()) e1_.a_expr.etype e1_.a_expr.epos in
 				let temp_local = {e1_.a_expr with eexpr = TLocal temp_var} in
 				let temp_var_expr = {e1_.a_expr with eexpr = TField(temp_local_l,fa)} in
 				let temp_var = {e1_.a_expr with eexpr = TVar(temp_var,Some temp_var_expr)} in
@@ -521,7 +520,7 @@ module Transformer = struct
 				assert false
 
 	and var_to_treturn_expr ?(capture = false) n t p =
-		let x = mk (TLocal (to_tvar ~capture:capture n t)) t p in
+		let x = mk (TLocal (to_tvar ~capture:capture n t p)) t p in
 		mk (TReturn (Some x)) t p
 
 	and exprs_to_func exprs name base =
@@ -568,7 +567,7 @@ module Transformer = struct
 			in
 			let f1 = { tf_args = []; tf_type = TFun([],ex.etype); tf_expr = ex} in
 			let fexpr = mk (TFunction f1) ex.etype ex.epos in
-			let fvar = to_tvar name fexpr.etype in
+			let fvar = to_tvar name fexpr.etype fexpr.epos in
 			let f = add_non_locals_to_func fexpr in
 			let assign = { ex with eexpr = TVar(fvar, Some(f))} in
 			let call_expr = (mk (TLocal fvar) fexpr.etype ex.epos ) in
@@ -627,7 +626,7 @@ module Transformer = struct
 				tf_type = tr;
 				tf_expr = my_block.a_expr;
 			}) ae.a_expr.etype ae.a_expr.epos in
-			let t_var = alloc_var name ae.a_expr.etype in
+			let t_var = alloc_var name ae.a_expr.etype ae.a_expr.epos in
 			let f = add_non_locals_to_func fn in
 			let fn_assign = mk (TVar (t_var,Some f)) ae.a_expr.etype ae.a_expr.epos in
 			let ev = mk (TLocal t_var) ae.a_expr.etype ae.a_expr.epos in
@@ -656,11 +655,9 @@ module Transformer = struct
 			let a2 = to_expr (trans false [] e2) in
 
 			let name = (ae.a_next_id ()) in
-			let t_var = alloc_var name e1.etype in
+			let t_var = alloc_var name e1.etype e1.epos in
 
-			let mk_local v p = { eexpr = TLocal v; etype = v.v_type; epos = p } in
-
-			let ev = mk_local t_var e1.epos in
+			let ev = make_local t_var e1.epos in
 			let ehasnext = mk (TField(ev,quick_field e1.etype "hasNext")) (tfun [] (!t_bool) ) e1.epos in
 			let ehasnext = mk (TCall(ehasnext,[])) ehasnext.etype ehasnext.epos in
 
@@ -688,7 +685,7 @@ module Transformer = struct
 				tf_expr = e1;
 			}) ef.etype ef.epos in
 			let f1 = add_non_locals_to_func f in
-			let var_n = alloc_var n ef.etype in
+			let var_n = alloc_var n ef.etype ef.epos in
 			let f1_assign = mk (TVar(var_n,Some f1)) !t_void f1.epos in
 			let var_local = mk (TLocal var_n) ef.etype f1.epos in
 			let er = mk (TReturn (Some var_local)) t_dynamic  ae.a_expr.epos in
@@ -799,13 +796,13 @@ module Transformer = struct
 			end
 		(* anon field access on optional params *)
 		| (is_value, TField(e,FAnon cf)) when Meta.has Meta.Optional cf.cf_meta ->
-			let e = dynamic_field_read e cf.cf_name in
+			let e = dynamic_field_read e cf.cf_name ae.a_expr.etype in
 			transform_expr ~is_value:is_value e
 		| (is_value, TBinop(OpAssign,{eexpr = TField(e1,FAnon cf)},e2)) when Meta.has Meta.Optional cf.cf_meta ->
 			let e = dynamic_field_write e1 cf.cf_name e2 in
 			transform_expr ~is_value:is_value e
-		| (is_value, TBinop(OpAssignOp op,{eexpr = TField(e1,FAnon cf)},e2)) when Meta.has Meta.Optional cf.cf_meta ->
-			let e = dynamic_field_read_write ae.a_next_id e1 cf.cf_name op e2 in
+		| (is_value, TBinop(OpAssignOp op,{eexpr = TField(e1,FAnon cf); etype = t},e2)) when Meta.has Meta.Optional cf.cf_meta ->
+			let e = dynamic_field_read_write ae.a_next_id e1 cf.cf_name op e2 t in
 			transform_expr ~is_value:is_value e
 		(* TODO we need to deal with Increment, Decrement too!
 
@@ -835,16 +832,16 @@ module Transformer = struct
 			lift_expr ~blocks:e1.a_blocks r
 
 		| (is_value, TField(e,FDynamic s)) ->
-			let e = dynamic_field_read e s in
+			let e = dynamic_field_read e s ae.a_expr.etype in
 			transform_expr ~is_value:is_value e
 		| (is_value, TBinop(OpAssign,{eexpr = TField(e1,FDynamic s)},e2)) ->
 			let e = dynamic_field_write e1 s e2 in
 			transform_expr ~is_value:is_value e
-		| (is_value, TBinop(OpAssignOp op,{eexpr = TField(e1,FDynamic s)},e2)) ->
-			let e = dynamic_field_read_write ae.a_next_id e1 s op e2 in
+		| (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))) ->
-			let e = dynamic_field_read e1 cf.cf_name in
+			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
@@ -900,7 +897,7 @@ module Transformer = struct
 		| (true, TTry(etry, catches)) ->
 
 			let id = ae.a_next_id () in
-			let temp_var = to_tvar id a_expr.etype in
+			let temp_var = to_tvar id a_expr.etype a_expr.epos in
 			let temp_var_def = { a_expr with eexpr = TVar(temp_var, None) } in
 			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
@@ -1138,8 +1135,8 @@ module Printer = struct
 			| Some s -> handle_keywords s
 		in
 		let s_args = print_args tf.tf_args p in
-		let s_expr = print_expr {pctx with pc_indent = "\t" ^ pctx.pc_indent} tf.tf_expr in
-		Printf.sprintf "def %s(%s):\n%s\t%s" s_name s_args pctx.pc_indent s_expr
+		let s_expr = print_expr {pctx with pc_indent = "    " ^ pctx.pc_indent} tf.tf_expr in
+		Printf.sprintf "def %s(%s):\n%s    %s" s_name s_args pctx.pc_indent s_expr
 
 	and print_tarray_list pctx e1 e2 =
 		let s1 = (print_expr pctx e1) in
@@ -1180,7 +1177,7 @@ module Printer = struct
 
 	and print_expr pctx e =
 		let indent = pctx.pc_indent in
-		let print_expr_indented e = print_expr {pctx with pc_indent = "\t" ^ pctx.pc_indent} e in
+		let print_expr_indented e = print_expr {pctx with pc_indent = "    " ^ pctx.pc_indent} e in
 		match e.eexpr with
 			| TConst ct ->
 				print_constant ct
@@ -1254,6 +1251,8 @@ module Printer = struct
 					(* Printf.sprintf "(%s %s %s)" (print_expr pctx e1) (fst ops) (print_expr pctx e2) *)
 				| TInst({cl_path = [],("list")},_), _ ->
 					Printf.sprintf "(%s %s %s)" (print_expr pctx e1) (fst ops) (print_expr pctx e2)
+				| x, _ when is_underlying_array x ->
+					Printf.sprintf "(%s %s %s)" (print_expr pctx e1) (fst ops) (print_expr pctx e2)
 				| TDynamic _, TDynamic _ ->
 					Printf.sprintf "%s(%s,%s)" (third ops) (print_expr pctx e1) (print_expr pctx e2)
 				| TDynamic _, x | x, TDynamic _ when is_list_or_anon x ->
@@ -1342,7 +1341,7 @@ module Printer = struct
 			| TIf(econd,eif,eelse) ->
 				print_if_else pctx econd eif eelse false
 			| TWhile(econd,e1,NormalWhile) ->
-				Printf.sprintf "while %s:\n%s\t%s" (print_expr pctx (remove_outer_parens econd)) indent (print_expr_indented e1)
+				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
 			| TTry(e1,catches) ->
@@ -1381,15 +1380,15 @@ module Printer = struct
 			| TParenthesis e -> e
 			| _ -> econd
 		in
-		let if_str = print_expr {pctx with pc_indent = "\t" ^ pctx.pc_indent} eif in
+		let if_str = print_expr {pctx with pc_indent = "    " ^ pctx.pc_indent} eif in
 		let indent = pctx.pc_indent in
 		let else_str = if as_elif then
 			opt eelse (print_expr pctx) "el"
 		else
-			opt eelse (print_expr {pctx with pc_indent = "\t" ^ pctx.pc_indent}) (Printf.sprintf "else:\n%s\t" indent)
+			opt eelse (print_expr {pctx with pc_indent = "    " ^ pctx.pc_indent}) (Printf.sprintf "else:\n%s    " indent)
 		in
 		let else_str = if else_str = "" then "" else "\n" ^ indent ^ else_str in
-		Printf.sprintf "if %s:\n%s\t%s%s" (print_expr pctx (remove_outer_parens econd1)) indent if_str else_str
+		Printf.sprintf "if %s:\n%s    %s%s" (print_expr pctx (remove_outer_parens econd1)) indent if_str else_str
 
 	and print_field pctx e1 fa is_assign =
 		let obj = match e1.eexpr with
@@ -1453,7 +1452,7 @@ module Printer = struct
 			let handle_base_type bt =
 				let t = print_base_type bt in
 				let print_type_check t_str =
-					Printf.sprintf "if isinstance(_hx_e1, %s):\n%s\t%s\t%s" t_str indent assign (print_expr {pctx with pc_indent = "\t" ^ pctx.pc_indent} e)
+					Printf.sprintf "if isinstance(_hx_e1, %s):\n%s    %s    %s" t_str indent assign (print_expr {pctx with pc_indent = "    " ^ pctx.pc_indent} e)
 				in
 				let res = match t with
 				| "str" -> print_type_check "str"
@@ -1473,7 +1472,7 @@ module Printer = struct
 						Printf.sprintf "%s%s" assign (print_expr pctx e)
 					else
 						(* Dynamic is always the last block *)
-						Printf.sprintf "%selse:\n\t%s%s\t%s" indent indent assign (print_expr {pctx with pc_indent = "\t" ^ pctx.pc_indent} e)
+						Printf.sprintf "%selse:\n    %s%s    %s" indent indent assign (print_expr {pctx with pc_indent = "    " ^ pctx.pc_indent} e)
 					end
 				| TInst(c,_) ->
 					handle_base_type (t_infos (TClassDecl c))
@@ -1485,15 +1484,15 @@ module Printer = struct
 					assert false
 		in
 		let indent = pctx.pc_indent in
-		let print_expr_indented e = print_expr {pctx with pc_indent = "\t" ^ pctx.pc_indent} e in
-		let try_str = Printf.sprintf "try:\n%s\t%s\n%s" indent (print_expr_indented e1) indent in
+		let print_expr_indented e = print_expr {pctx with pc_indent = "    " ^ pctx.pc_indent} e in
+		let try_str = Printf.sprintf "try:\n%s    %s\n%s" indent (print_expr_indented e1) indent in
 		let except = if has_feature pctx "has_throw" then
-			Printf.sprintf "except Exception as _hx_e:\n%s\t_hx_e1 = _hx_e.val if isinstance(_hx_e, _HxException) else _hx_e\n%s\t" indent indent
+			Printf.sprintf "except Exception as _hx_e:\n%s    _hx_e1 = _hx_e.val if isinstance(_hx_e, _HxException) else _hx_e\n%s    " indent indent
 		else
-			Printf.sprintf "except Exception as _hx_e:\n%s\t_hx_e1 = _hx_e\n%s\t" indent indent
+			Printf.sprintf "except Exception as _hx_e:\n%s    _hx_e1 = _hx_e\n%s    " indent indent
 		in
-		let catch_str = String.concat (Printf.sprintf "\n") (ExtList.List.mapi (fun i catch -> print_catch {pctx with pc_indent = "\t" ^ pctx.pc_indent} i catch) catches) in
-		let except_end = if not has_catch_all then Printf.sprintf "\n%s\telse:\n%s\t\traise _hx_e" indent indent else "" in
+		let catch_str = String.concat (Printf.sprintf "\n") (ExtList.List.mapi (fun i catch -> print_catch {pctx with pc_indent = "    " ^ pctx.pc_indent} i catch) catches) in
+		let except_end = if not has_catch_all then Printf.sprintf "\n%s    else:\n%s        raise _hx_e" indent indent else "" in
 		Printf.sprintf "%s%s%s%s" try_str except catch_str except_end
 
 	and print_call2 pctx e1 el =
@@ -1570,7 +1569,7 @@ module Printer = struct
 				in
 				Printf.sprintf "%s[%s%s]" (print_expr pctx e1) (print_exprs pctx ":" el) (if trailing_colon then ":" else "")
 			| "python_Syntax.isIn",[e1;e2] ->
-				Printf.sprintf "%s in %s" (print_expr pctx e1) (print_expr pctx e2)
+				Printf.sprintf "(%s in %s)" (print_expr pctx e1) (print_expr pctx e2)
 			| "python_Syntax.delete",[e1] ->
 				Printf.sprintf "del %s" (print_expr pctx e1)
 			| "python_Syntax.binop",[e0;{eexpr = TConst(TString id)};e2] ->
@@ -1584,7 +1583,7 @@ module Printer = struct
 			| "python_Syntax.opPow", [e1;e2] ->
 				Printf.sprintf "(%s ** %s)" (print_expr pctx e1) (print_expr pctx e2)
  			| "python_Syntax._foreach",[e1;e2;e3] ->
-				let pctx = {pctx with pc_indent = "\t" ^ pctx.pc_indent} in
+				let pctx = {pctx with pc_indent = "    " ^ pctx.pc_indent} in
 				let i = pctx.pc_indent in
 				Printf.sprintf "for %s in %s:\n%s%s" (print_expr pctx e1) (print_expr pctx e2) i (print_expr pctx e3)
 			| _,el ->
@@ -1687,7 +1686,7 @@ module Printer = struct
 			print_exprs pctx sep el
 
 	and print_exprs_named pctx sep fl =
-		let args = String.concat sep (List.map (fun (s,e) -> Printf.sprintf "'%s': %s" (handle_keywords s) (print_expr pctx e)) fl) in
+		let args = String.concat sep (List.map (fun (s,e) -> Printf.sprintf "'%s': %s" (Ast.s_escape (handle_keywords s)) (print_expr pctx e)) fl) in
 		Printf.sprintf "{%s}" args
 	and print_params_named pctx sep fl =
 		let args = String.concat sep (List.map (fun (s,e) -> Printf.sprintf "%s= %s" (handle_keywords s) (print_expr pctx e)) fl) in
@@ -1836,7 +1835,7 @@ module Generator = struct
 		) metas
 
 	let gen_expr ctx e field indent =
-		let pctx = Printer.create_context ("\t" ^ indent) ctx.com ctx.com.debug in
+		let pctx = Printer.create_context ("    " ^ indent) ctx.com ctx.com.debug in
 		let e = match e.eexpr with
 			| TFunction(f) ->
 				{e with eexpr = TBlock [e]}
@@ -1851,7 +1850,7 @@ module Generator = struct
 					| e_last :: el ->
 						let new_last = {e_last with eexpr = TReturn (Some e_last)} in
 						let new_block = {expr2 with eexpr = TBlock (List.rev (new_last :: el))} in
-						let v_name = alloc_var name (tfun [] e_last.etype) in
+						let v_name = alloc_var name (tfun [] e_last.etype) e_last.epos in
 						let f_name = mk (TLocal v_name) v_name.v_type e_last.epos in
 						let call_f = mk (TCall(f_name,[])) e_last.etype e_last.epos in
 						Some new_block,call_f
@@ -1866,7 +1865,7 @@ module Generator = struct
 			| Some e1,e2 ->
 				let expr_string_1 = texpr_str e1 pctx in
 				let expr_string_2 = texpr_str e2 pctx in
-				print ctx "%sdef %s():\n\t%s" indent name expr_string_1;
+				print ctx "%sdef %s():\n    %s" indent name expr_string_1;
 				newline ctx;
 				print ctx "%s%s = %s" indent field expr_string_2;
 			| None,e2 ->
@@ -1881,7 +1880,7 @@ module Generator = struct
 		let e = match e.eexpr with
 			| TFunction(f) ->
 				let args = List.map (fun s ->
-					alloc_var s t_dynamic,None
+					alloc_var s t_dynamic p,None
 				) extra_args in
 				{e with eexpr = TFunction {f with tf_args = args @ f.tf_args}}
 			| _ ->
@@ -1909,17 +1908,40 @@ module Generator = struct
 		begin match cf.cf_expr with
 			| Some ({eexpr = TFunction f} as ef) ->
 				let ethis = mk (TConst TThis) (TInst(c,List.map snd c.cl_params)) cf.cf_pos in
-				let member_data = List.map (fun cf ->
-					let ef = mk (TField(ethis,FInstance(c,[],cf))) cf.cf_type cf.cf_pos in (* TODO *)
-					mk (TBinop(OpAssign,ef,null ef.etype ef.epos)) ef.etype ef.epos
-				) member_inits in
+				let assigned_fields = ref [] in
+				(* Collect all fields that are assigned to but panic out as soon as `this`,
+				   `super`, `return` or `throw` appears (regardless of control flow). *)
+				let collect_assignments e =
+					let rec loop e = match e.eexpr with
+						| TBinop(OpAssign,{eexpr = TField({eexpr = TConst TThis}, FInstance(_,_,cf))},e2) ->
+							loop e2;
+							assigned_fields := cf :: !assigned_fields
+						| TConst (TSuper | TThis) | TThrow _ | TReturn _ ->
+							raise Exit
+						(* TODO: We could do some branch intersection stunts to make this more accurate. *)
+						| TIf(e1,_,_) | TSwitch(e1,_,_) | TWhile(e1,_,_) ->
+							loop e1
+						| _ ->
+							Type.iter loop e
+					in
+					try loop e with Exit -> ()
+				in
+				collect_assignments f.tf_expr;
+				let member_data = List.fold_left (fun acc cf ->
+					if not (List.memq cf !assigned_fields) then begin
+						let ef = mk (TField(ethis,FInstance(c,[],cf))) cf.cf_type cf.cf_pos in (* TODO *)
+						let e = mk (TBinop(OpAssign,ef,null ef.etype ef.epos)) ef.etype ef.epos in
+						e :: acc
+					end else
+						acc
+				) [] member_inits in
 				let e = concat (mk (TBlock member_data) ctx.com.basic.tvoid cf.cf_pos) f.tf_expr in
 				let ef = {ef with eexpr = TFunction {f with tf_expr = e}} in
 				cf.cf_expr <- Some ef;
 
 				newline ctx;
 				newline ctx;
-				gen_func_expr ctx ef c "__init__" py_metas ["self"] "\t" false cf.cf_pos
+				gen_func_expr ctx ef c "__init__" py_metas ["self"] "    " false cf.cf_pos
 			| _ ->
 				assert false
 		end
@@ -1928,17 +1950,17 @@ module Generator = struct
 		let field = handle_keywords cf.cf_name in
 		begin match cf.cf_expr with
 			| None ->
-				()(* print ctx "\t# var %s" field *)
+				()(* print ctx "    # var %s" field *)
 			| Some e ->
 				newline ctx;
 				newline ctx;
 				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"] "\t" false cf.cf_pos;
+						gen_func_expr ctx e c field py_metas ["self"] "    " false cf.cf_pos;
 
 					| _ ->
-						gen_expr ctx e (Printf.sprintf "# var %s" field) "\t";
+						gen_expr ctx e (Printf.sprintf "# var %s" field) "    ";
 				end
 		end
 
@@ -1946,7 +1968,7 @@ module Generator = struct
 		if has_feature ctx "Type.createEmptyInstance" then begin
 			newline ctx;
 			newline ctx;
-			print ctx "\t@staticmethod\n\tdef _hx_empty_init(_hx_o):";
+			print ctx "    @staticmethod\n    def _hx_empty_init(_hx_o):";
 			let found_fields = ref false in
 			List.iter (fun cf -> match cf.cf_kind with
 					| Var ({v_read = AccResolve | AccCall}) ->
@@ -1954,12 +1976,12 @@ module Generator = struct
 					| Var _ ->
 						found_fields := true;
 						newline ctx;
-						print ctx "\t\t_hx_o.%s = None" (handle_keywords cf.cf_name)
+						print ctx "        _hx_o.%s = None" (handle_keywords cf.cf_name)
 					| _ ->
 						()
 			) cfl;
 			if not !found_fields then
-				spr ctx "\t\tpass"
+				spr ctx "        pass"
 		end else begin
 			newline ctx
 		end
@@ -1980,7 +2002,7 @@ module Generator = struct
 			| None ->
 				has_empty_static_vars := true;
 				newline ctx;
-				print ctx "\t%s = None" field
+				print ctx "    %s = None" field
 			| Some e ->
 				(let f = fun () ->
 					newline ctx;
@@ -1997,7 +2019,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 [] "\t" true cf.cf_pos;
+			gen_func_expr ctx e c field py_metas [] "    " true cf.cf_pos;
 		) methods;
 
 		!has_static_methods || !has_empty_static_vars
@@ -2047,7 +2069,7 @@ module Generator = struct
 			if not is_nativegen then begin
 				if has_feature ctx "python._hx_class_name" then begin
 					use_pass := false;
-					print ctx "\n\t_hx_class_name = \"%s\"" p_name
+					print ctx "\n    _hx_class_name = \"%s\"" p_name
 				end;
 
 				let print_field names field quote =
@@ -2061,10 +2083,29 @@ module Generator = struct
 								"[" ^ (String.concat ", " (List.map q names)) ^ "]"
 						in
 						use_pass := false;
-						print ctx "\n\t%s = %s" field s
+						print ctx "\n    %s = %s" field s
 					with Exit -> ()
 				in
 
+				(try (
+					let real_fields =
+						List.filter (fun f -> match f.cf_kind with
+							| Method MethDynamic -> raise Exit (* if a class has dynamic method, we can't use __slots__ because python will complain *)
+							| Var _ -> not (is_extern_field f)
+							| _ -> false
+						) c.cl_ordered_fields
+					in
+					let field_names = List.map (fun f -> handle_keywords f.cf_name) real_fields in
+					let field_names = match c.cl_dynamic with Some _ -> "__dict__" :: field_names | None -> field_names in
+					use_pass := false;
+					print ctx "\n    __slots__ = (";
+					(match field_names with
+					| [] -> ()
+					| [name] -> print ctx "\"%s\"," name
+					| names -> print ctx "\"%s\"" (String.concat "\", \"" names));
+					print ctx ")";
+				) with Exit -> ());
+
 				print_field x.cfd_fields "_hx_fields" true;
 				print_field x.cfd_methods "_hx_methods" true;
 				(* TODO: It seems strange to have a separation for member fields but a plain _hx_statics for static ones *)
@@ -2075,7 +2116,7 @@ module Generator = struct
 					| None -> ()
 					| Some ps ->
 						use_pass := false;
-						print ctx "\n\t_hx_super = %s\n" ps
+						print ctx "\n    _hx_super = %s\n" ps
 				);
 
 			end;
@@ -2101,7 +2142,7 @@ module Generator = struct
 				| [] -> c.cl_constructor = None
 				| _ -> c.cl_interface
 			in
-			if use_pass then spr ctx "\n\tpass";
+			if use_pass then spr ctx "\n    pass";
 
 			if not is_nativegen then begin
 				if has_feature ctx "python._hx_class" then print ctx "\n%s._hx_class = %s" p p;
@@ -2131,18 +2172,15 @@ module Generator = struct
 		newline ctx;
 		newline ctx;
 		print ctx "class %s(Enum):" p;
-
-		let use_pass = ref true in
+		print ctx "\n    __slots__ = ()";
 
 		if has_feature ctx "python._hx_class_name" then begin
-			use_pass := false;
-			print ctx "\n\t_hx_class_name = \"%s\"" p_name
+			print ctx "\n    _hx_class_name = \"%s\"" p_name
 		end;
 		if has_feature ctx "python._hx_constructs" then begin
 			let fix = match enum_constructs with [] -> "" | _ -> "\"" in
 			let enum_constructs_str = fix ^ (String.concat ("\", \"") (List.map (fun ef -> ef.ef_name) enum_constructs)) ^ fix in
-			use_pass := false;
-			print ctx "\n\t_hx_constructs = [%s]" enum_constructs_str;
+			print ctx "\n    _hx_constructs = [%s]" enum_constructs_str;
 		end;
 
 		let const_constructors,param_constructors = List.partition (fun ef ->
@@ -2175,14 +2213,11 @@ module Generator = struct
 				let args_str = String.concat "," (List.map (fun (n,_,_) -> handle_keywords n) args) in
 				newline ctx;
 				newline ctx;
-				print ctx "\t@staticmethod\n\tdef %s(%s):\n" f param_str;
-				print ctx "\t\treturn %s(\"%s\", %i, [%s])" p ef.ef_name ef.ef_index args_str;
-				use_pass := false;
+				print ctx "    @staticmethod\n    def %s(%s):\n" f param_str;
+				print ctx "        return %s(\"%s\", %i, [%s])" p ef.ef_name ef.ef_index args_str;
 			| _ -> assert false
 		) param_constructors;
 
-		if !use_pass then spr ctx "\n\tpass";
-
 		List.iter (fun ef ->
 			(* TODO: haxe source has api.quoteString for ef.ef_name *)
 			let f = handle_keywords ef.ef_name in
@@ -2208,7 +2243,7 @@ module Generator = struct
 
 		if has_feature ctx "python._hx_class_name" then begin
 			use_pass := false;
-			print ctx "\n\t_hx_class_name = \"%s\"" p_name
+			print ctx "\n    _hx_class_name = \"%s\"" p_name
 		end;
 
 		(match a.a_impl with
@@ -2222,7 +2257,7 @@ module Generator = struct
 			) c.cl_ordered_statics
 		| None -> ());
 
-		if !use_pass then spr ctx "\n\tpass";
+		if !use_pass then spr ctx "\n    pass";
 
 		if has_feature ctx "python._hx_class" then print ctx "\n%s._hx_class = %s" p p;
 		if has_feature ctx "python._hx_classes" then print ctx "\n_hx_classes[\"%s\"] = %s" p_name p
@@ -2255,14 +2290,14 @@ module Generator = struct
 			newline ctx;
 			newline ctx;
 			spr ctx "def _hx_resources__():";
-			spr ctx "\n\timport inspect";
-			spr ctx "\n\timport sys";
-			spr ctx "\n\tif not hasattr(sys.modules[__name__], '__file__'):";
-			print ctx "\n\t\t_file = '%s'" file_name;
-			spr ctx "\n\telse:";
-			spr ctx "\n\t\t_file = __file__";
-
-			spr ctx "\n\treturn {";
+			spr ctx "\n    import inspect";
+			spr ctx "\n    import sys";
+			spr ctx "\n    if not hasattr(sys.modules[__name__], '__file__'):";
+			print ctx "\n        _file = '%s'" file_name;
+			spr ctx "\n    else:";
+			spr ctx "\n        _file = __file__";
+
+			spr ctx "\n    return {";
 			let first = ref true in
 			Hashtbl.iter (fun k v ->
 				let prefix = if !first then begin
@@ -2273,7 +2308,10 @@ module Generator = struct
 				in
 				let k_enc = Codegen.escape_res_name k false in
 				print ctx "%s\"%s\": open('%%s.%%s'%%(_file,'%s'),'rb').read()" prefix (Ast.s_escape k) k_enc;
-				Std.output_file (ctx.com.file ^ "." ^ k_enc) v
+
+				let f = open_out_bin (ctx.com.file ^ "." ^ k_enc) in
+				output_string f v;
+				close_out f
 			) ctx.com.resources;
 			spr ctx "}"
 		end
@@ -2309,7 +2347,10 @@ module Generator = struct
 				let import = match import_type with
 					| IModule module_name ->
 						(* importing whole module *)
-						"import " ^ module_name ^ " as " ^ class_name
+						if module_name = class_name then
+							"import " ^ module_name
+						else
+							"import " ^ module_name ^ " as " ^ class_name
 
 					| IObject (module_name,object_name) ->
 						if String.contains object_name '.' then
@@ -2317,13 +2358,16 @@ module Generator = struct
 							"import " ^ module_name ^ " as _hx_temp_import; " ^ class_name ^ " = _hx_temp_import." ^ object_name ^ "; del _hx_temp_import"
 						else
 							(* importing a class from a module *)
-							"from " ^ module_name ^ " import " ^ object_name ^ " as " ^ class_name
+							if object_name = class_name then
+								"from " ^ module_name ^ " import " ^ object_name
+							else
+								"from " ^ module_name ^ " import " ^ object_name ^ " as " ^ class_name
 				in
 				newline ctx;
 				if ignore_error then begin
-					spr ctx "try:\n\t";
+					spr ctx "try:\n    ";
 					spr_line ctx import;
-					spr ctx "except:\n\tpass"
+					spr ctx "except:\n    pass"
 				end else
 					spr ctx import
 			end
@@ -2342,17 +2386,17 @@ module Generator = struct
 			Utils.find_type ctx.com path
 		in
 		let need_anon_for_trace = (has_feature ctx "has_anon_trace") && (has_feature ctx "haxe.Log.trace") in
-		if (has_feature ctx "has_anon") || (has_feature ctx "_hx_AnonObject") || need_anon_for_trace then begin
-			let with_body = (has_feature ctx "has_anon") || need_anon_for_trace in
+		if (has_feature ctx "has_anon") || (has_feature ctx "_hx_AnonObject") || (has_feature ctx "has_metadata") || need_anon_for_trace then begin
+			let with_body = (has_feature ctx "has_anon") || (has_feature ctx "has_metadata") || need_anon_for_trace in
 			newline ctx;
 			newline ctx;
 			newline ctx;
 			spr ctx "class _hx_AnonObject:\n";
 			if with_body then begin
-				spr ctx "\tdef __init__(self, fields):\n";
-				spr ctx "\t\tself.__dict__ = fields"
+				spr ctx "    def __init__(self, fields):\n";
+				spr ctx "        self.__dict__ = fields"
 			end else
-				spr ctx "\tpass";
+				spr ctx "    pass";
 			Hashtbl.add used_paths ([],"_hx_AnonObject") true;
 		end;
 		if has_feature ctx "python._hx_classes" then begin
@@ -2387,13 +2431,18 @@ module Generator = struct
 			| Some e ->
 				newline ctx;
 				newline ctx;
-				gen_expr ctx e "" ""
+				match e.eexpr with
+				| TBlock el ->
+					List.iter (fun e -> gen_expr ctx e "" ""; newline ctx) el
+				| _ ->
+					gen_expr ctx e "" ""
 
 	(* Entry point *)
 
 	let run com =
 		Transformer.init com;
 		let ctx = mk_context com in
+		Codegen.map_source_header com (fun s -> print ctx "# %s\n" s);
 		gen_imports ctx;
 		gen_resources ctx;
 		gen_types ctx;

+ 41 - 44
genswf.ml → src/generators/genswf.ml

@@ -1,23 +1,20 @@
 (*
- * Copyright (C)2005-2013 Haxe Foundation
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
+	The 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 Swf
@@ -52,7 +49,7 @@ let rec make_tpath = function
 		{
 			tpackage = pack;
 			tname = name;
-			tparams = if !pdyn then [TPType (CTPath { tpackage = []; tname = "Dynamic"; tparams = []; tsub = None; })] else[];
+			tparams = if !pdyn then [TPType (CTPath { tpackage = []; tname = "Dynamic"; tparams = []; tsub = None; },null_pos)] else[];
 			tsub = None;
 		}
 	| HMName (id,ns) ->
@@ -90,11 +87,11 @@ let rec make_tpath = function
 	| HMAny ->
 		assert false
 	| HMParams (t,params) ->
-		let params = List.map (fun t -> TPType (CTPath (make_tpath t))) params in
+		let params = List.map (fun t -> TPType (CTPath (make_tpath t),null_pos)) params in
 		{ (make_tpath t) with tparams = params }
 
 let make_param cl p =
-	{ tpackage = fst cl; tname = snd cl; tparams = [TPType (CTPath { tpackage = fst p; tname = snd p; tparams = []; tsub = None })]; tsub = None }
+	{ tpackage = fst cl; tname = snd cl; tparams = [TPType (CTPath { tpackage = fst p; tname = snd p; tparams = []; tsub = None },null_pos)]; tsub = None }
 
 let make_topt = function
 	| None -> { tpackage = []; tname = "Dynamic"; tparams = []; tsub = None }
@@ -125,12 +122,12 @@ let build_class com c file =
 	match path with
 	| { tpackage = ["flash";"utils"]; tname = ("Object"|"Function") } ->
 		let inf = {
-			d_name = path.tname;
+			d_name = path.tname,null_pos;
 			d_doc = None;
 			d_params = [];
 			d_meta = [];
 			d_flags = [];
-			d_data = CTPath { tpackage = []; tname = "Dynamic"; tparams = []; tsub = None; };
+			d_data = CTPath { tpackage = []; tname = "Dynamic"; tparams = []; tsub = None; },null_pos;
 		} in
 		(path.tpackage, [(ETypedef inf,pos)])
 	| _ ->
@@ -140,7 +137,7 @@ let build_class com c file =
 	let flags = (match c.hlc_super with
 		| None | Some (HMPath ([],"Object")) -> flags
 		| Some (HMPath ([],"Function")) -> flags (* found in AIR SDK *)
-		| Some s -> HExtends (make_tpath s) :: flags
+		| Some s -> HExtends (make_tpath s,null_pos) :: flags
 	) in
 	let flags = List.map (fun i ->
 		let i = (match i with
@@ -154,9 +151,9 @@ let build_class com c file =
 			| HMPath _ -> i
 			| _ -> assert false
 		) in
-		if c.hlc_interface then HExtends (make_tpath i) else HImplements (make_tpath i)
+		if c.hlc_interface then HExtends (make_tpath i,null_pos) else HImplements (make_tpath i,null_pos)
 	) (Array.to_list c.hlc_implements) @ flags in
-	let flags = if c.hlc_sealed || Common.defined com Define.FlashStrict then flags else HImplements (make_tpath (HMPath ([],"Dynamic"))) :: flags in
+	let flags = if c.hlc_sealed || Common.defined com Define.FlashStrict then flags else HImplements (make_tpath (HMPath ([],"Dynamic")),null_pos) :: flags in
 	(* make fields *)
 	let getters = Hashtbl.create 0 in
 	let setters = Hashtbl.create 0 in
@@ -189,7 +186,7 @@ let build_class com c file =
 			List.map (fun (s,cl) -> s, List.map (fun c -> EConst c,pos) cl, pos) (!meta)
 		in
 		let cf = {
-			cff_name = name;
+			cff_name = name,null_pos;
 			cff_doc = None;
 			cff_pos = pos;
 			cff_meta = mk_meta();
@@ -199,9 +196,9 @@ 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),None)
+				cf.cff_kind <- FProp ("default","never",Some (make_type v.hlv_type,null_pos),None)
 			else
-				cf.cff_kind <- FVar (Some (make_dyn_type v.hlv_type),None);
+				cf.cff_kind <- FVar (Some (make_dyn_type v.hlv_type,null_pos),None);
 			cf :: acc
 		| HFMethod m when m.hlm_override ->
 			Hashtbl.add override (name,stat) ();
@@ -252,15 +249,15 @@ let build_class com c file =
 								meta := (Meta.DefParam,[String aname;v]) :: !meta;
 								Some (EConst v,pos)
 					in
-					(aname,!is_opt,Some t,def_val)
+					((aname,null_pos),!is_opt,[],Some (t,null_pos),def_val)
 				) t.hlmt_args in
 				let args = if t.hlmt_var_args then
-					args @ List.map (fun _ -> incr pn; ("p" ^ string_of_int !pn,true,Some (make_type None),None)) [1;2;3;4;5]
+					args @ List.map (fun _ -> incr pn; (("p" ^ string_of_int !pn,null_pos),true,[],Some (make_type None,null_pos),None)) [1;2;3;4;5]
 				else args in
 				let f = {
 					f_params = [];
 					f_args = args;
-					f_type = Some (make_type t.hlmt_ret);
+					f_type = Some (make_type t.hlmt_ret,null_pos);
 					f_expr = None;
 				} in
 				cf.cff_meta <- mk_meta();
@@ -299,12 +296,12 @@ let build_class com c file =
 		let flags = [APublic] in
 		let flags = if stat then AStatic :: flags else flags in
 		{
-			cff_name = name;
+			cff_name = name,null_pos;
 			cff_pos = pos;
 			cff_doc = None;
 			cff_access = flags;
 			cff_meta = [];
-			cff_kind = if get && set then FVar (Some (make_dyn_type t), None) else FProp ((if get then "default" else "never"),(if set then "default" else "never"),Some (make_dyn_type t),None);
+			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);
 		}
 	in
 	let fields = Hashtbl.fold (fun (name,stat) t acc ->
@@ -326,7 +323,8 @@ let build_class com c file =
 			| [] -> []
 			| f :: l ->
 				match f.cff_kind with
-				| FVar (Some (CTPath { tpackage = []; tname = ("String" | "Int" | "UInt") as tname }),None) when List.mem AStatic f.cff_access ->
+				| 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 ->
 					if !real_type = "" then real_type := tname else if !real_type <> tname then raise Exit;
 					{
 						ec_name = f.cff_name;
@@ -337,7 +335,7 @@ let build_class com c file =
 						ec_doc = None;
 						ec_type = None;
 					} :: loop l
-				| FFun { f_args = [] } when f.cff_name = "new" -> loop l
+				| FFun { f_args = [] } when fst f.cff_name = "new" -> loop l
 				| _ -> raise Exit
 		in
 		List.iter (function HExtends _ | HImplements _ -> raise Exit | _ -> ()) flags;
@@ -345,7 +343,7 @@ let build_class com c file =
 		let name = "fakeEnum:" ^ String.concat "." (path.tpackage @ [path.tname]) in
 		if not (Common.raw_defined com name) then raise Exit;
 		let enum_data = {
-			d_name = path.tname;
+			d_name = path.tname,null_pos;
 			d_doc = None;
 			d_params = [];
 			d_meta = [(Meta.FakeEnum,[EConst (Ident !real_type),pos],pos)];
@@ -355,10 +353,10 @@ let build_class com c file =
 		(path.tpackage, [(EEnum enum_data,pos)])
 	with Exit ->
 	let class_data = {
-		d_name = path.tname;
+		d_name = path.tname,null_pos;
 		d_doc = None;
 		d_params = [];
-		d_meta = if c.hlc_final && List.exists (fun f -> f.cff_name <> "new" && not (List.mem AStatic f.cff_access)) fields then [Meta.Final,[],pos] else [];
+		d_meta = if c.hlc_final && List.exists (fun f -> fst f.cff_name <> "new" && not (List.mem AStatic f.cff_access)) fields then [Meta.Final,[],pos] else [];
 		d_flags = flags;
 		d_data = fields;
 	} in
@@ -1076,8 +1074,7 @@ let merge com file priority (h1,tags1) (h2,tags2) =
 	let tags = loop tags1 tags2 in
 	header, tags
 
-let generate com swf_header =
-	let t = Common.timer "generate swf" in
+let generate swf_header com =
 	let swc = if Common.defined com Define.Swc then Some (ref "") else None in
 	let file , codeclip = (try let f , c = ExtString.String.split com.file "@" in f, Some c with _ -> com.file , None) in
 	(* list exports *)
@@ -1115,6 +1112,7 @@ let generate com swf_header =
 	let tags = build_swf9 com file swc in
 	let header, bg = (match swf_header with None -> default_header com | Some h -> convert_header com h) in
 	let bg = tag (TSetBgColor { cr = bg lsr 16; cg = (bg lsr 8) land 0xFF; cb = bg land 0xFF }) in
+	let scene = tag ~ext:true (TScenes ([(0,"Scene1")],[])) in
 	let swf_debug_password = try
 		Digest.to_hex(Digest.string (Common.defined_value com Define.SwfDebugPassword))
 	with Not_found ->
@@ -1147,7 +1145,7 @@ let generate com swf_header =
 	with Not_found ->
 		[]
 	in
-	let swf = header, fattr @ meta_data @ bg :: debug @ swf_script_limits @ tags @ [tag TShowFrame] in
+	let swf = header, fattr @ meta_data @ bg :: scene :: debug @ swf_script_limits @ tags @ [tag TShowFrame] in
 	(* merge swf libraries *)
 	let priority = ref (swf_header = None) in
 	let swf = List.fold_left (fun swf (file,lib,cl) ->
@@ -1165,7 +1163,6 @@ let generate com swf_header =
 		in
 		{header with h_frame_count = header.h_frame_count + 1},loop tags
 	| _ -> swf in
-	t();
 	(* write swf/swc *)
 	let t = Common.timer "write swf" in
 	let level = (try int_of_string (Common.defined_value com Define.SwfCompressLevel) with Not_found -> 9) in

+ 33 - 24
genswf9.ml → src/generators/genswf9.ml

@@ -1,23 +1,20 @@
 (*
- * Copyright (C)2005-2013 Haxe Foundation
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
+	The 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
@@ -216,9 +213,12 @@ let rec type_id ctx t =
 	| TInst ({ cl_path = ["haxe"],"Int32" },_) ->
 		type_path ctx ([],"Int")
 	| TInst ({ cl_path = ["flash"],"Vector" } as c,pl) ->
+		let def() = HMParams (type_path ctx c.cl_path,List.map (type_id ctx) pl) in
 		(match pl with
-		| [TInst({cl_kind = KTypeParameter _},_)] -> type_path ctx ([],"Object")
-		| _ -> HMParams (type_path ctx c.cl_path,List.map (type_id ctx) pl))
+		| [t] -> (match follow t with
+			| TInst({cl_kind = KTypeParameter _},_) -> type_path ctx ([],"Object")
+			| _ -> def())
+		| _ -> def())
 	| TInst (c,_) ->
 		(match c.cl_kind with
 		| KTypeParameter l ->
@@ -1994,7 +1994,7 @@ let generate_field_kind ctx f c stat =
 			) args;
 			let dparams = (match !dparams with None -> None | Some l -> Some (List.rev l)) in
 			Some (HFMethod {
-				hlm_type = end_fun ctx (List.map (fun (a,opt,t) -> alloc_var a t, (if opt then Some TNull else None)) args) dparams tret;
+				hlm_type = end_fun ctx (List.map (fun (a,opt,t) -> alloc_var a t f.cf_pos, (if opt then Some TNull else None)) args) dparams tret;
 				hlm_final = false;
 				hlm_override = false;
 				hlm_kind = snd (method_kind());
@@ -2020,7 +2020,16 @@ let check_constructor ctx c f =
 			error "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() *)
+	let rec has_super e =
+		Type.iter has_super e;
+		match e.eexpr with
+		| TCall ({ eexpr = TConst TSuper },_) -> raise Exit
+		| _ -> ()
+	in
 	try
+		has_super f.tf_expr
+	with Exit -> try
 		loop f.tf_expr
 	with Exit ->
 		()
@@ -2184,7 +2193,7 @@ let generate_class ctx c =
 let generate_enum ctx e meta =
 	let name_id = type_path ctx e.e_path in
 	let api = ctx.com.basic in
-	let f = begin_fun ctx [alloc_var "tag" api.tstring, None;alloc_var "index" api.tint, None;alloc_var "params" (api.tarray (mk_mono())), None] api.tvoid [ethis] false e.e_pos in
+	let f = begin_fun ctx [alloc_var "tag" api.tstring e.e_pos, None;alloc_var "index" api.tint e.e_pos, None;alloc_var "params" (api.tarray (mk_mono())) e.e_pos, None] api.tvoid [ethis] false e.e_pos in
 	let tag_id = ident "tag" in
 	let index_id = ident "index" in
 	let params_id = ident "params" in
@@ -2215,7 +2224,7 @@ let generate_enum ctx e meta =
 			hlf_slot = !st_count;
 			hlf_kind = (match f.ef_type with
 				| TFun (args,_) ->
-					let fdata = begin_fun ctx (List.map (fun (a,opt,t) -> alloc_var a t, (if opt then Some TNull else None)) args) (TEnum (e,[])) [] true f.ef_pos in
+					let fdata = begin_fun ctx (List.map (fun (a,opt,t) -> alloc_var a t e.e_pos, (if opt then Some TNull else None)) args) (TEnum (e,[])) [] true f.ef_pos in
 					write ctx (HFindPropStrict name_id);
 					write ctx (HString f.ef_name);
 					write ctx (HInt f.ef_index);

+ 19 - 26
genxml.ml → src/generators/genxml.ml

@@ -1,23 +1,20 @@
 (*
- * Copyright (C)2005-2013 Haxe Foundation
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
+	The 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
@@ -83,7 +80,7 @@ let rec follow_param t =
 		t
 
 let gen_meta meta =
-	let meta = List.filter (fun (m,_,_) -> match m with Meta.Used | Meta.MaybeUsed | Meta.RealPath -> false | _ -> true) meta in
+	let meta = List.filter (fun (m,_,_) -> match m with Meta.Used | Meta.MaybeUsed | Meta.RealPath | Meta.Pure -> false | _ -> true) meta in
 	match meta with
 	| [] -> []
 	| _ ->
@@ -432,11 +429,7 @@ let generate_type com t =
 			| l -> p "@%s(%s) " (fst (MetaInfo.to_string m)) (String.concat "," (List.map Ast.s_expr pl))
 		) ml
 	in
-	let access is_read a =
-		match a, pack with
-		| AccNever, "flash" :: _ -> "null"
-		| _ -> s_access is_read a
-	in
+	let access is_read a = s_access is_read a in
 	let rec print_field stat f =
 		p "\t";
 		print_meta f.cf_meta;
@@ -505,7 +498,7 @@ let generate_type com t =
 		) in
 		let ext = (match c.cl_path with
 			| ["flash";"utils"], "ByteArray" -> " implements ArrayAccess<Int>" :: ext
-			| ["flash";"utils"], "Dictionnary" -> [" implements ArrayAccess<Dynamic>"]
+			| ["flash";"utils"], "Dictionary" -> [" implements ArrayAccess<Dynamic>"]
 			| ["flash";"xml"], "XML" -> [" implements Dynamic<XMLList>"]
 			| ["flash";"xml"], "XMLList" -> [" implements ArrayAccess<XML>"]
 			| ["flash";"display"],"MovieClip" -> [" extends Sprite #if !flash_strict implements Dynamic #end"]

+ 104 - 0
src/json.ml

@@ -0,0 +1,104 @@
+(*
+	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.
+ *)
+
+type t =
+	| JString of string
+	| JFloat of float
+	| JInt of int
+	| JObject of (string * t) list
+	| JArray of t list
+	| JBool of bool
+	| JNull
+
+let write_iter f_el f_sep l =
+	let rec rest = function
+		| [] -> ()
+		| v :: l ->
+			f_sep();
+			f_el v;
+			rest l
+	in
+	match l with
+	| [] -> ()
+	| v :: l ->
+		f_el v;
+		rest l
+
+let write_sep w =
+	w ","
+
+let rec write_json w v =
+	match v with
+	| JNull -> write_null w
+	| JBool b -> write_bool w b
+	| JString s -> write_string w s
+	| JFloat f -> write_float w f
+	| JInt i -> write_int w i
+	| JObject o -> write_object w o
+	| JArray a -> write_array w a
+
+and write_null w =
+	w "null"
+
+and write_bool w b =
+	w (if b then "true" else "false")
+
+and write_string w s =
+	w "\"";
+	let b = Buffer.create (String.length s) in
+	for i = 0 to String.length s - 1 do
+		match String.unsafe_get s i with
+		| '"' -> Buffer.add_string b "\\\""
+		| '\t' -> Buffer.add_string b "\\t"
+		| '\r' -> Buffer.add_string b "\\r"
+		| '\b' -> Buffer.add_string b "\\b"
+		| '\n' -> Buffer.add_string b "\\n"
+		| '\012' -> Buffer.add_string b "\\f"
+		| '\\' -> Buffer.add_string b "\\\\"
+		| '\x00'..'\x1F' | '\x7F' as c -> Buffer.add_string b (Printf.sprintf "\\u%04X" (int_of_char c))
+		| c -> Buffer.add_char b c
+	done;
+	w (Buffer.contents b);
+	w "\""
+
+and write_int w i =
+	w (string_of_int i)
+
+and write_float w f =
+	match classify_float f with
+	| FP_nan | FP_infinite -> failwith "NaN and infinity floats are unsupported in JSON"
+	| _ ->
+		let s = Printf.sprintf "%.16g" f in
+		let s = if float_of_string s = f then s else Printf.sprintf "%.17g" f in
+		w s
+
+and write_array w a =
+	w "[";
+	write_iter (write_json w) (fun() -> write_sep w) a;
+	w "]"
+
+and write_object w o =
+	let write_el (k, v) =
+		write_string w k;
+		w ":";
+		write_json w v
+	in
+	w "{";
+	write_iter write_el (fun() -> write_sep w) o;
+	w "}"

+ 258 - 246
interp.ml → src/macro/interp.ml

@@ -1,23 +1,20 @@
 (*
- * Copyright (C)2005-2013 Haxe Foundation
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
+	The 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 Common
@@ -101,13 +98,15 @@ type extern_api = {
 	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 -> Ast.pos -> bool -> Ast.expr;
 	type_expr : Ast.expr -> Type.texpr;
+	resolve_type  : Ast.complex_type -> Ast.pos -> t;
+	type_macro_expr : Ast.expr -> Type.texpr;
 	store_typed_expr : Type.texpr -> Ast.expr;
-	get_display : string -> string;
 	allow_package : string -> unit;
 	type_patch : string -> string -> bool -> string option -> unit;
 	meta_patch : string -> string -> string option -> bool -> unit;
@@ -116,6 +115,7 @@ type extern_api = {
 	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;
@@ -124,6 +124,7 @@ type extern_api = {
 	define_module : string -> value list -> ((string * Ast.pos) list * Ast.import_mode) list -> Ast.type_path list -> unit;
 	module_dependency : string -> string -> bool -> unit;
 	current_module : unit -> module_def;
+	mutable current_macro_module : unit -> module_def;
 	delayed_macro : int -> (unit -> (unit -> value));
 	use_cache : unit -> bool;
 	format_string : string -> Ast.pos -> Ast.expr;
@@ -196,6 +197,8 @@ exception Sys_exit of int
 
 let get_ctx_ref = ref (fun() -> assert false)
 let encode_complex_type_ref = ref (fun t -> assert false)
+let decode_complex_type_ref = ref (fun t -> assert false)
+let decode_pos_ref = ref (fun v -> assert false)
 let encode_type_ref = ref (fun t -> assert false)
 let decode_type_ref = ref (fun t -> assert false)
 let encode_expr_ref = ref (fun e -> assert false)
@@ -203,19 +206,23 @@ let decode_expr_ref = ref (fun e -> assert false)
 let encode_texpr_ref = ref (fun e -> assert false)
 let decode_texpr_ref = ref (fun e -> assert false)
 let encode_clref_ref = ref (fun c -> assert false)
+let encode_module_type_ref = ref (fun mt -> assert false)
 let enc_hash_ref = ref (fun h -> assert false)
 let enc_array_ref = ref (fun l -> assert false)
 let dec_array_ref = ref (fun v -> assert false)
 let enc_string_ref = ref (fun s -> assert false)
-let make_ast_ref = ref (fun _ -> assert false)
-let make_complex_type_ref = ref (fun _ -> assert false)
 let encode_tvar_ref = ref (fun _ -> assert false)
 let decode_path_ref = ref (fun _ -> assert false)
 let decode_import_ref = ref (fun _ -> assert false)
+let encode_import_ref = ref (fun _ -> assert false)
+let eval_expr_ref : (context -> texpr -> value option) ref = ref (fun _ _ -> assert false)
 let get_ctx() = (!get_ctx_ref)()
 let enc_array (l:value list) : value = (!enc_array_ref) l
 let dec_array (l:value) : value list = (!dec_array_ref) l
-let encode_complex_type (t:Ast.complex_type) : value = (!encode_complex_type_ref) t
+
+let decode_complex_type (v:value) : Ast.type_hint = (!decode_complex_type_ref) v
+let encode_complex_type (t:Ast.type_hint) : value = (!encode_complex_type_ref) t
+let decode_pos (v:value) : Ast.pos = (!decode_pos_ref) v
 let encode_type (t:Type.t) : value = (!encode_type_ref) t
 let decode_type (v:value) : Type.t = (!decode_type_ref) v
 let encode_expr (e:Ast.expr) : value = (!encode_expr_ref) e
@@ -224,12 +231,11 @@ let encode_texpr (e:Type.texpr) : value = (!encode_texpr_ref) e
 let decode_texpr (v:value) : Type.texpr = (!decode_texpr_ref) v
 let encode_clref (c:tclass) : value = (!encode_clref_ref) c
 let enc_hash (h:('a,'b) Hashtbl.t) : value = (!enc_hash_ref) h
-let make_ast (e:texpr) : Ast.expr = (!make_ast_ref) e
 let enc_string (s:string) : value = (!enc_string_ref) s
-let make_complex_type (t:Type.t) : Ast.complex_type = (!make_complex_type_ref) t
 let encode_tvar (v:tvar) : value = (!encode_tvar_ref) v
-let decode_path (v:value) : Ast.type_path = (!decode_path_ref) v
-let decode_import (v:value) : ((string * Ast.pos) list * Ast.import_mode) = (!decode_import_ref) v
+let decode_path (v:value) : Ast.placed_type_path = (!decode_path_ref) v
+let encode_import (i:Ast.import) : value = (!encode_import_ref) i
+let decode_import (v:value) : Ast.import = (!decode_import_ref) v
 
 let to_int f = Int32.of_float (mod_float f 2147483648.0)
 let need_32_bits i = Int32.compare (Int32.logand (Int32.add i 0x40000000l) 0x80000000l) Int32.zero <> 0
@@ -276,8 +282,8 @@ let constants =
 	"$";"add";"remove";"has";"__t";"module";"isPrivate";"isPublic";"isExtern";"isInterface";"exclude";
 	"constructs";"names";"superClass";"interfaces";"fields";"statics";"constructor";"init";"t";
 	"gid";"uid";"atime";"mtime";"ctime";"dev";"ino";"nlink";"rdev";"size";"mode";"pos";"len";
-	"binops";"unops";"from";"to";"array";"op";"isPostfix";"impl";
-	"id";"capture";"extra";"v";"ids";"vars";"en";"overrides";"status"];
+	"binops";"unops";"from";"to";"array";"op";"isPostfix";"impl";"resolve";
+	"id";"capture";"extra";"v";"ids";"vars";"en";"overrides";"status";"overloads";"path"];
 	h
 
 let h_get = hash "__get" and h_set = hash "__set"
@@ -491,14 +497,35 @@ type neko_context = {
 	call : primitive -> value list -> value;
 }
 
-let neko =
-	let is_win = Sys.os_type = "Win32" || Sys.os_type = "Cygwin" in
-	let neko = Extc.dlopen (if is_win then "neko.dll" else "libneko.so") in
+(* try to load dl in order *)
+let rec dlopen dls =
 	let null = Extc.dlint 0 in
-	let neko = if Obj.magic neko == null && not is_win then Extc.dlopen "libneko.dylib" else neko in
-	if Obj.magic neko == null then
+	match dls with
+	| dl_path :: dls ->
+		let dl = Extc.dlopen dl_path in
+		if (Obj.magic dl) == null then
+			dlopen dls
+		else
+			Some dl;
+	| _ ->
 		None
+
+let neko =
+	let is_win = Sys.os_type = "Win32" || Sys.os_type = "Cygwin" in
+	match dlopen (if is_win then
+		["neko.dll"]
 	else
+		(*
+			By defualt, the makefile of neko produces libneko.so,
+			however, the debian package creates libneko.so.0 without libneko.so...
+			The fedora rpm package creates libneko.so linked to libneko.so.1.
+		*)
+		["libneko.so"; "libneko.so.0"; "libneko.so.1"; "libneko.so.2"; "libneko.dylib"]
+	) with
+	| None ->
+		None
+	| Some(neko) ->
+	let null = Extc.dlint 0 in
 	let load v =
 		let s = Extc.dlsym neko v in
 		if (Obj.magic s) == null then failwith ("Could not load neko." ^ v);
@@ -1749,6 +1776,20 @@ let std_lib =
 		"sys_exe_path", Fun0 (fun() ->
 			VString (Sys.argv.(0))
 		);
+		"sys_program_path", Fun0 (fun() ->
+			let ctx = get_ctx() in
+			let com = ctx.curapi.get_com() in
+			match com.main_class with
+			| None -> error()
+			| Some p ->
+				let path_s path =
+					match path with | ([], s) -> s | (p, s) -> (String.concat "." (fst path)) ^ "." ^ (snd path)
+				in
+				match ctx.curapi.get_type (path_s p) with
+				| Some(TInst (c, _)) ->
+					VString (Extc.get_full_path c.cl_pos.Ast.pfile)
+				| _ -> error();
+		);
 		"sys_env", Fun0 (fun() ->
 			let env = Unix.environment() in
 			let rec loop acc i =
@@ -1842,7 +1883,8 @@ let std_lib =
 	(* process *)
 		"process_run", (Fun2 (fun p args ->
 			match p, args with
-			| VString p, VArray args -> VAbstract (AProcess (Process.run p (Array.map vstring args)))
+			| VString p, VArray args -> VAbstract (AProcess (Process.run p (Some (Array.map vstring args))))
+			| VString p, _ -> VAbstract (AProcess (Process.run p None))
 			| _ -> error()
 		));
 		"process_stdout_read", (Fun4 (fun p str pos len ->
@@ -1880,6 +1922,11 @@ let std_lib =
 			| VAbstract (AProcess p) -> Process.close p; free_abstract vp; VNull
 			| _ -> error()
 		));
+		"process_kill", (Fun1 (fun vp ->
+			match vp with
+			| VAbstract (AProcess p) -> Process.kill p; free_abstract vp; VNull
+			| _ -> error()
+		));
 	(* xml *)
 		"parse_xml", (match neko with
 		| None -> Fun2 (fun str o ->
@@ -2210,9 +2257,19 @@ let macro_lib =
 				enc_array (List.map encode_type ((get_ctx()).curapi.get_module s))
 			| _ -> error()
 		);
+		"after_typing", Fun1 (fun f ->
+			match f with
+			| VFunction (Fun1 _) | VClosure _ ->
+				let ctx = get_ctx() in
+				ctx.curapi.after_typing (fun tl ->
+					ignore(catch_errors ctx (fun() -> ctx.do_call VNull f [enc_array (List.map !encode_module_type_ref tl)] null_pos));
+				);
+				VNull
+			| _ -> error()
+		);
 		"on_generate", Fun1 (fun f ->
 			match f with
-			| VFunction (Fun1 _) ->
+			| VFunction (Fun1 _) | VClosure _ ->
 				let ctx = get_ctx() in
 				ctx.curapi.on_generate (fun tl ->
 					ignore(catch_errors ctx (fun() -> ctx.do_call VNull f [enc_array (List.map encode_type tl)] null_pos));
@@ -2393,7 +2450,7 @@ let macro_lib =
 			VString (Digest.to_hex (Digest.string (Marshal.to_string v [Marshal.Closures])))
 		);
 		"to_complex", Fun1 (fun v ->
-			try	encode_complex_type (make_complex_type (decode_type v))
+			try	encode_complex_type (TExprToExpr.convert_type' (decode_type v))
 			with Exit -> VNull
 		);
 		"unify", Fun2 (fun t1 t2 ->
@@ -2407,11 +2464,14 @@ let macro_lib =
 		"type_expr", Fun1 (fun v ->
 			encode_texpr ((get_ctx()).curapi.type_expr (decode_expr v))
 		);
+		"resolve_type", Fun2 (fun t p ->
+			encode_type ((get_ctx()).curapi.resolve_type (fst (decode_complex_type t)) (decode_pos p));
+		);
 		"s_type", Fun1 (fun v ->
 			VString (Type.s_type (print_context()) (decode_type v))
 		);
 		"s_expr", Fun2 (fun v b ->
-			let f = match b with VBool true -> Type.s_expr_pretty "" | _ -> Type.s_expr_ast true "" in
+			let f = match b with VBool true -> Type.s_expr_pretty false "" | _ -> Type.s_expr_ast true "" in
 			VString (f (Type.s_type (print_context())) (decode_texpr v))
 		);
 		"is_fmt_string", Fun1 (fun v ->
@@ -2424,13 +2484,6 @@ let macro_lib =
 			| VString(s),VAbstract(APos p) -> encode_expr ((get_ctx()).curapi.format_string s p)
 			| _ -> VNull
 		);
-		"display", Fun1 (fun v ->
-			match v with
-			| VString s ->
-				VString ((get_ctx()).curapi.get_display s)
-			| _ ->
-				error()
-		);
 		"allow_package", Fun1 (fun v ->
 			match v with
 			| VString s ->
@@ -2485,7 +2538,8 @@ let macro_lib =
 			match name, data with
 			| VString name, VString data ->
 				Hashtbl.replace (ccom()).resources name data;
-				let m = (get_ctx()).curapi.current_module() in
+				if name = "" then failwith "Empty resource name";
+				let m = if name.[0] = '$' then (get_ctx()).curapi.current_macro_module() else (get_ctx()).curapi.current_module() in
 				m.m_extra.m_binded_res <- PMap.add name data m.m_extra.m_binded_res;
 				VNull
 			| _ -> error()
@@ -2521,6 +2575,9 @@ let macro_lib =
 		"local_using", Fun0 (fun() ->
 			enc_array (List.map encode_clref ((get_ctx()).curapi.get_local_using()))
 		);
+		"local_imports", Fun0 (fun() ->
+			enc_array (List.map encode_import ((get_ctx()).curapi.get_local_imports()))
+		);
 		"local_vars", Fun1 (fun as_var ->
 			let as_var = match as_var with
 				| VNull | VBool false -> false
@@ -2535,6 +2592,25 @@ let macro_lib =
 				PMap.iter (fun n v -> Hashtbl.replace h (VString n) (encode_type v.v_type)) vars;
 			enc_hash h
 		);
+		"follow_with_abstracts", Fun2 (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 (Ast.Meta.has Ast.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 (match once with VNull | VBool false -> Abstract.follow_with_abstracts t | VBool true -> follow_once t | _ -> error())
+		);
 		"follow", Fun2 (fun v once ->
 			let t = decode_type v in
 			let follow_once t =
@@ -2562,7 +2638,7 @@ let macro_lib =
 		"define_module", Fun4 (fun p v i u ->
 			match p, v, i, u with
 			| VString path, VArray vl, VArray ui, VArray ul ->
-				(get_ctx()).curapi.define_module path (Array.to_list vl) (List.map decode_import (Array.to_list ui)) (List.map decode_path (Array.to_list ul));
+				(get_ctx()).curapi.define_module path (Array.to_list vl) (List.map decode_import (Array.to_list ui)) (List.map fst (List.map decode_path (Array.to_list ul)));
 				VNull
 			| _ ->
 				error()
@@ -2571,7 +2647,13 @@ let macro_lib =
 			match v with
 			| VString cp ->
 				let com = ccom() in
-				com.class_path <- (Common.normalize_path cp) :: com.class_path;
+				let cp = Common.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
 			| _ ->
@@ -2626,7 +2708,7 @@ let macro_lib =
 		);
 		"get_typed_expr", Fun1 (fun e ->
 			let e = decode_texpr e in
-			encode_expr (make_ast e)
+			encode_expr (TExprToExpr.convert_expr e)
 		);
 		"store_typed_expr", Fun1 (fun e ->
 			let e = try decode_texpr e with Invalid_expr -> error() in
@@ -2683,6 +2765,27 @@ let macro_lib =
 			in
 			encode_type (apply_params tpl tl (map (decode_type t)))
 		);
+		"eval", Fun1 (fun v ->
+			let e = decode_expr v in
+			let e = ((get_ctx()).curapi.type_macro_expr e) in
+ 			match !eval_expr_ref (get_ctx()) e with
+			| Some v -> v
+			| None -> VNull
+		);
+		"include_file", Fun2 (fun file position ->
+			match file, position with
+			| VString file, VString position ->
+				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
+			| _ ->
+				error()
+		);
 	]
 
 (* ---------------------------------------------------------------------- *)
@@ -2726,7 +2829,11 @@ let get_ident ctx s =
 
 let no_env = [||]
 
-let rec eval ctx (e,p) =
+let rec eval_expr ctx e =
+	let e = Genneko.gen_expr ctx.gen e in
+	catch_errors ctx (fun() -> (eval ctx e)())
+
+and eval ctx (e,p) =
 	match e with
 	| EConst c ->
 		(match c with
@@ -3359,8 +3466,8 @@ and eval_op ctx op e1 e2 p =
 		let e2 = eval ctx e2 in
 		(fun() ->
 			match e1() with
-			| VBool false as v -> v
-			| _ -> e2())
+			| VBool true -> e2()
+			| _ -> VBool false)
 	| "||" ->
 		let e1 = eval ctx e1 in
 		let e2 = eval ctx e2 in
@@ -3411,7 +3518,7 @@ and call ctx vthis vfun pl p =
 		| Stack_overflow -> exc (VString "Compiler Stack overflow")
 		| Sys_error msg | Failure msg -> exc (VString msg)
 		| Unix.Unix_error (_,cmd,msg) -> exc (VString ("Error " ^ cmd ^ " " ^ msg))
-		(* | Invalid_expr -> exc (VString "Invalid input value") *)
+		| Invalid_expr -> exc (VString "Invalid input value")
 		| Builtin_error | Invalid_argument _ -> exc (VString "Invalid call")) in
 	ctx.vthis <- oldthis;
 	ctx.venv <- oldenv;
@@ -3466,7 +3573,7 @@ let rec to_string ctx n v =
 			Buffer.contents b
 
 let rec compare ctx a b =
-	let fcmp (a:float) b = if a = b then CEq else if a < b then CInf else CSup in
+	let fcmp (a:float) b = if a = b then CEq else if a < b then CInf else if a > b then CSup else CUndef in
 	let scmp (a:string) b = if a = b then CEq else if a < b then CInf else CSup in
 	let icmp (a:int32) b = let l = Int32.compare a b in if l = 0 then CEq else if l < 0 then CInf else CSup in
 	match a, b with
@@ -3601,21 +3708,22 @@ let can_reuse ctx types =
 	end
 
 let add_types ctx types ready =
-	let types = List.filter (fun t ->
-		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
+	let types = List.filter (fun t -> match t with
+		| TAbstractDecl a when not (Ast.Meta.has Ast.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;
 	let e = (EBlock (Genneko.build ctx.gen types), null_pos) in
 	ignore(catch_errors ctx (fun() -> ignore((eval ctx e)())))
 
-let eval_expr ctx e =
-	let e = Genneko.gen_expr ctx.gen e in
-	catch_errors ctx (fun() -> (eval ctx e)())
-
 let get_path ctx path p =
 	let rec loop = function
 		| [] -> assert false
@@ -3663,6 +3771,7 @@ type enum_index =
 	| IModuleType
 	| IFieldAccess
 	| IAnonStatus
+	| IImportMode
 
 let enum_name = function
 	| IExpr -> "ExprDef"
@@ -3683,9 +3792,10 @@ let enum_name = function
 	| IModuleType -> "ModuleType"
 	| IFieldAccess -> "FieldAccess"
 	| IAnonStatus -> "AnonStatus"
+	| IImportMode -> "ImportMode"
 
 let init ctx =
-	let enums = [IExpr;IBinop;IUnop;IConst;ITParam;ICType;IField;IType;IFieldKind;IMethodKind;IVarAccess;IAccess;IClassKind;ITypedExpr;ITConstant;IModuleType;IFieldAccess;IAnonStatus] in
+	let enums = [IExpr;IBinop;IUnop;IConst;ITParam;ICType;IField;IType;IFieldKind;IMethodKind;IVarAccess;IAccess;IClassKind;ITypedExpr;ITConstant;IModuleType;IFieldAccess;IAnonStatus;IImportMode] in
 	let get_enum_proto e =
 		match get_path ctx ["haxe";"macro";enum_name e] null_pos with
 		| VObject e ->
@@ -3747,17 +3857,18 @@ let enc_hash h =
 
 let enc_obj l = VObject (obj hash l)
 
-let enc_enum (i:enum_index) index pl =
+let enc_enum ?(pos=None) (i:enum_index) index pl =
 	let eindex : int = Obj.magic i in
 	let edef = (get_ctx()).enums.(eindex) in
 	if pl = [] then
 		fst edef.(index)
 	else
-		enc_inst ["haxe";"macro";enum_name i] [
-			"tag", VString (snd edef.(index));
-			"index", VInt index;
-			"args", VArray (Array.of_list pl);
-		]
+		enc_inst ["haxe";"macro";enum_name i] (
+			("tag", VString (snd edef.(index))) ::
+			("index", VInt index) ::
+			("args", VArray (Array.of_list pl)) ::
+			(match pos with None -> [] | Some p -> ["pos", encode_pos p])
+		)
 
 let compiler_error msg pos =
 	exc (enc_inst ["haxe";"macro";"Error"] [("message",enc_string msg);("pos",encode_pos pos)])
@@ -3810,7 +3921,22 @@ let encode_unop op =
 	in
 	enc_enum IUnop tag []
 
-let rec encode_path t =
+let encode_import (path,mode) =
+	let tag,pl = match mode with
+		| INormal -> 0, []
+		| IAsName s -> 1, [enc_string s]
+		| IAll -> 2,[]
+	in
+	let mode = enc_enum IImportMode tag pl in
+	enc_obj [
+		"path", enc_array (List.map (fun (name,p) -> enc_obj [ "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;
@@ -3853,7 +3979,8 @@ and encode_field (f:class_field) =
 		| FProp (get,set, t, e) -> 2, [enc_string get; enc_string set; null encode_ctype t; null encode_expr e]
 	in
 	enc_obj [
-		"name",enc_string f.cff_name;
+		"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", enc_enum IField tag pl;
@@ -3862,9 +3989,9 @@ and encode_field (f:class_field) =
 	]
 
 and encode_ctype t =
-	let tag, pl = match t with
+	let tag, pl = match fst t with
 	| CTPath p ->
-		0, [encode_path p]
+		0, [encode_path (p,null_pos)]
 	| CTFunction (pl,r) ->
 		1, [enc_array (List.map encode_ctype pl);encode_ctype r]
 	| CTAnonymous fl ->
@@ -3876,22 +4003,26 @@ and encode_ctype t =
 	| CTOptional t ->
 		5, [encode_ctype t]
 	in
-	enc_enum ICType tag pl
+	enc_enum ~pos:(Some (pos t)) ICType tag pl
 
 and encode_tparam_decl tp =
 	enc_obj [
-		"name", enc_string tp.tp_name;
+		"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 [
 		"params", enc_array (List.map encode_tparam_decl f.f_params);
-		"args", enc_array (List.map (fun (n,opt,t,e) ->
+		"args", enc_array (List.map (fun (n,opt,m,t,e) ->
 			enc_obj [
-				"name", enc_string n;
+				"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;
 			]
@@ -3929,7 +4060,8 @@ and encode_expr e =
 			| EVars vl ->
 				10, [enc_array (List.map (fun (v,t,eo) ->
 					enc_obj [
-						"name",enc_string v;
+						"name",encode_placed_name v;
+						"name_pos",encode_pos (pos v);
 						"type",null encode_ctype t;
 						"expr",null loop eo;
 					]
@@ -3957,7 +4089,8 @@ and encode_expr e =
 			| ETry (e,catches) ->
 				18, [loop e;enc_array (List.map (fun (v,t,e) ->
 					enc_obj [
-						"name",enc_string v;
+						"name",encode_placed_name v;
+						"name_pos",encode_pos (pos v);
 						"type",encode_ctype t;
 						"expr",loop e
 					]
@@ -4027,6 +4160,14 @@ let decode_enum v =
 	| VInt i, VArray a -> i, Array.to_list a
 	| _ -> raise Invalid_expr
 
+let decode_enum_with_pos v =
+	(match field v "index", field v "args" with
+	| VInt i, VNull -> i, []
+	| VInt i, VArray a -> i, Array.to_list a
+	| _ -> raise Invalid_expr),(match field v "pos" with
+		| VAbstract(APos p) -> p
+		| _ -> Ast.null_pos) (* Can happen from reification and other sources. *)
+
 let dec_bool = function
 	| VBool b -> b
 	| _ -> raise Invalid_expr
@@ -4096,13 +4237,16 @@ let decode_import_mode t =
 
 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 decode_placed_name vp v =
+	dec_string v,(match vp with VAbstract (APos p) -> p | _ -> null_pos)
+
 let rec decode_path t =
 	{
 		tpackage = List.map dec_string (dec_array (field t "pack"));
 		tname = dec_string (field t "name");
 		tparams = (match field t "params" with VNull -> [] | a -> List.map decode_tparam (dec_array a));
 		tsub = opt dec_string (field t "sub");
-	}
+	},null_pos
 
 and decode_tparam v =
 	match decode_enum v with
@@ -4116,16 +4260,21 @@ and decode_tparams = function
 
 and decode_tparam_decl v =
 	{
-		tp_name = dec_string (field v "name");
+		tp_name = decode_placed_name (field v "name_pos") (field v "name");
 		tp_constraints = (match field v "constraints" with VNull -> [] | a -> List.map decode_ctype (dec_array a));
 		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 ->
-			(dec_string (field o "name"),(match field o "opt" with VNull -> false | v -> dec_bool v),opt decode_ctype (field o "type"),opt decode_expr (field o "value"))
+			decode_placed_name (field o "name_pos") (field o "name"),
+			(match field o "opt" with VNull -> false | v -> dec_bool v),
+			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");
@@ -4161,7 +4310,7 @@ and decode_field v =
 			raise Invalid_expr
 	in
 	{
-		cff_name = dec_string (field v "name");
+		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;
@@ -4170,9 +4319,10 @@ and decode_field v =
 	}
 
 and decode_ctype t =
-	match decode_enum t with
+	let (i,args),p = decode_enum_with_pos t in
+	(match i,args with
 	| 0, [p] ->
-		CTPath (decode_path p)
+		CTPath (fst (decode_path p))
 	| 1, [a;r] ->
 		CTFunction (List.map decode_ctype (dec_array a), decode_ctype r)
 	| 2, [fl] ->
@@ -4184,12 +4334,13 @@ and decode_ctype t =
 	| 5, [t] ->
 		CTOptional (decode_ctype t)
 	| _ ->
-		raise Invalid_expr
+		raise Invalid_expr),p
 
 let rec decode_expr v =
 	let rec loop v =
-		(decode (field v "expr"), decode_pos (field v "pos"))
-	and decode e =
+		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)
@@ -4215,7 +4366,7 @@ let rec decode_expr v =
 			EUnop (decode_unop op,(if f then Postfix else Prefix),loop e)
 		| 10, [vl] ->
 			EVars (List.map (fun v ->
-				(dec_string (field v "name"),opt decode_ctype (field v "type"),opt loop (field v "expr"))
+				((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)
@@ -4236,7 +4387,7 @@ let rec decode_expr v =
 			ESwitch (loop e,cases,opt decode_null_expr eo)
 		| 18, [e;catches] ->
 			let catches = List.map (fun c ->
-				(dec_string (field c "name"),decode_ctype (field c "type"),loop (field c "expr"))
+				((decode_placed_name (field c "name_pos") (field c "name")),(decode_ctype (field c "type")),loop (field c "expr"))
 			) (dec_array catches) in
 			ETry (loop e, catches)
 		| 19, [e] ->
@@ -4258,7 +4409,7 @@ let rec decode_expr v =
 		| 27, [e1;e2;e3] ->
 			ETernary (loop e1,loop e2,loop e3)
 		| 28, [e;t] ->
-			ECheckType (loop e, decode_ctype t)
+			ECheckType (loop e, (decode_ctype t))
 		| 29, [m;e] ->
 			EMeta (decode_meta_entry m,loop e)
 		| 30, [e;f] ->
@@ -4371,6 +4522,7 @@ and encode_tabstract a =
 		"from", enc_array ((List.map (fun t -> enc_obj [ "t",encode_type t; "field",VNull]) a.a_from) @ (List.map (fun (t,cf) -> enc_obj [ "t",encode_type t; "field",encode_cfield cf]) a.a_from_field));
 		"to", enc_array ((List.map (fun t -> enc_obj [ "t",encode_type t; "field",VNull]) a.a_to) @ (List.map (fun (t,cf) -> enc_obj [ "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 =
@@ -4395,6 +4547,7 @@ and encode_cfield f =
 		"kind", encode_field_kind f.cf_kind;
 		"pos", encode_pos f.cf_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 =
@@ -4602,7 +4755,7 @@ and encode_tvar v =
 		"t", encode_type v.v_type;
 		"capture", VBool v.v_capture;
 		"extra", vopt f_extra v.v_extra;
-		"meta", encode_meta_content v.v_meta;
+		"meta", encode_meta v.v_meta (fun m -> v.v_meta <- m);
 		"$", VAbstract (AUnsafe (Obj.repr v));
 	]
 
@@ -4759,7 +4912,7 @@ let decode_cfield v =
 		cf_kind = decode_field_kind (field v "kind");
 		cf_params = decode_type_params (field v "params");
 		cf_expr = None;
-		cf_overloads = [];
+		cf_overloads = decode_ref (field v "overloads");
 	}
 
 let decode_efield v =
@@ -4849,7 +5002,7 @@ let rec decode_texpr v =
 
 let decode_type_def v =
 	let pack = List.map dec_string (dec_array (field v "pack")) in
-	let name = dec_string (field v "name") 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 = (match field v "isExtern" with VNull -> false | v -> dec_bool v) in
@@ -4867,7 +5020,7 @@ let decode_type_def v =
 	let tdef = (match decode_enum (field v "kind") with
 	| 0, [] ->
 		let conv f =
-			let loop (n,opt,t,_) =
+			let loop ((n,_),opt,_,t,_) =
 				match t with
 				| None -> raise Invalid_expr
 				| Some t -> n, opt, t
@@ -4889,7 +5042,7 @@ let decode_type_def v =
 		in
 		EEnum (mk (if isExtern then [EExtern] else []) (List.map conv fields))
 	| 1, [] ->
-		ETypedef (mk (if isExtern then [EExtern] else []) (CTAnonymous fields))
+		ETypedef (mk (if isExtern then [EExtern] else []) (CTAnonymous fields,null_pos))
 	| 2, [ext;impl;interf] ->
 		let flags = if isExtern then [HExtern] else [] in
 		let flags = (match interf with VNull | VBool false -> flags | VBool true -> HInterface :: flags | _ -> raise Invalid_expr) in
@@ -4909,7 +5062,7 @@ let decode_type_def v =
 	(* 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, name
+		| _ -> pack, fst name
 	) in
 	(pack, name), tdef, pos
 
@@ -4938,157 +5091,12 @@ let rec make_const e =
 (* ---------------------------------------------------------------------- *)
 (* TEXPR-TO-AST-EXPR *)
 
-open Ast
-
-let tpath p mp pl =
-	if snd mp = snd p then
-		CTPath {
-			tpackage = fst p;
-			tname = snd p;
-			tparams = List.map (fun t -> TPType t) pl;
-			tsub = None;
-		}
-	else CTPath {
-			tpackage = fst mp;
-			tname = snd mp;
-			tparams = List.map (fun t -> TPType t) pl;
-			tsub = Some (snd p);
-		}
 
-let rec make_type = function
-	| TMono r ->
-		(match !r with
-		| None -> raise Exit
-		| Some t -> make_type t)
-	| TEnum (e,pl) ->
-		tpath e.e_path e.e_module.m_path (List.map make_type pl)
-	| TInst({cl_kind = KTypeParameter _} as c,pl) ->
-		tpath ([],snd c.cl_path) ([],snd c.cl_path) (List.map make_type pl)
-	| TInst (c,pl) ->
-		tpath c.cl_path c.cl_module.m_path (List.map make_type pl)
-	| TType (t,pl) as tf ->
-		(* recurse on type-type *)
-		if (snd t.t_path).[0] = '#' then make_type (follow tf) else tpath t.t_path t.t_module.m_path (List.map make_type pl)
-	| TAbstract (a,pl) ->
-		tpath a.a_path a.a_module.m_path (List.map make_type pl)
-	| TFun (args,ret) ->
-		CTFunction (List.map (fun (_,_,t) -> make_type t) args, make_type ret)
-	| TAnon a ->
-		begin match !(a.a_status) with
-		| Statics c -> tpath ([],"Class") ([],"Class") [tpath c.cl_path c.cl_path []]
-		| EnumStatics e -> tpath ([],"Enum") ([],"Enum") [tpath e.e_path e.e_path []]
-		| _ ->
-			CTAnonymous (PMap.foldi (fun _ f acc ->
-				{
-					cff_name = f.cf_name;
-					cff_kind = FVar (mk_ot f.cf_type,None);
-					cff_pos = f.cf_pos;
-					cff_doc = f.cf_doc;
-					cff_meta = f.cf_meta;
-					cff_access = [];
-				} :: acc
-			) a.a_fields [])
-		end
-	| (TDynamic t2) as t ->
-		tpath ([],"Dynamic") ([],"Dynamic") (if t == t_dynamic then [] else [make_type t2])
-	| TLazy f ->
-		make_type ((!f)())
-
-and mk_ot t =
-	match follow t with
-	| TMono _ -> None
-	| _ -> (try Some (make_type t) with Exit -> None)
-
-let rec make_ast e =
-	let full_type_path t =
-		let mp,p = match t with
-		| TClassDecl c -> c.cl_module.m_path,c.cl_path
-		| TEnumDecl en -> en.e_module.m_path,en.e_path
-		| TAbstractDecl a -> a.a_module.m_path,a.a_path
-		| TTypeDecl t -> t.t_module.m_path,t.t_path
-		in
-		if snd mp = snd p then p else (fst mp) @ [snd mp],snd p
-	in
-	let mk_path (pack,name) p =
-		match List.rev pack with
-		| [] -> (EConst (Ident name),p)
-		| pl ->
-			let rec loop = function
-				| [] -> assert false
-				| [n] -> (EConst (Ident n),p)
-				| n :: l -> (EField (loop l, n),p)
-			in
-			(EField (loop pl,name),p)
-	in
-	let mk_const = function
-		| TInt i -> Int (Int32.to_string i)
-		| TFloat s -> Float s
-		| TString s -> String s
-		| TBool b -> Ident (if b then "true" else "false")
-		| TNull -> Ident "null"
-		| TThis -> Ident "this"
-		| TSuper -> Ident "super"
-	in
-	let mk_ident = function
-		| "`trace" -> Ident "trace"
-		| n -> Ident n
-	in
-	let eopt = function None -> None | Some e -> Some (make_ast e) in
-	((match e.eexpr with
-	| TConst c ->
-		EConst (mk_const c)
-	| TLocal v -> EConst (mk_ident v.v_name)
-	| TArray (e1,e2) -> EArray (make_ast e1,make_ast e2)
-	| TBinop (op,e1,e2) -> EBinop (op, make_ast e1, make_ast e2)
-	| TField (e,f) -> EField (make_ast e, Type.field_name f)
-	| TTypeExpr t -> fst (mk_path (full_type_path t) e.epos)
-	| TParenthesis e -> EParenthesis (make_ast e)
-	| TObjectDecl fl -> EObjectDecl (List.map (fun (f,e) -> f, make_ast e) fl)
-	| TArrayDecl el -> EArrayDecl (List.map make_ast el)
-	| TCall (e,el) -> ECall (make_ast e,List.map make_ast el)
-	| TNew (c,pl,el) -> ENew ((match (try make_type (TInst (c,pl)) with Exit -> make_type (TInst (c,[]))) with CTPath p -> p | _ -> assert false),List.map make_ast el)
-	| TUnop (op,p,e) -> EUnop (op,p,make_ast e)
-	| TFunction f ->
-		let arg (v,c) = v.v_name, false, mk_ot v.v_type, (match c with None -> None | Some c -> Some (EConst (mk_const c),e.epos)) in
-		EFunction (None,{ f_params = []; f_args = List.map arg f.tf_args; f_type = mk_ot f.tf_type; f_expr = Some (make_ast f.tf_expr) })
-	| TVar (v,eo) ->
-		EVars ([v.v_name, mk_ot v.v_type, eopt eo])
-	| TBlock el -> EBlock (List.map make_ast el)
-	| TFor (v,it,e) ->
-		let ein = (EIn ((EConst (Ident v.v_name),it.epos),make_ast it),it.epos) in
-		EFor (ein,make_ast e)
-	| TIf (e,e1,e2) -> EIf (make_ast e,make_ast e1,eopt e2)
-	| TWhile (e1,e2,flag) -> EWhile (make_ast e1, make_ast e2, flag)
-	| TSwitch (e,cases,def) ->
-		let cases = List.map (fun (vl,e) ->
-			List.map make_ast vl,None,(match e.eexpr with TBlock [] -> None | _ -> Some (make_ast e))
-		) cases in
-		let def = match eopt def with None -> None | Some (EBlock [],_) -> Some None | e -> Some e in
-		ESwitch (make_ast e,cases,def)
-	| TEnumParameter _ ->
-		(* these are considered complex, so the AST is handled in TMeta(Meta.Ast) *)
-		assert false
-	| TTry (e,catches) -> ETry (make_ast e,List.map (fun (v,e) -> v.v_name, (try make_type v.v_type with Exit -> assert false), make_ast e) catches)
-	| TReturn e -> EReturn (eopt e)
-	| TBreak -> EBreak
-	| TContinue -> EContinue
-	| TThrow e -> EThrow (make_ast e)
-	| TCast (e,t) ->
-		let t = (match t with
-			| None -> None
-			| Some t ->
-				let t = (match t with TClassDecl c -> TInst (c,[]) | TEnumDecl e -> TEnum (e,[]) | TTypeDecl t -> TType (t,[]) | TAbstractDecl a -> TAbstract (a,[])) in
-				Some (try make_type t with Exit -> assert false)
-		) in
-		ECast (make_ast e,t)
-	| TMeta ((Meta.Ast,[e1,_],_),_) -> e1
-	| TMeta (m,e) -> EMeta(m,make_ast e))
-	,e.epos)
 
 ;;
-make_ast_ref := make_ast;
-make_complex_type_ref := make_type;
 encode_complex_type_ref := encode_ctype;
+decode_complex_type_ref := decode_ctype;
+decode_pos_ref := decode_pos;
 enc_array_ref := enc_array;
 dec_array_ref := dec_array;
 encode_type_ref := encode_type;
@@ -5102,4 +5110,8 @@ encode_texpr_ref := encode_texpr;
 decode_texpr_ref := decode_texpr;
 encode_tvar_ref := encode_tvar;
 decode_path_ref := decode_path;
+encode_import_ref := encode_import;
 decode_import_ref := decode_import;
+eval_expr_ref := eval_expr;
+encode_import_ref := encode_import;
+encode_module_type_ref := encode_module_type;

+ 333 - 195
main.ml → src/main.ml

@@ -1,23 +1,20 @@
 (*
- * Copyright (C)2005-2013 Haxe Foundation
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
+	The 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.
  *)
 
 (*
@@ -60,17 +57,10 @@ type context = {
 	mutable has_error : bool;
 }
 
-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;
-}
-
 exception Abort
 exception Completion of string
 
-
-let version = 3201
+let version = 3300
 let version_major = version / 1000
 let version_minor = (version mod 1000) / 100
 let version_revision = (version mod 100)
@@ -79,7 +69,6 @@ let version_is_stable = version_minor land 1 = 0
 let measure_times = ref false
 let prompt = ref false
 let start_time = ref (get_time())
-let global_cache = ref None
 
 let path_sep = if Sys.os_type = "Unix" then "/" else "\\"
 
@@ -165,10 +154,11 @@ 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 reserved_flags = [
-	"cross";"js";"neko";"flash";"php";"cpp";"cs";"java";"python";
+	"cross";"js";"lua";"neko";"flash";"php";"cpp";"cs";"java";"python";
 	"as3";"swc";"macro";"sys"
 	]
 
@@ -179,10 +169,10 @@ let complete_fields com fields =
 	List.iter (fun (n,t,k,d) ->
 		let s_kind = match k with
 			| Some k -> (match k with
-				| Typer.FKVar -> "var"
-				| Typer.FKMethod -> "method"
-				| Typer.FKType -> "type"
-				| Typer.FKPackage -> "package")
+				| Display.FKVar -> "var"
+				| Display.FKMethod -> "method"
+				| Display.FKType -> "type"
+				| Display.FKPackage -> "package")
 			| None -> ""
 		in
 		if details then
@@ -204,12 +194,7 @@ let report_times print =
 	end
 
 let make_path 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
+	let cl = get_path_parts f in
 	let error msg =
 		let msg = "Could not process argument " ^ f ^ "\n" ^ msg in
 		failwith msg
@@ -225,10 +210,6 @@ let make_path f =
 		| [] ->
 			error "empty part"
 		| [x] ->
-			if String.length x = 0 then
-				error "empty part"
-			else if not (x.[0] = '_' || (x.[0] >= 'A' && x.[0] <= 'Z')) then
-				error "Class name must start with uppercase character";
 			invalid_char x;
 			[],x
 		| x :: l ->
@@ -242,6 +223,20 @@ let make_path f =
 	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 make_type_path f =
+	let pack,name = make_path f in
+	check_uppercase name;
+	pack,name
+
 let unique l =
 	let rec _unique = function
 		| [] -> []
@@ -370,20 +365,20 @@ let parse_hxml file =
 	IO.close_in ch;
 	parse_hxml_data data
 
-let lookup_classes com spath =
+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 = normalize_path (get_real_path (Common.unique_full_path cp)) in
+			let c = add_trailing_slash (get_real_path (Common.get_full_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 = make_path path in
+					let path = make_type_path path in
 					(match loop l with
-					| [x] when String.length (Ast.s_type_path x) < String.length (Ast.s_type_path path) -> [x]
-					| _ -> [path])
+					| Some x as r when String.length (Ast.s_type_path x) < String.length (Ast.s_type_path path) -> r
+					| _ -> Some path)
 				with _ -> loop l)
 			end else
 				loop l
@@ -619,7 +614,7 @@ let default_flush ctx =
 
 let create_context params =
 	let ctx = {
-		com = Common.create version params;
+		com = Common.create version s_version params;
 		flush = (fun()->());
 		setup = (fun()->());
 		messages = [];
@@ -649,7 +644,7 @@ let rec process_params create pl =
 			loop [] l
 		| "--cwd" :: dir :: l ->
 			(* we need to change it immediately since it will affect hxml loading *)
-			(try Unix.chdir dir with _ -> raise (Arg.Bad "Invalid directory"));
+			(try Unix.chdir dir with _ -> raise (Arg.Bad ("Invalid directory: " ^ dir)));
 			loop acc l
 		| "--connect" :: hp :: l ->
 			(match !global_cache with
@@ -680,17 +675,9 @@ let rec process_params create pl =
 	) in
 	loop [] pl
 
-and wait_loop boot_com 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));
-	Unix.listen sock 10;
+and wait_loop verbose accept =
 	Sys.catch_break false;
-	let verbose = boot_com.verbose in
 	let has_parse_error = ref false in
-	if verbose then print_endline ("Waiting on " ^ host ^ ":" ^ string_of_int port);
-	let bufsize = 1024 in
-	let tmp = String.create bufsize in
 	let cache = {
 		c_haxelib = Hashtbl.create 0;
 		c_files = Hashtbl.create 0;
@@ -698,21 +685,28 @@ and wait_loop boot_com host port =
 	} in
 	global_cache := Some cache;
 	Typer.macro_enable_cache := true;
+	let current_stdin = ref None in
 	Typeload.parse_hook := (fun com2 file p ->
-		let sign = get_signature com2 in
 		let ffile = Common.unique_full_path file in
-		let ftime = file_time ffile in
-		let fkey = ffile ^ "!" ^ sign in
-		try
-			let time, data = Hashtbl.find cache.c_files 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
-			if verbose then print_endline ("Parsed " ^ ffile);
-			if not !has_parse_error && ffile <> (!Parser.resume_display).Ast.pfile then Hashtbl.replace cache.c_files fkey (ftime,data);
-			data
+		let is_display_file = ffile = (!Parser.resume_display).Ast.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 = Hashtbl.find cache.c_files 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
+				if verbose then print_endline ("Parsed " ^ ffile);
+				if not !has_parse_error && (not is_display_file) then Hashtbl.replace cache.c_files fkey (ftime,data);
+				data
 	);
 	let cache_module m =
 		Hashtbl.replace cache.c_modules (m.m_path,m.m_extra.m_sign) m;
@@ -789,7 +783,7 @@ and wait_loop boot_com host port =
 				(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 ("Reusing  cached module " ^ Ast.s_type_path m.m_path);
 					m.m_extra.m_added <- !compilation_step;
@@ -810,7 +804,7 @@ and wait_loop boot_com host port =
 						| _ -> ()
 					) m.m_types;
 					if m.m_extra.m_kind <> MSub then Typeload.add_module ctx m p;
-					PMap.iter (Hashtbl.add com2.resources) m.m_extra.m_binded_res;
+					PMap.iter (Hashtbl.replace com2.resources) m.m_extra.m_binded_res;
 					PMap.iter (fun _ m2 -> add_modules m0 m2) m.m_extra.m_deps);
 					List.iter (Typer.call_init_macro ctx) m.m_extra.m_macro_calls
 			end
@@ -830,31 +824,8 @@ and wait_loop boot_com host port =
 	);
 	let run_count = ref 0 in
 	while true do
-		let sin, _ = Unix.accept sock in
+		let read, write, close = accept() in
 		let t0 = get_time() in
-		Unix.set_nonblock sin;
-		if verbose then print_endline "Client connected";
-		let b = Buffer.create 0 in
-		let rec read_loop count =
-			let r = try
-				Unix.recv sin tmp 0 bufsize []
-			with Unix.Unix_error((Unix.EWOULDBLOCK|Unix.EAGAIN),_,_) ->
-				0
-			in
-			if verbose then begin
-				if r > 0 then Printf.printf "Reading %d bytes\n" r else print_endline "Waiting for data...";
-			end;
-			Buffer.add_substring b tmp 0 r;
-			if r > 0 && tmp.[r-1] = '\000' then
-				Buffer.sub b 0 (Buffer.length b - 1)
-			else begin
-				if r = 0 then ignore(Unix.select [] [] [] 0.05); (* wait a bit *)
-				if count = 100 then
-					failwith "Aborting unactive connection"
-				else
-					read_loop (count + 1);
-			end;
-		in
 		let rec cache_context com =
 			if com.display = DMNone then begin
 				List.iter cache_module com.modules;
@@ -869,10 +840,15 @@ and wait_loop boot_com host port =
 			ctx.flush <- (fun() ->
 				incr compilation_step;
 				compilation_mark := !mark_loop;
-				List.iter (fun s -> ssend sin (s ^ "\n"); if verbose then print_endline ("> " ^ s)) (List.rev ctx.messages);
-				if ctx.has_error then ssend sin "\x02\n" else cache_context ctx.com;
+				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() ->
+				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 (get_signature ctx.com));
+				end;
 				Parser.display_error := (fun e p -> has_parse_error := true; ctx.com.error (Parser.error_msg e) p);
 				if ctx.com.display <> DMNone then begin
 					let file = (!Parser.resume_display).Ast.pfile in
@@ -883,12 +859,20 @@ and wait_loop boot_com host port =
 					Hashtbl.iter (fun _ m -> if m.m_extra.m_file = file then m.m_extra.m_dirty <- true) cache.c_modules
 				end
 			);
-			ctx.com.print <- (fun str -> ssend sin ("\x01" ^ String.concat "\x01" (ExtString.String.nsplit str "\n") ^ "\n"));
+			ctx.com.print <- (fun str -> write ("\x01" ^ String.concat "\x01" (ExtString.String.nsplit str "\n") ^ "\n"));
 			ctx
 		in
 		(try
-			let data = parse_hxml_data (read_loop 0) in
-			Unix.clear_nonblock sin;
+			let s = read() 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
 				Common.display_default := DMNone;
@@ -907,10 +891,13 @@ and wait_loop boot_com host port =
 				start_time := get_time();
 				process_params create data;
 				close_times();
-				if !measure_times then report_times (fun s -> ssend sin (s ^ "\n"))
-			with Completion str ->
+				if !measure_times then report_times (fun s -> write (s ^ "\n"))
+			with
+			| Completion str ->
 				if verbose then print_endline ("Completion Response =\n" ^ str);
-				ssend sin str
+				write str
+			| Arg.Bad msg ->
+				prerr_endline ("Error: " ^ msg);
 			);
 			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));
@@ -921,9 +908,11 @@ and wait_loop boot_com host port =
 		| e ->
 			let estr = Printexc.to_string e in
 			if verbose then print_endline ("Uncaught Error : " ^ estr);
-			(try ssend sin estr with _ -> ());
+			(try write estr with _ -> ());
+			if is_debug_run() then print_endline (Printexc.get_backtrace());
 		);
-		Unix.close sin;
+		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
@@ -937,6 +926,70 @@ and wait_loop boot_com host port =
 		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));
@@ -978,7 +1031,7 @@ and do_connect host port args =
 
 and init ctx =
 	let usage = Printf.sprintf
-		"Haxe Compiler %s - (C)2005-2015 Haxe Foundation\n Usage : haxe%s -main <class> [-swf|-js|-neko|-php|-cpp|-as3] <output> [options]\n Options :"
+		"Haxe Compiler %s - (C)2005-2016 Haxe Foundation\n Usage : haxe%s -main <class> [-swf|-js|-neko|-php|-cpp|-cppia|-as3|-cs|-java|-python|-hl|-lua] <output> [options]\n Options :"
 		s_version (if Sys.os_type = "Win32" then ".exe" else "")
 	in
 	let com = ctx.com in
@@ -998,7 +1051,6 @@ try
 	let swf_version = ref false in
 	let evals = ref [] in
 	Common.define_value com Define.HaxeVer (float_repres (float_of_int version /. 1000.));
-	Common.define_value com Define.HxcppApiLevel "321";
 	Common.raw_define com "haxe3";
 	Common.define_value com Define.Dce "std";
 	com.warning <- (fun msg p -> message ctx ("Warning : " ^ msg) p);
@@ -1018,13 +1070,13 @@ try
 				l
 		in
 		let parts = Str.split_delim (Str.regexp "[;:]") p in
-		com.class_path <- "" :: List.map normalize_path (loop parts)
+		com.class_path <- "" :: List.map add_trailing_slash (loop parts)
 	with
 		Not_found ->
 			if Sys.os_type = "Unix" then
 				com.class_path <- ["/usr/lib/haxe/std/";"/usr/share/haxe/std/";"/usr/local/lib/haxe/std/";"/usr/lib/haxe/extraLibs/";"/usr/local/lib/haxe/extraLibs/";""]
 			else
-				let base_path = normalize_path (get_real_path (try executable_path() with _ -> "./")) in
+				let base_path = add_trailing_slash (get_real_path (try executable_path() with _ -> "./")) in
 				com.class_path <- [base_path ^ "std/";base_path ^ "extraLibs/";""]);
 	com.std_path <- List.filter (fun p -> ExtString.String.ends_with p "std/" || ExtString.String.ends_with p "std\\") com.class_path;
 	let set_platform pf file =
@@ -1048,9 +1100,10 @@ try
 	let basic_args_spec = [
 		("-cp",Arg.String (fun path ->
 			process_libs();
-			com.class_path <- normalize_path path :: com.class_path
+			com.class_path <- add_trailing_slash path :: com.class_path
 		),"<path> : add a directory to find source files");
 		("-js",Arg.String (set_platform Js),"<file> : compile code to JavaScript file");
+		("-lua",Arg.String (set_platform Lua),"<file> : compile code to Lua file");
 		("-swf",Arg.String (set_platform Flash),"<file> : compile code to Flash SWF file");
 		("-as3",Arg.String (fun dir ->
 			set_platform Flash dir;
@@ -1065,6 +1118,10 @@ try
 		("-cpp",Arg.String (fun dir ->
 			set_platform Cpp dir;
 		),"<directory> : generate C++ code into target directory");
+		("-cppia",Arg.String (fun file ->
+			set_platform Cpp file;
+			Common.define com Define.Cppia;
+		),"<file> : generate Cppia code into target file");
 		("-cs",Arg.String (fun dir ->
 			cp_libs := "hxcs" :: !cp_libs;
 			set_platform Cs dir;
@@ -1076,13 +1133,16 @@ try
 		("-python",Arg.String (fun dir ->
 			set_platform Python dir;
 		),"<file> : generate Python code as target file");
+		("-hl",Arg.String (fun file ->
+			set_platform Hl file;
+		),"<file> : compile HL code as target file");
 		("-xml",Arg.String (fun file ->
 			Parser.use_doc := true;
 			xml_out := Some file
 		),"<file> : generate XML types description");
 		("-main",Arg.String (fun cl ->
 			if com.main_class <> None then raise (Arg.Bad "Multiple -main");
-			let cpath = make_path cl in
+			let cpath = make_type_path cl in
 			com.main_class <- Some cpath;
 			classes := cpath :: !classes
 		),"<class> : select startup class");
@@ -1097,7 +1157,7 @@ try
 				| _ -> 	if List.mem var reserved_flags then raise (Arg.Bad (var ^ " is a reserved compiler flag and cannot be defined from command line"));
 			end;
 			Common.raw_define com var;
-		),"<var> : define a conditional compilation flag");
+		),"<var[=value]> : define a conditional compilation flag");
 		("-v",Arg.Unit (fun () ->
 			com.verbose <- true
 		),": turn on verbose mode");
@@ -1112,7 +1172,7 @@ try
 			| "std" | "full" | "no" -> ()
 			| _ -> raise (Arg.Bad "Invalid DCE mode, expected std | full | no"));
 			Common.define_value com Define.Dce mode
-		),"[std|full|no] : set the dead code elimination mode");
+		),"[std|full|no] : set the dead code elimination mode (default std)");
 		("-swf-version",Arg.Float (fun v ->
 			if not !swf_version || com.flash_version < v then com.flash_version <- v;
 			swf_version := true;
@@ -1160,7 +1220,7 @@ try
 			let neko_file = file ^ ".n" in
 			set_platform Neko neko_file;
 			if com.main_class = None then begin
-				let cpath = make_path file in
+				let cpath = make_type_path file in
 				com.main_class <- Some cpath;
 				classes := cpath :: !classes
 			end;
@@ -1222,34 +1282,33 @@ try
 				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 activate_special_display_mode () =
-					Common.define com Define.NoCOpt;
-					Parser.use_parser_resume := false
-				in
 				let mode = match smode with
 					| "position" ->
-						activate_special_display_mode();
+						Common.define com Define.NoCOpt;
 						DMPosition
 					| "usage" ->
-						activate_special_display_mode();
+						Common.define com Define.NoCOpt;
 						DMUsage
 					| "type" ->
-						activate_special_display_mode();
+						Common.define com Define.NoCOpt;
 						DMType
 					| "toplevel" ->
-						activate_special_display_mode();
+						Common.define com Define.NoCOpt;
 						DMToplevel
+					| "module-symbols" ->
+						Common.define com Define.NoCOpt;
+						DMModuleSymbols;
+					| "diagnostics" ->
+						Common.define com Define.NoCOpt;
+						DMDiagnostics;
 					| "" ->
-						Parser.use_parser_resume := true;
 						DMDefault
 					| _ ->
 						let smode,arg = try ExtString.String.split smode "@" with _ -> pos,"" in
 						match smode with
 							| "resolve" ->
-								activate_special_display_mode();
 								DMResolve arg
 							| _ ->
-								Parser.use_parser_resume := true;
 								DMDefault
 				in
 				let pos = try int_of_string pos with _ -> failwith ("Invalid format : "  ^ pos) in
@@ -1290,7 +1349,6 @@ try
 		("--interp", Arg.Unit (fun() ->
 			Common.define com Define.Interp;
 			set_platform Neko "";
-			no_output := true;
 			interp := true;
 		),": interpret the program using internal macro system");
 		("--macro", Arg.String (fun e ->
@@ -1302,8 +1360,15 @@ try
 			evals := s :: !evals;
 		), " : evaluates argument as Haxe module code");
 		("--wait", Arg.String (fun hp ->
-			let host, port = (try ExtString.String.split hp ":" with _ -> "127.0.0.1", hp) in
-			wait_loop com host (try int_of_string port with _ -> raise (Arg.Bad "Invalid port"))
+			let accept = match hp with
+				| "stdio" ->
+					init_wait_stdio()
+				| _ ->
+					let host, port = (try ExtString.String.split hp ":" with _ -> "127.0.0.1", hp) in
+					let port = try int_of_string port with _ -> raise (Arg.Bad "Invalid port") in
+					init_wait_socket com.verbose host port
+			in
+			wait_loop com.verbose accept
 		),"<[host:]port> : wait on the given port for commands to run)");
 		("--connect",Arg.String (fun _ ->
 			assert false
@@ -1370,7 +1435,15 @@ try
 			did_something := true
 		),": print help for all compiler metadatas");
 	] in
-	let args_callback cl = classes := make_path cl :: !classes in
+	let args_callback cl =
+		let path,name = make_path cl in
+		if starts_uppercase name then
+			classes := (path,name) :: !classes
+		else begin
+			force_typing := true;
+			config_macros := (Printf.sprintf "include('%s', true, null, null, true)" cl) :: !config_macros;
+		end
+	in
 	let all_args_spec = basic_args_spec @ adv_args_spec in
 	let process args =
 		let current = ref 0 in
@@ -1383,7 +1456,7 @@ try
 				ignore(Str.search_forward r msg 0);
 				let s = Str.matched_group 1 msg in
 				let sl = List.map (fun (s,_,_) -> s) all_args_spec in
-				let msg = Typecore.string_error_raise s sl (Printf.sprintf "Invalid command: %s" s) in
+				let msg = StringError.string_error_raise s sl (Printf.sprintf "Invalid command: %s" s) in
 				raise (Arg.Bad msg)
 			with Not_found ->
 				raise exc);
@@ -1393,18 +1466,22 @@ try
 	process ctx.com.args;
 	process_libs();
 	if com.display <> DMNone then begin
-		com.warning <- message ctx;
+		com.warning <- if com.display = DMDiagnostics then (fun s p -> add_diagnostics_message com s p DiagnosticsSeverity.Warning) else message ctx;
 		com.error <- error ctx;
 		com.main_class <- None;
+		if com.display <> DMUsage then
+			classes := [];
 		let real = get_real_path (!Parser.resume_display).Ast.pfile in
-		classes := lookup_classes com real;
-		if !classes = [] then begin
+		(match get_module_path_from_file_path com real with
+		| Some 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_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";
-		end;
+			failwith "Display file was not found in class path"
+		);
 		Common.log com ("Display file : " ^ real);
 		Common.log com ("Classes found : ["  ^ (String.concat "," (List.map Ast.s_type_path !classes)) ^ "]");
 	end;
@@ -1433,13 +1510,37 @@ try
 			add_std "neko";
 			"n"
 		| Js ->
+			if not (PMap.exists (fst (Define.infos Define.JqueryVer)) com.defines) then
+				Common.define_value com Define.JqueryVer "11204";
+
+			let es_version =
+				try
+					int_of_string (Common.defined_value com Define.JsEs)
+				with
+				| Not_found ->
+					(Common.define_value com Define.JsEs "5"; 5)
+				| _ ->
+					0
+			in
+
+			if es_version < 3 || es_version = 4 then (* we don't support ancient and there's no 4th *)
+				failwith "Invalid -D js-es value";
+
+			if es_version >= 5 then Common.raw_define com "js-es5"; (* backward-compatibility *)
+
 			add_std "js";
 			"js"
+		| Lua ->
+			add_std "lua";
+			"lua"
 		| Php ->
 			add_std "php";
 			"php"
 		| Cpp ->
+			Common.define_value com Define.HxcppApiLevel "330";
 			add_std "cpp";
+			if Common.defined com Define.Cppia then
+				classes := (make_path "cpp.cppia.HostClasses" ) :: !classes;
 			"cpp"
 		| Cs ->
 			let old_flush = ctx.flush in
@@ -1461,6 +1562,9 @@ try
 		| Python ->
 			add_std "python";
 			"python"
+		| Hl ->
+			add_std "hl";
+			"hl"
 	) in
 	(* if we are at the last compilation step, allow all packages accesses - in case of macros or opening another project file *)
 	begin match com.display with
@@ -1484,7 +1588,7 @@ try
 	end else begin
 		ctx.setup();
 		Common.log com ("Classpath : " ^ (String.concat ";" com.class_path));
-		Common.log com ("Defines : " ^ (String.concat ";" (PMap.foldi (fun v _ acc -> v :: acc) com.defines [])));
+		Common.log com ("Defines : " ^ (String.concat ";" (PMap.foldi (fun k v acc -> (match v with "1" -> k | _ -> k ^ "=" ^ v) :: acc) com.defines [])));
 		let t = Common.timer "typing" in
 		Typecore.type_expr_ref := (fun ctx e with_type -> Typer.type_expr ctx e with_type);
 		let tctx = Typer.create com in
@@ -1494,8 +1598,8 @@ try
 		Typer.finalize tctx;
 		t();
 		if ctx.has_error then raise Abort;
-		begin match com.display with
-			| DMNone | DMUsage | DMPosition | DMType | DMResolve _ ->
+		begin match ctx.com.display with
+			| DMNone | DMUsage | DMDiagnostics ->
 				()
 			| _ ->
 				if ctx.has_next then raise Abort;
@@ -1506,6 +1610,10 @@ try
 		com.main <- main;
 		com.types <- types;
 		com.modules <- modules;
+		begin match com.display with
+			| DMUsage -> Codegen.detect_usage com;
+			| _ -> ()
+		end;
 		Filters.run com tctx main;
 		if ctx.has_error then raise Abort;
 		(* check file extension. In case of wrong commandline, we don't want
@@ -1520,8 +1628,8 @@ try
 			Common.mkdir_from_path file;
 			Genxml.generate com file);
 		if com.platform = Flash || com.platform = Cpp then List.iter (Codegen.fix_overrides com) com.types;
-		if Common.defined com Define.Dump then Codegen.dump_types com;
-		if Common.defined com Define.DumpDependencies then Codegen.dump_dependencies com;
+		if Common.defined com Define.Dump then Codegen.Dump.dump_types com;
+		if Common.defined com Define.DumpDependencies then Codegen.Dump.dump_dependencies com;
 		t();
 		if not !no_output then begin match com.platform with
 			| Neko when !interp -> ()
@@ -1529,48 +1637,51 @@ try
 			| Cpp | Cs | Java | Php -> Common.mkdir_from_path (com.file ^ "/.")
 			| _ -> Common.mkdir_from_path com.file
 		end;
-		(match com.platform with
-		| _ when !no_output ->
+		if not !no_output then begin
 			if !interp then begin
 				let ctx = Interp.create com (Typer.make_macro_api tctx Ast.null_pos) in
 				Interp.add_types ctx com.types (fun t -> ());
 				(match com.main with
 				| None -> ()
 				| Some e -> ignore(Interp.eval_expr ctx e));
-			end;
-		| Cross ->
-			()
-		| Flash when Common.defined com Define.As3 ->
-			Common.log com ("Generating AS3 in : " ^ com.file);
-			Genas3.generate com;
-		| Flash ->
-			Common.log com ("Generating swf : " ^ com.file);
-			Genswf.generate com !swf_header;
-		| Neko ->
-			Common.log com ("Generating neko : " ^ com.file);
-			Genneko.generate com;
-		| Js ->
-			Common.log com ("Generating js : " ^ com.file);
-			Genjs.generate com
-		| Php ->
-			Common.log com ("Generating PHP in : " ^ com.file);
-			Genphp.generate com;
-		| Cpp ->
-			Common.log com ("Generating Cpp in : " ^ com.file);
-			Gencpp.generate com;
-		| Cs ->
-			Common.log com ("Generating Cs in : " ^ com.file);
-			Gencs.generate com;
-		| Java ->
-			Common.log com ("Generating Java in : " ^ com.file);
-			Genjava.generate com;
-		| Python ->
-			Common.log com ("Generating python in : " ^ com.file);
-			Genpy.generate com;
-		);
+			end else if com.platform = Cross then
+				()
+			else begin
+				let generate,name = match com.platform with
+				| Flash when Common.defined com Define.As3 ->
+					Genas3.generate,"AS3"
+				| Flash ->
+					Genswf.generate !swf_header,"swf"
+				| Neko ->
+					Genneko.generate,"neko"
+				| Js ->
+					Genjs.generate,"js"
+				| Lua ->
+					Genlua.generate,"lua"
+				| Php ->
+					Genphp.generate,"php"
+				| Cpp ->
+					Gencpp.generate,"cpp"
+				| Cs ->
+					Gencs.generate,"cs"
+				| Java ->
+					Genjava.generate,"java"
+				| Python ->
+					Genpy.generate,"python"
+				| Hl ->
+					Genhl.generate,"hl"
+				| Cross ->
+					assert false
+				in
+				Common.log com ("Generating " ^ name ^ ": " ^ com.file);
+				let t = Common.timer ("generate " ^ name) in
+				generate com;
+				t()
+			end
+		end
 	end;
 	Sys.catch_break false;
-	List.iter (fun f -> f()) (List.rev com.final_filters);
+	List.iter (fun f -> f()) (List.rev com.callbacks.after_generation);
 	if not !no_output then begin
 		List.iter (fun c ->
 			let r = run_command ctx c in
@@ -1604,7 +1715,7 @@ with
 		message ctx msg p;
 		List.iter (message ctx "Called from") l;
 		error ctx "Aborted" Ast.null_pos;
-	| Codegen.Generic_Exception(m,p) ->
+	| Typeload.Generic_Exception(m,p) ->
 		error ctx m p
 	| Arg.Bad msg ->
 		error ctx ("Error: " ^ msg) Ast.null_pos
@@ -1612,7 +1723,7 @@ with
 		error ctx ("Error: " ^ msg) Ast.null_pos
 	| Arg.Help msg ->
 		message ctx msg Ast.null_pos
-	| Typer.DisplayFields fields ->
+	| Display.DisplayFields fields ->
 		let ctx = print_context() in
 		let fields = List.map (fun (name,t,kind,doc) -> name, s_type ctx t, kind, (match doc with None -> "" | Some d -> d)) fields in
 		let fields = if !measure_times then begin
@@ -1629,16 +1740,29 @@ with
 			fields
 		in
 		complete_fields com fields
-	| Typecore.DisplayTypes tl ->
+	| Display.DisplayType (t,p) ->
+		let ctx = print_context() in
+		let b = Buffer.create 0 in
+		if p = null_pos then
+			Buffer.add_string b "<type>\n"
+		else begin
+			let error_printer file line = sprintf "%s:%d:" (Common.unique_full_path file) line in
+			let epos = Lexer.get_error_pos error_printer p in
+			Buffer.add_string b ("<type p=\"" ^ (htmlescape epos) ^ "\">\n")
+		end;
+		Buffer.add_string b (htmlescape (s_type ctx t));
+		Buffer.add_string b "\n</type>\n";
+		raise (Completion (Buffer.contents b))
+	| Display.DisplaySignatures tl ->
 		let ctx = print_context() in
 		let b = Buffer.create 0 in
-		List.iter (fun t ->
+		List.iter (fun (t,doc) ->
 			Buffer.add_string b "<type>\n";
-			Buffer.add_string b (htmlescape (s_type ctx t));
+			Buffer.add_string b (htmlescape (s_type ctx (follow t)));
 			Buffer.add_string b "\n</type>\n";
 		) tl;
 		raise (Completion (Buffer.contents b))
-	| Typecore.DisplayPosition pl ->
+	| Display.DisplayPosition pl ->
 		let b = Buffer.create 0 in
 		let error_printer file line = sprintf "%s:%d:" (Common.unique_full_path file) line in
 		Buffer.add_string b "<list>\n";
@@ -1650,19 +1774,28 @@ with
 		) pl;
 		Buffer.add_string b "</list>";
 		raise (Completion (Buffer.contents b))
-	| Typer.DisplayToplevel il ->
+	| Display.DisplayToplevel il ->
 		let b = Buffer.create 0 in
 		Buffer.add_string b "<il>\n";
 		let ctx = print_context() in
 		let s_type t = htmlescape (s_type ctx t) in
+		let s_doc d = Option.map_default (fun s -> Printf.sprintf " d=\"%s\"" (htmlescape s)) "" d in
 		List.iter (fun id -> match id with
-			| Typer.ITLocal v -> Buffer.add_string b (Printf.sprintf "<i k=\"local\" t=\"%s\">%s</i>\n" (s_type v.v_type) v.v_name);
-			| Typer.ITMember(c,cf) -> Buffer.add_string b (Printf.sprintf "<i k=\"member\" t=\"%s\">%s</i>\n" (s_type cf.cf_type) cf.cf_name);
-			| Typer.ITStatic(c,cf) -> Buffer.add_string b (Printf.sprintf "<i k=\"static\" t=\"%s\">%s</i>\n" (s_type cf.cf_type) cf.cf_name);
-			| Typer.ITEnum(en,ef) -> Buffer.add_string b (Printf.sprintf "<i k=\"enum\" t=\"%s\">%s</i>\n" (s_type ef.ef_type) ef.ef_name);
-			| Typer.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);
-			| Typer.ITType(mt) -> Buffer.add_string b (Printf.sprintf "<i k=\"type\" p=\"%s\">%s</i>\n" (s_type_path (t_infos mt).mt_path) (snd (t_infos mt).mt_path));
-			| Typer.ITPackage s -> Buffer.add_string b (Printf.sprintf "<i k=\"package\">%s</i>\n" s)
+			| 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.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)
 		) il;
 		Buffer.add_string b "</il>";
 		raise (Completion (Buffer.contents b))
@@ -1675,7 +1808,7 @@ with
 			else
 				complete_fields com (
 					let convert k f = (f,"",Some k,"") in
-					(List.map (convert Typer.FKPackage) packs) @ (List.map (convert Typer.FKType) classes)
+					(List.map (convert Display.FKPackage) packs) @ (List.map (convert Display.FKType) classes)
 				)
 		| Some (c,cur_package) ->
 			try
@@ -1708,12 +1841,12 @@ with
 					end;
 					not tinfos.mt_private
 				) m.m_types in
-				let types = if c <> s_module then [] else List.map (fun t -> snd (t_path t),"",Some Typer.FKType,"") public_types in
+				let types = if c <> s_module then [] else List.map (fun t -> snd (t_path t),"",Some Display.FKType,"") public_types in
 				let ctx = print_context() in
 				let make_field_doc cf =
 					cf.cf_name,
 					s_type ctx cf.cf_type,
-					Some (match cf.cf_kind with Method _ -> Typer.FKMethod | Var _ -> Typer.FKVar),
+					Some (match cf.cf_kind with Method _ -> Display.FKMethod | Var _ -> Display.FKVar),
 					(match cf.cf_doc with Some s -> s | None -> "")
 				in
 				let types = match !statics with
@@ -1725,6 +1858,8 @@ with
 				raise (Completion c)
 			| _ ->
 				error ctx ("Could not load module " ^ (Ast.s_type_path (p,c))) Ast.null_pos)
+	| Display.ModuleSymbols s | Display.Diagnostics s ->
+		raise (Completion s)
 	| Interp.Sys_exit i ->
 		ctx.flush();
 		exit i
@@ -1744,6 +1879,9 @@ with Not_found -> try
 with Completion c ->
 	prerr_endline c;
 	exit 0
+| Arg.Bad msg ->
+	prerr_endline ("Error: " ^ msg);
+	exit 1
 );
 other();
 if !measure_times then report_times prerr_endline

+ 1262 - 0
src/optimization/analyzer.ml

@@ -0,0 +1,1262 @@
+(*
+	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 Type
+open Common
+open AnalyzerTexpr
+open AnalyzerTypes
+
+(* File organization:
+	* analyzer.ml: The controlling file with all graph-based optimizations
+	* analyzerConfig.ml: The general configuration which is used in multiple modules
+	* analyzerTexpr.ml: Transformations and query functions on texpr, independent of graph/blocks
+	* analyzerTexprTransformer.ml: Translation of texpr to graph/blocks
+	* analyzerTypes.ml: Definition of graph, block and the analyzer context
+*)
+
+(*
+	Ssa changes the expressions of a graph to conform to SSA rules. All variables are assigned to only once
+	and SSA-phi expressions are created where necessary.
+
+	The first pass inserts SSA-phi expressions for each variable in the dominance frontier of all its defining
+	blocks.
+
+	The second pass then creates and renames variables to ensure SSA property.
+*)
+module Ssa = struct
+	open BasicBlock
+	open Graph
+
+	let add_phi g bb v =
+		let p = bb.bb_pos in
+		let ev = mk (TLocal v) v.v_type p in
+		let el = List.map (fun _ -> ev) bb.bb_incoming in
+		let e_phi = mk (TConst (TString "phi")) t_dynamic p in
+		let ec = mk (TCall(e_phi,el)) t_dynamic p in
+		let e = mk (TBinop(OpAssign,ev,ec)) t_dynamic p in
+		DynArray.add bb.bb_phi e
+
+	let insert_phi ctx =
+		DynArray.iter (fun vi ->
+			let v = vi.vi_var in
+			if vi.vi_bb_declare == ctx.graph.g_unreachable then
+				()
+			else begin
+				let done_list = Hashtbl.create 0 in
+				let w = ref vi.vi_writes in
+				while !w <> [] do
+					let x = List.hd !w in
+					w := List.tl !w;
+					List.iter (fun y ->
+						if not (Hashtbl.mem done_list y.bb_id) then begin
+							Hashtbl.add done_list y.bb_id true;
+							if in_scope y vi.vi_bb_declare then begin
+								add_phi ctx.graph y v;
+								if not (List.memq y vi.vi_writes) then
+									w := y :: !w
+							end
+						end
+					) x.bb_df;
+				done
+			end
+		) ctx.graph.g_var_infos
+
+	let set_reaching_def g v vo =
+		let vi = get_var_info g v in
+		vi.vi_reaching_def <- vo
+
+	let get_reaching_def g v =
+		(get_var_info g v).vi_reaching_def
+
+	let rec dominates bb_dom bb =
+		bb_dom == bb || bb.bb_dominator == bb_dom || (bb.bb_dominator != bb && dominates bb_dom bb.bb_dominator)
+
+	let dominates ctx r bb =
+		let l = (get_var_info ctx.graph r).vi_writes in
+		List.exists (fun bb' -> dominates bb' bb) l
+
+	let update_reaching_def ctx v bb =
+		let rec loop r = match r with
+			| Some r ->
+				if dominates ctx r bb then
+					Some r
+				else
+					loop (get_reaching_def ctx.graph r)
+			| None ->
+				None
+		in
+		let v' = (loop (get_reaching_def ctx.graph v)) in
+		set_reaching_def ctx.graph v v'
+
+	let local ctx e v bb =
+		update_reaching_def ctx v bb;
+		match get_reaching_def ctx.graph v with
+			| Some v' -> v'
+			| None -> v
+
+	let update_phi ctx edge =
+		let bb = edge.cfg_to in
+		let rec loop i e =
+			match e.eexpr with
+			| TBinop(OpAssign,({eexpr = TLocal v0} as e1), ({eexpr = TCall({eexpr = TConst (TString "phi")} as ephi,el)} as ecall)) ->
+				let el = List.map2 (fun e inc ->
+					let bb_pred = inc.cfg_from in
+					if bb_pred != edge.cfg_from then
+						e
+					else match e.eexpr with
+					| TLocal v ->
+						let v' = local ctx e v edge.cfg_from in
+						add_ssa_edge ctx.graph v' bb true i;
+						{e with eexpr = TLocal v'}
+					| _ ->
+						assert false
+				) el edge.cfg_to.bb_incoming in
+				let ephi = {ecall with eexpr = TCall(ephi,el)} in
+				set_var_value ctx.graph v0 bb true i;
+				{e with eexpr = TBinop(OpAssign,e1,ephi)}
+			| _ ->
+				Type.map_expr (loop i) e
+		in
+		dynarray_mapi loop bb.bb_phi
+
+	let rec rename_in_block ctx bb =
+		let write_var v is_phi i =
+			update_reaching_def ctx v bb;
+			let v' = alloc_var (v.v_name) v.v_type v.v_pos in
+			declare_var ctx.graph v' bb;
+			v'.v_meta <- v.v_meta;
+			v'.v_capture <- v.v_capture;
+			add_var_def ctx.graph bb v';
+			set_reaching_def ctx.graph v' (get_reaching_def ctx.graph v);
+			set_reaching_def ctx.graph v (Some v');
+			set_var_value ctx.graph v' bb is_phi i;
+			add_var_origin ctx.graph v' v;
+			v'
+		in
+		let rec loop is_phi i e = match e.eexpr with
+			| TLocal v when not (is_unbound v) ->
+				let v' = local ctx e v bb in
+				add_ssa_edge ctx.graph v' bb is_phi i;
+				{e with eexpr = TLocal v'}
+			| TVar(v,Some e1) when not (is_unbound v) ->
+				let e1 = (loop is_phi i) e1 in
+				let v' = write_var v is_phi i in
+				{e with eexpr = TVar(v',Some e1)}
+			| TBinop(OpAssign,({eexpr = TLocal v} as e1),e2) when not (is_unbound v) ->
+				let e2 = (loop is_phi i) e2 in
+				let v' = write_var v is_phi i in
+				{e with eexpr = TBinop(OpAssign,{e1 with eexpr = TLocal v'},e2)};
+			| TCall({eexpr = TConst (TString "phi")},_) ->
+				e
+			| _ ->
+				Type.map_expr (loop is_phi i) e
+		in
+		dynarray_mapi (loop true) bb.bb_phi;
+		dynarray_mapi (loop false) bb.bb_el;
+		List.iter (update_phi ctx) bb.bb_outgoing;
+		List.iter (rename_in_block ctx) bb.bb_dominated
+
+	let apply ctx =
+		Graph.infer_dominance_frontier ctx.graph;
+		insert_phi ctx;
+		rename_in_block ctx ctx.graph.g_root
+end
+
+module type DataFlowApi = sig
+	type t
+	val flag : BasicBlock.cfg_edge_Flag
+	val transfer : analyzer_context -> BasicBlock.t -> texpr -> t (* The transfer function *)
+	val equals : t -> t -> bool                                   (* The equality function *)
+	val bottom : t                                                (* The bottom element of the lattice *)
+	val top : t                                                   (* The top element of the lattice *)
+	val get_cell : int -> t                                       (* Lattice cell getter *)
+	val set_cell : int -> t -> unit                               (* Lattice cell setter *)
+	val init : analyzer_context -> unit                           (* The initialization function which is called at the start *)
+	val commit : analyzer_context -> unit                         (* The commit function which is called at the end *)
+	val conditional : bool                                        (* Whether or not conditional branches are checked *)
+end
+
+(*
+	DataFlow provides a framework for data flow analysis. It follows CFG edges from the root of the graph
+	and visits the expressions and SSA-phi expressions of blocks on its way.
+
+	If such an expression assigns to a variable (TVar or TBinop(OpAsssign)), all uses of that variable are
+	checked by following the variable's SSA edges.
+
+	A conditional branch edge (CFGCondBranch and CFGCondElse) is only followed if the available information
+	suggests that it might be executable. This causes information from dead branches to not be taken into
+	account.
+
+	For SSA-phi nodes, only those incoming edges which are considered to be executable are processed.
+
+	The algorithm continues until no further changes occur.
+*)
+module DataFlow (M : DataFlowApi) = struct
+	open Graph
+	open BasicBlock
+
+	let get_ssa_edges_from g v =
+		(get_var_info g v).vi_ssa_edges
+
+	let run ctx =
+		let g = ctx.graph in
+		let ssa_work_list = ref [] in
+		let cfg_work_list = ref g.g_root.bb_outgoing in
+		let add_ssa_edge edge =
+			ssa_work_list := edge :: !ssa_work_list
+		in
+		let add_cfg_edge edge =
+			cfg_work_list := edge :: !cfg_work_list
+		in
+		let visit_phi bb v el =
+			let el = List.fold_left2 (fun acc e edge ->
+				if has_flag edge M.flag then e :: acc else acc
+			) [] el bb.bb_incoming in
+			let el = List.map (fun e -> M.transfer ctx bb e) el in
+			match el with
+				| e1 :: el when List.for_all (M.equals e1) el ->
+					e1;
+				| _ ->
+					M.bottom;
+		in
+		let set_lattice_cell v e =
+			let e' = M.get_cell v.v_id in
+			M.set_cell v.v_id e;
+			if not (M.equals e e') then
+				List.iter (fun edge -> add_ssa_edge edge) (get_ssa_edges_from g v);
+		in
+		let visit_assignment bb v e =
+			match e.eexpr with
+			| TCall({eexpr = TConst (TString "phi")},el) ->
+				set_lattice_cell v (visit_phi bb v el)
+			| _ ->
+				if List.exists (fun edge -> has_flag edge M.flag) bb.bb_incoming then
+					set_lattice_cell v (M.transfer ctx bb e)
+		in
+		let visit_expression bb e =
+			match e.eexpr with
+			| TBinop(OpAssign,{eexpr = TLocal v},e2) | TVar(v,Some e2) ->
+				visit_assignment bb v e2;
+				false
+			| TMeta((Meta.Custom ":cond-branch",_,_),e1) when M.conditional ->
+				let e1 = M.transfer ctx bb e1 in
+				let edges = if e1 == M.bottom || e1 == M.top then
+					bb.bb_outgoing
+				else begin
+					let rec loop yes maybe also edges = match edges with
+						| edge :: edges ->
+							begin match edge.cfg_kind with
+							| CFGCondBranch e ->
+								let e = M.transfer ctx bb e in
+								if M.equals e e1 then
+									loop (edge :: yes) maybe also edges
+								else
+									loop yes maybe also edges
+							| CFGCondElse ->
+								loop yes (edge :: maybe) also edges
+							| CFGGoto | CFGFunction | CFGMaybeThrow ->
+								loop yes maybe (edge :: also) edges
+							end
+						| [] ->
+							yes,maybe,also
+					in
+					let yes,maybe,also = loop [] [] [] bb.bb_outgoing in
+					match yes,maybe with
+						| [],[] -> bb.bb_outgoing
+						| [],maybe -> maybe @ also
+						| yes,_ -> yes @ also
+				end in
+				List.iter add_cfg_edge edges;
+				true
+			| _ ->
+				false
+		in
+		let visit_expressions bb =
+			let b = DynArray.fold_left (fun b e ->
+				visit_expression bb e || b
+			) false bb.bb_el in
+			if not b then List.iter add_cfg_edge bb.bb_outgoing
+		in
+		let visit_phis bb =
+			DynArray.iter (fun e ->
+				match e.eexpr with
+					| TBinop(OpAssign,{eexpr = TLocal v},{eexpr = TCall({eexpr = TConst (TString "phi")},el)}) ->
+						set_lattice_cell v (visit_phi bb v el)
+					| _ -> assert false
+			) bb.bb_phi
+		in
+		let rec loop () = match !cfg_work_list,!ssa_work_list with
+			| edge :: edges,_ ->
+				cfg_work_list := edges;
+				if not (has_flag edge M.flag) then begin
+					edge.cfg_flags <- M.flag :: edge.cfg_flags;
+					visit_phis edge.cfg_to;
+					let i = List.fold_left (fun i edge -> i + if has_flag edge M.flag then 1 else 0) 0 edge.cfg_to.bb_incoming in
+					if i = 1 || edge.cfg_to == g.g_root then
+						visit_expressions edge.cfg_to;
+					begin match edge.cfg_to.bb_outgoing with
+						| [edge] -> add_cfg_edge edge
+						| _ -> ()
+					end
+				end;
+				loop();
+			| [],((bb,is_phi,i) :: edges) ->
+				ssa_work_list := edges;
+				let e = get_texpr bb is_phi i in
+				ignore(visit_expression bb e);
+				loop()
+			| [],[] ->
+				()
+		in
+		loop ()
+
+	let apply ctx =
+		M.init ctx;
+		run ctx;
+		M.commit ctx
+end
+
+(*
+	ConstPropagation implements sparse conditional constant propagation using the DataFlow algorithm. Its lattice consists of
+	constants and enum values, but only the former are propagated. Enum values are treated as immutable data tuples and allow
+	extracting constants, their index or other enum values.
+
+	This module also deals with binop/unop optimization and standard API inlining.
+*)
+module ConstPropagation = DataFlow(struct
+	open BasicBlock
+
+	type t =
+		| Top
+		| Bottom
+		| Const of tconstant
+		| EnumValue of int * t list
+
+	let conditional = true
+	let flag = FlagExecutable
+
+	let lattice = Hashtbl.create 0
+
+	let get_cell i = try Hashtbl.find lattice i with Not_found -> Top
+	let set_cell i ct = Hashtbl.replace lattice i ct
+
+	let top = Top
+	let bottom = Bottom
+
+	let equals lat1 lat2 = match lat1,lat2 with
+		| Top,Top | Bottom,Bottom -> true
+		| Const ct1,Const ct2 -> ct1 = ct2
+		| EnumValue(i1,_),EnumValue(i2,_) -> i1 = i2
+		| _ -> false
+
+	let transfer ctx bb e =
+		let rec eval bb e =
+			let wrap = function
+				| Const ct -> mk (TConst ct) t_dynamic null_pos
+				| _ -> raise Exit
+			in
+			let unwrap e = match e.eexpr with
+				| TConst ct -> Const ct
+				| _ -> raise Exit
+			in
+			match e.eexpr with
+			| TConst (TSuper | TThis | TNull) ->
+				Bottom
+			| TConst ct ->
+				Const ct
+			| TLocal v ->
+				if is_unbound v || (follow v.v_type) == t_dynamic || v.v_capture then
+					Bottom
+				else
+					get_cell v.v_id
+			| TBinop(OpAssign,_,e2) ->
+				eval bb e2
+			| TBinop(op,e1,e2) ->
+				let cl1 = eval bb e1 in
+				let cl2 = eval bb e2 in
+				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
+				if e != e' then
+					eval bb e'
+				else
+					unwrap e'
+			| TUnop(op,flag,e1) ->
+				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
+				if e != e' then
+					eval bb e'
+				else
+					unwrap e'
+			| TField(_,FEnum(_,ef)) ->
+				EnumValue(ef.ef_index,[])
+			| TCall({eexpr = TField(_,FEnum(_,ef))},el) ->
+				let cll = List.map (fun e -> try eval bb e with Exit -> Bottom) el in
+				EnumValue(ef.ef_index,cll)
+			| TEnumParameter(e1,_,i) ->
+				begin match eval bb e1 with
+					| EnumValue(_,el) -> (try List.nth el i with Failure _ -> raise Exit)
+					| _ -> raise Exit
+				end;
+			| TCall ({ eexpr = TField (_,FStatic(c,cf))},el) ->
+				let el = List.map (eval bb) el in
+				let el = List.map wrap el in
+				begin match Optimizer.api_inline2 ctx.com c cf.cf_name el e.epos with
+					| None -> raise Exit
+					| Some e -> eval bb e
+				end
+			| TParenthesis e1 | TMeta(_,e1) | TCast(e1,None) ->
+				eval bb e1
+			| _ ->
+				let e1 = match ctx.com.platform,e.eexpr with
+					| Js,TArray(e1,{eexpr = TConst(TInt i)}) when Int32.to_int i = 1 -> e1
+					| Cpp,TCall({eexpr = TField(e1,FDynamic "__Index")},[]) -> e1
+					| Neko,TField(e1,FDynamic "index") -> e1
+					| _ -> raise Exit
+				in
+				begin match follow e1.etype,eval bb e1 with
+					| TEnum _,EnumValue(i,_) -> Const (TInt (Int32.of_int i))
+					| _ -> raise Exit
+				end
+		in
+		try
+			eval bb e
+		with Exit ->
+			Bottom
+
+	let init ctx =
+		Hashtbl.clear lattice
+
+	let commit ctx =
+		let inline e i = match get_cell i with
+			| Top | Bottom | EnumValue _ ->
+				raise Not_found
+			| Const ct ->
+				let e' = Codegen.type_constant ctx.com (tconst_to_const ct) e.epos in
+				if not (type_change_ok ctx.com e'.etype e.etype) then raise Not_found;
+				e'
+		in
+		let rec commit e = match e.eexpr with
+			| TLocal v when not v.v_capture ->
+				begin try
+					inline e v.v_id
+				with Not_found ->
+					e
+				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;
+					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) ->
+				let e1 = try inline e1 v.v_id with Not_found -> commit e1 in
+				{e with eexpr = TVar(v,Some e1)}
+			| _ ->
+				Type.map_expr commit e
+		in
+		Graph.iter_dom_tree ctx.graph (fun bb ->
+			if not (List.exists (fun edge -> has_flag edge FlagExecutable) bb.bb_incoming) then bb.bb_dominator <- ctx.graph.Graph.g_unreachable;
+			dynarray_map commit bb.bb_el
+		);
+end)
+
+(*
+	Propagates local variables to other local variables.
+
+	Respects scopes on targets where it matters (all except JS and As3).
+*)
+module CopyPropagation = DataFlow(struct
+	open BasicBlock
+	open Graph
+
+	type t =
+		| Top
+		| Bottom
+		| Local of tvar
+
+	let to_string = function
+		| Top -> "Top"
+		| Bottom -> "Bottom"
+		| Local v -> Printf.sprintf "%s<%i>" v.v_name v.v_id
+
+	let conditional = false
+	let flag = FlagCopyPropagation
+	let lattice = Hashtbl.create 0
+
+	let get_cell i = try Hashtbl.find lattice i with Not_found -> Top
+	let set_cell i ct = Hashtbl.replace lattice i ct
+
+	let top = Top
+	let bottom = Bottom
+
+	let equals t1 t2 = match t1,t2 with
+		| Top,Top -> true
+		| Bottom,Bottom -> true
+		| Local v1,Local v2 -> v1.v_id = v2.v_id
+		| _ -> false
+
+	let transfer ctx bb e =
+		let rec loop e = match e.eexpr with
+			| TLocal v when not v.v_capture ->
+				Local v
+			| TParenthesis e1 | TMeta(_,e1) | TCast(e1,None) ->
+				loop e1
+			| _ ->
+				Bottom
+		in
+		loop e
+
+	let init ctx =
+		Hashtbl.clear lattice
+
+	let commit ctx =
+		let rec commit bb e = match e.eexpr with
+			| TLocal v when not v.v_capture ->
+				begin try
+					let lat = get_cell v.v_id in
+					let leave () =
+						Hashtbl.remove lattice v.v_id;
+						raise Not_found
+					in
+					let v' = match lat with Local v -> v | _ -> leave() in
+					if not (type_change_ok ctx.com v'.v_type v.v_type) then leave();
+					let v'' = get_var_origin ctx.graph v' in
+					(* This restriction is in place due to how we currently reconstruct the AST. Multiple SSA-vars may be turned back to
+					   the same origin var, which creates interference that is not tracked in the analysis. We address this by only
+					   considering variables whose origin-variables are assigned to at most once. *)
+					let writes = (get_var_info ctx.graph v'').vi_writes in
+					begin match writes with
+						| [bb'] when in_scope bb bb' -> ()
+						| _ -> leave()
+					end;
+					commit bb {e with eexpr = TLocal v'}
+				with Not_found ->
+					e
+				end
+			| TBinop((OpAssign | OpAssignOp _ as op),({eexpr = TLocal _} as e1),e2) ->
+				let e2 = commit bb e2 in
+				{e with eexpr = TBinop(op,e1,e2)}
+			| _ ->
+				Type.map_expr (commit bb) e
+		in
+		Graph.iter_dom_tree ctx.graph (fun bb ->
+			dynarray_map (commit bb) bb.bb_el
+		);
+end)
+
+module CodeMotion = DataFlow(struct
+	open Graph
+	open BasicBlock
+
+	let conditional = false
+	let flag = FlagCodeMotion
+		type t_def =
+		| Top
+		| Bottom
+		| Const of tconstant
+		| Local of tvar
+		| Binop of binop * t * t
+
+	and t = (t_def * Type.t * pos)
+
+	let top = (Top,t_dynamic,null_pos)
+	let bottom = (Bottom,t_dynamic,null_pos)
+
+	let rec equals (lat1,_,_) (lat2,_,_) = match lat1,lat2 with
+		| Top,Top
+		| Bottom,Bottom ->
+			true
+		| Const ct1,Const ct2 ->
+			ct1 = ct2
+		| Local v1,Local v2 ->
+			v1 == v2
+		| Binop(op1,lat11,lat12),Binop(op2,lat21,lat22) ->
+			op1 = op2 && equals lat11 lat21 && equals lat12 lat22
+		| _ ->
+			false
+
+	let lattice = Hashtbl.create 0
+
+	let get_cell i = try Hashtbl.find lattice i with Not_found -> top
+	let set_cell i ct = Hashtbl.replace lattice i ct
+
+	let rec transfer ctx bb e =
+		let rec eval e = match e.eexpr with
+			| TConst ct ->
+				Const ct
+			| TLocal v ->
+				Local v
+			| TBinop(op,e1,e2) ->
+				let lat1 = transfer ctx bb e1 in
+				let lat2 = transfer ctx bb e2 in
+				Binop(op,lat1,lat2)
+			| _ ->
+				raise Exit
+		in
+		try
+			(eval e,e.etype,e.epos)
+		with Exit | Not_found ->
+			bottom
+
+	let init ctx =
+		Hashtbl.clear lattice
+
+	let commit ctx =
+		let rec filter_loops lat loops = match lat with
+			| Local v,_,_ ->
+				let bb = match (get_var_info ctx.graph v).vi_writes with [bb] -> bb | _ -> raise Exit in
+				let loops2 = List.filter (fun i -> not (List.mem i bb.bb_loop_groups)) loops in
+				if loops2 = [] then filter_loops (get_cell v.v_id) loops else true,lat,loops2
+			| Const _,_,_ ->
+				false,lat,loops
+			| Binop(op,lat1,lat2),t,p ->
+				let has_local1,lat1,loops = filter_loops lat1 loops in
+				let has_local2,lat2,loops = filter_loops lat2 loops in
+				has_local1 || has_local2,(Binop(op,lat1,lat2),t,p),loops
+			| _ ->
+				raise Exit
+		in
+		let rec to_texpr (lat,t,p) =
+			let def = match lat with
+				| Local v -> TLocal v
+				| Const ct -> TConst ct
+				| Binop(op,lat1,lat2) -> TBinop(op,to_texpr lat1,to_texpr lat2)
+				| _ -> raise Exit
+			in
+			{ eexpr = def; etype = t; epos = p }
+		in
+		let cache = Hashtbl.create 0 in
+		let replace decl bb v =
+			let lat,t,p = get_cell v.v_id in
+			match lat with
+			| Binop(op,lat1,lat2) ->
+				let has_local1,lat1,loops = filter_loops lat1 bb.bb_loop_groups in
+				let has_local2,lat2,loops = filter_loops lat2 loops in
+				if loops = [] || not (has_local1 || has_local2) then raise Exit;
+				let lat = ((Binop(op,lat1,lat2)),t,p) in
+				let bb_loop_pre = IntMap.find (List.hd loops) ctx.graph.g_loops in
+				let v' = try
+					let l = Hashtbl.find cache bb_loop_pre.bb_id in
+					snd (List.find (fun (lat',e) -> equals lat lat') l)
+				with Not_found ->
+					let v' = if decl then begin
+						v
+					end else begin
+						let v' = alloc_var ctx.temp_var_name v.v_type v.v_pos in
+						declare_var ctx.graph v' bb_loop_pre;
+						v'.v_meta <- [Meta.CompilerGenerated,[],p];
+						v'
+					end in
+					let e = to_texpr lat in
+					let e = mk (TVar(v',Some e)) ctx.com.basic.tvoid p in
+					add_texpr bb_loop_pre e;
+					set_var_value ctx.graph v' bb_loop_pre false (DynArray.length bb_loop_pre.bb_el - 1);
+					Hashtbl.replace cache bb_loop_pre.bb_id ((lat,v') :: try Hashtbl.find cache bb_loop_pre.bb_id with Not_found -> []);
+					v'
+				in
+				let ev' = mk (TLocal v') v'.v_type p in
+				if decl then begin
+					if v == v' then
+						mk (TConst TNull) t p
+					else
+						mk (TVar(v,Some ev')) ctx.com.basic.tvoid p
+				end else begin
+					let ev = mk (TLocal v) v.v_type p in
+					mk (TBinop(OpAssign,ev,ev')) t p
+				end
+			| _ ->
+				raise Exit
+		in
+		let rec commit bb e = match e.eexpr with
+			| TBinop(OpAssign,({eexpr = TLocal v} as e1),e2) ->
+				begin try
+					replace false bb v
+				with Exit ->
+					{e with eexpr = TBinop(OpAssign,e1,commit bb e2)}
+				end
+			| TVar(v,Some e1) when Meta.has Meta.CompilerGenerated v.v_meta ->
+				begin try
+					replace true bb v
+				with Exit ->
+					{e with eexpr = TVar(v,Some (commit bb e1))}
+				end
+			| _ ->
+				Type.map_expr (commit bb) e
+		in
+		Graph.iter_dom_tree ctx.graph (fun bb ->
+			if bb.bb_loop_groups <> [] then dynarray_map (commit bb) bb.bb_el
+		);
+end)
+
+module LoopInductionVariables = struct
+	open Graph
+
+	type book = {
+		tvar : tvar;
+		index : int;
+		mutable lowlink : int;
+		mutable on_stack : bool
+	}
+
+	let find_cycles g =
+		let index = ref 0 in
+		let s = ref [] in
+		let book = ref IntMap.empty in
+		let add_book_entry v =
+			let entry = {
+				tvar = v;
+				index = !index;
+				lowlink = !index;
+				on_stack = true;
+			} in
+			incr index;
+			book := IntMap.add v.v_id entry !book;
+			entry
+		in
+		let rec strong_connect vi =
+			let v_entry = add_book_entry vi.vi_var in
+			s := v_entry :: !s;
+			List.iter (fun (bb,is_phi,i) ->
+				try
+					let e = BasicBlock.get_texpr bb is_phi i in
+					let w = match e.eexpr with
+						| TVar(v,_) | TBinop(OpAssign,{eexpr = TLocal v},_) -> v
+						| _ -> raise Exit
+					in
+					begin try
+						let w_entry = IntMap.find w.v_id !book in
+						if w_entry.on_stack then
+							v_entry.lowlink <- min v_entry.lowlink w_entry.index
+					with Not_found ->
+						let w_entry = strong_connect (get_var_info g w) in
+						v_entry.lowlink <- min v_entry.lowlink w_entry.lowlink;
+					end
+				with Exit ->
+					()
+			) vi.vi_ssa_edges;
+			if v_entry.lowlink = v_entry.index then begin
+				let rec loop acc entries = match entries with
+					| w_entry :: entries ->
+						w_entry.on_stack <- false;
+						if w_entry == v_entry then w_entry :: acc,entries
+						else loop (w_entry :: acc) entries
+					| [] ->
+						acc,[]
+				in
+				let scc,rest = loop [] !s in
+				begin match scc with
+					| [] | [_] ->
+						()
+					| _ ->
+						print_endline "SCC:";
+						List.iter (fun entry -> print_endline (Printf.sprintf "%s<%i>" entry.tvar.v_name entry.tvar.v_id)) scc;
+						(* now what? *)
+				end;
+				s := rest
+			end;
+			v_entry
+		in
+		DynArray.iter (fun vi -> match vi.vi_ssa_edges with
+			| [] ->
+				()
+			| _ ->
+				if not (IntMap.mem vi.vi_var.v_id !book) then
+					ignore(strong_connect vi)
+		) g.g_var_infos
+
+	let apply ctx =
+		find_cycles ctx.graph
+end
+
+(*
+	LocalDce implements a mark & sweep dead code elimination. The mark phase follows the CFG edges of the graphs to find
+	variable usages and marks variables accordingly. If ConstPropagation was run before, only CFG edges which are
+	considered executable are processed.
+
+	If a variable is marked as used, its reaching definition is recursively marked as used too. Furthermore its
+	value is processed as an expression.
+
+	The sweep phase removes all variable declarations and assignments of unused variables, keeping only the assigned
+	expression in case of side-effects.
+*)
+module LocalDce = struct
+	open BasicBlock
+	open Graph
+	open AnalyzerConfig
+
+	let rec 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(_,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
+			| TNew _ | TCall _ | TBinop ((OpAssignOp _ | OpAssign),_,_) | TUnop ((Increment|Decrement),_,_) -> raise Exit
+			| TReturn _ | TBreak | TContinue | TThrow _ | TCast (_,Some _) -> raise Exit
+			| TFor _ -> raise Exit
+			| TArray _ | TEnumParameter _ | TCast (_,None) | TBinop _ | TUnop _ | TParenthesis _ | TMeta _ | TWhile _
+			| TField _ | TIf _ | TTry _ | TSwitch _ | TArrayDecl _ | TBlock _ | TObjectDecl _ | TVar _ -> Type.iter loop e
+		in
+		try
+			loop e;
+			false
+		with Exit ->
+			true
+
+	let rec apply ctx =
+		let is_used v =
+			Meta.has Meta.Used v.v_meta
+		in
+		let keep v =
+			is_used v || (not (Meta.has Meta.CompilerGenerated v.v_meta) && not ctx.config.local_dce) || is_ref_type v.v_type || v.v_capture || Meta.has Meta.This v.v_meta
+		in
+		let rec use v =
+			if not (is_used v) then begin
+				v.v_meta <- (Meta.Used,[],null_pos) :: v.v_meta;
+				(try expr (get_var_value ctx.graph v) with Not_found -> ());
+				begin match Ssa.get_reaching_def ctx.graph v with
+					| None -> use (get_var_origin ctx.graph v)
+					| Some v -> use v;
+				end
+			end
+		and expr e = match e.eexpr with
+			| TLocal v when not (is_unbound v) ->
+				use v;
+			| TBinop(OpAssign,{eexpr = TLocal v},e1) | TVar(v,Some e1) when not (is_unbound v) ->
+				if has_side_effect e1 || keep v then expr e1
+				else ()
+			| _ ->
+				Type.iter expr e
+		in
+		let bb_marked = ref [] in
+		let rec mark bb =
+			bb_marked := bb :: !bb_marked;
+			DynArray.iter expr bb.bb_el;
+			DynArray.iter expr bb.bb_phi;
+			List.iter (fun edge ->
+				if not (has_flag edge FlagDce) then begin
+					edge.cfg_flags <- FlagDce :: edge.cfg_flags;
+					if not ctx.config.const_propagation || has_flag edge FlagExecutable then
+						mark edge.cfg_from;
+				end
+			) bb.bb_incoming
+		in
+		mark ctx.graph.g_exit;
+		let rec sweep e = match e.eexpr with
+			| TBinop(OpAssign,{eexpr = TLocal v},e2) | TVar(v,Some e2) when not (keep v) ->
+				if has_side_effect e2 then
+					e2
+				else
+					mk (TConst TNull) e.etype e.epos
+			| TVar(v,None) when not (keep v) ->
+				mk (TConst TNull) e.etype e.epos
+			| _ ->
+				Type.map_expr sweep e
+		in
+		List.iter (fun bb ->
+			dynarray_map sweep bb.bb_el
+		) !bb_marked;
+end
+
+module Debug = struct
+	open BasicBlock
+	open Graph
+
+	type node_info =
+		| NIExpr
+		| NIVars
+		| NIPhi
+		| NILoopGroups
+		| NIScopes
+
+	let s_var v = Printf.sprintf "%s<%i>" v.v_name v.v_id
+
+	let dot_debug_node g ch nil bb =
+		let s = Printf.sprintf "(%i)" bb.bb_id in
+		let s = List.fold_left (fun s ni -> s ^ match ni with
+			| NIExpr -> if DynArray.length bb.bb_el = 0 then "" else "\n" ^  String.concat "\n" (DynArray.to_list (DynArray.map s_expr_pretty bb.bb_el))
+			| NIPhi -> if DynArray.length bb.bb_phi = 0 then "" else "\n" ^ String.concat "\n" (DynArray.to_list (DynArray.map (fun e -> s_expr_pretty e) bb.bb_phi))
+			| NIVars -> if bb.bb_var_writes = [] then "" else "\n" ^ String.concat ", " (List.map (fun v -> s_var v) bb.bb_var_writes)
+			| NILoopGroups -> if bb.bb_loop_groups = [] then "" else "\nLoops: " ^ (String.concat ", " (List.map string_of_int bb.bb_loop_groups))
+			| NIScopes -> if bb.bb_scopes = [] then "" else "\nScopes: " ^ (String.concat ", " (List.map string_of_int bb.bb_scopes))
+		) s nil in
+		let s_kind = match bb.bb_kind with
+			| BKRoot -> "<root>\n"
+			| BKFunctionBegin _ -> "<function-begin>\n"
+			| BKFunctionEnd -> "<function-end>\n"
+			| BKLoopHead -> "<loop-head>\n"
+			| _ -> ""
+		in
+		Printf.fprintf ch "n%i [shape=box,label=\"%s%s\"];\n" bb.bb_id s_kind (s_escape s)
+
+	let dot_debug_cfg_edge ch edge =
+		let label = match edge.cfg_kind with
+			| CFGGoto -> "goto"
+			| CFGFunction -> "function"
+			| CFGMaybeThrow -> "throw?"
+			| CFGCondBranch _ -> "branch"
+			| CFGCondElse -> "else"
+		in
+		let s_edge_flag = function
+			| FlagExecutable -> "exe"
+			| FlagDce -> "dce"
+			| FlagCodeMotion -> "motion"
+			| FlagCopyPropagation -> "copy"
+		in
+		let label = label ^ match edge.cfg_flags with
+			| [] -> ""
+			| _ -> Printf.sprintf " [%s]" (String.concat ", " (List.map s_edge_flag edge.cfg_flags))
+		in
+		Printf.fprintf ch "n%i -> n%i[label=\"%s\"];\n" edge.cfg_from.bb_id edge.cfg_to.bb_id (s_escape label)
+
+	let dot_debug_syntax_edge ch bb se =
+		let edge bb' label =
+			Printf.fprintf ch "n%i -> n%i[style=\"dashed\",color=\"gray\",label=\"%s\"];\n" bb.bb_id bb'.bb_id label;
+		in
+		match se with
+		| SESubBlock(bb_sub,bb_next) ->
+			edge bb_sub "sub";
+			edge bb_next "next";
+		| SEIfThen(bb_then,bb_next,_) ->
+			edge bb_then "then";
+			edge bb_next "next"
+		| SEIfThenElse(bb_then,bb_else,bb_next,_,_) ->
+			edge bb_then "then";
+			edge bb_else "else";
+			edge bb_next "next";
+		| SEWhile(bb_head,bb_body,bb_next) ->
+			edge bb_head "loop-head";
+			edge bb_body "loop-body";
+			edge bb_next "next";
+		| SEMerge bb_next ->
+			edge bb_next "merge"
+		| SESwitch(bbl,bo,bb_next,_) ->
+			List.iter (fun (el,bb) -> edge bb ("case " ^ (String.concat " | " (List.map s_expr_pretty el)))) bbl;
+			(match bo with None -> () | Some bb -> edge bb "default");
+			edge bb_next "next";
+		| SETry(bb_try,_,bbl,bb_next,_) ->
+			edge bb_try "try";
+			List.iter (fun (_,bb_catch) -> edge bb_catch "catch") bbl;
+			edge bb_next "next";
+		| SEEnd ->
+			()
+		| SENone ->
+			()
+
+	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
+		s
+
+	let generate_cfg_ssa ch g =
+		Printf.fprintf ch "\tnode [shape=plaintext];\n";
+		let expr_name b i = Printf.sprintf "e%s%i" (if b then "p" else "") i in
+		List.iter (fun bb ->
+			Printf.fprintf ch "n%i[label=<<table BORDER=\"0\" CELLBORDER=\"1\" CELLSPACING=\"0\">\n\t<tr><td port=\"in\" bgcolor=\"lightgray\">(%i) %s</td></tr>\n" bb.bb_id bb.bb_id (BasicBlock.s_block_kind bb.bb_kind);
+			let s_expr b i e =
+				Printf.fprintf ch "\t<tr><td port=\"%s\" align=\"left\">%s</td></tr>\n" (expr_name b i) (s_escape (htmlescape (s_expr_pretty e)))
+			in
+			DynArray.iteri (s_expr true) bb.bb_phi;
+			DynArray.iteri (s_expr false) bb.bb_el;
+			Printf.fprintf ch "\t<tr><td port=\"out\"></td></tr>\n</table>>];\n";
+		) g.g_nodes;
+		Graph.iter_edges g (fun edge ->
+			Printf.fprintf ch "n%i:out -> n%i:in[label=\"%s\"];\n" edge.cfg_from.bb_id edge.cfg_to.bb_id (BasicBlock.s_cfg_edge_kind edge.cfg_kind)
+		);
+		DynArray.iter (fun vi ->
+			begin try
+				let (bb,is_phi,i) = match vi.vi_value with None -> raise Not_found | Some i -> i in
+				let n1 = Printf.sprintf "n%i:%s" bb.bb_id (expr_name is_phi i) in
+				List.iter (fun (bb',is_phi',i') ->
+					if bb != bb' then begin (* intra-node edges look stupid in dot *)
+						let n2 = Printf.sprintf "n%i:%s" bb'.bb_id (expr_name is_phi' i') in
+						Printf.fprintf ch "%s -> %s[color=lightblue,constraint=false];\n" n1 n2;
+					end
+				) vi.vi_ssa_edges;
+			with Not_found ->
+				()
+			end
+		) 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]
+
+	let dot_debug ctx c cf =
+		let g = ctx.graph in
+		let start_graph ?(graph_config=[]) suffix =
+			let ch = Codegen.Dump.create_file suffix [] (get_dump_path ctx c cf) in
+			Printf.fprintf ch "digraph graphname {\n";
+			List.iter (fun s -> Printf.fprintf ch "%s;\n" s) graph_config;
+			ch,(fun () ->
+				Printf.fprintf ch "}\n";
+				close_out ch
+			)
+		in
+		let ch,f = start_graph "-cfg.dot" in
+		List.iter (fun bb -> dot_debug_node g ch [NILoopGroups;NIScopes;NIPhi;NIExpr] bb) g.g_nodes;
+		Graph.iter_edges g (dot_debug_cfg_edge ch);
+		f();
+		let ch,f = start_graph "-cfg-ssa.dot" in
+		generate_cfg_ssa ch g;
+		f();
+		let ch,f = start_graph "-dj.dot" in
+		List.iter (fun bb ->
+			dot_debug_node g ch [] bb;
+			List.iter (fun einc ->
+				let bb' = einc.cfg_from in
+				let style = if bb' == bb.bb_dominator then "solid" else "dashed" in
+				Printf.fprintf ch "n%i -> n%i[style=\"%s\"];\n" bb'.bb_id bb.bb_id style;
+			) bb.bb_incoming;
+		) g.g_nodes;
+		f();
+		let ch,f = start_graph "-df.dot" in
+		List.iter (fun bb ->
+			dot_debug_node g ch [NIVars] bb;
+			List.iter (fun bb' -> Printf.fprintf ch "n%i -> n%i;\n" bb.bb_id bb'.bb_id) bb.bb_df;
+		) g.g_nodes;
+		f();
+		let ch,f = start_graph "-dom.dot" in
+		List.iter (fun bb ->
+			dot_debug_node g ch [NIVars] bb;
+			List.iter (fun bb' -> Printf.fprintf ch "n%i -> n%i;\n" bb.bb_id bb'.bb_id) bb.bb_dominated;
+		) g.g_nodes;
+		f();
+		let ch,f = start_graph "-syntax.dot" in
+		List.iter (fun bb ->
+			dot_debug_node g ch [NIExpr] bb;
+			dot_debug_syntax_edge ch bb bb.bb_syntax_edge
+		) g.g_nodes;
+		f();
+		let ch,f = start_graph "-ssa-edges.dot" in
+		let nodes = ref PMap.empty in
+		let node_name bb is_phi i = Printf.sprintf "e%i_%b_%i" bb.bb_id is_phi i in
+		let node_name2 bb is_phi i =
+			let n = node_name bb is_phi i in
+			nodes := PMap.add n true !nodes;
+			n
+		in
+		DynArray.iter (fun vi ->
+			begin try
+				let (bb,is_phi,i) = match vi.vi_value with None -> raise Not_found | Some i -> i in
+				let n1 = node_name2 bb is_phi i in
+				List.iter (fun (bb',is_phi',i') ->
+					let n2 = node_name2 bb' is_phi' i' in
+					Printf.fprintf ch "%s -> %s;\n" n1 n2
+				) vi.vi_ssa_edges
+			with Not_found ->
+				()
+			end
+		) g.g_var_infos;
+		List.iter (fun bb ->
+			let f is_phi acc i e =
+				let n = node_name bb is_phi i in
+				(i + 1),if PMap.mem n !nodes then
+					(n,s_expr_pretty e) :: acc
+				else
+					acc
+			in
+			let _,active_nodes = DynArray.fold_left (fun (i,acc) -> f true acc i) (0,[]) bb.bb_phi in
+			let _,active_nodes = DynArray.fold_left (fun (i,acc) -> f false acc i) (0,active_nodes) bb.bb_el in
+			if active_nodes <> [] then begin
+				Printf.fprintf ch "subgraph cluster_%i {\n" bb.bb_id;
+				Printf.fprintf ch "label=%i;\n" bb.bb_id;
+				Printf.fprintf ch "style=filled;\n";
+				Printf.fprintf ch "color=lightblue;\n";
+				List.iter (fun (n,s) ->
+					Printf.fprintf ch "%s[shape=box,label=\"%s\"];\n" n (s_escape s)
+				) active_nodes;
+				Printf.fprintf ch "}\n";
+			end;
+		) g.g_nodes;
+		f()
+end
+
+module Run = struct
+	open AnalyzerConfig
+	open Graph
+
+	let with_timer s f =
+		let timer = timer s in
+		let r = f() in
+		timer();
+		r
+
+	let create_analyzer_context com config e =
+		let g = Graph.create e.etype e.epos in
+		let ctx = {
+			com = com;
+			config = config;
+			graph = g;
+			(* For CPP we want to use variable names which are "probably" not used by users in order to
+			   avoid problems with the debugger, see https://github.com/HaxeFoundation/hxcpp/issues/365 *)
+			temp_var_name = (match com.platform with Cpp -> "_hx_tmp" | _ -> "tmp");
+			entry = g.g_unreachable;
+			has_unbound = false;
+			loop_counter = 0;
+			loop_stack = [];
+			debug_exprs = [];
+		} in
+		ctx
+
+	let add_debug_expr ctx s e =
+		ctx.debug_exprs <- (s,e) :: ctx.debug_exprs
+
+	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
+		if actx.com.debug then add_debug_expr actx "after filter-apply" e;
+		let tf,is_real_function = match e.eexpr with
+			| TFunction tf ->
+				tf,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
+		in
+		with_timer "analyzer-from-texpr" (fun () -> AnalyzerTexprTransformer.from_tfunction actx tf e.etype 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
+		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
+		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 = 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 _ -> e
+				| _ -> Type.map_expr (loop first) e
+			in
+			loop true e
+		end in
+		e
+
+	let run_on_expr actx e =
+		let is_real_function = there actx e in
+		Graph.infer_immediate_dominators actx.graph;
+		Graph.infer_scopes actx.graph;
+		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);
+		end;
+		back_again actx is_real_function
+
+	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) ->
+			let config = update_config_from_meta ctx.Typecore.com config cf.cf_meta in
+			let actx = create_analyzer_context ctx.Typecore.com config e in
+			let 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 (Printf.sprintf "</%s>" s);
+				) (List.rev actx.debug_exprs);
+				Debug.dot_debug actx c cf;
+				prerr_endline (Printf.sprintf "dot graph written to %s" (String.concat "/" (Debug.get_dump_path actx c cf)));
+			in
+			let e = try
+				run_on_expr actx e
+			with
+			| Error _ | Abort _ as exc ->
+				raise exc
+			| exc ->
+				debug();
+				raise exc
+			in
+			let e = Cleanup.reduce_control_flow ctx e in
+			begin match config.debug_kind with
+				| DebugNone -> ()
+				| DebugDot -> Debug.dot_debug actx c cf;
+				| DebugFull -> debug()
+			end;
+			cf.cf_expr <- Some e;
+		| _ -> ()
+
+	let run_on_class ctx config c =
+		let config = update_config_from_meta ctx.Typecore.com config c.cl_meta in
+		let process_field stat cf = match cf.cf_kind with
+			| Var _ when not stat -> ()
+			| _ -> run_on_field ctx config c cf
+		in
+		List.iter (process_field false) c.cl_ordered_fields;
+		List.iter (process_field true) c.cl_ordered_statics;
+		begin match c.cl_constructor with
+			| None -> ()
+			| Some f -> process_field false f;
+		end;
+		begin match c.cl_init with
+			| None ->
+				()
+			| Some e ->
+				let tf = { tf_args = []; tf_type = e.etype; tf_expr = e; } in
+				let e = mk (TFunction tf) (tfun [] e.etype) e.epos in
+				let actx = create_analyzer_context ctx.Typecore.com {config with optimize = false} e in
+				let e = run_on_expr actx e in
+				let e = match e.eexpr with
+					| TFunction tf -> tf.tf_expr
+					| _ -> assert false
+				in
+				c.cl_init <- Some e
+		end
+
+	let run_on_type ctx config t =
+		match t with
+		| TClassDecl c when (is_ignored c.cl_meta) -> ()
+		| TClassDecl c -> run_on_class ctx config c
+		| TEnumDecl _ -> ()
+		| TTypeDecl _ -> ()
+		| TAbstractDecl _ -> ()
+
+	let run_on_types ctx types =
+		let com = ctx.Typecore.com in
+		let config = get_base_config com in
+		let cfl = if config.optimize && config.purity_inference then Purity.infer com else [] in
+		List.iter (run_on_type ctx config) types;
+		List.iter (fun cf -> cf.cf_meta <- List.filter (fun (m,_,_) -> m <> Meta.Pure) cf.cf_meta) cfl
+end
+;;
+Typecore.analyzer_run_on_expr_ref := (fun com e ->
+	let actx = Run.create_analyzer_context com (AnalyzerConfig.get_base_config com) e in
+	Run.run_on_expr actx e
+)

+ 126 - 0
src/optimization/analyzerConfig.ml

@@ -0,0 +1,126 @@
+(*
+	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 Type
+open Common
+
+type debug_kind =
+	| DebugNone
+	| DebugDot
+	| DebugFull
+
+type t = {
+	optimize : bool;
+	const_propagation : bool;
+	copy_propagation : bool;
+	code_motion : bool;
+	local_dce : bool;
+	fusion : bool;
+	purity_inference : bool;
+	debug_kind : debug_kind;
+}
+
+let flag_const_propagation = "const_propagation"
+let flag_copy_propagation = "copy_propagation"
+let flag_code_motion = "code_motion"
+let flag_local_dce = "local_dce"
+let flag_fusion = "fusion"
+let flag_purity_inference = "purity_inference"
+let flag_ignore = "ignore"
+let flag_dot_debug = "dot_debug"
+let flag_full_debug = "full_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]
+
+let has_analyzer_option meta s =
+	try
+		let rec loop ml = match ml with
+			| (Meta.Analyzer,el,_) :: ml ->
+				if List.exists (fun (e,p) ->
+					match e with
+						| EConst(Ident s2) when s = s2 -> true
+						| _ -> false
+				) el then
+					true
+				else
+					loop ml
+			| _ :: ml ->
+				loop ml
+			| [] ->
+				false
+		in
+		loop meta
+	with Not_found ->
+		false
+
+let is_ignored meta =
+	has_analyzer_option meta flag_ignore
+
+let get_base_config com =
+	{
+		optimize = not (Common.defined com Define.NoAnalyzer);
+		const_propagation = not (Common.raw_defined com "analyzer-no-const-propagation");
+		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);
+		purity_inference = not (Common.raw_defined com "analyzer-no-purity-inference");
+		debug_kind = DebugNone;
+	}
+
+let update_config_from_meta com config meta =
+	List.fold_left (fun config meta -> match meta with
+		| (Meta.Analyzer,el,_) ->
+			List.fold_left (fun config e -> match fst e with
+				| EConst (Ident s) when s = "no_" ^ flag_const_propagation -> { config with const_propagation = false}
+				| EConst (Ident s) when s = flag_const_propagation -> { config with const_propagation = true}
+				| EConst (Ident s) when s = "no_" ^ flag_copy_propagation -> { config with copy_propagation = false}
+				| EConst (Ident s) when s = flag_copy_propagation -> { config with copy_propagation = true}
+				| EConst (Ident s) when s = "no_" ^ flag_code_motion -> { config with code_motion = false}
+				| EConst (Ident s) when s = flag_code_motion -> { config with code_motion = true}
+				| EConst (Ident s) when s = "no_" ^ flag_local_dce -> { config with local_dce = false}
+				| EConst (Ident s) when s = flag_local_dce -> { config with local_dce = true}
+				| EConst (Ident s) when s = "no_" ^ flag_fusion -> { config with fusion = false}
+				| EConst (Ident s) when s = flag_fusion -> { config with fusion = true}
+				| EConst (Ident s) when s = "no_" ^ flag_purity_inference -> { config with purity_inference = false}
+				| 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}
+				| _ ->
+					let s = Ast.s_expr e in
+					com.warning (StringError.string_error s all_flags ("Unrecognized analyzer option: " ^ s)) (pos e);
+					config
+			) config el
+		| (Meta.HasUntyped,_,_) ->
+			{config with optimize = false}
+		| _ ->
+			config
+	) config meta
+
+let get_class_config com c =
+	let config = get_base_config com in
+	update_config_from_meta com config c.cl_meta
+
+let get_field_config com c cf =
+	let config = get_class_config com c in
+	update_config_from_meta com config cf.cf_meta

+ 725 - 0
src/optimization/analyzerTexpr.ml

@@ -0,0 +1,725 @@
+(*
+	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 Type
+open Common
+
+let s_expr_pretty e = s_expr_pretty false "" (s_type (print_context())) e
+
+let rec is_true_expr e1 = match e1.eexpr with
+	| TConst(TBool true) -> true
+	| TParenthesis e1 -> is_true_expr e1
+	| _ -> false
+
+let map_values ?(allow_control_flow=true) f e =
+	let branching = ref false in
+	let efinal = ref None in
+	let f e =
+		if !branching then
+			f e
+		else begin
+			efinal := Some e;
+			mk (TConst TNull) e.etype e.epos
+		end
+	in
+	let rec loop complex e = match e.eexpr with
+		| TIf(e1,e2,Some e3) ->
+			branching := true;
+			let e2 = loop true e2 in
+			let e3 = loop true e3 in
+			{e with eexpr = TIf(e1,e2,Some e3)}
+		| TSwitch(e1,cases,edef) ->
+			branching := true;
+			let cases = List.map (fun (el,e) -> el,loop true e) cases in
+			let edef = Option.map (loop true) edef in
+			{e with eexpr = TSwitch(e1,cases,edef)}
+		| TBlock [e1] ->
+			loop complex e1
+		| TBlock el ->
+			begin match List.rev el with
+			| e1 :: el ->
+				let e1 = loop true e1 in
+				let e = {e with eexpr = TBlock (List.rev (e1 :: el))} in
+				{e with eexpr = TMeta((Meta.MergeBlock,[],e.epos),e)}
+			| [] ->
+				f e
+			end
+		| TTry(e1,catches) ->
+			branching := true;
+			let e1 = loop true e1 in
+			let catches = List.map (fun (v,e) -> v,loop true e) catches in
+			{e with eexpr = TTry(e1,catches)}
+		| TMeta(m,e1) ->
+			{e with eexpr = TMeta(m,loop complex e1)}
+		| TParenthesis e1 ->
+			{e with eexpr = TParenthesis (loop complex e1)}
+		| TBreak | TContinue | TThrow _ | TReturn _ ->
+			if not allow_control_flow then raise Exit;
+			e
+		| _ ->
+			if not complex then raise Exit;
+			f e
+	in
+	let e = loop false e in
+	e,!efinal
+
+let can_throw e =
+	let rec loop e = match e.eexpr with
+		| TConst _ | TLocal _ | TTypeExpr _ | TFunction _ | TBlock _ -> ()
+		| TCall _ | TNew _ | TThrow _ | TCast(_,Some _) -> raise Exit
+		| TField _ | TArray _ -> raise Exit (* sigh *)
+		| _ -> Type.iter loop e
+	in
+	try
+		loop e; false
+	with Exit ->
+		true
+
+let rec can_be_inlined e = match e.eexpr with
+	| TConst _ -> true
+	| TParenthesis e1 | TMeta(_,e1) -> can_be_inlined e1
+	| _ -> false
+
+let target_handles_unops com = match com.platform with
+	| Lua | Python -> false
+	| _ -> true
+
+let target_handles_assign_ops com = match com.platform with
+	| Lua -> false
+	| _ -> true
+
+let rec can_be_used_as_value com e =
+	let rec loop e = match e.eexpr with
+		| TBlock [e] -> loop e
+		| TBlock _ | TSwitch _ | TTry _ -> raise Exit
+		| TCall({eexpr = TConst (TString "phi")},_) -> raise Exit
+		(* | TCall _ | TNew _ when (match com.platform with Cpp | Php -> true | _ -> false) -> raise Exit *)
+		| TReturn _ | TThrow _ | TBreak | TContinue -> raise Exit
+		| TUnop((Increment | Decrement),_,_) when not (target_handles_unops com) -> raise Exit
+		| TNew _ when com.platform = Php -> raise Exit
+		| TFunction _ -> ()
+		| TConst TNull when (match com.platform with Cs | Cpp | Java | Flash -> true | _ -> false) -> raise Exit
+		| _ -> Type.iter loop e
+	in
+	try
+		loop e;
+		true
+	with Exit ->
+		false
+
+let has_pure_meta meta = Meta.has Meta.Pure meta
+
+let is_pure c cf = has_pure_meta c.cl_meta || has_pure_meta cf.cf_meta
+
+let wrap_meta s e =
+	mk (TMeta((Meta.Custom s,[],e.epos),e)) e.etype e.epos
+
+let is_really_unbound v =
+	v.v_name <> "`trace" && is_unbound v
+
+let r = Str.regexp "^\\([A-Za-z0-9_]\\)+$"
+let is_unbound_call_that_might_have_side_effects v el = match v.v_name,el with
+	| "__js__",[{eexpr = TConst (TString s)}] when Str.string_match r s 0 -> false
+	| _ -> true
+
+let is_ref_type = function
+	| TType({t_path = ["cs"],("Ref" | "Out")},_) -> true
+	| TAbstract({a_path=["hl";"types"],"Ref"},_) -> true
+	| _ -> false
+
+let type_change_ok com t1 t2 =
+	if t1 == t2 then
+		true
+	else begin
+		let rec map t = match t with
+			| TMono r -> (match !r with None -> t_dynamic | Some t -> map t)
+			| _ -> Type.map map t
+		in
+		let t1 = map t1 in
+		let t2 = map t2 in
+		let rec is_nullable_or_whatever = function
+			| TMono r ->
+				(match !r with None -> false | Some t -> is_nullable_or_whatever t)
+			| TType ({ t_path = ([],"Null") },[_]) ->
+				true
+			| TLazy f ->
+				is_nullable_or_whatever (!f())
+			| TType (t,tl) ->
+				is_nullable_or_whatever (apply_params t.t_params tl t.t_type)
+			| TFun _ ->
+				false
+			| TInst ({ cl_kind = KTypeParameter _ },_) ->
+				false
+			| TAbstract (a,_) when Meta.has Meta.CoreType a.a_meta ->
+				not (Meta.has Meta.NotNull a.a_meta)
+			| TAbstract (a,tl) ->
+				not (Meta.has Meta.NotNull a.a_meta) && is_nullable_or_whatever (apply_params a.a_params tl a.a_this)
+			| _ ->
+				true
+		in
+		(* Check equality again to cover cases where TMono became t_dynamic *)
+		t1 == t2 || match follow t1,follow t2 with
+			| TDynamic _,_ | _,TDynamic _ -> false
+			| _ ->
+				if com.config.pf_static && is_nullable_or_whatever t1 <> is_nullable_or_whatever t2 then false
+				else type_iseq t1 t2
+	end
+
+let dynarray_map f d =
+	DynArray.iteri (fun i e -> DynArray.unsafe_set d i (f e)) d
+
+let dynarray_mapi f d =
+	DynArray.iteri (fun i e -> DynArray.unsafe_set d i (f i e)) d
+
+(*
+	This module rewrites some expressions to reduce the amount of special cases for subsequent analysis. After analysis
+	it restores some of these expressions back to their original form.
+
+	The following expressions are removed from the AST after `apply` has run:
+	- OpBoolAnd and OpBoolOr binary operations are rewritten to TIf
+	- OpAssignOp on a variable is rewritten to OpAssign
+	- Prefix increment/decrement operations are rewritten to OpAssign
+	- Postfix increment/decrement operations are rewritten to a TBlock with OpAssign and OpAdd/OpSub
+	- `do {} while(true)` is rewritten to `while(true) {}`
+	- TWhile expressions are rewritten to `while (true)` with appropriate conditional TBreak
+	- TFor is rewritten to TWhile
+*)
+module TexprFilter = struct
+	let apply com e =
+		let rec loop e = match e.eexpr with
+		| TBinop(OpBoolAnd | OpBoolOr as op,e1,e2) ->
+			let e_then = e2 in
+			let e_if,e_else = if op = OpBoolOr then
+				mk (TUnop(Not,Prefix,e1)) com.basic.tbool e.epos,mk (TConst (TBool(true))) com.basic.tbool e.epos
+			else
+				e1,mk (TConst (TBool(false))) com.basic.tbool e.epos
+			in
+			loop (mk (TIf(e_if,e_then,Some e_else)) e.etype e.epos)
+		| TBinop(OpAssignOp op,({eexpr = TLocal _} as e1),e2) ->
+			let e = {e with eexpr = TBinop(op,e1,e2)} in
+			loop {e with eexpr = TBinop(OpAssign,e1,e)}
+		| TUnop((Increment | Decrement as op),flag,({eexpr = TLocal _} as e1)) ->
+			let e_one = mk (TConst (TInt (Int32.of_int 1))) com.basic.tint e1.epos in
+			let e = {e with eexpr = TBinop(OpAssignOp (if op = Increment then OpAdd else OpSub),e1,e_one)} in
+			let e = if flag = Prefix then
+				e
+			else
+				mk (TBlock [
+					{e with eexpr = TBinop(OpAssignOp (if op = Increment then OpAdd else OpSub),e1,e_one)};
+					{e with eexpr = TBinop((if op = Increment then OpSub else OpAdd),e1,e_one)};
+				]) e.etype e.epos
+			in
+			loop e
+		| TWhile(e1,e2,DoWhile) when is_true_expr e1 ->
+			loop {e with eexpr = TWhile(e1,e2,NormalWhile)}
+		| TWhile(e1,e2,flag) when not (is_true_expr e1) ->
+			let p = e.epos in
+			let e_break = mk TBreak t_dynamic p in
+			let e_not = mk (TUnop(Not,Prefix,Codegen.mk_parent e1)) e1.etype e1.epos in
+			let e_if eo = mk (TIf(e_not,e_break,eo)) com.basic.tvoid p in
+			let rec map_continue e = match e.eexpr with
+				| TContinue ->
+					Texpr.duplicate_tvars (e_if (Some e))
+				| TWhile _ | TFor _ ->
+					e
+				| _ ->
+					Type.map_expr map_continue e
+			in
+			let e2 = if flag = NormalWhile then e2 else map_continue e2 in
+			let e_if = e_if None in
+			let e_block = if flag = NormalWhile then Type.concat e_if e2 else Type.concat e2 e_if in
+			let e_true = mk (TConst (TBool true)) com.basic.tbool p in
+			let e = mk (TWhile(Codegen.mk_parent e_true,e_block,NormalWhile)) e.etype p in
+			loop e
+		| TFor(v,e1,e2) ->
+			let v' = alloc_var "tmp" 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 e.epos in
+			let ebody = Type.concat eassign e2 in
+			let e = 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 e.epos in
+			loop e
+		| _ ->
+			Type.map_expr loop e
+		in
+		loop e
+end
+
+module Fusion = struct
+
+	type interference_kind =
+		| IKVarMod of tvar list
+		| IKSideEffect
+		| IKNone
+
+	let get_interference_kind e =
+		let vars = ref [] in
+		let rec loop e = match e.eexpr with
+			| TMeta((Meta.Pure,_,_),_) ->
+				()
+			| TUnop((Increment | Decrement),_,{eexpr = TLocal v}) ->
+				vars := v :: !vars
+			| TBinop((OpAssign | OpAssignOp _),{eexpr = TLocal v},e2) ->
+				vars := v :: !vars;
+				loop e2
+			| TBinop((OpAssign | OpAssignOp _),_,_) | TUnop((Increment | Decrement),_,_) ->
+				raise Exit
+			| TCall({eexpr = TLocal v},el) when not (is_unbound_call_that_might_have_side_effects v el) ->
+				List.iter loop el
+			| TCall({eexpr = TField(_,FStatic(c,cf))},el) when is_pure c cf ->
+				List.iter loop el
+			| TNew(c,_,el) when (match c.cl_constructor with Some cf when is_pure c cf -> true | _ -> false) ->
+				List.iter loop el;
+			| TCall _ | TNew _ ->
+				raise Exit
+			| _ ->
+				Type.iter loop e
+		in
+		try
+			loop e;
+			begin match !vars with
+				| [] -> IKNone
+				| vars -> IKVarMod vars
+			end
+		with Exit ->
+			IKSideEffect
+
+	let apply com config e =
+		let rec block_element acc el = match el with
+			| {eexpr = TBinop((OpAssign | OpAssignOp _),_,_) | TUnop((Increment | Decrement),_,_)} as e1 :: el ->
+				block_element (e1 :: acc) el
+			| {eexpr = TLocal _} as e1 :: el when not config.AnalyzerConfig.local_dce ->
+				block_element (e1 :: acc) el
+			(* no-side-effect *)
+			| {eexpr = TEnumParameter _ | TFunction _ | TConst _ | TTypeExpr _ | TLocal _} :: el ->
+				block_element acc el
+			(* no-side-effect composites *)
+			| {eexpr = TParenthesis e1 | TMeta(_,e1) | TCast(e1,None) | TField(e1,_) | TUnop(_,_,e1)} :: el ->
+				block_element acc (e1 :: el)
+			| {eexpr = TArray(e1,e2) | TBinop(_,e1,e2)} :: el ->
+				block_element acc (e1 :: e2 :: el)
+			| {eexpr = TArrayDecl el1 | TCall({eexpr = TField(_,FEnum _)},el1)} :: el2 -> (* TODO: check e1 of FEnum *)
+				block_element acc (el1 @ el2)
+			| {eexpr = TObjectDecl fl} :: el ->
+				block_element acc ((List.map snd fl) @ el)
+			| {eexpr = TIf(e1,{eexpr = TBlock []},(Some {eexpr = TBlock []} | None))} :: el ->
+				block_element acc (e1 :: el)
+			| {eexpr = TBlock [e1]} :: el ->
+				block_element acc (e1 :: el)
+			| {eexpr = TBlock []} :: el ->
+				block_element acc el
+			| e1 :: el ->
+				block_element (e1 :: acc) el
+			| [] ->
+				acc
+		in
+		let changed = ref false in
+		let var_uses = Hashtbl.create 0 in
+		let var_writes = Hashtbl.create 0 in
+		let get_num_uses v =
+			try Hashtbl.find var_uses v.v_id with Not_found -> 0
+		in
+		let get_num_writes v =
+			try Hashtbl.find var_writes v.v_id with Not_found -> 0
+		in
+		let change map v delta =
+			Hashtbl.replace map v.v_id ((try Hashtbl.find map v.v_id with Not_found -> 0) + delta);
+		in
+		let change_num_uses v delta =
+			change var_uses v delta
+		in
+		let change_num_writes v delta =
+			change var_writes v delta
+		in
+		let rec loop e = match e.eexpr with
+			| TLocal v ->
+				change_num_uses v 1;
+			| TBinop(OpAssign,{eexpr = TLocal v},e2) ->
+				change_num_writes v 1;
+				loop e2
+			| _ ->
+				Type.iter loop e
+		in
+		loop e;
+		let can_be_fused v e =
+			let check_switch_variable v = match com.platform with
+				| Python | Lua when Meta.has Meta.SwitchVariable v.v_meta -> false
+				| _ -> true
+			in
+			let b = get_num_uses v <= 1 &&
+			        get_num_writes v = 0 &&
+			        can_be_used_as_value com e &&
+			        (Meta.has Meta.CompilerGenerated v.v_meta || config.AnalyzerConfig.optimize && config.AnalyzerConfig.fusion && type_change_ok com v.v_type e.etype && v.v_extra = None) &&
+			        check_switch_variable v
+			in
+			(* let st = s_type (print_context()) in *)
+			(* if e.epos.pfile = "src/Main.hx" then print_endline (Printf.sprintf "%s: %i %i %b %s %s (%b %b %b %b %b) -> %b" v.v_name (get_num_uses v) (get_num_writes v) (can_be_used_as_value com e) (st v.v_type) (st e.etype) (Meta.has Meta.CompilerGenerated v.v_meta) config.Config.optimize config.Config.fusion (type_change_ok com v.v_type e.etype) (v.v_extra = None) b); *)
+			b
+		in
+		let rec fuse acc el = match el with
+			| ({eexpr = TVar(v1,None)} as e1) :: {eexpr = TBinop(OpAssign,{eexpr = TLocal v2},e2)} :: el when v1 == v2 ->
+				changed := true;
+				let e1 = {e1 with eexpr = TVar(v1,Some e2)} in
+				change_num_writes v1 (-1);
+				fuse (e1 :: acc) el
+			| ({eexpr = TVar(v1,None)} as e1) :: ({eexpr = TIf(eif,_,Some _)} as e2) :: el when can_be_used_as_value com e2 && (match com.platform with Php -> false | Cpp when not (Common.defined com Define.Cppia) -> false | _ -> true) ->
+				begin try
+					let i = ref 0 in
+					let check_assign e = match e.eexpr with
+						| TBinop(OpAssign,{eexpr = TLocal v2},e2) when v1 == v2 -> incr i; e2
+						| _ -> raise Exit
+					in
+					let e,_ = map_values ~allow_control_flow:false check_assign e2 in
+					let e = match follow e.etype with
+						| TAbstract({a_path=[],"Void"},_) -> {e with etype = v1.v_type}
+						| _ -> e
+					in
+					let e1 = {e1 with eexpr = TVar(v1,Some e)} in
+					changed := true;
+					change_num_writes v1 (- !i);
+					fuse (e1 :: acc) el
+				with Exit ->
+					fuse (e1 :: acc) (e2 :: el)
+				end
+			| ({eexpr = TVar(v1,Some e1)} as ev) :: e2 :: el when can_be_fused v1 e1 ->
+				let found = ref false in
+				let affected = ref false in
+				let ik1 = get_interference_kind e1 in
+				let check_interference e2 =
+					let check ik e2 = match ik with
+						| IKNone -> ()
+						| IKSideEffect -> (* TODO: Could this miss a IKVarMod case? *)
+							let rec loop e = match e.eexpr with
+								| TMeta((Meta.Pure,_,_),_) ->
+									()
+								| TArray _ ->
+									raise Exit
+								| TField _ when Optimizer.is_affected_type e.etype ->
+									raise Exit
+								| TCall({eexpr = TField(_,FStatic(c,cf))},el) when is_pure c cf ->
+									List.iter loop el
+								| TNew(c,_,el) when (match c.cl_constructor with Some cf when is_pure c cf -> true | _ -> false) ->
+									List.iter loop el
+								| TCall _ | TNew _ | TBinop((OpAssign | OpAssignOp _),_,_) | TUnop((Increment | Decrement),_,_) ->
+									raise Exit
+								| _ ->
+									Type.iter loop e
+							in
+							loop e2
+						| IKVarMod vl ->
+							let rec loop e = match e.eexpr with
+								| TLocal v when List.exists (fun v' -> v == v') vl -> raise Exit
+								| _ -> Type.iter loop e
+							in
+							loop e2
+					in
+					try
+						check ik1 e2;
+						check (get_interference_kind e2) e1
+					with Exit -> match com.platform with
+						| Cpp when not (Common.defined com Define.Cppia) -> raise Exit
+						| Php -> raise Exit (* They don't define evaluation order, so let's exit *)
+						| _ -> affected := true;
+				in
+				let rec replace e =
+					let e = match e.eexpr with
+						| TWhile _ | TFunction _ ->
+							e
+						| TIf(e1,e2,eo) ->
+							let e1 = replace e1 in
+							{e with eexpr = TIf(e1,e2,eo)}
+						| TSwitch(e1,cases,edef) ->
+							let e1 = replace e1 in
+							{e with eexpr = TSwitch(e1,cases,edef)}
+						| TLocal v2 when v1 == v2 && not !affected ->
+							found := true;
+							e1
+						| TBinop((OpAssign | OpAssignOp _ as op),({eexpr = TArray(e1,e2)} as ea),e3) ->
+							let e1 = replace e1 in
+							let e2 = replace e2 in
+							let ea = {ea with eexpr = TArray(e1,e2)} in
+							let e3 = replace e3 in
+							{e with eexpr = TBinop(op,ea,e3)}
+						| TBinop((OpAssign | OpAssignOp _ as op),e1,e2) ->
+							let e2 = replace e2 in
+							let e1 = match e1.eexpr with TLocal _ -> e1 | _ -> replace e1 in
+							{e with eexpr = TBinop(op,e1,e2)}
+						| TUnop((Increment | Decrement),_,{eexpr = TLocal _}) ->
+							e
+						| TCall({eexpr = TLocal v},_) when is_really_unbound v ->
+							e
+						| TCall(e1,el) when com.platform = Neko ->
+							(* Neko has this reversed at the moment (issue #4787) *)
+							let el = List.map replace el in
+							let e1 = replace e1 in
+							{e with eexpr = TCall(e1,el)}
+						| _ ->
+							Type.map_expr replace e
+					in
+					check_interference e;
+					e
+				in
+				begin try
+					let e = replace e2 in
+					if not !found then raise Exit;
+					changed := true;
+					change_num_uses v1 (-1);
+					fuse (e :: acc) el
+				with Exit ->
+					fuse (ev :: acc) (e2 :: el)
+				end
+			| {eexpr = TUnop((Increment | Decrement as op,Prefix,({eexpr = TLocal v} as ev)))} as e1 :: e2 :: el ->
+				begin try
+					let e2,f = match e2.eexpr with
+						| TReturn (Some e2) -> e2,(fun e -> {e2 with eexpr = TReturn (Some e)})
+						| TBinop(OpAssign,e21,e22) -> e22,(fun e -> {e2 with eexpr = TBinop(OpAssign,e21,e)})
+						| TVar(v,Some e2) -> e2,(fun e -> {e2 with eexpr = TVar(v,Some e)})
+						| _ -> raise Exit
+					in
+					let ops_match op1 op2 = match op1,op2 with
+						| Increment,OpSub
+						| Decrement,OpAdd ->
+							true
+						| _ ->
+							false
+					in
+					begin match e2.eexpr with
+						| TBinop(op2,{eexpr = TLocal v2},{eexpr = TConst (TInt i32)}) when v == v2 && Int32.to_int i32 = 1 && ops_match op op2 ->
+							changed := true;
+							change_num_uses v2 (-1);
+							let e = (f {e1 with eexpr = TUnop(op,Postfix,ev)}) in
+							fuse (e :: acc) el
+						| TLocal v2 when v == v2 ->
+							changed := true;
+							change_num_uses v2 (-1);
+							let e = (f {e1 with eexpr = TUnop(op,Prefix,ev)}) in
+							fuse (e :: acc) el
+						| _ ->
+							raise Exit
+					end
+				with Exit ->
+					fuse (e1 :: acc) (e2 :: el)
+				end
+			| e1 :: el ->
+				fuse (e1 :: acc) el
+			| [] ->
+				acc
+		in
+		let rec loop e = match e.eexpr with
+			| TBlock el ->
+				let el = List.map loop el in
+				(* fuse flips element order, but block_element doesn't care and flips it back *)
+				let el = fuse [] el in
+				let el = block_element [] el in
+				let rec fuse_loop el =
+					changed := false;
+					let el = fuse [] el in
+					let el = block_element [] el in
+					if !changed then fuse_loop el else el
+				in
+				let el = fuse_loop el in
+				{e with eexpr = TBlock el}
+			| TCall({eexpr = TLocal v},_) when is_really_unbound v ->
+				e
+			| _ ->
+				Type.map_expr loop e
+		in
+		let e = loop e in
+		e
+end
+
+module Cleanup = struct
+	let apply com e =
+		let if_or_op e e1 e2 e3 = match (Texpr.skip e1).eexpr,(Texpr.skip e3).eexpr with
+			| TUnop(Not,Prefix,e1),TConst (TBool true) -> Optimizer.optimize_binop {e with eexpr = TBinop(OpBoolOr,e1,e2)} OpBoolOr e1 e2
+			| _,TConst (TBool false) -> Optimizer.optimize_binop {e with eexpr = TBinop(OpBoolAnd,e1,e2)} OpBoolAnd e1 e2
+			| _,TBlock [] -> {e with eexpr = TIf(e1,e2,None)}
+			| _ -> match (Texpr.skip e2).eexpr with
+				| TBlock [] when com.platform <> Cs ->
+					let e1' = mk (TUnop(Not,Prefix,e1)) e1.etype e1.epos in
+					let e1' = Optimizer.optimize_unop e1' Not Prefix e1 in
+					{e with eexpr = TIf(e1',e3,None)}
+				| _ ->
+					{e with eexpr = TIf(e1,e2,Some e3)}
+		in
+		let rec loop e = match e.eexpr with
+			| TIf(e1,e2,Some e3) ->
+				let e1 = loop e1 in
+				let e2 = loop e2 in
+				let e3 = loop e3 in
+				if_or_op e e1 e2 e3;
+			| TBlock el ->
+				let el = List.map (fun e ->
+					let e = loop e in
+					match e.eexpr with
+					| TIf _ -> {e with etype = com.basic.tvoid}
+					| _ -> e
+				) el in
+				{e with eexpr = TBlock el}
+			| TWhile(e1,e2,NormalWhile) ->
+				let e1 = loop e1 in
+				let e2 = loop e2 in
+				begin match e2.eexpr with
+					| TBlock ({eexpr = TIf(e1,({eexpr = TBlock[{eexpr = TBreak}]} as eb),None)} :: el2) ->
+						let e1 = Texpr.skip e1 in
+						let e1 = match e1.eexpr with TUnop(_,_,e1) -> e1 | _ -> {e1 with eexpr = TUnop(Not,Prefix,e1)} in
+						{e with eexpr = TWhile(e1,{eb with eexpr = TBlock el2},NormalWhile)}
+					| TBlock el ->
+						let rec loop2 el = match el with
+							| {eexpr = TBreak | TContinue | TReturn _ | TThrow _} as e :: el ->
+								[e]
+							| e :: el ->
+								e :: (loop2 el)
+							| [] ->
+								[]
+						in
+						let el = loop2 el in
+						{e with eexpr = TWhile(e1,{e2 with eexpr = TBlock el},NormalWhile)}
+					| _ ->
+						{e with eexpr = TWhile(e1,e2,NormalWhile)}
+				end
+			| _ ->
+				Type.map_expr loop e
+		in
+		loop e
+
+	let rec reduce_control_flow ctx e =
+		Type.map_expr (reduce_control_flow ctx) (Optimizer.reduce_control_flow ctx e)
+end
+
+module Purity = struct
+	type purity =
+		| Pure
+		| NotPure
+		| MaybePure
+
+	type purity_node = {
+		pn_field : tclass_field;
+		mutable pn_purity : purity;
+		mutable pn_dependents : purity_node list;
+	}
+
+	let node_lut = Hashtbl.create 0
+
+	let get_field_id c cf = Printf.sprintf "%s.%s" (s_type_path c.cl_path) cf.cf_name
+
+	let get_node c cf =
+		try
+			Hashtbl.find node_lut (get_field_id c cf)
+		with Not_found ->
+			let node = {
+				pn_field = cf;
+				pn_purity = MaybePure;
+				pn_dependents = []
+			} in
+			Hashtbl.replace node_lut (get_field_id c cf) node;
+			node
+
+	let apply_to_field com is_ctor c cf =
+		let node = get_node c cf in
+		let rec taint node =
+			if node.pn_purity <> NotPure then begin
+				node.pn_purity <- NotPure;
+				List.iter taint node.pn_dependents
+			end
+		in
+		let taint_raise node =
+			taint node;
+			raise Exit;
+		in
+		let check_field c cf =
+			let node' = get_node c cf in
+			match node'.pn_purity with
+				| Pure -> ()
+				| NotPure -> taint_raise node;
+				| MaybePure -> node'.pn_dependents <- node :: node'.pn_dependents
+		in
+		let rec check_write e1 =
+			begin match e1.eexpr with
+				| TLocal v ->
+					() (* Writing to locals does not violate purity. *)
+				| TField({eexpr = TConst TThis},_) when is_ctor ->
+					() (* A constructor can write to its own fields without violating purity. *)
+				| _ ->
+					taint_raise node
+			end
+		and loop e = match e.eexpr with
+			| TMeta((Meta.Pure,_,_),_) ->
+				()
+			| TThrow _ ->
+				taint_raise node;
+			| TBinop((OpAssign | OpAssignOp _),e1,e2) ->
+				check_write e1;
+				loop e2;
+			| TUnop((Increment | Decrement),_,e1) ->
+				check_write e1;
+			| TCall({eexpr = TField(_,FStatic(c,cf))},el) ->
+				List.iter loop el;
+				check_field c cf;
+			| TNew(c,_,el) ->
+				List.iter loop el;
+				begin match c.cl_constructor with
+					| Some cf -> check_field c cf
+					| None -> taint_raise node
+				end
+			| TCall({eexpr = TLocal v},el) when not (is_unbound_call_that_might_have_side_effects v el) ->
+				List.iter loop el;
+			| TCall _ ->
+				taint_raise node
+			| _ ->
+				Type.iter loop e
+		in
+		match cf.cf_expr with
+		| None ->
+			taint node
+		| Some e ->
+			try
+				if (Meta.has (Meta.Custom ":impure")) cf.cf_meta then taint_raise node;
+				if is_pure c cf then raise Exit;
+				loop e;
+				node.pn_purity <- Pure;
+			with Exit ->
+				()
+
+	let apply_to_class com c =
+		List.iter (apply_to_field com false c) c.cl_ordered_fields;
+		List.iter (apply_to_field com false c) c.cl_ordered_statics;
+		(match c.cl_constructor with Some cf -> apply_to_field com true c cf | None -> ())
+
+	let infer com =
+		Hashtbl.clear node_lut;
+		List.iter (fun mt -> match mt with
+			| TClassDecl c -> apply_to_class com c
+			| _ -> ()
+		) com.types;
+		Hashtbl.fold (fun _ node acc ->
+			if node.pn_purity = Pure then begin
+				node.pn_field.cf_meta <- (Meta.Pure,[],node.pn_field.cf_pos) :: node.pn_field.cf_meta;
+				node.pn_field :: acc
+			end else acc
+		) node_lut [];
+end

+ 684 - 0
src/optimization/analyzerTexprTransformer.ml

@@ -0,0 +1,684 @@
+(*
+	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 Type
+open Common
+open AnalyzerConfig
+open AnalyzerTypes
+open AnalyzerTypes.BasicBlock
+open AnalyzerTypes.Graph
+open AnalyzerTexpr
+
+(*
+	Transforms an expression to a graph, and a graph back to an expression. This module relies on TexprFilter being
+	run first.
+
+	The created graph is intact and can immediately be transformed back to an expression, or used for analysis first.
+*)
+
+let rec func ctx bb tf t p =
+	let g = ctx.graph in
+	let create_node kind t p =
+		let bb = Graph.create_node g kind t p in
+		bb.bb_loop_groups <- ctx.loop_stack;
+		bb
+	in
+	let bb_root = create_node (BKFunctionBegin tf) tf.tf_expr.etype tf.tf_expr.epos in
+	let bb_exit = create_node BKFunctionEnd tf.tf_expr.etype tf.tf_expr.epos in
+	add_function g tf t p bb_root;
+	add_cfg_edge bb bb_root CFGFunction;
+	let make_block_meta b =
+		let e = mk (TConst (TInt (Int32.of_int b.bb_id))) ctx.com.basic.tint b.bb_pos in
+		wrap_meta ":block" e
+	in
+	let bb_breaks = ref [] in
+	let bb_continue = ref None in
+	let b_try_stack = ref [] in
+	let begin_loop bb_loop_pre bb_continue' =
+		let old = !bb_breaks,!bb_continue in
+		bb_breaks := [];
+		bb_continue := Some bb_continue';
+		let id = ctx.loop_counter in
+		g.g_loops <- IntMap.add id bb_loop_pre g.g_loops;
+		ctx.loop_stack <- id :: ctx.loop_stack;
+		bb_continue'.bb_loop_groups <- id :: bb_continue'.bb_loop_groups;
+		ctx.loop_counter <- id + 1;
+		(fun () ->
+			let breaks = !bb_breaks in
+			bb_breaks := fst old;
+			bb_continue := snd old;
+			ctx.loop_stack <- List.tl ctx.loop_stack;
+			breaks;
+		)
+	in
+	let begin_try b =
+		b_try_stack := b :: !b_try_stack;
+		(fun () ->
+			b_try_stack := List.tl !b_try_stack
+		)
+	in
+	let add_terminator bb e =
+		add_texpr bb e;
+		close_node g bb;
+		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
+	in
+	let no_void t p =
+		if ExtType.is_void (follow t) then error "Cannot use Void as value" p
+	in
+	let rec value' bb e = match e.eexpr with
+		| TLocal v ->
+			bb,e
+		| TBinop(OpAssign,({eexpr = TLocal v} as e1),e2) ->
+			block_element bb e,e1
+		| TBlock [e1] ->
+			value bb e1
+		| TBlock _ | TIf _ | TSwitch _ | TTry _ ->
+			bind_to_temp bb false e
+		| TCall({eexpr = TLocal v},el) when is_really_unbound v ->
+			check_unbound_call v el;
+			bb,e
+		| TCall(e1,el) ->
+			call bb e e1 el
+		| TBinop((OpAssign | OpAssignOp _) as op,e1,e2) ->
+			let bb,e2 = value bb e2 in
+			let bb,e1 = value bb e1 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
+				| bb,[e1;e2] -> bb,e1,e2
+				| _ -> assert false
+			in
+			bb,{e with eexpr = TBinop(op,e1,e2)}
+		| TUnop(op,flag,e1) ->
+			let bb,e1 = value bb e1 in
+			bb,{e with eexpr = TUnop(op,flag,e1)}
+		| TArrayDecl el ->
+			let bb,el = ordered_value_list bb el in
+			bb,{e with eexpr = TArrayDecl el}
+		| TObjectDecl fl ->
+			let el = List.map snd fl in
+			let bb,el = ordered_value_list bb el in
+			bb,{e with eexpr = TObjectDecl (List.map2 (fun (s,_) e -> s,e) fl el)}
+		| TField({eexpr = TTypeExpr _},fa) ->
+			bb,e
+		| TField(e1,fa) ->
+			let bb,e1 = value bb e1 in
+			bb,{e with eexpr = TField(e1,fa)}
+		| TArray(e1,e2) ->
+			let bb,e1,e2 = match ordered_value_list bb [e1;e2] with
+				| bb,[e1;e2] -> bb,e1,e2
+				| _ -> assert false
+			in
+			bb,{e with eexpr = TArray(e1,e2)}
+		| TMeta(m,e1) ->
+			let bb,e1 = value bb e1 in
+			bb,{e with eexpr = TMeta(m,e1)}
+		| TParenthesis e1 ->
+			let bb,e1 = value bb e1 in
+			bb,{e with eexpr = TParenthesis e1}
+		| TCast(e1,mto) ->
+			let bb,e1 = value bb e1 in
+			bb,{e with eexpr = TCast(e1,mto)}
+		| TNew(c,tl,el) ->
+			let bb,el = ordered_value_list bb el in
+			bb,{e with eexpr = TNew(c,tl,el)}
+		| TEnumParameter(e1,ef,ei) ->
+			let bb,e1 = value bb e1 in
+			bb,{e with eexpr = TEnumParameter(e1,ef,ei)}
+		| TFunction tf ->
+			let bb_func,bb_func_end = func ctx bb tf e.etype e.epos in
+			let e_fun = mk (TConst (TString "fun")) t_dynamic p in
+			let econst = mk (TConst (TInt (Int32.of_int bb_func.bb_id))) ctx.com.basic.tint e.epos in
+			let ec = mk (TCall(e_fun,[econst])) t_dynamic p in
+			let bb_next = create_node BKNormal bb.bb_type bb.bb_pos in
+			add_cfg_edge bb bb_next CFGGoto;
+			set_syntax_edge bb (SEMerge bb_next);
+			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
+		| 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
+	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 rec can_be_optimized e = match e.eexpr with
+			| TBinop _ | TArray _ | TCall _ -> true
+			| TParenthesis e1 -> can_be_optimized e1
+			| _ -> false
+		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)
+			else begin
+				let had_side_effect = Optimizer.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)
+			end
+		) (false,[]) (List.rev el) in
+		let bb,values = List.fold_left (fun (bb,acc) (aff,opt,e) ->
+			let bb,value = if aff || opt then bind_to_temp bb aff e else value bb e in
+			bb,(value :: acc)
+		) (bb,[]) el in
+		bb,List.rev values
+	and bind_to_temp bb sequential e =
+		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) ->
+				loop ((fun e' -> {e with eexpr = TField(e',fa)}) :: fl) e1
+			| _ ->
+				fl,e
+		in
+		let fl,e = loop [] e in
+		let v = alloc_var ctx.temp_var_name e.etype e.epos in
+		begin match ctx.com.platform with
+			| 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
+		let e = {e with eexpr = TLocal v} in
+		let e = List.fold_left (fun e f -> f e) e (List.rev fl) in
+		bb,e
+	and declare_var_and_assign bb v e p =
+		let rec loop bb e = match e.eexpr with
+			| TParenthesis e1 ->
+				loop bb e1
+			| TBlock el ->
+				let rec loop2 bb el = match el with
+					| [e] ->
+						bb,e
+					| e1 :: el ->
+						let bb = block_element bb e1 in
+						loop2 bb el
+					| [] ->
+						assert false
+				in
+				let bb,e = loop2 bb el in
+				loop bb e
+			| _ ->
+				bb,e
+		in
+		let bb,e = loop bb e in
+		no_void v.v_type p;
+		let ev = mk (TLocal v) v.v_type p in
+		let was_assigned = ref false in
+		let assign e =
+			if not !was_assigned then begin
+				was_assigned := true;
+				add_texpr bb (mk (TVar(v,None)) ctx.com.basic.tvoid ev.epos);
+			end;
+			mk (TBinop(OpAssign,ev,e)) ev.etype ev.epos
+		in
+		begin 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
+	and block_element_plus bb (e,efinal) f =
+		let bb = block_element bb e in
+		let bb = match efinal with
+			| None -> bb
+			| Some e -> block_element bb (f e)
+		in
+		bb
+	and block_element_value bb e f =
+		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 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
+	and block_element bb e = match e.eexpr with
+		(* variables *)
+		| TVar(v,None) ->
+			add_texpr bb e;
+			bb
+		| TVar(v,Some e1) ->
+			declare_var_and_assign bb v e1 e.epos
+		| TBinop(OpAssign,({eexpr = TLocal v} as e1),e2) ->
+			let assign e =
+				mk (TBinop(OpAssign,e1,e)) e.etype e.epos
+			in
+			begin 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
+		(* branching *)
+		| TMeta((Meta.MergeBlock,_,_),{eexpr = TBlock el}) ->
+			block_el bb el
+		| TBlock el ->
+			let bb_sub = create_node BKSub e.etype e.epos in
+			add_cfg_edge bb bb_sub CFGGoto;
+			close_node g bb;
+			let bb_sub_next = block_el bb_sub el in
+			if bb_sub_next != g.g_unreachable then begin
+				let bb_next = create_node BKNormal bb.bb_type bb.bb_pos in
+				set_syntax_edge bb (SESubBlock(bb_sub,bb_next));
+				add_cfg_edge bb_sub_next bb_next CFGGoto;
+				close_node g bb_sub_next;
+				bb_next;
+			end else begin
+				set_syntax_edge bb (SEMerge bb_sub);
+				close_node g bb_sub_next;
+				bb_sub_next
+			end
+		| TIf(e1,e2,None) ->
+			let bb,e1 = bind_to_temp bb false e1 in
+			let bb_then = create_node BKConditional e2.etype e2.epos in
+			add_texpr bb (wrap_meta ":cond-branch" e1);
+			add_cfg_edge bb bb_then (CFGCondBranch (mk (TConst (TBool true)) ctx.com.basic.tbool e2.epos));
+			let bb_then_next = block bb_then e2 in
+			let bb_next = create_node BKNormal bb.bb_type bb.bb_pos in
+			set_syntax_edge bb (SEIfThen(bb_then,bb_next,e.epos));
+			add_cfg_edge bb bb_next CFGCondElse;
+			close_node g bb;
+			add_cfg_edge bb_then_next bb_next CFGGoto;
+			close_node g bb_then_next;
+			bb_next
+		| TIf(e1,e2,Some e3) ->
+			let bb,e1 = bind_to_temp bb false e1 in
+			let bb_then = create_node BKConditional e2.etype e2.epos in
+			let bb_else = create_node BKConditional e3.etype e3.epos in
+			add_texpr bb (wrap_meta ":cond-branch" e1);
+			add_cfg_edge bb bb_then (CFGCondBranch (mk (TConst (TBool true)) ctx.com.basic.tbool e2.epos));
+			add_cfg_edge bb bb_else CFGCondElse;
+			close_node g bb;
+			let bb_then_next = block bb_then e2 in
+			let bb_else_next = block bb_else e3 in
+			if bb_then_next == g.g_unreachable && bb_else_next == g.g_unreachable then begin
+				set_syntax_edge bb (SEIfThenElse(bb_then,bb_else,g.g_unreachable,e.etype,e.epos));
+				g.g_unreachable
+			end else begin
+				let bb_next = create_node BKNormal bb.bb_type bb.bb_pos in
+				set_syntax_edge bb (SEIfThenElse(bb_then,bb_else,bb_next,e.etype,e.epos));
+				add_cfg_edge bb_then_next bb_next CFGGoto;
+				add_cfg_edge bb_else_next bb_next CFGGoto;
+				close_node g bb_then_next;
+				close_node g bb_else_next;
+				bb_next
+			end
+		| TSwitch(e1,cases,edef) ->
+			let is_exhaustive = edef <> None || Optimizer.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
+			let make_case e =
+				let bb_case = create_node BKConditional e.etype e.epos in
+				let bb_case_next = block bb_case e in
+				if bb_case_next != g.g_unreachable then
+					reachable := bb_case_next :: !reachable;
+				close_node g bb_case_next;
+				bb_case
+			in
+			let cases = List.map (fun (el,e) ->
+				let bb_case = make_case e in
+				List.iter (fun e -> add_cfg_edge bb bb_case (CFGCondBranch e)) el;
+				el,bb_case
+			) cases in
+			let def = match edef with
+				| None ->
+					None
+				| Some e ->
+					let bb_case = make_case e in
+					add_cfg_edge bb bb_case (CFGCondElse);
+					Some (bb_case)
+			in
+			if is_exhaustive && !reachable = [] then begin
+				set_syntax_edge bb (SESwitch(cases,def,g.g_unreachable,e.epos));
+				close_node g bb;
+				g.g_unreachable;
+			end else begin
+				let bb_next = create_node BKNormal bb.bb_type bb.bb_pos in
+				if not is_exhaustive then add_cfg_edge bb bb_next CFGGoto;
+				List.iter (fun bb -> add_cfg_edge bb bb_next CFGGoto) !reachable;
+				set_syntax_edge bb (SESwitch(cases,def,bb_next,e.epos));
+				close_node g bb;
+				bb_next
+			end
+		| TWhile(e1,e2,NormalWhile) ->
+			let bb_loop_pre = create_node BKNormal e1.etype e1.epos in
+			add_cfg_edge bb bb_loop_pre CFGGoto;
+			set_syntax_edge bb (SEMerge bb_loop_pre);
+			close_node g bb;
+			let bb_loop_head = create_node BKLoopHead e1.etype e1.epos in
+			add_cfg_edge bb_loop_pre bb_loop_head CFGGoto;
+			let close = begin_loop bb bb_loop_head in
+			let bb_loop_body = create_node BKNormal e2.etype e2.epos in
+			let bb_loop_body_next = block bb_loop_body e2 in
+			let bb_breaks = close() in
+			let bb_next = if bb_breaks = [] then begin
+				(* The loop appears to be infinite, let's assume that something within it throws.
+				   Otherwise DCE's mark-pass won't see its body and removes everything. *)
+				add_cfg_edge bb_loop_body_next bb_exit CFGMaybeThrow;
+				g.g_unreachable
+			end else
+				create_node BKNormal bb.bb_type bb.bb_pos
+			in
+			List.iter (fun bb -> add_cfg_edge bb bb_next CFGGoto) bb_breaks;
+			set_syntax_edge bb_loop_pre (SEWhile(bb_loop_head,bb_loop_body,bb_next));
+			close_node g bb_loop_pre;
+			add_texpr bb_loop_pre {e with eexpr = TWhile(e1,make_block_meta bb_loop_body,NormalWhile)};
+			add_cfg_edge bb_loop_body_next bb_loop_head CFGGoto;
+			add_cfg_edge bb_loop_head bb_loop_body CFGGoto;
+			close_node g bb_loop_body_next;
+			close_node g bb_loop_head;
+			bb_next;
+		| TTry(e1,catches) ->
+			let bb_try = create_node BKNormal e1.etype e1.epos in
+			let bb_exc = create_node BKException t_dynamic e.epos in
+			add_cfg_edge bb bb_try CFGGoto;
+			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
+            close_node g bb_exc;
+            close_node g bb;
+			bb_next
+		(* control flow *)
+		| TReturn None ->
+			add_cfg_edge bb bb_exit CFGGoto;
+			add_terminator bb e
+		| TReturn (Some e1) when ExtType.is_void (follow e1.etype) ->
+			let bb = block_element bb e1 in
+			block_element bb (mk (TReturn None) t_dynamic e.epos)
+		| TReturn (Some e1) ->
+			begin try
+				let mk_return e1 = mk (TReturn (Some e1)) t_dynamic e.epos in
+				block_element_value bb e1 mk_return
+			with Exit ->
+				let bb,e1 = value bb e1 in
+				add_cfg_edge bb bb_exit CFGGoto;
+				add_terminator bb {e with eexpr = TReturn(Some e1)};
+			end
+		| TBreak ->
+			bb_breaks := bb :: !bb_breaks;
+			add_terminator bb e
+		| TContinue ->
+			begin match !bb_continue with
+				| Some bb_continue -> add_cfg_edge bb bb_continue CFGGoto
+				| _ -> assert false
+			end;
+			add_terminator bb e
+		| TThrow e1 ->
+			begin try
+				let mk_throw e1 =
+					mk (TThrow e1) t_dynamic e.epos
+				in
+				block_element_value bb e1 mk_throw
+			with Exit ->
+				let bb,e1 = value bb e1 in
+				begin match !b_try_stack with
+					| [] -> add_cfg_edge bb bb_exit CFGGoto
+					| _ -> List.iter (fun bb_exc -> add_cfg_edge bb bb_exc CFGGoto) !b_try_stack;
+				end;
+				add_terminator bb {e with eexpr = TThrow e1};
+			end
+		(* side_effects *)
+		| TCall({eexpr = TLocal v},el) when is_really_unbound v ->
+			check_unbound_call v el;
+			add_texpr bb e;
+			bb
+		| TCall(e1,el) ->
+			let bb,e = call bb e e1 el in
+			add_texpr bb e;
+			bb
+		| TNew(c,tl,el) ->
+			let bb,el = ordered_value_list bb el in
+			add_texpr bb {e with eexpr = TNew(c,tl,el)};
+			bb
+		| TCast(e1,Some mt) ->
+			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) ->
+			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)};
+			bb
+		| TBinop((OpAssign | OpAssignOp _ as op),e1,e2) ->
+			let bb,e1 = value bb e1 in
+			let bb,e2 = value bb e2 in
+			add_texpr bb {e with eexpr = TBinop(op,e1,e2)};
+			bb
+		| TUnop((Increment | Decrement as op),flag,e1) ->
+			let bb,e1 = value bb e1 in
+			add_texpr bb {e with eexpr = TUnop(op,flag,e1)};
+			bb
+		| TLocal _ when not ctx.config.AnalyzerConfig.local_dce ->
+			add_texpr bb e;
+			bb
+		(* no-side-effect *)
+		| TEnumParameter _ | TFunction _ | TConst _ | TTypeExpr _ | TLocal _ ->
+			bb
+		(* no-side-effect composites *)
+		| TParenthesis e1 | TMeta(_,e1) | TCast(e1,None) | TField(e1,_) | TUnop(_,_,e1) ->
+			block_element bb e1
+		| TArray(e1,e2) | TBinop(_,e1,e2) ->
+			let bb = block_element bb e1 in
+			block_element bb e2
+		| TArrayDecl el ->
+			block_el bb el
+		| TObjectDecl fl ->
+			block_el bb (List.map snd fl)
+		| TFor _ | TWhile(_,_,DoWhile) ->
+			assert false
+	and block_el bb el =
+		match !b_try_stack with
+		| [] ->
+			let rec loop bb el = match el with
+				| [] -> bb
+				| e :: el ->
+					let bb = block_element bb e in
+					if bb == g.g_unreachable then bb else loop bb el
+			in
+			loop bb el
+		| bbl ->
+			let rec loop bb el = match el with
+				| [] -> bb
+				| e :: el ->
+					let bb = if not (can_throw e) then
+						block_element bb e
+					else begin
+						let bb' = create_node BKNormal e.etype e.epos in
+						add_cfg_edge bb bb' CFGGoto;
+						List.iter (fun bb_exc -> add_cfg_edge bb bb_exc CFGMaybeThrow) bbl;
+						set_syntax_edge bb (SEMerge bb');
+						close_node g bb;
+						block_element bb' e
+					end in
+					if bb == g.g_unreachable then bb else loop bb el
+			in
+			loop bb el
+	and block bb e =
+		let el = match e.eexpr with
+			| TBlock el -> el
+			| _ -> [e]
+		in
+		block_el bb el
+	in
+	let bb_last = block bb_root tf.tf_expr in
+	close_node g bb_last;
+	add_cfg_edge bb_last bb_exit CFGGoto; (* implied return *)
+	close_node g bb_exit;
+	bb_root,bb_exit
+
+let from_tfunction ctx tf t p =
+	let g = ctx.graph in
+	let bb_func,bb_exit = func ctx g.g_root tf t p in
+	ctx.entry <- bb_func;
+	close_node g g.g_root;
+	g.g_exit <- bb_exit;
+	set_syntax_edge bb_exit SEEnd
+
+let rec block_to_texpr_el ctx bb =
+	if bb.bb_dominator == ctx.graph.g_unreachable then
+		[]
+	else begin
+		let block bb = block_to_texpr ctx bb in
+		let rec loop bb se =
+			let el = List.rev (DynArray.to_list bb.bb_el) in
+			match el,se with
+			| el,SESubBlock(bb_sub,bb_next) ->
+				Some bb_next,(block bb_sub) :: el
+			| el,SEMerge bb_next ->
+				Some bb_next,el
+			| el,(SEEnd | SENone) ->
+				None,el
+			| {eexpr = TWhile(e1,_,flag)} as e :: el,(SEWhile(_,bb_body,bb_next)) ->
+				let e2 = block bb_body in
+				Some bb_next,{e with eexpr = TWhile(e1,e2,flag)} :: el
+			| el,SETry(bb_try,_,bbl,bb_next,p) ->
+				Some bb_next,(mk (TTry(block bb_try,List.map (fun (v,bb) -> v,block bb) bbl)) ctx.com.basic.tvoid p) :: el
+			| e1 :: el,se ->
+				let e1 = Texpr.skip e1 in
+				let bb_next,e1_def,t,p = match se with
+					| SEIfThen(bb_then,bb_next,p) -> Some bb_next,TIf(e1,block bb_then,None),ctx.com.basic.tvoid,p
+					| SEIfThenElse(bb_then,bb_else,bb_next,t,p) -> Some bb_next,TIf(e1,block bb_then,Some (block bb_else)),t,p
+					| SESwitch(bbl,bo,bb_next,p) -> Some bb_next,TSwitch(e1,List.map (fun (el,bb) -> el,block bb) bbl,Option.map block bo),ctx.com.basic.tvoid,p
+					| _ -> error (Printf.sprintf "Invalid node exit: %s" (s_expr_pretty e1)) bb.bb_pos
+				in
+				bb_next,(mk e1_def t p) :: el
+			| [],_ ->
+				None,[]
+		in
+		let bb_next,el = loop bb bb.bb_syntax_edge in
+		let el = match bb_next with
+			| None -> el
+			| Some bb -> (block_to_texpr_el ctx bb) @ el
+		in
+		el
+	end
+
+and block_to_texpr ctx bb =
+	assert(bb.bb_closed);
+	let el = block_to_texpr_el ctx bb in
+	let e = mk (TBlock (List.rev el)) bb.bb_type bb.bb_pos in
+	e
+
+and func ctx i =
+	let bb,t,p,tf = Hashtbl.find ctx.graph.g_functions i in
+	let e = block_to_texpr ctx bb in
+	let rec loop e = match e.eexpr with
+		| TLocal v when not (is_unbound v) ->
+			{e with eexpr = TLocal (get_var_origin ctx.graph v)}
+		| TVar(v,eo) when not (is_unbound v) ->
+			let eo = Option.map loop eo in
+			let v' = get_var_origin ctx.graph v in
+			{e with eexpr = TVar(v',eo)}
+		| TBinop(OpAssign,e1,({eexpr = TBinop(op,e2,e3)} as e4)) when target_handles_assign_ops ctx.com ->
+			let e1 = loop e1 in
+			let e2 = loop e2 in
+			let e3 = loop e3 in
+			let is_valid_assign_op = function
+				| OpAdd | OpMult | OpDiv | OpSub | OpAnd
+				| OpOr | OpXor | OpShl | OpShr | OpUShr | OpMod ->
+					true
+				| OpAssignOp _ | OpInterval | OpArrow | OpAssign | OpEq
+				| OpNotEq | OpGt | OpGte | OpLt | OpLte | OpBoolAnd | OpBoolOr ->
+					false
+			in
+			begin match e1.eexpr,e2.eexpr with
+				| TLocal v1,TLocal v2 when v1 == v2 && 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)}
+						| _ -> {e with eexpr = TBinop(OpAssignOp op,e1,e3)}
+					end
+				| _ ->
+					{e with eexpr = TBinop(OpAssign,e1,{e4 with eexpr = TBinop(op,e2,e3)})}
+			end
+		| TCall({eexpr = TConst (TString "fun")},[{eexpr = TConst (TInt i32)}]) ->
+			func ctx (Int32.to_int i32)
+		| TCall({eexpr = TLocal v},_) when is_really_unbound v ->
+			e
+		| _ ->
+			Type.map_expr loop e
+	in
+	let e = loop e in
+	mk (TFunction {tf with tf_expr = e}) t p
+
+let to_texpr ctx =
+	func ctx ctx.entry.bb_id

+ 525 - 0
src/optimization/analyzerTypes.ml

@@ -0,0 +1,525 @@
+(*
+	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 Type
+open Common
+
+(*
+	A BasicBlock represents a node in the control flow. It has expression elements similar to TBlock in the AST,
+	but also holds additional information related to control flow and variables.
+
+	Basic blocks are created whenever it is relevant for control flow. They differ from TBlock in that only their
+	final element can be a control flow expression (the terminator). As a consequence, a given TBlock is split up
+	into several basic blocks when control flow expressions are encountered.
+*)
+module BasicBlock = struct
+	type block_kind =
+		| BKRoot                    (* The unique root block of the graph *)
+		| BKNormal                  (* A normal block *)
+		| BKFunctionBegin of tfunc  (* Entry block of a function *)
+		| BKFunctionEnd             (* Exit block of a function *)
+		| BKSub                     (* A sub block *)
+		| BKConditional             (* A "then", "else" or "case" block *)
+		| BKLoopHead                (* Header block of a loop *)
+		| BKException               (* Relay block for exceptions *)
+		| BKUnreachable             (* The unique unreachable block *)
+		| BKCatch of tvar           (* A catch block *)
+
+	type cfg_edge_Flag =
+		| FlagExecutable      (* Used by constant propagation to handle live edges *)
+		| FlagDce             (* Used by DCE to keep track of handled edges *)
+		| FlagCodeMotion      (* Used by code motion to track handled edges *)
+		| FlagCopyPropagation (* Used by copy propagation to track handled eges *)
+
+	type cfg_edge_kind =
+		| CFGGoto                (* An unconditional branch *)
+		| CFGFunction            (* Link to a function *)
+		| CFGMaybeThrow          (* The block may or may not throw an exception *)
+		| CFGCondBranch of texpr (* A conditional branch *)
+		| CFGCondElse            (* A conditional alternative (else,default) *)
+
+	and cfg_edge = {
+		cfg_from : t;                           (* The source block *)
+		cfg_to : t;                             (* The target block *)
+		cfg_kind : cfg_edge_kind;               (* The edge kind *)
+		mutable cfg_flags : cfg_edge_Flag list; (* Edge flags *)
+	}
+
+	and syntax_edge =
+		| SEIfThen of t * t * pos                                (* `if` with "then" and "next" *)
+		| SEIfThenElse of t * t * t * Type.t * pos               (* `if` with "then", "else" and "next" *)
+		| SESwitch of (texpr list * t) list * t option * t * pos (* `switch` with cases, "default" and "next" *)
+		| SETry of t * t * (tvar * t) list * t *  pos            (* `try` with "exc", catches and "next" *)
+		| SEWhile of t * t * t                                   (* `while` with "head", "body" and "next" *)
+		| SESubBlock of t * t                                    (* "sub" with "next" *)
+		| SEMerge of t                                           (* Merge to same block *)
+		| SEEnd                                                  (* End of syntax *)
+		| SENone                                                 (* No syntax exit *)
+
+	and t = {
+		bb_id : int;                          (* The unique ID of the block *)
+		bb_type : Type.t;                     (* The block type *)
+		bb_pos : pos;                         (* The block position *)
+		bb_kind : block_kind;                 (* The block kind *)
+		mutable bb_closed : bool;             (* Whether or not the block has been closed *)
+		(* elements *)
+		bb_el : texpr DynArray.t;             (* The block expressions *)
+		bb_phi : texpr DynArray.t;            (* SSA-phi expressions *)
+		(* relations *)
+		mutable bb_outgoing : cfg_edge list;  (* Outgoing edges *)
+		mutable bb_incoming : cfg_edge list;  (* Incoming edges *)
+		mutable bb_dominator : t;             (* The block's dominator *)
+		mutable bb_dominated : t list;        (* The dominated blocks *)
+		mutable bb_df : t list;               (* The dominance frontier *)
+		mutable bb_syntax_edge : syntax_edge; (* The syntactic edge *)
+		mutable bb_loop_groups : int list;    (* The loop groups this block belongs to *)
+		mutable bb_scopes : int list;         (* The scopes this block belongs to *)
+		(* variables *)
+		mutable bb_var_writes : tvar list;    (* List of assigned variables *)
+	}
+
+	let s_block_kind = function
+		| BKRoot -> "BKRoot"
+		| BKNormal -> "BKNormal"
+		| BKFunctionBegin _ -> "BKFunctionBegin"
+		| BKFunctionEnd -> "BKFunctionEnd"
+		| BKSub -> "BKSub"
+		| BKConditional -> "BKConditional"
+		| BKLoopHead -> "BKLoopHead"
+		| BKException -> "BKException"
+		| BKUnreachable -> "BKUnreachable"
+		| BKCatch _ -> "BKCatch"
+
+	let s_cfg_edge_kind = function
+		| CFGGoto -> "CFGGoto"
+		| CFGFunction -> "CFGFunction"
+		| CFGMaybeThrow -> "CFGMaybeThrow"
+		| CFGCondBranch e -> "CFGCondBranch " ^ (s_expr_pretty false "" (s_type (print_context())) e)
+		| CFGCondElse -> "CFGCondElse"
+
+	let has_flag edge flag =
+		List.mem flag edge.cfg_flags
+
+	(* expressions *)
+
+	let add_texpr bb e =
+		DynArray.add bb.bb_el e
+
+	let get_texpr bb is_phi i =
+		DynArray.get (if is_phi then bb.bb_phi else bb.bb_el) i
+
+	(* edges *)
+
+	let set_syntax_edge bb se =
+		bb.bb_syntax_edge <- se
+
+	let add_cfg_edge bb_from bb_to kind =
+		if bb_from.bb_kind <> BKUnreachable then begin
+			let edge = { cfg_from = bb_from; cfg_to = bb_to; cfg_kind = kind; cfg_flags = [] } in
+			bb_from.bb_outgoing <- edge :: bb_from.bb_outgoing;
+			bb_to.bb_incoming <- edge :: bb_to.bb_incoming;
+		end
+
+	let _create id kind t p =
+		let rec bb = {
+			bb_kind = kind;
+			bb_id = id;
+			bb_type = t;
+			bb_pos = p;
+			bb_closed = false;
+			bb_el = DynArray.create();
+			bb_phi = DynArray.create();
+			bb_outgoing = [];
+			bb_incoming = [];
+			bb_dominator = bb;
+			bb_dominated = [];
+			bb_df = [];
+			bb_syntax_edge = SENone;
+			bb_loop_groups = [];
+			bb_var_writes = [];
+			bb_scopes = [];
+		} in
+		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
+		| scope :: _ -> List.mem scope bb.bb_scopes
+end
+
+(*
+	A Graph contains all relevant information for a given method. It is built from the field expression
+	and then refined in subsequent modules such as Ssa.
+*)
+module Graph = struct
+	open BasicBlock
+
+	type texpr_lookup = BasicBlock.t * bool * int
+	type tfunc_info = BasicBlock.t * Type.t * pos * tfunc
+	type var_write = BasicBlock.t list
+	type 'a itbl = (int,'a) Hashtbl.t
+
+	type var_info = {
+		vi_var : tvar;                            (* The variable itself *)
+		vi_extra : tvar_extra;                    (* The original v_extra *)
+		vi_bb_declare : BasicBlock.t;             (* The block where this variable was declared *)
+		mutable vi_origin : tvar;                 (* The origin variable of this variable *)
+		mutable vi_writes : var_write;            (* A list of blocks that assign to this variable *)
+		mutable vi_value : texpr_lookup option;   (* The value of this variable, if known *)
+		mutable vi_ssa_edges : texpr_lookup list; (* The expressions this variable influences *)
+		mutable vi_reaching_def : tvar option;    (* The current reaching definition variable of this variable *)
+	}
+
+	type t = {
+		mutable g_root : BasicBlock.t;             (* The unique root block *)
+		mutable g_exit : BasicBlock.t;             (* The unique exit block *)
+		mutable g_unreachable : BasicBlock.t;      (* The unique unreachable block *)
+		mutable g_functions : tfunc_info itbl;     (* A map of functions, indexed by their block IDs *)
+		mutable g_nodes : BasicBlock.t list;       (* A list of all blocks *)
+		g_var_infos : var_info DynArray.t;         (* A map of variable information *)
+		mutable g_loops : BasicBlock.t IntMap.t;   (* A map containing loop information *)
+	}
+
+	(* variables *)
+
+	let create_var_info g bb v =
+		let vi = {
+			vi_var = v;
+			vi_extra = v.v_extra;
+			vi_bb_declare = bb;
+			vi_origin = v;
+			vi_writes = [];
+			vi_value = None;
+			vi_ssa_edges = [];
+			vi_reaching_def = None;
+		} in
+		DynArray.add g.g_var_infos vi;
+		let i = DynArray.length g.g_var_infos - 1 in
+		v.v_extra <- Some([],Some (mk (TConst (TInt (Int32.of_int i))) t_dynamic null_pos))
+
+	let get_var_info g v = match v.v_extra with
+		| Some(_,Some {eexpr = TConst (TInt i32)}) -> DynArray.get g.g_var_infos (Int32.to_int i32)
+		| _ ->
+			prerr_endline "Unbound variable, please report this";
+			prerr_endline (Printer.s_tvar v);
+			assert false
+
+	let declare_var g v bb =
+		create_var_info g bb v
+
+	let add_var_def g bb v =
+		if bb.bb_id > 0 then begin
+			bb.bb_var_writes <- v :: bb.bb_var_writes;
+			let vi = get_var_info g v in
+			vi.vi_writes <- bb :: vi.vi_writes;
+		end
+
+	let set_var_value g v bb is_phi i =
+		(get_var_info g v).vi_value <- Some (bb,is_phi,i)
+
+	let get_var_value g v =
+		let value = (get_var_info g v).vi_value in
+		let bb,is_phi,i = match value with
+			| None -> raise Not_found
+			| Some l -> l
+		in
+		match (get_texpr bb is_phi i).eexpr with
+		| TVar(_,Some e) | TBinop(OpAssign,_,e) -> e
+		| _ -> assert false
+
+	let add_var_origin g v v_origin =
+		(get_var_info g v).vi_origin <- v_origin
+
+	let get_var_origin g v =
+		(get_var_info g v).vi_origin
+
+	let add_ssa_edge g v bb is_phi i =
+		let vi = get_var_info g v in
+		vi.vi_ssa_edges <- (bb,is_phi,i) :: vi.vi_ssa_edges
+
+	(* nodes *)
+
+	let add_function g tf t p bb =
+		Hashtbl.add g.g_functions bb.bb_id (bb,t,p,tf)
+
+	let alloc_id =
+		let r = ref 1 in
+		(fun () ->
+			incr r;
+			!r
+		)
+
+	let create_node g kind t p =
+		let bb = BasicBlock._create (alloc_id()) kind t p in
+		g.g_nodes <- bb :: g.g_nodes;
+		bb
+
+	let close_node g bb =
+		if bb.bb_id > 0 then begin
+			assert(not bb.bb_closed);
+			bb.bb_closed <- true
+		end
+
+	let iter_dom_tree_from g bb f =
+		let rec loop bb =
+			f bb;
+			List.iter loop bb.bb_dominated
+		in
+		loop bb
+
+	let iter_dom_tree g f =
+		iter_dom_tree_from g g.g_root f
+
+	let iter_edges_from g bb f =
+		iter_dom_tree_from g bb (fun bb -> List.iter f bb.bb_outgoing)
+
+	let iter_edges g f =
+		iter_dom_tree g (fun bb -> List.iter f bb.bb_outgoing)
+
+	(* graph *)
+
+	let create t p =
+		let bb_root = BasicBlock._create 1 BKRoot t p; in
+		let bb_unreachable = BasicBlock._create 0 BKUnreachable t_dynamic null_pos in
+		{
+			g_root = bb_root;
+			g_exit = bb_unreachable;
+			g_unreachable = bb_unreachable;
+			g_functions = Hashtbl.create 0;
+			g_nodes = [bb_root];
+			g_var_infos = DynArray.create();
+			g_loops = IntMap.empty;
+		}
+
+	let check_integrity g =
+		List.iter (fun bb ->
+			List.iter (fun edge ->
+				if edge.cfg_to = g.g_unreachable then
+					prerr_endline (Printf.sprintf "Outgoing edge from %i to the unreachable block" bb.bb_id)
+				else if not (List.memq edge edge.cfg_to.bb_incoming) then
+					prerr_endline (Printf.sprintf "Outgoing edge %i -> %i has no matching incoming edge" edge.cfg_from.bb_id edge.cfg_to.bb_id)
+			) bb.bb_outgoing;
+			List.iter (fun edge ->
+				if edge.cfg_from == g.g_unreachable then
+					prerr_endline (Printf.sprintf "Incoming edge to %i from the unreachable block" bb.bb_id)
+				else if not (List.memq edge edge.cfg_from.bb_outgoing) then
+					prerr_endline (Printf.sprintf "Incoming edge %i <- %i has no matching outgoing edge" edge.cfg_to.bb_id edge.cfg_from.bb_id)
+			) bb.bb_incoming
+		) g.g_nodes
+
+	(* inference *)
+
+	type dom_bb_info = {
+		bb : BasicBlock.t;
+		parent : dom_bb_info;
+		mutable idom : dom_bb_info;
+		mutable semi : int;
+		mutable label : dom_bb_info;
+		mutable ancestor : dom_bb_info;
+		mutable bucket : dom_bb_info list;
+	}
+
+	(* Infers the immediate dominators for all reachable blocks. This function can be run multiple times
+	   in case an update is necessary. *)
+	let infer_immediate_dominators g =
+		let info = Hashtbl.create 0 in
+		let nodes = DynArray.create () in
+		let get_info i = Hashtbl.find info i in
+		let add_info bb bb_parent =
+			let rec bbi = {
+				bb = bb;
+				parent = bbi;
+				idom = bbi;
+				semi = DynArray.length nodes;
+				label = bbi;
+				ancestor = bbi;
+				bucket = [];
+			} in
+			let bbi = if bb == bb_parent then bbi else {bbi with parent = get_info bb_parent.bb_id} in
+			Hashtbl.add info bb.bb_id bbi;
+			DynArray.add nodes bbi;
+		in
+		let rec loop bb_parent bb =
+			bb.bb_dominated <- [];
+			add_info bb bb_parent;
+			List.iter (fun edge ->
+				let bb_to = edge.cfg_to in
+				if not (Hashtbl.mem info bb_to.bb_id) then
+					loop bb bb_to
+			) bb.bb_outgoing
+		in
+		loop g.g_root g.g_root;
+		let compress bbi =
+			let rec loop l bbi =
+				if bbi.ancestor == bbi then l else loop (bbi :: l) bbi.ancestor
+			in
+			let worklist = loop [bbi] bbi.ancestor in
+			match worklist with
+				| a :: worklist ->
+					ignore(List.fold_left (fun (a,min_semi) bbi_desc ->
+						let bbi = bbi_desc.label in
+						if bbi.semi > min_semi then begin
+							bbi_desc.label <- a.label;
+							(bbi_desc,min_semi)
+						end else
+							(bbi_desc,bbi.semi)
+					) (a,a.label.semi) worklist)
+				| [] ->
+					assert false
+		in
+		let eval v =
+			let bbi = get_info v in
+			if bbi.ancestor != bbi then begin
+				compress bbi;
+				bbi.label
+			end else
+				bbi
+		in
+		let rec loop nodes' = match nodes' with
+			| [_] -> ()
+			| [] -> assert false
+			| w :: nodes' ->
+				let semi = List.fold_left (fun acc v ->
+					min acc (eval v.cfg_from.bb_id).semi) w.semi w.bb.bb_incoming
+				in
+				w.semi <- semi;
+				let bbi = DynArray.get nodes semi in
+				bbi.bucket <- w :: bbi.bucket;
+				let bbi_p = w.parent in
+				w.ancestor <- bbi_p;
+				List.iter (fun v ->
+					let u = eval v.bb.bb_id in
+					if u.semi < v.semi then
+						v.idom <- u
+					else
+						v.idom <- bbi_p
+				) bbi_p.bucket;
+				bbi_p.bucket <- [];
+				loop nodes'
+		in
+		let l = DynArray.to_list nodes in
+		loop (List.rev l);
+		List.iter (fun w ->
+			if w.idom != (DynArray.get nodes w.semi) then w.idom <- w.idom.idom
+		) (List.tl l);
+		DynArray.iter (fun bbi ->
+			if bbi.idom != bbi then begin
+				let bb = bbi.bb in
+				let bb' = bbi.idom.bb in
+				if bb != bb' then begin
+					bb.bb_dominator <- bb';
+					bb'.bb_dominated <- bb :: bb'.bb_dominated
+				end
+			end
+		) nodes
+
+	(* Infers the dominance frontier for all reachable blocks. This function should only be run once. *)
+	let infer_dominance_frontier g =
+		iter_edges g (fun edge ->
+			let rec loop bb =
+				if bb != g.g_unreachable && bb != edge.cfg_to && bb != edge.cfg_to.bb_dominator then begin
+					if edge.cfg_to != g.g_exit then bb.bb_df <- edge.cfg_to :: bb.bb_df;
+					if bb.bb_dominator != bb then loop bb.bb_dominator
+				end
+			in
+			loop edge.cfg_from
+		)
+
+	(* Infers variable declarations and definitions. This function should only be run once. *)
+	let infer_var_writes g =
+		iter_dom_tree g (fun bb ->
+			begin match bb.bb_kind with
+				| BKCatch v ->
+					declare_var g v bb;
+					add_var_def g bb v
+				| BKFunctionBegin tf ->
+					List.iter (fun (v,_) ->
+						declare_var g v bb;
+						add_var_def g bb v
+					) tf.tf_args;
+				| _ ->
+					()
+			end;
+			DynArray.iter (fun e -> match e.eexpr with
+				| TVar(v,eo) when not (is_unbound v) ->
+					declare_var g v bb;
+					if eo <> None then add_var_def g bb v;
+				| TBinop(OpAssign,{eexpr = TLocal v},_) when not (is_unbound v) ->
+					add_var_def g bb v
+				| _ ->
+					()
+			) bb.bb_el
+		)
+
+	(* Infers the scopes of all reachable blocks. This function can be run multiple times
+	   in case an update is necessary *)
+	let infer_scopes g =
+		let next_scope_id = ref 0 in
+		let next_scope scopes =
+			incr next_scope_id;
+			!next_scope_id :: scopes
+		in
+		let rec loop scopes bb =
+			bb.bb_scopes <- scopes;
+			begin match bb.bb_syntax_edge with
+				| SEIfThen(bb_then,bb_next,_) ->
+					loop (next_scope scopes) bb_then;
+					loop scopes bb_next
+				| SEIfThenElse(bb_then,bb_else,bb_next,_,_) ->
+					loop (next_scope scopes) bb_then;
+					loop (next_scope scopes) bb_else;
+					loop scopes bb_next
+				| SESwitch(cases,bbo,bb_next,_) ->
+					List.iter (fun (_,bb_case) -> loop (next_scope scopes) bb_case) cases;
+					(match bbo with None -> () | Some bb -> loop (next_scope scopes) bb);
+					loop scopes bb_next;
+				| SETry(bb_try,bb_exc,catches,bb_next,_) ->
+					let scopes' = next_scope scopes in
+					loop scopes' bb_try;
+					loop scopes' bb_exc;
+					List.iter (fun (_,bb_catch) -> loop (next_scope scopes) bb_catch) catches;
+					loop scopes bb_next
+				| SEWhile(bb_head,bb_body,bb_next) ->
+					let scopes' = next_scope scopes in
+					loop scopes' bb_head;
+					loop scopes' bb_body;
+					loop scopes bb_next;
+				| SESubBlock(bb_sub,bb_next) ->
+					loop (next_scope scopes) bb_sub;
+					loop scopes bb_next
+				| SEMerge bb ->
+					loop scopes bb
+				| SEEnd | SENone ->
+					()
+			end
+		in
+		Hashtbl.iter (fun _ (bb,_,_,_) -> loop [0] bb) g.g_functions
+end
+
+type analyzer_context = {
+	com : Common.context;
+	config : AnalyzerConfig.t;
+	graph : Graph.t;
+	temp_var_name : string;
+	mutable entry : BasicBlock.t;
+	mutable has_unbound : bool;
+	mutable loop_counter : int;
+	mutable loop_stack : int list;
+	mutable debug_exprs : (string * texpr) list;
+}

+ 62 - 42
dce.ml → src/optimization/dce.ml

@@ -1,23 +1,20 @@
 (*
- * Copyright (C)2005-2013 Haxe Foundation
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
+	The 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
@@ -88,7 +85,7 @@ let rec check_feature dce s =
 
 and check_and_add_feature dce s =
 	check_feature dce s;
-	Common.add_feature dce.com s;
+	Hashtbl.replace dce.curclass.cl_module.m_extra.m_features s true
 
 (* mark a field as kept *)
 and mark_field dce c cf stat =
@@ -101,23 +98,24 @@ and mark_field dce c cf stat =
 		end
 	in
 	if cf.cf_name = "new" then begin
-		let rec loop c = match c.cl_super with
-			| None -> ()
-			| Some (csup,_) ->
-				begin match csup.cl_constructor with
-				| None -> ()
+		let rec loop c =
+			begin match c.cl_constructor with
 				| Some cf -> add cf
-				end;
-				loop csup
+				| None -> ()
+			end;
+			match c.cl_super with
+			| Some(csup,_) -> loop csup
+			| None -> ()
 		in
 		loop c
-	end;
-	if not (PMap.mem cf.cf_name (if stat then c.cl_statics else c.cl_fields)) then begin
-		match c.cl_super with
-		| None -> add cf
-		| Some (c,_) -> mark_field dce c cf stat
-	end else
-		add cf
+	end else begin
+		if not (PMap.mem cf.cf_name (if stat then c.cl_statics else c.cl_fields)) then begin
+			match c.cl_super with
+			| None -> add cf
+			| Some (c,_) -> mark_field dce c cf stat
+		end else
+			add cf
+	end
 
 let rec update_marked_class_fields dce c =
 	(* mark all :?used fields as surely :used now *)
@@ -141,7 +139,11 @@ end
 
 let rec mark_enum dce e = if not (Meta.has Meta.Used e.e_meta) then begin
 	e.e_meta <- (Meta.Used,[],e.e_pos) :: e.e_meta;
-	check_and_add_feature dce "has_enum";
+
+	(* do not generate has_enum feature for @:fakeEnum enums since they are not really enums *)
+	if not (Meta.has Meta.FakeEnum e.e_meta) then
+		check_and_add_feature dce "has_enum";
+
 	check_feature dce (Printf.sprintf "%s.*" (s_type_path e.e_path));
 	PMap.iter (fun _ ef -> mark_t dce ef.ef_pos ef.ef_type) e.e_constrs;
 end
@@ -403,11 +405,19 @@ and expr dce e =
 	| TCall ({eexpr = TConst TSuper} as e,el) ->
 		mark_t dce e.epos e.etype;
 		List.iter (expr dce) el;
+	| TUnop((Increment | Decrement),_,({eexpr = TArray _} as e1)) ->
+		check_and_add_feature dce "array_write";
+		check_and_add_feature dce "array_read";
+		expr dce e1;
 	| TBinop(OpAdd,e1,e2) when is_dynamic e1.etype || is_dynamic e2.etype ->
 		check_and_add_feature dce "add_dynamic";
 		expr dce e1;
 		expr dce e2;
-	| TBinop( (OpAdd | (OpAssignOp OpAdd)),e1,e2) when ((is_string e1.etype || is_string e2.etype) && not ( is_const_string e1 && is_const_string e2)) ->
+	| TBinop( (OpAdd | (OpAssignOp OpAdd)) as op,e1,e2) when ((is_string e1.etype || is_string e2.etype) && not ( is_const_string e1 && is_const_string e2)) ->
+
+		(* add array_write if we're doing += to an array element, since this pattern comes before the array_write one *)
+		(match op, e1 with (OpAssignOp _, {eexpr = TArray({etype = t},_)}) when is_array t -> check_and_add_feature dce "array_write" | _ -> ());
+
 		check_and_add_feature dce "unsafe_string_concat";
 		expr dce e1;
 		expr dce e2;
@@ -461,7 +471,7 @@ and expr dce e =
 		check_and_add_feature dce "binop_%";
 		expr dce e1;
 		expr dce e2;
-	| TBinop(OpUShr,e1,e2) ->
+	| TBinop((OpUShr | OpAssignOp OpUShr),e1,e2) ->
 		check_and_add_feature dce "binop_>>>";
 		expr dce e1;
 		expr dce e2;
@@ -514,6 +524,16 @@ and expr dce e =
 
 let fix_accessors com =
 	List.iter (fun mt -> match mt with
+		(* filter empty abstract implementation classes (issue #1885). *)
+		| TClassDecl({cl_kind = KAbstractImpl _} as c) when c.cl_ordered_statics = [] && c.cl_ordered_fields = [] && not (Meta.has Meta.Used c.cl_meta) ->
+			c.cl_extern <- true
+		| TClassDecl({cl_kind = KAbstractImpl a} as c) when Meta.has Meta.Enum a.a_meta ->
+			let is_runtime_field cf =
+				not (Meta.has Meta.Enum cf.cf_meta)
+			in
+			(* also filter abstract implementation classes that have only @:enum fields (issue #2858) *)
+			if not (List.exists is_runtime_field c.cl_ordered_statics) then
+				c.cl_extern <- true
 		| (TClassDecl c) ->
 			let rec has_accessor c n stat =
 				PMap.mem n (if stat then c.cl_statics else c.cl_fields)
@@ -552,7 +572,7 @@ let run com main full =
 		curclass = null_class;
 	} in
 	begin match main with
-		| Some {eexpr = TCall({eexpr = TField(e,(FStatic(c,cf)))},_)} ->
+		| Some {eexpr = TCall({eexpr = TField(e,(FStatic(c,cf)))},_)} | Some {eexpr = TBlock ({ eexpr = TCall({eexpr = TField(e,(FStatic(c,cf)))},_)} :: _)} ->
 			cf.cf_meta <- (Meta.Keep,[],cf.cf_pos) :: cf.cf_meta
 		| _ ->
 			()
@@ -714,7 +734,9 @@ let run com main full =
 		| _ -> ()
 	) com.types;
 
-	(* mark extern classes as really used if they are extended by non-extern ones *)
+	(*
+		Mark extern classes as really used if they are extended by non-extern ones.
+	*)
 	List.iter (function
 		| TClassDecl ({cl_extern = false; cl_super = Some ({cl_extern = true} as csup, _)}) ->
 			mark_directly_used_class csup
@@ -730,6 +752,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

+ 167 - 291
filters.ml → src/optimization/filters.ml

@@ -1,3 +1,22 @@
+(*
+	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
 open Type
@@ -5,103 +24,6 @@ open Typecore
 
 (* PASS 1 begin *)
 
-let rec verify_ast e = match e.eexpr with
-	| TField(_) ->
-		()
-	| TTypeExpr(TClassDecl {cl_kind = KAbstractImpl a}) when not (Meta.has Meta.RuntimeValue a.a_meta) ->
-		error "Cannot use abstract as value" e.epos
-	| _ ->
-		Type.iter verify_ast e
-
-(*
-	Wraps implicit blocks in TIf, TFor, TWhile, TFunction and TTry with real ones
-*)
-let rec blockify_ast e =
-	match e.eexpr with
-	| TIf(e1,e2,eo) ->
-		{e with eexpr = TIf(blockify_ast e1,mk_block (blockify_ast e2),match eo with None -> None | Some e -> Some (mk_block (blockify_ast e)))}
-	| TFor(v,e1,e2) ->
-		{e with eexpr = TFor(v,blockify_ast e1,mk_block (blockify_ast e2))}
-	| TWhile(e1,e2,flag) ->
-		{e with eexpr = TWhile(blockify_ast e1,mk_block (blockify_ast e2),flag)}
-	| TFunction tf ->
-		{e with eexpr = TFunction {tf with tf_expr = mk_block (blockify_ast tf.tf_expr)}}
-	| TTry(e1,cl) ->
-		{e with eexpr = TTry(mk_block (blockify_ast e1),List.map (fun (v,e) -> v,mk_block (blockify_ast e)) cl)}
-	| TSwitch(e1,cases,def) ->
-		let e1 = blockify_ast e1 in
-		let cases = List.map (fun (el,e) ->
-			el,mk_block (blockify_ast e)
-		) cases in
-		let def = match def with None -> None | Some e -> Some (mk_block (blockify_ast e)) in
-		{e with eexpr = TSwitch(e1,cases,def)}
-	| _ ->
-		Type.map_expr blockify_ast e
-
-(*
-	Pushes complex right-hand side expression inwards.
-
-	return { exprs; value; } -> { exprs; return value; }
-	x = { exprs; value; } -> { exprs; x = value; }
-	var x = { exprs; value; } -> { var x; exprs; x = value; }
-*)
-let promote_complex_rhs com e =
-	let rec is_complex e = match e.eexpr with
-		| TBlock _ | TSwitch _ | TIf _ | TTry _ | TCast(_,Some _) -> true
-		| TBinop(_,e1,e2) -> is_complex e1 || is_complex e2
-		| TParenthesis e | TMeta(_,e) | TCast(e, None) | TField(e,_) -> is_complex e
-		| _ -> false
-	in
-	let rec loop f e = match e.eexpr with
-		| TBlock(el) ->
-			begin match List.rev el with
-				| elast :: el -> {e with eexpr = TBlock(block (List.rev ((loop f elast) :: el)))}
-				| [] -> e
-			end
-		| TSwitch(es,cases,edef) ->
-			{e with eexpr = TSwitch(es,List.map (fun (el,e) -> List.map find el,loop f e) cases,match edef with None -> None | Some e -> Some (loop f e)); }
-		| TIf(eif,ethen,eelse) ->
-			{e with eexpr = TIf(find eif, loop f ethen, match eelse with None -> None | Some e -> Some (loop f e)); }
-		| TTry(e1,el) ->
-			{e with eexpr = TTry(loop f e1, List.map (fun (el,e) -> el,loop f e) el); }
-		| TParenthesis e1 when not (Common.defined com Define.As3) ->
-			{e with eexpr = TParenthesis(loop f e1)}
-		| TMeta(m,e1) ->
-			{ e with eexpr = TMeta(m,loop f e1)}
-		| TReturn _ | TThrow _ ->
-			find e
-		| TContinue | TBreak ->
-			e
-		| _ ->
-			f (find e)
-	and block el =
-		let r = ref [] in
-		List.iter (fun e ->
-			match e.eexpr with
-			| TVar(v,eo) ->
-				begin match eo with
-					| Some e when is_complex e ->
-						let e = find e in
-						r := (loop (fun e -> mk (TBinop(OpAssign,mk (TLocal v) v.v_type e.epos,e)) v.v_type e.epos) e)
-							:: ((mk (TVar (v,None)) com.basic.tvoid e.epos))
-							:: !r
-					| Some e ->
-						r := (mk (TVar (v,Some (find e))) com.basic.tvoid e.epos) :: !r
-					| None -> r := (mk (TVar (v,None)) com.basic.tvoid e.epos) :: !r
-				end
-			| TReturn (Some e1) when (match follow e1.etype with TAbstract({a_path=[],"Void"},_) -> true | _ -> false) ->
-				r := ({e with eexpr = TReturn None}) :: e1 :: !r
-			| _ -> r := (find e) :: !r
-		) el;
-		List.rev !r
-	and find e = match e.eexpr with
-		| TReturn (Some e1) -> loop (fun er -> {e with eexpr = TReturn (Some er)}) e1
-		| TBinop(OpAssign | OpAssignOp _ as op, ({eexpr = TLocal _ | TField _ | TArray _} as e1), e2) -> loop (fun er -> {e with eexpr = TBinop(op, e1, er)}) e2
-		| TBlock(el) -> {e with eexpr = TBlock (block el)}
-		| _ -> Type.map_expr find e
-	in
-	find e
-
 (* Adds final returns to functions as required by some platforms *)
 let rec add_final_return e =
 	let rec loop e t =
@@ -112,7 +34,7 @@ let rec add_final_return e =
 				| TAbstract ({ a_path = [],"Bool" },_) -> TBool false
 				| _ -> TNull
 			) in
-			{ eexpr = TReturn (Some { eexpr = TConst c; epos = p; etype = t }); etype = t; epos = p }
+			{ eexpr = TReturn (Some { eexpr = TConst c; epos = p; etype = t }); etype = t_dynamic; epos = p }
 		in
 		match e.eexpr with
 		| TBlock el ->
@@ -134,7 +56,7 @@ let rec add_final_return e =
 		| TFunction f ->
 			let f = (match follow f.tf_type with
 				| TAbstract ({ a_path = [],"Void" },[]) -> f
-				| t -> { f with tf_expr = loop f.tf_expr t }
+				| _ -> { f with tf_expr = loop f.tf_expr f.tf_type }
 			) in
 			{ e with eexpr = TFunction f }
 		| _ -> e
@@ -151,8 +73,15 @@ let rec wrap_js_exceptions com e =
 		| TThrow eerr when not (is_error eerr.etype) ->
 			let terr = List.find (fun mt -> match mt with TClassDecl {cl_path = ["js";"_Boot"],"HaxeError"} -> true | _ -> false) com.types in
 			let cerr = match terr with TClassDecl c -> c | _ -> assert false in
-			let ewrap = { eerr with eexpr = TNew (cerr,[],[eerr]) } in
-			{ e with eexpr = TThrow ewrap }
+			(match eerr.etype with
+			| TDynamic _ ->
+				let eterr = Codegen.ExprBuilder.make_static_this cerr e.epos in
+				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
+				{ e with eexpr = TThrow ewrap }
+			)
 		| _ ->
 			Type.map_expr loop e
 	in
@@ -176,11 +105,12 @@ let check_local_vars_init e =
 		) !vars declared;
 	in
 	let declared = ref [] in
+	let outside_vars = ref IntMap.empty in
 	let rec loop vars e =
 		match e.eexpr with
 		| TLocal v ->
 			let init = (try PMap.find v.v_id !vars with Not_found -> true) in
-			if not init then begin
+			if not init && not (IntMap.mem v.v_id !outside_vars) then begin
 				if v.v_name = "this" then error "Missing this = value" e.epos
 				else error ("Local variable " ^ v.v_name ^ " used without being initialized") e.epos
 			end
@@ -264,6 +194,14 @@ let check_local_vars_init e =
 		| TThrow e | TReturn (Some e) ->
 			loop vars e;
 			vars := PMap.map (fun _ -> true) !vars
+		| TFunction tf ->
+			let old = !outside_vars in
+			(* Mark all known variables as "outside" so we can ignore their initialization state within the function.
+			   We cannot use `vars` directly because we still care about initializations the function might make.
+			*)
+			PMap.iter (fun i _ -> outside_vars := IntMap.add i true !outside_vars) !vars;
+			loop vars tf.tf_expr;
+			outside_vars := old;
 		| _ ->
 			Type.iter (loop vars) e
 	in
@@ -389,7 +327,7 @@ let captured_vars com e =
 	in
 
 	let mk_var v used =
-		let v2 = alloc_var v.v_name (PMap.find v.v_id used) in
+		let v2 = alloc_var v.v_name (PMap.find v.v_id used) v.v_pos in
 		v2.v_meta <- v.v_meta;
 		v2
 	in
@@ -454,12 +392,27 @@ let captured_vars com e =
 				will not be overwritten in next loop iteration
 			*)
 			if com.config.pf_capture_policy = CPLoopVars then
+				(* We don't want to duplicate any variable declarations, so let's make copies (issue #3902). *)
+				let new_vars = List.map (fun v -> v.v_id,alloc_var v.v_name v.v_type v.v_pos) vars in
+				let rec loop e = match e.eexpr with
+					| TLocal v ->
+						begin try
+							let v' = List.assoc v.v_id new_vars in
+							v'.v_capture <- true;
+							{e with eexpr = TLocal v'}
+						with Not_found ->
+							e
+						end
+					| _ ->
+						Type.map_expr loop e
+				in
+				let e = loop e in
 				mk (TCall (
 					Codegen.mk_parent (mk (TFunction {
-						tf_args = List.map (fun v -> v, None) vars;
+						tf_args = List.map (fun (_,v) -> v, None) new_vars;
 						tf_type = e.etype;
 						tf_expr = mk_block (mk (TReturn (Some e)) e.etype e.epos);
-					}) (TFun (List.map (fun v -> v.v_name,false,v.v_type) vars,e.etype)) e.epos),
+					}) (TFun (List.map (fun (_,v) -> v.v_name,false,v.v_type) new_vars,e.etype)) e.epos),
 					List.map (fun v -> mk (TLocal v) v.v_type e.epos) vars)
 				) e.etype e.epos
 			else
@@ -569,64 +522,17 @@ let captured_vars com e =
 (* RENAME LOCAL VARS *)
 
 let rename_local_vars ctx e =
-	let cfg = ctx.com.config in
-	let all_scope = (not cfg.pf_captured_scope) || (not cfg.pf_locals_scope) in
-	let vars = ref PMap.empty in
-	let all_vars = ref PMap.empty in
-	let vtemp = alloc_var "~" t_dynamic in
-	let rebuild_vars = ref false in
-	let rebuild m =
-		PMap.fold (fun v acc -> PMap.add v.v_name v acc) m PMap.empty
-	in
-	let save() =
-		let old = !vars in
-		if cfg.pf_unique_locals || not cfg.pf_locals_scope then (fun() -> ()) else (fun() -> vars := if !rebuild_vars then rebuild old else old)
+	let vars = ref [] in
+	let declare v =
+		vars := v :: !vars
 	in
-	let rename vars v =
-		let count = ref 1 in
-		while PMap.mem (v.v_name ^ string_of_int !count) vars do
-			incr count;
-		done;
-		v.v_name <- v.v_name ^ string_of_int !count;
-	in
-	let declare v p =
-		(match follow v.v_type with
-			| TAbstract ({a_path = [],"Void"},_) -> error "Arguments and variables of type Void are not allowed" p
-			| _ -> ());
-		(* chop escape char for all local variables generated *)
-		if is_gen_local v then v.v_name <- "_g" ^ String.sub v.v_name 1 (String.length v.v_name - 1);
-		let look_vars = (if not cfg.pf_captured_scope && v.v_capture then !all_vars else !vars) in
-		(try
-			let v2 = PMap.find v.v_name look_vars in
-			(*
-				block_vars will create some wrapper-functions that are declaring
-				the same variable twice. In that case do not perform a rename since
-				we are sure it's actually the same variable
-			*)
-			if v == v2 then raise Not_found;
-			rename look_vars v;
-		with Not_found ->
-			());
-		vars := PMap.add v.v_name v !vars;
-		if all_scope then all_vars := PMap.add v.v_name v !all_vars;
+	let reserved = ref StringMap.empty in
+	let reserve name =
+		reserved := StringMap.add name true !reserved
 	in
-	(*
-		This is quite a rare case, when a local variable would otherwise prevent
-		accessing a type because it masks the type value or the package name.
-	*)
 	let check t =
 		match (t_infos t).mt_path with
-		| [], name | name :: _, _ ->
-			let vars = if cfg.pf_locals_scope then vars else all_vars in
-			(try
-				let v = PMap.find name !vars in
-				if v == vtemp then raise Not_found; (* ignore *)
-				rename (!vars) v;
-				rebuild_vars := true;
-				vars := PMap.add v.v_name v !vars
-			with Not_found ->
-				());
-			vars := PMap.add name vtemp !vars
+		| [], name | name :: _, _ -> reserve name
 	in
 	let check_type t =
 		match follow t with
@@ -636,70 +542,62 @@ let rename_local_vars ctx e =
 		| TAbstract (a,_) -> check (TAbstractDecl a)
 		| TMono _ | TLazy _ | TAnon _ | TDynamic _ | TFun _ -> ()
 	in
-	let rec loop e =
-		match e.eexpr with
-		| TVar (v,eo) ->
-			if not cfg.pf_locals_scope then declare v e.epos;
-			(match eo with None -> () | Some e -> loop e);
-			if cfg.pf_locals_scope then declare v e.epos;
-		| TFunction tf ->
-			let old = save() in
-			List.iter (fun (v,_) -> declare v e.epos) tf.tf_args;
-			loop tf.tf_expr;
-			old()
-		| TBlock el ->
-			let old = save() in
-			(* we have to look ahead for vars on these targets (issue #3344) *)
-			begin match ctx.com.platform with
-				| Js ->
-					let rec check_var e = match e.eexpr with
-						| TVar (v,eo) ->
-							(match eo with None -> () | Some e -> loop e);
-							declare v e.epos
-						| TBlock _ ->
-							()
-						| _ ->
-							Type.iter check_var e
-					in
-					List.iter check_var el
-				| _ ->
-					()
-			end;
-			List.iter loop el;
-			old()
-		| TFor (v,it,e1) ->
-			loop it;
-			let old = save() in
-			declare v e.epos;
-			loop e1;
-			old()
-		| TTry (e,catchs) ->
-			loop e;
+	let rec collect e = match e.eexpr with
+ 		| TVar(v,eo) ->
+			declare v;
+			(match eo with None -> () | Some e -> collect e)
+		| TFor(v,e1,e2) ->
+			declare v;
+			collect e1;
+			collect e2;
+		| TTry(e1,catches) ->
+			collect e1;
 			List.iter (fun (v,e) ->
-				let old = save() in
-				declare v e.epos;
+				declare v;
 				check_type v.v_type;
-				loop e;
-				old()
-			) catchs;
+				collect e
+			) catches
+		| TFunction tf ->
+			List.iter (fun (v,_) -> declare v) tf.tf_args;
+			collect tf.tf_expr
 		| TTypeExpr t ->
 			check t
 		| TNew (c,_,_) ->
-			Type.iter loop e;
+			Type.iter collect e;
 			check (TClassDecl c);
 		| TCast (e,Some t) ->
-			loop e;
+			collect e;
 			check t;
 		| TConst TSuper ->
 			check_type e.etype
 		| _ ->
-			Type.iter loop e
+			Type.iter collect e
 	in
-	declare (alloc_var "this" t_dynamic) Ast.null_pos; (* force renaming of 'this' vars in abstract *)
+	(* Pass 1: Collect used identifiers and variables. *)
+	reserve "this";
+	if ctx.com.platform = Java then reserve "_";
 	begin match ctx.curclass.cl_path with
-		| s :: _,_ | [],s -> declare (alloc_var s t_dynamic) Ast.null_pos
+		| s :: _,_ | [],s -> reserve s
 	end;
-	loop e;
+	collect e;
+	(* Pass 2: Check and rename variables. *)
+	let count_table = Hashtbl.create 0 in
+	let maybe_rename v =
+		(* chop escape char for all local variables generated *)
+		if is_gen_local v then v.v_name <- "_g" ^ String.sub v.v_name 1 (String.length v.v_name - 1);
+		let name = ref v.v_name in
+		let count = ref (try Hashtbl.find count_table v.v_name with Not_found -> 0) in
+		while StringMap.mem !name !reserved do
+			incr count;
+			name := v.v_name ^ (string_of_int !count);
+		done;
+		reserve !name;
+		Hashtbl.replace count_table v.v_name !count;
+		if not (Meta.has Meta.RealPath v.v_meta) then
+			v.v_meta <- (Meta.RealPath,[EConst (String v.v_name),e.epos],e.epos) :: v.v_meta;
+		v.v_name <- !name;
+	in
+	List.iter maybe_rename (List.rev !vars);
 	e
 
 let check_unification ctx e t =
@@ -770,7 +668,7 @@ let rec is_removable_class c =
 			| _ -> false) ||
 		List.exists (fun (_,t) -> match follow t with
 			| TInst(c,_) ->
-				Codegen.has_ctor_constraint c
+				has_ctor_constraint c || Meta.has Meta.Const c.cl_meta
 			| _ ->
 				false
 		) c.cl_params)
@@ -904,7 +802,7 @@ let add_field_inits ctx t =
 	let apply c =
 		let ethis = mk (TConst TThis) (TInst (c,List.map snd c.cl_params)) c.cl_pos in
 		(* TODO: we have to find a variable name which is not used in any of the functions *)
-		let v = alloc_var "_g" ethis.etype in
+		let v = alloc_var "_g" ethis.etype ethis.epos in
 		let need_this = ref false in
 		let inits,fields = List.fold_left (fun (inits,fields) cf ->
 			match cf.cf_kind,cf.cf_expr with
@@ -950,7 +848,7 @@ let add_field_inits ctx t =
 						eassign;
 			) inits in
 			let el = if !need_this then (mk (TVar((v, Some ethis))) ethis.etype ethis.epos) :: el else el in
-			match c.cl_constructor with
+			let cf = match c.cl_constructor with
 			| None ->
 				let ct = TFun([],ctx.com.basic.tvoid) in
 				let ce = mk (TFunction {
@@ -960,15 +858,27 @@ let add_field_inits ctx t =
 				}) ct c.cl_pos in
 				let ctor = mk_field "new" ct c.cl_pos in
 				ctor.cf_kind <- Method MethNormal;
-				c.cl_constructor <- Some { ctor with cf_expr = Some ce };
+				{ ctor with cf_expr = Some ce }
 			| Some cf ->
 				match cf.cf_expr with
 				| Some { eexpr = TFunction f } ->
 					let bl = match f.tf_expr with {eexpr = TBlock b } -> b | x -> [x] in
 					let ce = mk (TFunction {f with tf_expr = mk (TBlock (el @ bl)) ctx.com.basic.tvoid c.cl_pos }) cf.cf_type cf.cf_pos in
-					c.cl_constructor <- Some {cf with cf_expr = Some ce }
+					{cf with cf_expr = Some ce };
 				| _ ->
 					assert false
+			in
+			let config = AnalyzerConfig.get_field_config ctx.com c cf in
+			Analyzer.Run.run_on_field ctx config c cf;
+			(match cf.cf_expr with
+			| Some e ->
+				(* This seems a bit expensive, but hopefully constructor expressions aren't that massive. *)
+				let e = rename_local_vars ctx e in
+				let e = Optimizer.sanitize ctx.com e in
+				cf.cf_expr <- Some e
+			| _ ->
+				());
+			c.cl_constructor <- Some cf
 	in
 	match t with
 	| TClassDecl c ->
@@ -982,10 +892,28 @@ let add_meta_field ctx t = match t with
 		(match Codegen.build_metadata ctx.com t with
 		| None -> ()
 		| Some e ->
+			add_feature ctx.com "has_metadata";
 			let f = mk_field "__meta__" t_dynamic c.cl_pos in
 			f.cf_expr <- Some e;
-			c.cl_ordered_statics <- f :: c.cl_ordered_statics;
-			c.cl_statics <- PMap.add f.cf_name f c.cl_statics)
+			let can_deal_with_interface_metadata () = match ctx.com.platform with
+				| Flash when Common.defined ctx.com Define.As3 -> false
+				| Php -> false
+				| _ -> true
+			in
+			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
+				cf.cf_expr <- Some e;
+				ncls.cl_statics <- PMap.add "__meta__" cf ncls.cl_statics;
+				ncls.cl_ordered_statics <- cf :: ncls.cl_ordered_statics;
+				ctx.com.types <- (TClassDecl ncls) :: ctx.com.types;
+				c.cl_meta <- (Meta.Custom ":hasMetadata",[],e.epos) :: c.cl_meta
+			end else begin
+				c.cl_ordered_statics <- f :: c.cl_ordered_statics;
+				c.cl_statics <- PMap.add f.cf_name f c.cl_statics
+			end)
 	| _ ->
 		()
 
@@ -1097,76 +1025,37 @@ let iter_expressions fl mt =
 		()
 
 let run com tctx main =
-	begin match com.display with
-		| DMUsage | DMPosition ->
-			Codegen.detect_usage com;
-		| _ ->
-			()
-	end;
 	if not (Common.defined com Define.NoDeprecationWarnings) then
 		Codegen.DeprecationCheck.run com;
-	let use_static_analyzer = Common.defined com Define.Analyzer in
-	(* this part will be a bit messy until we make the analyzer the default *)
 	let new_types = List.filter (fun t -> not (is_cached t)) com.types in
-	if use_static_analyzer then begin
-		(* PASS 1: general expression filters *)
-		let filters = [
-			Codegen.UnificationCallback.run (check_unification tctx);
-			Codegen.AbstractCast.handle_abstract_casts tctx;
-			Optimizer.inline_constructors tctx;
-			Optimizer.reduce_expression tctx;
-			blockify_ast;
-			captured_vars com;
-		] in
-		List.iter (run_expression_filters tctx filters) new_types;
-		Analyzer.Run.run_on_types tctx new_types;
-		List.iter (iter_expressions [verify_ast]) new_types;
-		let filters = [
-			Optimizer.sanitize com;
-			if com.config.pf_add_final_return then add_final_return else (fun e -> e);
-			if com.platform = Js then wrap_js_exceptions com else (fun e -> e);
-			rename_local_vars tctx;
-		] in
-		List.iter (run_expression_filters tctx filters) new_types;
-	end else begin
-		(* PASS 1: general expression filters *)
-		let filters = [
-			Codegen.UnificationCallback.run (check_unification tctx);
-			Codegen.AbstractCast.handle_abstract_casts tctx;
-			blockify_ast;
-			check_local_vars_init;
-			( if (Common.defined com Define.NoSimplify) || (Common.defined com Define.Cppia) ||
-						( match com.platform with Cpp -> false | _ -> true ) then
-					fun e -> e
-				else
-					fun e ->
-						let save = save_locals tctx in
-						let timer = timer "analyzer-simplify-apply" in
-						let e = try snd (Analyzer.Simplifier.apply com e) with Exit -> e in
-						timer();
-						save();
-					e );
-			if com.foptimize then (fun e -> Optimizer.reduce_expression tctx (Optimizer.inline_constructors tctx e)) else Optimizer.sanitize com;
-			captured_vars com;
-			promote_complex_rhs com;
-			if com.config.pf_add_final_return then add_final_return else (fun e -> e);
-			if com.platform = Js then wrap_js_exceptions com else (fun e -> e);
-			rename_local_vars tctx;
-		] in
-		List.iter (run_expression_filters tctx filters) new_types;
-		List.iter (iter_expressions [verify_ast]) new_types;
-	end;
+	(* PASS 1: general expression filters *)
+	let filters = [
+		Codegen.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;
+	if com.platform <> Cross then Analyzer.Run.run_on_types tctx new_types;
+	let filters = [
+		Optimizer.sanitize com;
+		if com.config.pf_add_final_return then add_final_return else (fun e -> e);
+		if com.platform = Js then wrap_js_exceptions com else (fun e -> e);
+		rename_local_vars tctx;
+	] in
+	List.iter (run_expression_filters tctx filters) new_types;
 	next_compilation();
-	List.iter (fun f -> f()) (List.rev com.filters); (* macros onGenerate etc. *)
+	List.iter (fun f -> f()) (List.rev com.callbacks.before_dce); (* macros onGenerate etc. *)
 	List.iter (save_class_state tctx) new_types;
+	(* PASS 2: type filters pre-DCE *)
 	List.iter (fun t ->
 		remove_generic_base tctx t;
 		remove_extern_fields tctx t;
+		Codegen.update_cache_dependencies t;
+		(* check @:remove metadata before DCE so it is ignored there (issue #2923) *)
+		check_remove_metadata tctx t;
 	) com.types;
-	(* update cache dependencies before DCE is run *)
-	Codegen.update_cache_dependencies com;
-	(* check @:remove metadata before DCE so it is ignored there (issue #2923) *)
-	List.iter (check_remove_metadata tctx) com.types;
 	(* DCE *)
 	let dce_mode = if Common.defined com Define.As3 then
 		"no"
@@ -1179,20 +1068,7 @@ let run com tctx main =
 		| "no" -> Dce.fix_accessors com
 		| _ -> failwith ("Unknown DCE mode " ^ dce_mode)
 	end;
-	(* always filter empty abstract implementation classes (issue #1885) *)
-	List.iter (fun mt -> match mt with
-		| TClassDecl({cl_kind = KAbstractImpl _} as c) when c.cl_ordered_statics = [] && c.cl_ordered_fields = [] && not (Meta.has Meta.Used c.cl_meta) ->
-			c.cl_extern <- true
-		| TClassDecl({cl_kind = KAbstractImpl a} as c) when Meta.has Meta.Enum a.a_meta ->
-			let is_runtime_field cf =
-				not (Meta.has Meta.Enum cf.cf_meta)
-			in
-			(* also filter abstract implementation classes that have only @:enum fields (issue #2858) *)
-			if not (List.exists is_runtime_field c.cl_ordered_statics) then
-				c.cl_extern <- true
-		| _ -> ()
-	) com.types;
-	(* PASS 3: type filters *)
+	(* PASS 3: type filters post-DCE *)
 	let type_filters = [
 		check_private_path;
 		apply_native_paths;

文件差异内容过多而无法显示
+ 464 - 312
src/optimization/optimizer.ml


+ 288 - 82
ast.ml → src/syntax/ast.ml

@@ -1,23 +1,20 @@
 (*
- * Copyright (C)2005-2013 Haxe Foundation
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
+	The 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.
  *)
 
 type pos = {
@@ -27,6 +24,7 @@ type pos = {
 }
 
 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 =
@@ -39,6 +37,7 @@ module Meta = struct
 		| Annotation
 		| ArrayAccess
 		| Ast
+		| AstSource
 		| AutoBuild
 		| Bind
 		| Bitmap
@@ -50,6 +49,7 @@ module Meta = struct
 		| ClassCode
 		| Commutative
 		| CompilerGenerated
+		| Const
 		| CoreApi
 		| CoreType
 		| CppFileCode
@@ -65,6 +65,7 @@ module Meta = struct
 		| Deprecated
 		| DirectlyUsed
 		| DynamicObject
+		| Eager
 		| Enum
 		| EnumConstructorParam
 		| Event
@@ -73,10 +74,13 @@ module Meta = struct
 		| Extern
 		| FakeEnum
 		| File
+		| FileXml
 		| Final
+		| Fixed
 		| FlatEnum
 		| Font
 		| Forward
+		| ForwardStatics
 		| From
 		| FunctionCode
 		| FunctionTailCode
@@ -107,6 +111,7 @@ module Meta = struct
 		| KeepInit
 		| KeepSub
 		| LibType
+		| LuaRequire
 		| Meta
 		| Macro
 		| MaybeUsed
@@ -117,6 +122,7 @@ module Meta = struct
 		| NativeGen
 		| NativeGeneric
 		| NativeProperty
+		| NativeStaticExtension
 		| NoCompletion
 		| NoDebug
 		| NoDoc
@@ -129,41 +135,50 @@ module Meta = struct
 		| NotNull
 		| NoUsing
 		| Ns
+		| Objc
 		| Op
 		| Optional
 		| Overload
+		| PhpConstants
+		| PhpGlobal
 		| PrivateAccess
 		| Property
 		| Protected
 		| Public
 		| PublicFields
+		| Pure
 		| QuotedField
 		| ReadOnly
 		| RealPath
 		| Remove
 		| Require
 		| RequiresAssign
-		(* | Resolve *)
+		| 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
@@ -309,25 +324,31 @@ type type_path = {
 	tsub : string option;
 }
 
+and placed_type_path = type_path * pos
+
 and type_param_or_const =
-	| TPType of complex_type
+	| TPType of type_hint
 	| TPExpr of expr
 
 and complex_type =
 	| CTPath of type_path
-	| CTFunction of complex_type list * complex_type
+	| CTFunction of type_hint list * type_hint
 	| CTAnonymous of class_field list
-	| CTParent of complex_type
-	| CTExtend of type_path list * class_field list
-	| CTOptional of complex_type
+	| CTParent of type_hint
+	| CTExtend of placed_type_path list * class_field list
+	| CTOptional of type_hint
+
+and type_hint = complex_type * pos
 
 and func = {
 	f_params : type_param list;
-	f_args : (string * bool * complex_type option * expr option) list;
-	f_type : complex_type option;
+	f_args : (placed_name * bool * metadata * type_hint option * expr option) list;
+	f_type : type_hint option;
 	f_expr : expr option;
 }
 
+and placed_name = string * pos
+
 and expr_def =
 	| EConst of constant
 	| EArray of expr * expr
@@ -337,9 +358,9 @@ and expr_def =
 	| EObjectDecl of (string * expr) list
 	| EArrayDecl of expr list
 	| ECall of expr * expr list
-	| ENew of type_path * expr list
+	| ENew of placed_type_path * expr list
 	| EUnop of unop * unop_flag * expr
-	| EVars of (string * complex_type option * expr option) list
+	| EVars of (placed_name * type_hint option * expr option) list
 	| EFunction of string option * func
 	| EBlock of expr list
 	| EFor of expr * expr
@@ -347,25 +368,26 @@ and expr_def =
 	| 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 * (string * complex_type * expr) list
+	| ETry of expr * (placed_name * type_hint * expr) list
 	| EReturn of expr option
 	| EBreak
 	| EContinue
 	| EUntyped of expr
 	| EThrow of expr
-	| ECast of expr * complex_type option
+	| ECast of expr * type_hint option
 	| EDisplay of expr * bool
-	| EDisplayNew of type_path
+	| EDisplayNew of placed_type_path
 	| ETernary of expr * expr * expr
-	| ECheckType of expr * complex_type
+	| ECheckType of expr * type_hint
 	| EMeta of metadata_entry * expr
 
 and expr = expr_def * pos
 
 and type_param = {
-	tp_name : string;
+	tp_name : placed_name;
 	tp_params :	type_param list;
-	tp_constraints : complex_type list;
+	tp_constraints : type_hint list;
+	tp_meta : metadata;
 }
 
 and documentation = string option
@@ -383,12 +405,12 @@ and access =
 	| AMacro
 
 and class_field_kind =
-	| FVar of complex_type option * expr option
+	| FVar of type_hint option * expr option
 	| FFun of func
-	| FProp of string * string * complex_type option * expr option
+	| FProp of string * string * type_hint option * expr option
 
 and class_field = {
-	cff_name : string;
+	cff_name : placed_name;
 	cff_doc : documentation;
 	cff_pos : pos;
 	mutable cff_meta : metadata;
@@ -404,27 +426,28 @@ type class_flag =
 	| HInterface
 	| HExtern
 	| HPrivate
-	| HExtends of type_path
-	| HImplements of type_path
+	| HExtends of placed_type_path
+	| HImplements of placed_type_path
 
 type abstract_flag =
 	| APrivAbstract
-	| AFromType of complex_type
-	| AToType of complex_type
-	| AIsType of complex_type
+	| AFromType of type_hint
+	| AToType of type_hint
+	| AIsType of type_hint
+	| AExtern
 
 type enum_constructor = {
-	ec_name : string;
+	ec_name : placed_name;
 	ec_doc : documentation;
 	ec_meta : metadata;
-	ec_args : (string * bool * complex_type) list;
+	ec_args : (string * bool * type_hint) list;
 	ec_pos : pos;
 	ec_params : type_param list;
-	ec_type : complex_type option;
+	ec_type : type_hint option;
 }
 
 type ('a,'b) definition = {
-	d_name : string;
+	d_name : placed_name;
 	d_doc : documentation;
 	d_params : type_param list;
 	d_meta : metadata;
@@ -437,13 +460,15 @@ type import_mode =
 	| IAsName of string
 	| IAll
 
+type import = placed_name list * import_mode
+
 type type_def =
 	| EClass of (class_flag, class_field list) definition
 	| EEnum of (enum_flag, enum_constructor list) definition
-	| ETypedef of (enum_flag, complex_type) definition
+	| ETypedef of (enum_flag, type_hint) definition
 	| EAbstract of (abstract_flag, class_field list) definition
-	| EImport of (string * pos) list * import_mode
-	| EUsing of type_path
+	| EImport of import
+	| EUsing of placed_name list
 
 type type_decl = type_def * pos
 
@@ -463,8 +488,8 @@ let is_lower_ident i =
 let pos = snd
 
 let rec is_postfix (e,_) op = match op with
-	| Increment | Decrement -> true
-	| Not | Neg | NegBits -> false
+	| Increment | Decrement | Not -> true
+	| Neg | NegBits -> false
 
 let is_prefix = function
 	| Increment | Decrement -> true
@@ -627,6 +652,8 @@ let s_token = function
 	| At -> "@"
 	| Dollar v -> "$" ^ v
 
+exception Invalid_escape_sequence of char * int
+
 let unescape s =
 	let b = Buffer.create 0 in
 	let rec loop esc i =
@@ -634,6 +661,7 @@ let unescape s =
 			()
 		else
 			let c = s.[i] in
+			let fail () = raise (Invalid_escape_sequence(c,i)) in
 			if esc then begin
 				let inext = ref (i + 1) in
 				(match c with
@@ -642,11 +670,11 @@ let unescape s =
 				| 't' -> Buffer.add_char b '\t'
 				| '"' | '\'' | '\\' -> Buffer.add_char b c
 				| '0'..'3' ->
-					let c = (try char_of_int (int_of_string ("0o" ^ String.sub s i 3)) with _ -> raise Exit) in
+					let c = (try char_of_int (int_of_string ("0o" ^ String.sub s i 3)) with _ -> fail()) in
 					Buffer.add_char b c;
 					inext := !inext + 2;
 				| 'x' ->
-					let c = (try char_of_int (int_of_string ("0x" ^ String.sub s (i+1) 2)) with _ -> raise Exit) in
+					let c = (try char_of_int (int_of_string ("0x" ^ String.sub s (i+1) 2)) with _ -> fail()) in
 					Buffer.add_char b c;
 					inext := !inext + 2;
 				| 'u' ->
@@ -660,14 +688,14 @@ let unescape s =
 							assert (u <= 0x10FFFF);
 							(u, l+2)
 						with _ ->
-							raise Exit
+							fail()
 					in
 					let ub = UTF8.Buf.create 0 in
 					UTF8.Buf.add_char ub (UChar.uchar_of_int u);
 					Buffer.add_string b (UTF8.Buf.contents ub);
 					inext := !inext + a;
 				| _ ->
-					raise Exit);
+					fail());
 				loop false !inext;
 			end else
 				match c with
@@ -679,37 +707,36 @@ let unescape s =
 	loop false 0;
 	Buffer.contents b
 
-
 let map_expr loop (e,p) =
 	let opt f o =
 		match o with None -> None | Some v -> Some (f v)
 	in
 	let rec tparam = function
-		| TPType t -> TPType (ctype t)
+		| TPType t -> TPType (type_hint t)
 		| TPExpr e -> TPExpr (loop e)
 	and cfield f =
 		{ f with cff_kind = (match f.cff_kind with
-			| FVar (t,e) -> FVar (opt ctype t, opt loop e)
+			| FVar (t,e) -> FVar (opt type_hint t, opt loop e)
 			| FFun f -> FFun (func f)
-			| FProp (get,set,t,e) -> FProp (get,set,opt ctype t,opt loop e))
+			| FProp (get,set,t,e) -> FProp (get,set,opt type_hint t,opt loop e))
 		}
-	and ctype = function
-		| CTPath t -> CTPath (tpath t)
-		| CTFunction (cl,c) -> CTFunction (List.map ctype cl, ctype c)
+	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)
 		| CTAnonymous fl -> CTAnonymous (List.map cfield fl)
-		| CTParent t -> CTParent (ctype t)
+		| CTParent t -> CTParent (type_hint t)
 		| CTExtend (tl,fl) -> CTExtend (List.map tpath tl, List.map cfield fl)
-		| CTOptional t -> CTOptional (ctype t)
+		| CTOptional t -> CTOptional (type_hint t)),p
 	and tparamdecl t =
-		{ tp_name = t.tp_name; tp_constraints = List.map ctype t.tp_constraints; tp_params = List.map tparamdecl t.tp_params }
+		{ 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 }
 	and func f =
 		{
 			f_params = List.map tparamdecl f.f_params;
-			f_args = List.map (fun (n,o,t,e) -> n,o,opt ctype t,opt loop e) f.f_args;
-			f_type = opt ctype f.f_type;
+			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;
 		}
-	and tpath t = { t with tparams = List.map tparam t.tparams }
+	and tpath (t,p) = { t with tparams = List.map tparam t.tparams },p
 	in
 	let e = (match e with
 	| EConst _ -> e
@@ -722,7 +749,7 @@ let map_expr loop (e,p) =
 	| ECall (e,el) -> ECall (loop e, List.map loop el)
 	| ENew (t,el) -> ENew (tpath t,List.map loop el)
 	| EUnop (op,f,e) -> EUnop (op,f,loop e)
-	| EVars vl -> EVars (List.map (fun (n,t,eo) -> n,opt ctype t,opt loop eo) vl)
+	| EVars vl -> EVars (List.map (fun (n,t,eo) -> n,opt type_hint t,opt loop eo) vl)
 	| EFunction (n,f) -> EFunction (n,func f)
 	| EBlock el -> EBlock (List.map loop el)
 	| EFor (e1,e2) -> EFor (loop e1, loop e2)
@@ -730,31 +757,163 @@ let map_expr loop (e,p) =
 	| 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,ctype t,loop e) catches)
+	| ETry (e,catches) -> ETry (loop e, List.map (fun (n,t,e) -> n,type_hint t,loop 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 ctype t)
+	| ECast (e,t) -> ECast (loop e,opt type_hint 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, ctype t)
+	| ECheckType (e,t) -> ECheckType (loop e, type_hint t)
 	| EMeta (m,e) -> EMeta(m, loop e)
 	) in
 	(e,p)
 
-let rec s_expr (e,_) =
+let iter_expr loop (e,p) =
+	let opt eo = match eo with None -> () | Some e -> loop e in
+	let exprs = List.iter loop in
 	match e with
-	| EConst c -> s_constant c
-	| EParenthesis e -> "(" ^ (s_expr e) ^ ")"
-	| EArrayDecl el -> "[" ^ (String.concat "," (List.map s_expr el)) ^ "]"
-	| EObjectDecl fl -> "{" ^ (String.concat "," (List.map (fun (n,e) -> n ^ ":" ^ (s_expr e)) fl)) ^ "}"
-	| EBinop (op,e1,e2) -> s_expr e1 ^ s_binop op ^ s_expr e2
-	| ECall (e,el) -> s_expr e ^ "(" ^ (String.concat ", " (List.map s_expr el)) ^ ")"
-	| EField (e,f) -> s_expr e ^ "." ^ f
-	| _ -> "'???'"
+	| EConst _ | EContinue | EBreak | EDisplayNew _ | EReturn None -> ()
+	| EParenthesis e1 | EField(e1,_) | EUnop(_,_,e1) | EReturn(Some e1) | EThrow e1 | EMeta(_,e1)
+	| ECheckType(e1,_) | EDisplay(e1,_) | ECast(e1,_) | EUntyped e1 -> loop e1;
+	| EArray(e1,e2) | EBinop(_,e1,e2) | EIn(e1,e2) | EFor(e1,e2) | EWhile(e1,e2,_) | EIf(e1,e2,None) -> loop e1; loop e2;
+	| ETernary(e1,e2,e3) | EIf(e1,e2,Some e3) -> loop e1; loop e2; loop e3;
+	| EArrayDecl el | ENew(_,el) | EBlock el -> List.iter loop el
+	| ECall(e1,el) -> loop e1; exprs el;
+	| EObjectDecl fl -> List.iter (fun (_,e) -> loop e) fl;
+	| ETry(e1,catches) ->
+		loop e1;
+		List.iter (fun (_,_,e) -> loop e) catches
+	| ESwitch(e1,cases,def) ->
+		loop e1;
+		List.iter (fun (el,eg,e) ->
+			exprs el;
+			opt eg;
+			opt e;
+		) cases;
+		(match def with None -> () | Some e -> opt e);
+	| EFunction(_,f) ->
+		List.iter (fun (_,_,_,_,eo) -> opt eo) f.f_args;
+		opt f.f_expr
+	| EVars vl -> List.iter (fun (_,_,eo) -> opt eo) vl
+
+let s_expr e =
+	let rec s_expr_inner tabs (e,_) =
+		match e with
+		| EConst c -> s_constant c
+		| EArray (e1,e2) -> s_expr_inner tabs e1 ^ "[" ^ s_expr_inner tabs e2 ^ "]"
+		| EBinop (op,e1,e2) -> s_expr_inner tabs e1 ^ " " ^ s_binop op ^ " " ^ s_expr_inner tabs e2
+		| EField (e,f) -> s_expr_inner tabs e ^ "." ^ f
+		| EParenthesis e -> "(" ^ (s_expr_inner tabs e) ^ ")"
+		| 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 ", " ^ ")"
+		| EUnop (op,Postfix,e) -> s_expr_inner tabs e ^ s_unop op
+		| EUnop (op,Prefix,e) -> s_unop op ^ s_expr_inner tabs e
+		| EFunction (Some n,f) -> "function " ^ n ^ s_func tabs f
+		| EFunction (None,f) -> "function" ^ s_func tabs f
+		| EVars vl -> "var " ^ String.concat ", " (List.map (s_var tabs) vl)
+		| EBlock [] -> "{ }"
+		| EBlock el -> s_block tabs el "{" "\n" "}"
+		| EFor (e1,e2) -> "for (" ^ s_expr_inner tabs e1 ^ ") " ^ s_expr_inner tabs e2
+		| EIn (e1,e2) -> s_expr_inner tabs e1 ^ " in " ^ s_expr_inner tabs e2
+		| EIf (e,e1,None) -> "if (" ^ s_expr_inner tabs e ^ ") " ^ s_expr_inner tabs e1
+		| EIf (e,e1,Some e2) -> "if (" ^ s_expr_inner tabs e ^ ") " ^ s_expr_inner tabs e1 ^ " else " ^ s_expr_inner tabs e2
+		| 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 -> 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 " "
+		| EBreak -> "break"
+		| EContinue -> "continue"
+		| EUntyped e -> "untyped " ^ s_expr_inner tabs e
+		| EThrow e -> "throw " ^ s_expr_inner tabs e
+		| ECast (e,Some (t,_)) -> "cast (" ^ s_expr_inner tabs e ^ ", " ^ s_complex_type tabs t ^ ")"
+		| ECast (e,None) -> "cast " ^ s_expr_inner tabs 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)
+		| 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
+	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)) ^ ">"
+		else ""
+	and s_type_param_or_const tabs p =
+		match p with
+		| TPType (t,_) -> s_complex_type tabs t
+		| TPExpr e -> s_expr_inner tabs e
+	and s_complex_type tabs ct =
+		match ct with
+		| CTPath t -> s_complex_type_path tabs (t,null_pos)
+		| CTFunction (cl,(c,_)) -> if List.length cl > 0 then String.concat " -> " (List.map (fun (t,_) -> s_complex_type tabs t) cl) else "Void" ^ " -> " ^ s_complex_type tabs c
+		| CTAnonymous fl -> "{ " ^ String.concat "; " (List.map (s_class_field tabs) fl) ^ "}";
+		| CTParent(t,_) -> "(" ^ s_complex_type tabs t ^ ")"
+		| CTOptional(t,_) -> "?" ^ s_complex_type tabs t
+		| CTExtend (tl, fl) -> "{> " ^ String.concat " >, " (List.map (s_complex_type_path tabs) tl) ^ ", " ^ String.concat ", " (List.map (s_class_field tabs) fl) ^ " }"
+	and s_class_field tabs f =
+		match f.cff_doc with
+		| Some s -> "/**\n\t" ^ tabs ^ s ^ "\n**/\n"
+		| None -> "" ^
+		if List.length f.cff_meta > 0 then String.concat ("\n" ^ tabs) (List.map (s_metadata tabs) f.cff_meta) else "" ^
+		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 " = "
+		| 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 ""
+	and s_opt_expr tabs e pre =
+		match e with
+		| Some s -> pre ^ s_expr_inner tabs s
+		| None -> ""
+	and s_opt_type_hint tabs t pre =
+		match t with
+		| Some(t,_) -> pre ^ s_complex_type tabs t
+		| None -> ""
+	and s_func tabs f =
+		s_type_param_list tabs f.f_params ^
+		"(" ^ String.concat ", " (List.map (s_func_arg tabs) f.f_args) ^ ")" ^
+		s_opt_type_hint tabs f.f_type ":" ^
+		s_opt_expr tabs f.f_expr " "
+	and s_type_param tabs t =
+		fst (t.tp_name) ^ s_type_param_list tabs t.tp_params ^
+		if List.length t.tp_constraints > 0 then ":(" ^ String.concat ", " (List.map ((fun (t,_) -> s_complex_type tabs t)) t.tp_constraints) ^ ")" else ""
+	and s_type_param_list tabs tl =
+		if List.length tl > 0 then "<" ^ String.concat ", " (List.map (s_type_param tabs) tl) ^ ">" else ""
+	and s_func_arg tabs ((n,_),o,_,t,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) =
+		"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) =
+		" 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
+	and s_expr_omit_block tabs e =
+		match e with
+		| (EBlock [],_) -> ""
+		| (EBlock el,_) -> s_block (tabs ^ "\t") el "" "" ""
+		| _ -> s_expr_inner (tabs ^ "\t") e ^ ";"
+	in s_expr_inner "" e
 
 let get_value_meta meta =
 	try
@@ -765,8 +924,55 @@ let get_value_meta meta =
 	with Not_found ->
 		PMap.empty
 
+(* Type path related functions *)
+
 let rec string_list_of_expr_path_raise (e,p) =
 	match e with
 	| EConst (Ident i) -> [i]
 	| EField (e,f) -> f :: string_list_of_expr_path_raise e
 	| _ -> raise Exit
+
+let expr_of_type_path (sl,s) p =
+	match sl with
+	| [] -> (EConst(Ident s),p)
+	| s1 :: sl ->
+		let e1 = (EConst(Ident s1),p) in
+		let e = List.fold_left (fun e s -> (EField(e,s),p)) e1 sl in
+		EField(e,s),p
+
+let match_path recursive sl sl_pattern =
+	let rec loop sl1 sl2 = match sl1,sl2 with
+		| [],[] ->
+			true
+		(* always recurse into types of package paths *)
+		| (s1 :: s11 :: _),[s2] when is_lower_ident s2 && not (is_lower_ident s11)->
+			s1 = s2
+		| [_],[""] ->
+			true
+		| _,([] | [""]) ->
+			recursive
+		| [],_ ->
+			false
+		| (s1 :: sl1),(s2 :: sl2) ->
+			s1 = s2 && loop sl1 sl2
+	in
+	loop sl sl_pattern
+
+let full_dot_path mpath tpath =
+	if mpath = tpath then
+		(fst tpath) @ [snd tpath]
+	else
+		(fst mpath) @ [snd mpath;snd tpath]
+
+let safe_for_all2 f a b =
+	try List.for_all2 f a b with _ -> false
+
+let rec remove_duplicates f l = match l with
+	| [] -> []
+	| x :: l -> x :: (remove_duplicates f (List.filter (fun x' -> f x x') l))
+
+module Expr = struct
+	let ensure_block e = match fst e with
+		| EBlock _ -> e
+		| _ -> (EBlock [e],pos e)
+end

+ 29 - 26
lexer.mll → src/syntax/lexer.mll

@@ -1,23 +1,20 @@
 (*
- * Copyright (C)2005-2012 Haxe Foundation
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
+	The 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.
  *)
 
 {
@@ -30,7 +27,7 @@ type error_msg =
 	| Unterminated_regexp
 	| Unclosed_comment
 	| Unclosed_code
-	| Invalid_escape
+	| Invalid_escape of char
 	| Invalid_option
 
 exception Error of error_msg * pos
@@ -42,7 +39,7 @@ let error_msg = function
 	| Unterminated_regexp -> "Unterminated regular expression"
 	| Unclosed_comment -> "Unclosed comment"
 	| Unclosed_code -> "Unclosed code string"
-	| Invalid_escape -> "Invalid escape sequence"
+	| Invalid_escape c -> Printf.sprintf "Invalid escape sequence \\%s" (Char.escaped c)
 	| Invalid_option -> "Invalid regular expression option"
 
 type lexer_file = {
@@ -185,13 +182,17 @@ let get_error_line p =
 	let l, _ = find_pos p in
 	l
 
+let get_pos_coords p =
+	let file = find_file p.pfile in
+	let l1, p1 = find_line p.pmin file in
+	let l2, p2 = find_line p.pmax file in
+	l1, p1, l2, p2
+
 let get_error_pos printer p =
 	if p.pmin = -1 then
 		"(unknown)"
 	else
-		let file = find_file p.pfile in
-		let l1, p1 = find_line p.pmin file in
-		let l2, p2 = find_line p.pmax file in
+		let l1, p1, l2, p2 = get_pos_coords p in
 		if l1 = l2 then begin
 			let s = (if p1 = p2 then Printf.sprintf " %d" p1 else Printf.sprintf "s %d-%d" p1 p2) in
 			Printf.sprintf "%s character%s" (printer p.pfile l1) s
@@ -258,6 +259,8 @@ and token = parse
 	| "*=" { mk lexbuf (Binop (OpAssignOp OpMult)) }
 	| "/=" { mk lexbuf (Binop (OpAssignOp OpDiv)) }
 	| "<<=" { mk lexbuf (Binop (OpAssignOp OpShl)) }
+	| "||=" { mk lexbuf (Binop (OpAssignOp OpBoolOr)) }
+	| "&&=" { mk lexbuf (Binop (OpAssignOp OpBoolAnd)) }
 (*//| ">>=" { mk lexbuf (Binop (OpAssignOp OpShr)) } *)
 (*//| ">>>=" { mk lexbuf (Binop (OpAssignOp OpUShr)) } *)
 	| "==" { mk lexbuf (Binop OpEq) }
@@ -304,14 +307,14 @@ and token = parse
 			reset();
 			let pmin = lexeme_start lexbuf in
 			let pmax = (try string lexbuf with Exit -> error Unterminated_string pmin) in
-			let str = (try unescape (contents()) with Exit -> error Invalid_escape pmin) in
+			let str = (try unescape (contents()) with Invalid_escape_sequence(c,i) -> error (Invalid_escape c) (pmin + i)) in
 			mk_tok (Const (String str)) pmin pmax;
 		}
 	| "'" {
 			reset();
 			let pmin = lexeme_start lexbuf in
 			let pmax = (try string2 lexbuf with Exit -> error Unterminated_string pmin) in
-			let str = (try unescape (contents()) with Exit -> error Invalid_escape pmin) in
+			let str = (try unescape (contents()) with Invalid_escape_sequence(c,i) -> error (Invalid_escape c) (pmin + i)) in
 			let t = mk_tok (Const (String str)) pmin pmax in
 			fast_add_fmt_string (snd t);
 			t

+ 200 - 134
parser.ml → src/syntax/parser.ml

@@ -1,23 +1,20 @@
 (*
- * Copyright (C)2005-2013 Haxe Foundation
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
+	The 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
@@ -74,7 +71,6 @@ let unquote_ident f =
 let cache = ref (DynArray.create())
 let last_doc = ref None
 let use_doc = ref false
-let use_parser_resume = ref true
 let resume_display = ref null_pos
 let in_macro = ref false
 
@@ -94,7 +90,7 @@ let type_path sl in_import = match sl with
 
 let is_resuming p =
 	let p2 = !resume_display in
-	p.pmax = p2.pmin && !use_parser_resume && Common.unique_full_path p.pfile = p2.pfile
+	p.pmax = p2.pmin && Common.unique_full_path p.pfile = p2.pfile
 
 let set_resume p =
 	resume_display := { p with pfile = Common.unique_full_path p.pfile }
@@ -144,15 +140,19 @@ let rec make_unop op ((v,p2) as e) p1 =
 	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
-	| _ ->
-		EUnop (op,Prefix,e), punion p1 p2
+	| _ -> EUnop (op,Prefix,e), punion p1 p2
 
 let rec make_meta name params ((v,p2) as e) p1 =
 	match v with
+	| EBinop ((OpAssign | OpAssignOp _),_,_) -> EMeta((name,params,p1),e),punion p1 p2
 	| EBinop (bop,e,e2) -> EBinop (bop, make_meta name params e p1 , e2) , (punion p1 p2)
 	| ETernary (e1,e2,e3) -> ETernary (make_meta name params e1 p1 , e2, e3), punion p1 p2
-	| _ ->
-		EMeta((name,params,p1),e),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
+	ECall(e_is,[e;e2]),p
 
 let reify in_macro =
 	let cur_pos = ref None in
@@ -205,6 +205,9 @@ let reify in_macro =
 		else
 			(EConst (String s),p)
 	in
+	let to_placed_name (s,p) =
+		to_string s p
+	in
 	let to_array f a p =
 		(EArrayDecl (List.map (fun s -> f s p) a),p)
 	in
@@ -228,11 +231,25 @@ let reify in_macro =
 			| TPExpr e -> "TPExpr", to_expr e p
 		) in
 		mk_enum "TypeParam" n [v] p
-	and to_tpath t p =
+	and to_tpath (t,_) p =
 		let len = String.length t.tname in
-		if t.tpackage = [] && len > 1 && t.tname.[0] = '$' then
-			(EConst (Ident (String.sub t.tname 1 (len - 1))),p)
-		else begin
+		if t.tpackage = [] && len > 1 && t.tname.[0] = '$' then begin
+			let name = String.sub t.tname 1 (len - 1) in
+			let ei = (EConst (Ident name),p) in
+			match t.tparams with
+			| [] -> ei
+			| _ ->
+				(* `macro : $TP<Int>` conveys the intent to use TP and overwrite the
+				   type parameters. *)
+				let ea = to_array to_tparam t.tparams p in
+				let fields = [
+					("pack", (EField(ei,"pack"),p));
+					("name", (EField(ei,"name"),p));
+					("sub", (EField(ei,"sub"),p));
+					("params", ea);
+				] in
+				to_obj fields p
+		end else begin
 			let fields = [
 				("pack", to_array to_string t.tpackage p);
 				("name", to_string t.tname p);
@@ -242,27 +259,30 @@ let reify in_macro =
 		end
 	and to_ctype t p =
 		let ct n vl = mk_enum "ComplexType" n vl p in
-		match t with
-		| CTPath { tpackage = []; tparams = []; tsub = None; tname = n } when n.[0] = '$' ->
+		match fst t with
+		| CTPath ({ tpackage = []; tparams = []; tsub = None; tname = n }) when n.[0] = '$' ->
 			to_string n p
-		| CTPath t -> ct "TPath" [to_tpath t p]
-		| CTFunction (args,ret) -> ct "TFunction" [to_array to_ctype args p; to_ctype ret p]
+		| CTPath t -> ct "TPath" [to_tpath (t,p) p]
+		| CTFunction (args,ret) -> ct "TFunction" [to_array to_type_hint args p; to_type_hint ret p]
 		| CTAnonymous fields -> ct "TAnonymous" [to_array to_cfield fields p]
-		| CTParent t -> ct "TParent" [to_ctype t p]
+		| CTParent t -> ct "TParent" [to_type_hint t p]
 		| CTExtend (tl,fields) -> ct "TExtend" [to_array to_tpath tl p; to_array to_cfield fields p]
-		| CTOptional t -> ct "TOptional" [to_ctype t p]
+		| CTOptional t -> ct "TOptional" [to_type_hint t p]
+	and to_type_hint (t,p) _ =
+		(* to_obj ["type",to_ctype t p;"pos",to_pos p] p *)
+		to_ctype (t,p) p
 	and to_fun f p =
-		let farg (n,o,t,e) p =
+		let farg ((n,_),o,_,t,e) p =
 			let fields = [
 				"name", to_string n p;
 				"opt", to_bool o p;
-				"type", to_opt to_ctype t p;
+				"type", to_opt to_type_hint t p;
 			] in
 			to_obj (match e with None -> fields | Some e -> fields @ ["value",to_expr e p]) p
 		in
 		let rec fparam t p =
 			let fields = [
-				"name", to_string t.tp_name p;
+				"name", to_placed_name t.tp_name;
 				"constraints", to_array to_ctype t.tp_constraints p;
 				"params", to_array fparam t.tp_params p;
 			] in
@@ -270,7 +290,7 @@ let reify in_macro =
 		in
 		let fields = [
 			("args",to_array farg f.f_args p);
-			("ret",to_opt to_ctype f.f_type p);
+			("ret",to_opt to_type_hint f.f_type p);
 			("expr",to_opt to_expr f.f_expr p);
 			("params",to_array fparam f.f_params p);
 		] in
@@ -291,14 +311,14 @@ let reify in_macro =
 		in
 		let to_kind k =
 			let n, vl = (match k with
-				| FVar (ct,e) -> "FVar", [to_opt to_ctype ct p;to_opt to_expr e p]
+				| 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_ctype t p; to_opt to_expr e 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]
 			) in
 			mk_enum "FieldType" n vl p
 		in
 		let fields = [
-			Some ("name", to_string f.cff_name p);
+			Some ("name", to_placed_name f.cff_name);
 			(match f.cff_doc with None -> None | Some s -> Some ("doc", to_string s p));
 			(match f.cff_access with [] -> None | l -> Some ("access", to_array to_access l p));
 			Some ("kind", to_kind f.cff_kind);
@@ -369,10 +389,11 @@ let reify in_macro =
 			) [] p in
 			expr "EUnop" [op;to_bool (flag = Postfix) p;loop e]
 		| EVars vl ->
-			expr "EVars" [to_array (fun (v,t,e) p ->
+			expr "EVars" [to_array (fun ((n,pn),th,e) p ->
 				let fields = [
-					"name", to_string v p;
-					"type", to_opt to_ctype t p;
+					(* "name", to_obj ["name",to_string n pn;"pos",to_pos pn] p; *)
+					"name", to_string n pn;
+					"type", to_opt to_type_hint th p;
 					"expr", to_opt to_expr e p;
 				] in
 				to_obj fields p
@@ -408,7 +429,7 @@ let reify in_macro =
 			in
 			expr "ESwitch" [loop e1;to_array scase cases p;to_opt (to_opt to_expr) 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]
@@ -423,7 +444,7 @@ let reify in_macro =
 		| EThrow e ->
 			expr "EThrow" [loop e]
 		| ECast (e,ct) ->
-			expr "ECast" [loop e; to_opt to_ctype ct p]
+			expr "ECast" [loop e; to_opt to_type_hint ct p]
 		| EDisplay (e,flag) ->
 			expr "EDisplay" [loop e; to_bool flag p]
 		| EDisplayNew t ->
@@ -431,7 +452,7 @@ let reify in_macro =
 		| ETernary (e1,e2,e3) ->
 			expr "ETernary" [loop e1;loop e2;loop e3]
 		| ECheckType (e1,ct) ->
-			expr "ECheckType" [loop e1; to_ctype ct p]
+			expr "ECheckType" [loop e1; to_type_hint ct p]
 		| EMeta ((m,ml,p),e1) ->
 			match m, ml with
 			| Meta.Dollar ("" | "e"), _ ->
@@ -443,9 +464,9 @@ let reify in_macro =
 			(* TODO: can $v and $i be implemented better? *)
 			| Meta.Dollar "v", _ ->
 				begin match fst e1 with
-				| EParenthesis (ECheckType (e2, CTPath{tname="String";tpackage=[]}),_) -> expr "EConst" [mk_enum "Constant" "CString" [e2] (pos e2)]
-				| EParenthesis (ECheckType (e2, CTPath{tname="Int";tpackage=[]}),_) -> expr "EConst" [mk_enum "Constant" "CInt" [e2] (pos e2)]
-				| EParenthesis (ECheckType (e2, CTPath{tname="Float";tpackage=[]}),_) -> expr "EConst" [mk_enum "Constant" "CFloat" [e2] (pos e2)]
+				| EParenthesis (ECheckType (e2, (CTPath{tname="String";tpackage=[]},_)),_) -> expr "EConst" [mk_enum "Constant" "CString" [e2] (pos e2)]
+				| EParenthesis (ECheckType (e2, (CTPath{tname="Int";tpackage=[]},_)),_) -> expr "EConst" [mk_enum "Constant" "CInt" [e2] (pos e2)]
+				| EParenthesis (ECheckType (e2, (CTPath{tname="Float";tpackage=[]},_)),_) -> expr "EConst" [mk_enum "Constant" "CFloat" [e2] (pos e2)]
 				| _ -> (ECall ((EField ((EField ((EField ((EConst (Ident "haxe"),p),"macro"),p),"Context"),p),"makeExpr"),p),[e; to_pos (pos e)]),p)
 				end
 			| Meta.Dollar "i", _ ->
@@ -462,7 +483,7 @@ let reify in_macro =
 				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]
 	and to_tparam_decl p t =
 		to_obj [
-			"name", to_string t.tp_name p;
+			"name", to_placed_name t.tp_name;
 			"params", (EArrayDecl (List.map (to_tparam_decl p) t.tp_params),p);
 			"constraints", (EArrayDecl (List.map (fun t -> to_ctype t p) t.tp_constraints),p)
 		] p
@@ -474,11 +495,11 @@ let reify in_macro =
 				| HExtern | HPrivate -> ()
 				| HInterface -> interf := true;
 				| HExtends t -> ext := Some (to_tpath t p)
-				| HImplements i -> impl := (to_tpath i p) :: !impl
+				| HImplements i-> impl := (to_tpath i p) :: !impl
 			) d.d_flags;
 			to_obj [
 				"pack", (EArrayDecl [],p);
-				"name", to_string d.d_name p;
+				"name", to_string (fst d.d_name) (pos d.d_name);
 				"pos", to_pos p;
 				"meta", to_meta d.d_meta p;
 				"params", (EArrayDecl (List.map (to_tparam_decl p) d.d_params),p);
@@ -525,10 +546,6 @@ let lower_ident_or_macro = parser
 	| [< '(Kwd Macro,_) >] -> "macro"
 	| [< '(Kwd Extern,_) >] -> "extern"
 
-let any_enum_ident = parser
-	| [< i = ident >] -> i
-	| [< '(Kwd k,p) when Filename.basename p.pfile = "StdTypes.hx" >] -> s_keyword k, p
-
 let property_ident = parser
 	| [< i, _ = ident >] -> i
 	| [< '(Kwd Dynamic,_) >] -> "dynamic"
@@ -591,7 +608,7 @@ and parse_type_decls pack acc s =
 and parse_type_decl s =
 	match s with parser
 	| [< '(Kwd Import,p1) >] -> parse_import s p1
-	| [< '(Kwd Using,p1); t = parse_type_path; p2 = semicolon >] -> EUsing t, punion p1 p2
+	| [< '(Kwd Using,p1) >] -> parse_using s p1
 	| [< doc = get_doc; meta = parse_meta; c = parse_common_flags; s >] ->
 		match s with parser
 		| [< n , p1 = parse_enum_flags; name = type_name; tl = parse_constraint_params; '(BrOpen,_); l = plist parse_enum; '(BrClose,p2) >] ->
@@ -623,9 +640,9 @@ and parse_type_decl s =
 				d_params = tl;
 				d_flags = List.map snd c;
 				d_data = t;
-			}, punion p1 p2)
+			}, punion p1 (pos t))
 		| [< '(Kwd Abstract,p1); name = type_name; tl = parse_constraint_params; st = parse_abstract_subtype; sl = plist parse_abstract_relations; '(BrOpen,_); fl, p2 = parse_class_fields false p1 >] ->
-			let flags = List.map (fun (_,c) -> match c with EPrivate -> APrivAbstract | EExtern -> error (Custom "extern abstract not allowed") p1) c in
+			let flags = List.map (fun (_,c) -> match c with EPrivate -> APrivAbstract | EExtern -> AExtern) c in
 			let flags = (match st with None -> flags | Some t -> AIsType t :: flags) in
 			(EAbstract {
 				d_name = name;
@@ -637,7 +654,7 @@ and parse_type_decl s =
 			},punion p1 p2)
 
 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 -> "" | Some n -> n) in
+	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 >] ->
 		(EClass {
@@ -686,6 +703,29 @@ and parse_import s p1 =
 	) in
 	(EImport (path,mode),punion p1 p2)
 
+and parse_using s p1 =
+	let rec loop acc =
+		match s with parser
+		| [< '(Dot,p) >] ->
+			begin match s with parser
+			| [< '(Const (Ident k),p) >] ->
+				loop ((k,p) :: acc)
+			| [< '(Kwd Macro,p) >] ->
+				loop (("macro",p) :: acc)
+			| [< '(Kwd Extern,p) >] ->
+				loop (("extern",p) :: acc)
+			| [< >] ->
+				serror()
+			end
+		| [< '(Semicolon,p2) >] ->
+			p2,List.rev acc
+	in
+	let p2, path = (match s with parser
+		| [< '(Const (Ident name),p) >] -> loop [name,p]
+		| [< >] -> serror()
+	) in
+	(EUsing path,punion p1 p2)
+
 and parse_abstract_relations s =
 	match s with parser
 	| [< '(Const (Ident "to"),_); t = parse_complex_type >] -> AToType t
@@ -702,7 +742,7 @@ and parse_class_fields tdecl p1 s =
 	let l = parse_class_field_resume tdecl s in
 	let p2 = (match s with parser
 		| [< '(BrClose,p2) >] -> p2
-		| [< >] -> if do_resume() then p1 else serror()
+		| [< >] -> if do_resume() then pos (last_token s) else serror()
 	) in
 	l, p2
 
@@ -821,6 +861,9 @@ and parse_class_flags = parser
 and parse_type_hint = parser
 	| [< '(DblDot,_); t = parse_complex_type >] -> t
 
+and parse_type_hint_with_pos s = match s with parser
+	| [< '(DblDot,p1); t = parse_complex_type >] -> t
+
 and parse_type_opt = parser
 	| [< t = parse_type_hint >] -> Some t
 	| [< >] -> None
@@ -834,59 +877,59 @@ and parse_structural_extension = parser
 		t
 
 and parse_complex_type_inner = parser
-	| [< '(POpen,_); t = parse_complex_type; '(PClose,_) >] -> CTParent t
+	| [< '(POpen,p1); t = parse_complex_type; '(PClose,p2) >] -> CTParent t,punion p1 p2
 	| [< '(BrOpen,p1); s >] ->
 		(match s with parser
-		| [< l = parse_type_anonymous false >] -> CTAnonymous l
+		| [< l,p2 = parse_type_anonymous false >] -> CTAnonymous l,punion p1 p2
 		| [< t = parse_structural_extension; s>] ->
 			let tl = t :: plist parse_structural_extension s in
 			(match s with parser
-			| [< l = parse_type_anonymous false >] -> CTExtend (tl,l)
-			| [< l, _ = parse_class_fields true p1 >] -> CTExtend (tl,l))
-		| [< l, _ = parse_class_fields true p1 >] -> CTAnonymous l
+			| [< l,p2 = parse_type_anonymous false >] -> CTExtend (tl,l),punion p1 p2
+			| [< l,p2 = parse_class_fields true p1 >] -> CTExtend (tl,l),punion p1 p2)
+		| [< l,p2 = parse_class_fields true p1 >] -> CTAnonymous l,punion p1 p2
 		| [< >] -> serror())
-	| [< '(Question,_); t = parse_complex_type_inner >] ->
-		CTOptional t
-	| [< t = parse_type_path >] ->
-		CTPath t
+	| [< '(Question,p1); t,p2 = parse_complex_type_inner >] ->
+		CTOptional (t,p2),punion p1 p2
+	| [< t,p = parse_type_path >] ->
+		CTPath t,p
 
-and parse_type_path s = parse_type_path1 [] s
+and parse_type_path s = parse_type_path1 None [] s
 
-and parse_type_path1 pack = parser
-	| [< name, p = dollar_ident_macro pack; s >] ->
+and parse_type_path1 p0 pack = parser
+	| [< name, p1 = dollar_ident_macro pack; s >] ->
 		if is_lower_ident name then
 			(match s with parser
 			| [< '(Dot,p) >] ->
 				if is_resuming p then
 					raise (TypePath (List.rev (name :: pack),None,false))
 				else
-					parse_type_path1 (name :: pack) s
+					parse_type_path1 (match p0 with None -> Some p1 | Some _ -> p0) (name :: pack) s
 			| [< '(Semicolon,_) >] ->
-				error (Custom "Type name should start with an uppercase letter") p
+				error (Custom "Type name should start with an uppercase letter") p1
 			| [< >] -> serror())
 		else
-			let sub = (match s with parser
+			let sub,p2 = (match s with parser
 				| [< '(Dot,p); s >] ->
 					(if is_resuming p then
 						raise (TypePath (List.rev pack,Some (name,false),false))
 					else match s with parser
-						| [< '(Const (Ident name),_) when not (is_lower_ident name) >] -> Some name
+						| [< '(Const (Ident name),p2) when not (is_lower_ident name) >] -> Some name,p2
 						| [< '(Binop OpOr,_) when do_resume() >] ->
 							set_resume p;
 							raise (TypePath (List.rev pack,Some (name,false),false))
 						| [< >] -> serror())
-				| [< >] -> None
+				| [< >] -> None,p1
 			) in
-			let params = (match s with parser
-				| [< '(Binop OpLt,_); l = psep Comma parse_type_path_or_const; '(Binop OpGt,_) >] -> l
-				| [< >] -> []
+			let params,p2 = (match s with parser
+				| [< '(Binop OpLt,_); l = psep Comma parse_type_path_or_const; '(Binop OpGt,p2) >] -> l,p2
+				| [< >] -> [],p2
 			) in
 			{
 				tpackage = List.rev pack;
 				tname = name;
 				tparams = params;
 				tsub = sub;
-			}
+			},punion (match p0 with None -> p1 | Some p -> p) p2
 	| [< '(Binop OpOr,_) when do_resume() >] ->
 		raise (TypePath (List.rev pack,None,false))
 
@@ -895,8 +938,8 @@ and type_name = parser
 		if is_lower_ident name then
 			error (Custom "Type name should start with an uppercase letter") p
 		else
-			name
-	| [< '(Dollar name,_) >] -> "$" ^ name
+			name,p
+	| [< '(Dollar name,p) >] -> "$" ^ name,p
 
 and parse_type_path_or_const = parser
 	(* we can't allow (expr) here *)
@@ -906,21 +949,21 @@ and parse_type_path_or_const = parser
 	| [< e = expr >] -> TPExpr e
 	| [< >] -> serror()
 
-and parse_complex_type_next t = parser
-	| [< '(Arrow,_); t2 = parse_complex_type >] ->
+and parse_complex_type_next (t : type_hint) = parser
+	| [< '(Arrow,_); t2,p2 = parse_complex_type >] ->
 		(match t2 with
 		| CTFunction (args,r) ->
-			CTFunction (t :: args,r)
+			CTFunction (t :: args,r),punion (pos t) p2
 		| _ ->
-			CTFunction ([t] , t2))
+			CTFunction ([t] , (t2,p2)),punion (pos t) p2)
 	| [< >] -> t
 
 and parse_type_anonymous opt = parser
 	| [< '(Question,_) when not opt; s >] -> parse_type_anonymous true s
-	| [< name, p1 = ident; t = parse_type_hint; s >] ->
+	| [< name, p1 = ident; t = parse_type_hint_with_pos; s >] ->
 		let next p2 acc =
 			{
-				cff_name = name;
+				cff_name = name,p1;
 				cff_meta = if opt then [Meta.Optional,[],p1] else [];
 				cff_access = [];
 				cff_doc = None;
@@ -929,11 +972,11 @@ and parse_type_anonymous opt = parser
 			} :: acc
 		in
 		match s with parser
-		| [< '(BrClose,p2) >] -> next p2 []
+		| [< '(BrClose,p2) >] -> next p2 [],p2
 		| [< '(Comma,p2) >] ->
 			(match s with parser
-			| [< '(BrClose,_) >] -> next p2 []
-			| [< l = parse_type_anonymous false >] -> next p2 l
+			| [< '(BrClose,p2) >] -> next p2 [],p2
+			| [< l,p2 = parse_type_anonymous false >] -> next p2 l,punion p1 p2
 			| [< >] -> serror());
 		| [< >] -> serror()
 
@@ -941,18 +984,18 @@ and parse_enum s =
 	let doc = get_doc s in
 	let meta = parse_meta s in
 	match s with parser
-	| [< name, p1 = any_enum_ident; params = parse_constraint_params; s >] ->
+	| [< name, p1 = ident; params = parse_constraint_params; s >] ->
 		let args = (match s with parser
 		| [< '(POpen,_); l = psep Comma parse_enum_param; '(PClose,_) >] -> l
 		| [< >] -> []
 		) in
-		let t = parse_type_opt s in
+		let t = popt parse_type_hint_with_pos s in
 		let p2 = (match s with parser
 			| [< p = semicolon >] -> p
 			| [< >] -> serror()
 		) in
 		{
-			ec_name = name;
+			ec_name = name,p1;
 			ec_doc = doc;
 			ec_meta = meta;
 			ec_args = args;
@@ -962,32 +1005,32 @@ and parse_enum s =
 		}
 
 and parse_enum_param = parser
-	| [< '(Question,_); name, _ = ident; t = parse_type_hint >] -> (name,true,t)
-	| [< name, _ = ident; t = parse_type_hint >] -> (name,false,t)
+	| [< '(Question,_); name, _ = ident; t = parse_type_hint_with_pos >] -> (name,true,t)
+	| [< name, _ = ident; t = parse_type_hint_with_pos >] -> (name,false,t)
 
 and parse_class_field s =
 	let doc = get_doc s in
 	match s with parser
 	| [< meta = parse_meta; al = parse_cf_rights true []; s >] ->
 		let name, pos, k = (match s with parser
-		| [< '(Kwd Var,p1); name, _ = dollar_ident; s >] ->
+		| [< '(Kwd Var,p1); name = dollar_ident; s >] ->
 			(match s with parser
 			| [< '(POpen,_); i1 = property_ident; '(Comma,_); i2 = property_ident; '(PClose,_) >] ->
-				let t = parse_type_opt s in
+				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
 				| [< >] -> serror()
 				) in
 				name, punion p1 p2, FProp (i1,i2,t, e)
-			| [< t = parse_type_opt; s >] ->
+			| [< 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
 				| [< >] -> serror()
 				) in
 				name, punion p1 p2, FVar (t,e))
-		| [< '(Kwd Function,p1); name = parse_fun_name; pl = parse_constraint_params; '(POpen,_); al = psep Comma parse_fun_param; '(PClose,_); t = parse_type_opt; s >] ->
+		| [< '(Kwd Function,p1); name = parse_fun_name; pl = parse_constraint_params; '(POpen,_); al = psep Comma parse_fun_param; '(PClose,_); t = popt parse_type_hint_with_pos; s >] ->
 			let e, p2 = (match s with parser
 				| [< e = toplevel_expr; s >] ->
 					(try ignore(semicolon s) with Error (Missing_semicolon,p) -> !display_error Missing_semicolon p);
@@ -1025,12 +1068,14 @@ and parse_cf_rights allow_static l = parser
 	| [< >] -> l
 
 and parse_fun_name = parser
-	| [< name,_ = dollar_ident >] -> name
-	| [< '(Kwd New,_) >] -> "new"
+	| [< name,p = dollar_ident >] -> name,p
+	| [< '(Kwd New,p) >] -> "new",p
 
-and parse_fun_param = parser
-	| [< '(Question,_); name, _ = dollar_ident; t = parse_type_opt; c = parse_fun_param_value >] -> (name,true,t,c)
-	| [< name, _ = dollar_ident; t = parse_type_opt; c = parse_fun_param_value >] -> (name,false,t,c)
+and parse_fun_param s =
+	let meta = parse_meta s in
+	match s with parser
+	| [< '(Question,_); name, pn = dollar_ident; t = popt parse_type_hint_with_pos; c = parse_fun_param_value >] -> ((name,pn),true,meta,t,c)
+	| [< name, pn = dollar_ident; t = popt parse_type_hint_with_pos; c = parse_fun_param_value >] -> ((name,pn),false,meta,t,c)
 
 and parse_fun_param_value = parser
 	| [< '(Binop OpAssign,_); e = toplevel_expr >] -> Some e
@@ -1045,7 +1090,7 @@ and parse_constraint_params = parser
 	| [< >] -> []
 
 and parse_constraint_param = parser
-	| [< name = type_name; s >] ->
+	| [< meta = parse_meta; name = type_name; s >] ->
 		let params = (match s with parser
 			| [< >] -> []
 		) in
@@ -1061,6 +1106,7 @@ and parse_constraint_param = parser
 			tp_name = name;
 			tp_params = params;
 			tp_constraints = ctl;
+			tp_meta = meta;
 		}
 
 and parse_class_herit = parser
@@ -1125,7 +1171,7 @@ and parse_array_decl = parser
 		[]
 
 and parse_var_decl_head = parser
-	| [< name, _ = dollar_ident; t = parse_type_opt >] -> (name,t)
+	| [< name, p = dollar_ident; t = popt parse_type_hint_with_pos >] -> (name,t,p)
 
 and parse_var_assignment = parser
 	| [< '(Binop OpAssign,p1); s >] ->
@@ -1136,12 +1182,12 @@ and parse_var_assignment = parser
 	| [< >] -> None
 
 and parse_var_decls_next vl = parser
-	| [< '(Comma,p1); name,t = parse_var_decl_head; s >] ->
+	| [< '(Comma,p1); name,t,pn = parse_var_decl_head; s >] ->
 		begin try
 			let eo = parse_var_assignment s in
-			parse_var_decls_next ((name,t,eo) :: vl) s
+			parse_var_decls_next (((name,pn),t,eo) :: vl) s
 		with Display e ->
-			let v = (name,t,Some e) in
+			let v = ((name,pn),t,Some e) in
 			let e = (EVars(List.rev (v :: vl)),punion p1 (pos e)) in
 			display e
 		end
@@ -1149,13 +1195,13 @@ and parse_var_decls_next vl = parser
 		vl
 
 and parse_var_decls p1 = parser
-	| [< name,t = parse_var_decl_head; s >] ->
+	| [< name,t,pn = parse_var_decl_head; s >] ->
 		let eo = parse_var_assignment s in
-		List.rev (parse_var_decls_next [name,t,eo] s)
+		List.rev (parse_var_decls_next [(name,pn),t,eo] s)
 	| [< s >] -> error (Custom "Missing variable identifier") p1
 
 and parse_var_decl = parser
-	| [< name,t = parse_var_decl_head; eo = parse_var_assignment >] -> (name,t,eo)
+	| [< name,t,pn = parse_var_decl_head; eo = parse_var_assignment >] -> ((name,pn),t,eo)
 
 and inline_function = parser
 	| [< '(Kwd Inline,_); '(Kwd Function,p1) >] -> true, p1
@@ -1164,23 +1210,23 @@ and inline_function = parser
 and reify_expr e =
 	let to_expr,_,_ = reify !in_macro in
 	let e = to_expr e in
-	(ECheckType (e,(CTPath { tpackage = ["haxe";"macro"]; tname = "Expr"; tsub = None; tparams = [] })),pos e)
+	(ECheckType (e,(CTPath { tpackage = ["haxe";"macro"]; tname = "Expr"; tsub = None; tparams = [] },null_pos)),pos e)
 
 and parse_macro_expr p = parser
 	| [< '(DblDot,_); t = parse_complex_type >] ->
 		let _, to_type, _  = reify !in_macro in
 		let t = to_type t p in
-		(ECheckType (t,(CTPath { tpackage = ["haxe";"macro"]; tname = "Expr"; tsub = Some "ComplexType"; tparams = [] })),p)
+		(ECheckType (t,(CTPath { tpackage = ["haxe";"macro"]; tname = "Expr"; tsub = Some "ComplexType"; tparams = [] },null_pos)),p)
 	| [< '(Kwd Var,p1); vl = psep Comma parse_var_decl >] ->
 		reify_expr (EVars vl,p1)
 	| [< d = parse_class None [] [] false >] ->
 		let _,_,to_type = reify !in_macro in
-		(ECheckType (to_type d,(CTPath { tpackage = ["haxe";"macro"]; tname = "Expr"; tsub = Some "TypeDefinition"; tparams = [] })),p)
+		(ECheckType (to_type d,(CTPath { tpackage = ["haxe";"macro"]; tname = "Expr"; tsub = Some "TypeDefinition"; tparams = [] },null_pos)),p)
 	| [< e = secure_expr >] ->
 		reify_expr e
 
 and parse_function p1 inl = parser
-	| [< name = popt dollar_ident; pl = parse_constraint_params; '(POpen,_); al = psep Comma parse_fun_param; '(PClose,_); t = parse_type_opt; s >] ->
+	| [< name = popt dollar_ident; pl = parse_constraint_params; '(POpen,_); al = psep Comma parse_fun_param; '(PClose,_); t = popt parse_type_hint_with_pos; s >] ->
 		let make e =
 			let f = {
 				f_params = pl;
@@ -1201,11 +1247,26 @@ and expr = parser
 			make_meta name params (secure_expr s) p
 		with Display e ->
 			display (make_meta name params e p))
-	| [< '(BrOpen,p1); b = block1; '(BrClose,p2); s >] ->
-		let e = (b,punion p1 p2) in
-		(match b with
-		| EObjectDecl _ -> expr_next e s
-		| _ -> e)
+	| [< '(BrOpen,p1); s >] ->
+		if is_resuming p1 then display (EDisplay ((EObjectDecl [],p1),false),p1);
+		(match s with parser
+		| [< '(Binop OpOr,p2) when do_resume() >] ->
+			set_resume p1;
+			display (EDisplay ((EObjectDecl [],p1),false),p1);
+		| [< b = block1; s >] ->
+			let p2 = match s with 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()
+			in
+			let e = (b,punion p1 p2) in
+			(match b with
+			| EObjectDecl _ -> expr_next e s
+			| _ -> e))
 	| [< '(Kwd Macro,p); s >] ->
 		parse_macro_expr p s
 	| [< '(Kwd Var,p1); v = parse_var_decl >] -> (EVars [v],p1)
@@ -1218,10 +1279,13 @@ and expr = parser
 		(match s with parser
 		| [< '(POpen,pp); e = expr; s >] ->
 			(match s with parser
-			| [< '(Comma,_); t = parse_complex_type; '(PClose,p2); s >] -> expr_next (ECast (e,Some t),punion p1 p2) s
-			| [< t = parse_type_hint; '(PClose,p2); s >] ->
-				let ep = EParenthesis (ECheckType(e,t),punion p1 p2), punion p1 p2 in
+			| [< '(Comma,pc); t = parse_complex_type; '(PClose,p2); s >] -> expr_next (ECast (e,Some t),punion p1 p2) s
+			| [< 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
+				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
 				expr_next (ECast (ep,None),punion p1 (pos ep)) s
@@ -1235,7 +1299,8 @@ and expr = parser
 		| [< >] -> serror())
 	| [< '(POpen,p1); e = expr; s >] -> (match s with parser
 		| [< '(PClose,p2); s >] -> expr_next (EParenthesis e, punion p1 p2) s
-		| [< t = parse_type_hint; '(PClose,p2); s >] -> expr_next (EParenthesis (ECheckType(e,t),punion p1 p2), 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
 		| [< >] -> 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
@@ -1299,6 +1364,7 @@ and expr_next e1 = parser
 		if is_resuming p then display (EDisplay (e1,false),p);
 		(match s with parser
 		| [< '(Kwd Macro,p2) when p.pmax = p2.pmin; s >] -> expr_next (EField (e1,"macro") , punion (pos e1) p2) s
+		| [< '(Kwd Extern,p2) when p.pmax = p2.pmin; s >] -> expr_next (EField (e1,"extern") , punion (pos e1) p2) s
 		| [< '(Kwd New,p2) when p.pmax = p2.pmin; s >] -> expr_next (EField (e1,"new") , punion (pos e1) p2) s
 		| [< '(Const (Ident f),p2) when p.pmax = p2.pmin; s >] -> expr_next (EField (e1,f) , punion (pos e1) p2) s
 		| [< '(Dollar v,p2); s >] -> expr_next (EField (e1,"$"^v) , punion (pos e1) p2) s
@@ -1378,13 +1444,13 @@ and parse_switch_cases eswitch cases = parser
 		List.rev cases , None
 
 and parse_catch etry = parser
-	| [< '(Kwd Catch,p); '(POpen,_); name, _ = dollar_ident; s >] ->
+	| [< '(Kwd Catch,p); '(POpen,_); name, pn = dollar_ident; s >] ->
 		match s with parser
-		| [< t = parse_type_hint; '(PClose,_); s >] ->
+		| [< t,pt = parse_type_hint_with_pos; '(PClose,_); s >] ->
 			(try
-				(name,t,secure_expr s)
+				((name,pn),(t,pt),secure_expr s)
 			with
-				Display e -> display (ETry (etry,[name,t,e]),punion (pos etry) (pos e)))
+				Display e -> display (ETry (etry,[(name,pn),(t,pt),e]),punion (pos etry) (pos e)))
 		| [< '(_,p) >] -> error Missing_type p
 
 and parse_call_params ec s =

+ 229 - 158
common.ml → src/typing/common.ml

@@ -1,23 +1,20 @@
 (*
- * Copyright (C)2005-2013 Haxe Foundation
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
+	The 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
@@ -50,6 +47,7 @@ type stats = {
 type platform =
 	| Cross
 	| Js
+	| Lua
 	| Neko
 	| Flash
 	| Php
@@ -57,6 +55,7 @@ type platform =
 	| Cs
 	| Java
 	| Python
+	| Hl
 
 (**
 	The capture policy tells which handling we make of captured locals
@@ -77,12 +76,6 @@ type platform_config = {
 	pf_static : bool;
 	(** has access to the "sys" package *)
 	pf_sys : bool;
-	(** local variables are block-scoped *)
-	pf_locals_scope : bool;
-	(** captured local variables are scoped *)
-	pf_captured_scope : bool;
-	(** generated locals must be absolutely unique wrt the current function *)
-	pf_unique_locals : bool;
 	(** captured variables handling (see before) *)
 	pf_capture_policy : capture_policy;
 	(** when calling a method with optional args, do we replace the missing args with "null" constants *)
@@ -91,8 +84,6 @@ type platform_config = {
 	pf_add_final_return : bool;
 	(** does the platform natively support overloaded functions *)
 	pf_overload : bool;
-	(** does the platform generator handle pattern matching *)
-	pf_pattern_matching : bool;
 	(** can the platform use default values for non-nullable arguments *)
 	pf_can_skip_non_nullable_argument : bool;
 	(** type paths that are reserved on the platform *)
@@ -107,11 +98,68 @@ type display_mode =
 	| DMToplevel
 	| DMResolve of string
 	| DMType
+	| DMModuleSymbols
+	| DMDiagnostics
+
+type compiler_callback = {
+	mutable after_typing : (module_type list -> unit) list;
+	mutable before_dce : (unit -> unit) list;
+	mutable after_generation : (unit -> unit) list;
+}
+
+module IdentifierType = struct
+	type t =
+		| ITLocal of tvar
+		| ITMember of tclass * tclass_field
+		| ITStatic of tclass * tclass_field
+		| ITEnum of tenum * tenum_field
+		| ITGlobal of module_type * string * Type.t
+		| ITType of module_type
+		| ITPackage of string
+
+	let get_name = function
+		| ITLocal v -> v.v_name
+		| ITMember(_,cf) | ITStatic(_,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
+end
+
+type shared_display_information = {
+	mutable import_positions : (pos,bool ref) PMap.t;
+	mutable diagnostics_messages : (string * pos * DiagnosticsSeverity.t) list;
+}
+
+type display_information = {
+	mutable unresolved_identifiers : (string * pos * (string * IdentifierType.t) list) list;
+}
+
+(* This information is shared between normal and macro context. *)
+type shared_context = {
+	shared_display_information : shared_display_information;
+}
 
 type context = {
 	(* config *)
 	version : int;
 	args : string list;
+	shared : shared_context;
+	display_information : display_information;
 	mutable sys_args : string list;
 	mutable display : display_mode;
 	mutable debug : bool;
@@ -127,13 +175,14 @@ type context = {
 	mutable error : string -> pos -> unit;
 	mutable warning : string -> pos -> unit;
 	mutable load_extern_type : (path -> pos -> (string * Ast.package) option) list; (* allow finding types which are not in sources *)
-	mutable filters : (unit -> unit) list;
-	mutable final_filters : (unit -> unit) list;
+	callbacks : compiler_callback;
 	mutable defines_signature : string option;
 	mutable print : string -> unit;
 	mutable get_macros : unit -> context option;
 	mutable run_command : string -> int;
 	file_lookup_cache : (string,string option) Hashtbl.t;
+	parser_cache : (string,(type_def * pos) list) Hashtbl.t;
+	cached_macros : (path * string,((string * bool * t) list * t * tclass * Type.tclass_field)) Hashtbl.t;
 	mutable stored_typed_exprs : (int, texpr) PMap.t;
 	(* output *)
 	mutable file : string;
@@ -144,6 +193,7 @@ type context = {
 	mutable types : Type.module_type list;
 	mutable resources : (string,string) Hashtbl.t;
 	mutable neko_libs : string list;
+	mutable include_files : (string * string) list;
 	mutable php_front : string option;
 	mutable php_lib : string option;
 	mutable php_prefix : string option;
@@ -163,12 +213,21 @@ exception Abort of string * Ast.pos
 
 let display_default = ref 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 global_cache : cache option ref = ref None
+
 module Define = struct
 
 	type strict_defined =
 		| AbsolutePath
 		| AdvancedTelemetry
-		| Analyzer
+		| AnnotateSource
+		(* | Analyzer *)
 		| As3
 		| CheckXmlProxy
 		| CoreApi
@@ -178,13 +237,16 @@ module Define = struct
 		| DceDebug
 		| Debug
 		| Display
+		| DisplayStdin
 		| DllExport
 		| DllImport
 		| DocGen
 		| Dump
 		| DumpDependencies
 		| DumpIgnoreVarIds
+		| DynamicInterfaceClosures
 		| EraseGenerics
+		| FastCast
 		| Fdb
 		| FileExtension
 		| FlashStrict
@@ -199,11 +261,14 @@ module Define = struct
 		| IncludePrefix
 		| Interp
 		| JavaVer
+		| JqueryVer
 		| JsClassic
-		| JsEs5
+		| JsEs
 		| JsUnflatten
 		| KeepOldOutput
 		| LoopUnrollMaxCost
+		| LuaVer
+		| LuaJit
 		| Macro
 		| MacroTimes
 		| NekoSource
@@ -211,6 +276,7 @@ module Define = struct
 		| NetworkSandbox
 		| NetVer
 		| NetTarget
+		| NoAnalyzer
 		| NoCompilation
 		| NoCOpt
 		| NoDeprecationWarnings
@@ -220,14 +286,15 @@ module Define = struct
 		| NoOpt
 		| NoPatternMatching
 		| NoRoot
-		| NoSimplify
 		| NoSwfCompress
 		| NoTraces
+		| Objc
 		| PhpPrefix
 		| RealPosition
 		| ReplaceFiles
 		| Scriptable
 		| ShallowExpose
+		| SourceHeader
 		| SourceMapContent
 		| Swc
 		| SwfCompressLevel
@@ -250,23 +317,27 @@ module Define = struct
 	let infos = function
 		| AbsolutePath -> ("absolute_path","Print absolute file path in trace output")
 		| AdvancedTelemetry -> ("advanced-telemetry","Allow the SWF to be measured with Monocle tool")
-		| Analyzer -> ("analyzer","Use static analyzer for optimization (experimental)")
+		| AnnotateSource -> ("annotate_source","Add additional comments to generated source code")
+		(* | Analyzer -> ("analyzer","Use static analyzer for optimization (experimental)") *)
 		| As3 -> ("as3","Defined when outputing flash9 as3 source code")
 		| CheckXmlProxy -> ("check_xml_proxy","Check the used fields of the xml proxy")
 		| CoreApi -> ("core_api","Defined in the core api context")
-		| CoreApiSerialize -> ("core_api_serialize","Sets so some generated core api classes be marked with the Serializable attribute on C#")
-		| Cppia -> ("cppia", "Generate experimental cpp instruction assembly")
-		| Dce -> ("dce","The current DCE mode")
+		| CoreApiSerialize -> ("core_api_serialize","Mark some generated core api classes with the Serializable attribute on C#")
+		| Cppia -> ("cppia", "Generate cpp instruction assembly")
+		| Dce -> ("dce","<mode:std|full|no> Set the dead code elimination mode (default std)")
 		| DceDebug -> ("dce_debug","Show DCE log")
 		| Debug -> ("debug","Activated when compiling with -debug")
 		| Display -> ("display","Activated during completion")
+		| DisplayStdin -> ("display_stdin","Read the contents of a file specified in --display from standard input")
 		| DllExport -> ("dll_export", "GenCPP experimental linking")
 		| DllImport -> ("dll_import", "GenCPP experimental linking")
 		| DocGen -> ("doc_gen","Do not perform any removal/change in order to correctly generate documentation")
-		| Dump -> ("dump","Dump the complete typed AST for internal debugging")
-		| DumpDependencies -> ("dump_dependencies","Dump the classes dependencies")
-		| DumpIgnoreVarIds -> ("dump_ignore_var_ids","Dump files do not contain variable IDs (helps with diff)")
+		| Dump -> ("dump","<mode:pretty|record|legacy> Dump typed AST in dump subdirectory using specified mode or non-prettified default")
+		| DumpDependencies -> ("dump_dependencies","Dump the classes dependencies in a dump subdirectory")
+		| DumpIgnoreVarIds -> ("dump_ignore_var_ids","Remove variable IDs from non-pretty dumps (helps with diff)")
+		| DynamicInterfaceClosures -> ("dynamic_interface_closures","Use slow path for interface closures to save space")
 		| EraseGenerics -> ("erase_generics","Erase generic classes on C#")
+		| FastCast -> ("fast_cast","Enables an experimental casts cleanup on C# and Java")
 		| Fdb -> ("fdb","Enable full flash debug infos for FDB interactive debugging")
 		| FileExtension -> ("file_extension","Output filename extension for cpp source code")
 		| FlashStrict -> ("flash_strict","More strict typing for flash target")
@@ -282,18 +353,22 @@ module Define = struct
 		| 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")
+		| JqueryVer -> ("jquery_ver", "The jQuery version supported by js.jquery.*. The version is encoded as an interger. e.g. 1.11.3 is encoded as 11103")
 		| JsClassic -> ("js_classic","Don't use a function wrapper and strict mode in JS output")
-		| JsEs5 -> ("js_es5","Generate JS for ES5-compliant runtimes")
+		| JsEs -> ("js_es","Generate JS compilant with given ES standard version (default 5)")
 		| JsUnflatten -> ("js_unflatten","Generate nested objects for packages and types")
 		| 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)")
-		| Macro -> ("macro","Defined when we compile code in the macro context")
+		| 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")
 		| 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")
 		| NekoSource -> ("neko_source","Output neko source instead of bytecode")
 		| NekoV1 -> ("neko_v1","Keep Neko 1.x compatibility")
 		| NetworkSandbox -> ("network-sandbox","Use local network sandbox instead of local file access one")
+		| NoAnalyzer -> ("no-analyzer","Disable the static analyzer")
 		| NoCompilation -> ("no-compilation","Disable final compilation for Cs, Cpp and Java")
 		| NoCOpt -> ("no_copt","Disable completion optimization (for debug purposes)")
 		| NoDebug -> ("no_debug","Remove all debug macros from cpp output")
@@ -304,21 +379,22 @@ module Define = struct
 		| NoInline -> ("no_inline","Disable inlining")
 		| NoRoot -> ("no_root","Generate top-level types into haxe.root namespace")
 		| NoMacroCache -> ("no_macro_cache","Disable macro context caching")
-		| NoSimplify -> "no_simplify",("Disable simplification filter")
 		| NoSwfCompress -> ("no_swf_compress","Disable SWF output compression")
 		| NoTraces -> ("no_traces","Disable all trace calls")
+		| Objc -> ("objc","Sets the hxcpp output to objective-c++ classes. Must be defined for interop")
 		| PhpPrefix -> ("php_prefix","Compiled with --php-prefix")
-		| RealPosition -> ("real_position","Disables haxe source mapping when targetting C#")
+		| RealPosition -> ("real_position","Disables Haxe source mapping when targetting C#, removes position comments in Java output")
 		| ReplaceFiles -> ("replace_files","GenCommon internal")
 		| Scriptable -> ("scriptable","GenCPP internal")
 		| ShallowExpose -> ("shallow-expose","Expose types to surrounding scope of Haxe generated closure without writing to window object")
+		| SourceHeader -> ("source-header","Print value as comment on top of generated files, use '' value to disable")
 		| SourceMapContent -> ("source-map-content","Include the hx sources as part of the JS source map")
 		| Swc -> ("swc","Output a SWC instead of a SWF")
 		| SwfCompressLevel -> ("swf_compress_level","<level:1-9> Set the amount of compression for the SWF output")
 		| SwfDebugPassword -> ("swf_debug_password", "Set a password for debugging")
 		| SwfDirectBlit -> ("swf_direct_blit", "Use hardware acceleration to blit graphics")
 		| SwfGpu -> ("swf_gpu", "Use GPU compositing features when drawing graphics")
-		| SwfMetadata -> ("swf_metadata", "=<file> Include contents of <file> as metadata in the swf")
+		| SwfMetadata -> ("swf_metadata", "<file> Include contents of <file> as metadata in the swf")
 		| SwfPreloaderFrame -> ("swf_preloader_frame", "Insert empty first frame in swf")
 		| SwfProtected -> ("swf_protected","Compile Haxe private as protected in the SWF instead of public")
 		| SwfScriptTimeout -> ("swf_script_timeout", "Maximum ActionScript processing time before script stuck dialog box displays (in seconds)")
@@ -342,6 +418,7 @@ module MetaInfo = struct
 		| TTypedef
 		| TAnyField
 		| TExpr
+		| TTypeParameter
 
 	type meta_parameter =
 		| HasParam of string
@@ -361,6 +438,7 @@ module MetaInfo = struct
 		| 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])
@@ -372,6 +450,7 @@ module MetaInfo = struct
 		| 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])
@@ -384,10 +463,11 @@ module MetaInfo = struct
 		| DefParam -> ":defParam",("?",[])
 		| Delegate -> ":delegate",("Automatically added by -net-lib on delegates",[Platform Cs; UsedOn TAbstract])
 		| Depend -> ":depend",("",[Platform Cpp])
-		| Deprecated -> ":deprecated",("Automatically added by -java-lib on class fields annotated with @Deprecated annotation. Has no effect on types compiled by Haxe",[Platform Java; UsedOnEither [TClass;TEnum;TClassField]])
+		| 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])
-		| Enum -> ":enum",("Used internally to annotate a class that was generated from an enum",[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])
@@ -395,12 +475,15 @@ module MetaInfo = struct
 		| 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",("",[Platform Cpp])
+		| 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])
@@ -425,6 +508,7 @@ module MetaInfo = struct
 		| 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])
@@ -439,6 +523,7 @@ module MetaInfo = struct
 		| 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",[])
@@ -451,27 +536,33 @@ module MetaInfo = struct
 		| 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]) *)
+		| 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])
@@ -480,7 +571,10 @@ module MetaInfo = struct
 		| 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])
@@ -534,14 +628,10 @@ let default_config =
 	{
 		pf_static = true;
 		pf_sys = true;
-		pf_locals_scope = true;
-		pf_captured_scope = true;
-		pf_unique_locals = false;
 		pf_capture_policy = CPNone;
 		pf_pad_nulls = false;
 		pf_add_final_return = false;
 		pf_overload = false;
-		pf_pattern_matching = false;
 		pf_can_skip_non_nullable_argument = true;
 		pf_reserved_type_paths = [];
 	}
@@ -553,147 +643,109 @@ let get_config com =
 		default_config
 	| Js ->
 		{
+			default_config with
 			pf_static = false;
 			pf_sys = false;
-			pf_locals_scope = false;
-			pf_captured_scope = false;
-			pf_unique_locals = false;
 			pf_capture_policy = CPLoopVars;
-			pf_pad_nulls = false;
-			pf_add_final_return = false;
-			pf_overload = false;
-			pf_pattern_matching = false;
-			pf_can_skip_non_nullable_argument = true;
 			pf_reserved_type_paths = [([],"Object");([],"Error")];
 		}
+	| Lua ->
+		{
+			default_config with
+			pf_static = false;
+			pf_capture_policy = CPLoopVars;
+		}
 	| Neko ->
 		{
+			default_config with
 			pf_static = false;
-			pf_sys = true;
-			pf_locals_scope = true;
-			pf_captured_scope = true;
-			pf_unique_locals = false;
-			pf_capture_policy = CPNone;
 			pf_pad_nulls = true;
-			pf_add_final_return = false;
-			pf_overload = false;
-			pf_pattern_matching = false;
-			pf_can_skip_non_nullable_argument = true;
-			pf_reserved_type_paths = [];
 		}
 	| Flash when defined Define.As3 ->
 		{
-			pf_static = true;
+			default_config with
 			pf_sys = false;
-			pf_locals_scope = false;
-			pf_captured_scope = true;
-			pf_unique_locals = true;
 			pf_capture_policy = CPLoopVars;
-			pf_pad_nulls = false;
 			pf_add_final_return = true;
-			pf_overload = false;
-			pf_pattern_matching = false;
 			pf_can_skip_non_nullable_argument = false;
-			pf_reserved_type_paths = [];
 		}
 	| Flash ->
 		{
-			pf_static = true;
+			default_config with
 			pf_sys = false;
-			pf_locals_scope = true;
-			pf_captured_scope = true; (* handled by genSwf9 *)
-			pf_unique_locals = false;
 			pf_capture_policy = CPLoopVars;
-			pf_pad_nulls = false;
-			pf_add_final_return = false;
-			pf_overload = false;
-			pf_pattern_matching = false;
 			pf_can_skip_non_nullable_argument = false;
 			pf_reserved_type_paths = [([],"Object");([],"Error")];
 		}
 	| Php ->
 		{
+			default_config with
 			pf_static = false;
-			pf_sys = true;
-			pf_locals_scope = false; (* some duplicate work is done in genPhp *)
-			pf_captured_scope = false;
-			pf_unique_locals = false;
-			pf_capture_policy = CPNone;
 			pf_pad_nulls = true;
-			pf_add_final_return = false;
-			pf_overload = false;
-			pf_pattern_matching = false;
-			pf_can_skip_non_nullable_argument = true;
-			pf_reserved_type_paths = [];
 		}
 	| Cpp ->
 		{
-			pf_static = true;
-			pf_sys = true;
-			pf_locals_scope = true;
-			pf_captured_scope = true;
-			pf_unique_locals = false;
+			default_config with
 			pf_capture_policy = CPWrapRef;
 			pf_pad_nulls = true;
 			pf_add_final_return = true;
-			pf_overload = false;
-			pf_pattern_matching = false;
-			pf_can_skip_non_nullable_argument = true;
-			pf_reserved_type_paths = [];
 		}
 	| Cs ->
 		{
-			pf_static = true;
-			pf_sys = true;
-			pf_locals_scope = false;
-			pf_captured_scope = true;
-			pf_unique_locals = true;
+			default_config with
 			pf_capture_policy = CPWrapRef;
 			pf_pad_nulls = true;
-			pf_add_final_return = false;
 			pf_overload = true;
-			pf_pattern_matching = false;
-			pf_can_skip_non_nullable_argument = true;
-			pf_reserved_type_paths = [];
 		}
 	| Java ->
 		{
-			pf_static = true;
-			pf_sys = true;
-			pf_locals_scope = false;
-			pf_captured_scope = true;
-			pf_unique_locals = false;
+			default_config with
 			pf_capture_policy = CPWrapRef;
 			pf_pad_nulls = true;
-			pf_add_final_return = false;
 			pf_overload = true;
-			pf_pattern_matching = false;
-			pf_can_skip_non_nullable_argument = true;
-			pf_reserved_type_paths = [];
 		}
 	| Python ->
 		{
+			default_config with
 			pf_static = false;
-			pf_sys = true;
-			pf_locals_scope = false;
-			pf_captured_scope = false;
-			pf_unique_locals = false;
 			pf_capture_policy = CPLoopVars;
-			pf_pad_nulls = false;
-			pf_add_final_return = false;
-			pf_overload = false;
-			pf_pattern_matching = false;
-			pf_can_skip_non_nullable_argument = true;
-			pf_reserved_type_paths = [];
+		}
+	| Hl ->
+		{
+			default_config with
+			pf_capture_policy = CPWrapRef;
+			pf_pad_nulls = true;
+			pf_can_skip_non_nullable_argument = false;
 		}
 
 let memory_marker = [|Unix.time()|]
 
-let create v args =
+let create_callbacks () =
+	{
+		after_typing = [];
+		before_dce = [];
+		after_generation = [];
+	}
+
+let create version s_version args =
 	let m = Type.mk_mono() in
+	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))
+	in
 	{
-		version = v;
+		version = version;
 		args = args;
+		shared = {
+			shared_display_information = {
+				import_positions = PMap.empty;
+				diagnostics_messages = [];
+			}
+		};
+		display_information = {
+			unresolved_identifiers = [];
+		};
 		sys_args = args;
 		debug = false;
 		display = !display_default;
@@ -707,12 +759,11 @@ let create v args =
 		std_path = [];
 		class_path = [];
 		main_class = None;
-		defines = PMap.add "true" "1" (if !display_default <> DMNone then PMap.add "display" "1" PMap.empty else PMap.empty);
+		defines = defines;
 		package_rules = PMap.empty;
 		file = "";
 		types = [];
-		filters = [];
-		final_filters = [];
+		callbacks = create_callbacks();
 		modules = [];
 		main = None;
 		flash_version = 10.;
@@ -726,6 +777,7 @@ let create v args =
 		net_path_map = Hashtbl.create 0;
 		c_args = [];
 		neko_libs = [];
+		include_files = [];
 		php_prefix = None;
 		js_gen = None;
 		load_extern_type = [];
@@ -744,7 +796,9 @@ let create v args =
 		};
 		file_lookup_cache = Hashtbl.create 0;
 		stored_typed_exprs = PMap.empty;
+		cached_macros = Hashtbl.create 0;
 		memory_marker = memory_marker;
+		parser_cache = Hashtbl.create 0;
 	}
 
 let log com str =
@@ -757,6 +811,7 @@ let clone com =
 		main_class = None;
 		features = Hashtbl.create 0;
 		file_lookup_cache = Hashtbl.create 0;
+		callbacks = create_callbacks();
 	}
 
 let file_time file =
@@ -766,12 +821,13 @@ let get_signature com =
 	match com.defines_signature with
 	| Some s -> s
 	| None ->
-		let str = String.concat "@" (PMap.foldi (fun k v acc ->
+		let defines = PMap.foldi (fun k v acc ->
 			(* don't make much difference between these special compilation flags *)
-			match k with
-			| "display" | "use_rtti_doc" | "macrotimes" -> acc
-			| _ -> k :: v :: acc
-		) com.defines []) in
+			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
@@ -783,6 +839,7 @@ let file_extension file =
 
 let platforms = [
 	Js;
+	Lua;
 	Neko;
 	Flash;
 	Php;
@@ -790,11 +847,13 @@ let platforms = [
 	Cs;
 	Java;
 	Python;
+	Hl;
 ]
 
 let platform_name = function
 	| Cross -> "cross"
 	| Js -> "js"
+	| Lua -> "lua"
 	| Neko -> "neko"
 	| Flash -> "flash"
 	| Php -> "php"
@@ -802,6 +861,7 @@ let platform_name = function
 	| Cs -> "cs"
 	| Java -> "java"
 	| Python -> "python"
+	| Hl -> "hl"
 
 let flash_versions = List.map (fun v ->
 	let maj = int_of_float v in
@@ -827,12 +887,7 @@ let flash_version_tag = function
 	| 11.7 -> 20
 	| 11.8 -> 21
 	| 11.9 -> 22
-	| 12.0 -> 23
-	| 13.0 -> 24
-	| 14.0 -> 25
-	| 15.0 -> 26
-	| 16.0 -> 27
-	| 17.0 -> 28
+	| v when v >= 12.0 && float_of_int (int_of_float v) = v -> int_of_float v + 11
 	| v -> failwith ("Invalid SWF version " ^ string_of_float v)
 
 let raw_defined ctx v =
@@ -928,11 +983,14 @@ let error msg p = raise (Abort (msg,p))
 
 let platform ctx p = ctx.platform = p
 
+let add_typing_filter ctx f =
+	ctx.callbacks.after_typing <- f :: ctx.callbacks.after_typing
+
 let add_filter ctx f =
-	ctx.filters <- f :: ctx.filters
+	ctx.callbacks.before_dce <- f :: ctx.callbacks.before_dce
 
 let add_final_filter ctx f =
-	ctx.final_filters <- f :: ctx.final_filters
+	ctx.callbacks.after_generation <- f :: ctx.callbacks.after_generation
 
 let find_file ctx f =
 	try
@@ -958,12 +1016,20 @@ 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 normalize_path p =
+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
 		"./"
@@ -976,9 +1042,9 @@ let rec mkdir_recursive base dir_list =
 	| [] -> ()
 	| dir :: remaining ->
 		let path = match base with
-		           | "" ->  dir
-		           | "/" -> "/" ^ dir
-		           | _ -> base ^ "/" ^ dir
+				   | "" ->  dir
+				   | "/" -> "/" ^ dir
+				   | _ -> base ^ "/" ^ dir
 		in
 		if not ( (path = "") || ( ((String.length path) = 2) && ((String.sub path 1 1) = ":") ) ) then
 			if not (Sys.file_exists path) then
@@ -1051,8 +1117,8 @@ Ast.Meta.to_string_ref := fun m -> fst (MetaInfo.to_string m)
 
 (*  Taken from OCaml source typing/oprint.ml
 
-    This is a better version of string_of_float which prints without loss of precision
-    so that float_of_string (float_repres x) = x for all floats x
+	This is a better version of string_of_float which prints without loss of precision
+	so that float_of_string (float_repres x) = x for all floats x
 *)
 let valid_float_lexeme s =
 	let l = String.length s in
@@ -1076,3 +1142,8 @@ let float_repres f =
 			if f = float_of_string s2 then s2 else
 			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

+ 1428 - 0
src/typing/matcher.ml

@@ -0,0 +1,1428 @@
+(*
+	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 Type
+open Common
+
+exception Internal_match_failure
+
+let s_type = s_type (print_context())
+let s_expr_pretty = s_expr_pretty false "" s_type
+
+let fake_tuple_type = TInst(mk_class null_module ([],"-Tuple") null_pos, [])
+
+let tuple_type tl =
+	tfun tl fake_tuple_type
+
+let make_offset_list left right middle other =
+	(ExtList.List.make left other) @ [middle] @ (ExtList.List.make right other)
+
+let type_field_access ctx ?(resume=false) e name =
+	Typer.acc_get ctx (Typer.type_field ~resume ctx e name e.epos Typer.MGet) e.epos
+
+let unapply_type_parameters params monos =
+	List.iter2 (fun (_,t1) t2 -> match t2,follow t2 with TMono m1,TMono m2 when m1 == m2 -> Type.unify t1 t2 | _ -> ()) params monos
+
+let get_general_module_type ctx mt p =
+	let rec loop = function
+		| TClassDecl _ -> "Class"
+		| TEnumDecl _ -> "Enum"
+		| TAbstractDecl a when Meta.has Meta.RuntimeValue a.a_meta -> "Class"
+		| TTypeDecl t ->
+			begin match follow (monomorphs t.t_params t.t_type) with
+				| TInst(c,_) -> loop (TClassDecl c)
+				| TEnum(en,_) -> loop (TEnumDecl en)
+				| TAbstract(a,_) -> loop (TAbstractDecl a)
+				| _ -> error "Cannot use this type as a value" p
+			end
+		| _ -> error "Cannot use this type as a value" p
+	in
+	Typeload.load_instance ctx ({tname=loop mt;tpackage=[];tsub=None;tparams=[]},null_pos) true p
+
+module Constructor = struct
+	type t =
+		| ConConst of tconstant
+		| ConEnum of tenum * tenum_field
+		| ConStatic of tclass * tclass_field
+		| ConTypeExpr of module_type
+		| ConFields of string list
+		| ConArray of int
+
+	let to_string con = match con with
+		| ConConst ct -> s_const ct
+		| ConEnum(en,ef) -> ef.ef_name
+		| ConStatic(c,cf) -> Printf.sprintf "%s.%s" (s_type_path (match c.cl_kind with KAbstractImpl a -> a.a_path | _ -> c.cl_path)) cf.cf_name
+		| ConTypeExpr mt -> s_type_path (t_infos mt).mt_path
+		| ConFields fields -> Printf.sprintf "{ %s }" (String.concat ", " fields)
+		| ConArray i -> Printf.sprintf "<array %i>" i
+
+	let equal con1 con2 = match con1,con2 with
+		| ConConst ct1,ConConst ct2 -> ct1 = ct2
+		| ConEnum(en1,ef1),ConEnum(en2,ef2) -> en1 == en2 && ef1 == ef2
+		| ConStatic(c1,cf1),ConStatic(c2,cf2) -> c1 == c2 && cf1 == cf2
+		| ConTypeExpr mt1,ConTypeExpr mt2 -> mt1 == mt2
+		| ConFields _,ConFields _ -> true
+		| ConArray i1,ConArray i2 -> i1 = i2
+		| _ -> false
+
+	let arity con = match con with
+		| ConEnum (_,{ef_type = TFun(args,_)}) -> List.length args
+		| ConEnum _ -> 0
+		| ConConst _ -> 0
+		| ConFields fields -> List.length fields
+		| ConArray i -> i
+		| ConTypeExpr _ -> 0
+		| ConStatic _ -> 0
+
+	let compare con1 con2 = match con1,con2 with
+		| ConConst ct1,ConConst ct2 -> compare ct1 ct2
+		| ConEnum(en1,ef1),ConEnum(en2,ef2) -> compare ef1.ef_index ef2.ef_index
+		| ConStatic(c1,cf1),ConStatic(c2,cf2) -> compare cf1.cf_name cf2.cf_name
+		| ConTypeExpr mt1,ConTypeExpr mt2 -> compare (t_infos mt1).mt_path (t_infos mt2).mt_path
+		| ConFields _,ConFields _ -> 0
+		| ConArray i1,ConArray i2 -> i1 - i2
+		| _ -> -1 (* Could assert... *)
+
+	open Typecore
+
+	let to_texpr ctx match_debug p con = match con with
+		| ConEnum(en,ef) ->
+			if Meta.has Meta.FakeEnum en.e_meta then begin
+				let e_mt = !type_module_type_ref ctx (TEnumDecl en) None p in
+ 				mk (TField(e_mt,FEnum(en,ef))) ef.ef_type p
+ 			end else if match_debug then mk (TConst (TString ef.ef_name)) ctx.t.tstring p
+			else mk (TConst (TInt (Int32.of_int ef.ef_index))) ctx.t.tint p
+		| ConConst ct -> Codegen.ExprBuilder.make_const_texpr ctx.com ct p
+		| ConArray i -> Codegen.ExprBuilder.make_int ctx.com i p
+		| ConTypeExpr mt -> Typer.type_module_type ctx mt None p
+		| ConStatic(c,cf) -> Codegen.ExprBuilder.make_static_field c cf p
+		| ConFields _ -> error "Something went wrong" p
+
+	let hash = Hashtbl.hash
+end
+
+module Pattern = struct
+	open Typecore
+	open Constructor
+
+	type t =
+		| PatConstructor of Constructor.t * pattern list
+		| PatVariable of tvar
+		| PatAny
+		| PatBind of tvar * pattern
+		| PatOr of pattern * pattern
+		| PatTuple of pattern list
+		| PatExtractor of tvar * texpr * pattern
+
+	and pattern = t * pos
+
+	type pattern_context = {
+		ctx : typer;
+		or_locals : (string, tvar * pos) PMap.t option;
+		mutable current_locals : (string, tvar * pos) PMap.t;
+		mutable in_reification : bool;
+	}
+
+	exception Bad_pattern of string
+
+	let rec to_string pat = match fst pat with
+		| PatConstructor(con,patterns) -> Printf.sprintf "%s(%s)" (Constructor.to_string con) (String.concat ", " (List.map to_string patterns))
+		| PatVariable v -> Printf.sprintf "%s<%i>" v.v_name v.v_id
+		| PatAny -> "_"
+		| PatBind(v,pat1) -> Printf.sprintf "%s = %s" v.v_name (to_string pat1)
+		| PatOr(pat1,pat2) -> Printf.sprintf "(%s) | (%s)" (to_string pat1) (to_string pat2)
+		| PatTuple pl -> Printf.sprintf "[%s]" (String.concat ", " (List.map to_string pl))
+		| PatExtractor(v,e,pat1) -> Printf.sprintf "%s => %s" (s_expr_pretty e) (to_string pat1)
+
+	let unify_type_pattern ctx mt t p =
+		let tcl = get_general_module_type ctx mt p in
+		match tcl with
+			| TAbstract(a,_) -> unify ctx (TAbstract(a,[mk_mono()])) t p
+			| _ -> assert false
+
+	let rec make pctx t e =
+		let ctx = pctx.ctx in
+		let p = pos e in
+		let fail () =
+			error ("Unrecognized pattern: " ^ (Ast.s_expr e)) p
+		in
+		let unify_expected t' =
+			unify ctx t' t p
+		in
+		let verror name p =
+			error (Printf.sprintf "Variable %s must appear exactly once in each sub-pattern" name) p
+		in
+		let add_local name p =
+			let is_wildcard_local = name = "_" in
+			if not is_wildcard_local && PMap.mem name pctx.current_locals then error (Printf.sprintf "Variable %s is bound multiple times" name) p;
+			match pctx.or_locals with
+			| Some map when not is_wildcard_local ->
+				let v,p = try PMap.find name map with Not_found -> verror name p in
+				unify ctx t v.v_type p;
+				pctx.current_locals <- PMap.add name (v,p) pctx.current_locals;
+				v
+			| _ ->
+				let v = alloc_var name t p in
+				pctx.current_locals <- PMap.add name (v,p) pctx.current_locals;
+				ctx.locals <- PMap.add name v ctx.locals;
+				v
+		in
+		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 | _ -> ());
+					PatConstructor(ConEnum(en,ef),[])
+				| TField(_,FStatic(c,({cf_kind = Var {v_write = AccNever}} as cf))) ->
+					PatConstructor(ConStatic(c,cf),[])
+				| TConst ct ->
+					PatConstructor(ConConst ct,[])
+				| TCast(e1,None) ->
+					loop e1
+				| TField _ ->
+					raise (Bad_pattern "Only inline or read-only (default, never) fields can be used as a pattern")
+				| _ ->
+					raise Exit
+			in
+			loop e
+		in
+		let try_typing e =
+			let old = ctx.untyped in
+			ctx.untyped <- true;
+			let e = try type_expr ctx e (WithType t) with exc -> ctx.untyped <- old; raise exc in
+			ctx.untyped <- old;
+			match e.eexpr with
+				| TTypeExpr mt ->
+					unify_type_pattern ctx mt t e.epos;
+					PatConstructor(ConTypeExpr mt,[])
+				| _ ->
+					begin try
+						Type.unify e.etype t
+					with (Unify_error l) ->
+						(* Hack: Allow matching the underlying type against its abstract. *)
+						begin match follow e.etype with
+							| TAbstract(a,tl) when not (Meta.has Meta.CoreType a.a_meta) && type_iseq t (Abstract.get_underlying_type a tl) -> ()
+							| _ -> raise_or_display ctx l p
+						end
+					end;
+					check_expr e
+		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;
+				(fun () ->
+					ctx.in_call_args <- fst old;
+					ctx.locals <- snd 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 _ ->
+				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
+		in
+		let rec loop e = match fst e with
+			| EParenthesis e1 | ECast(e1,None) ->
+				loop e1
+			| ECheckType(e, (CTPath({tpackage=["haxe";"macro"]; tname="Expr"}),_)) ->
+				let old = pctx.in_reification in
+				pctx.in_reification <- true;
+				let e = loop e in
+				pctx.in_reification <- old;
+				e
+			| EConst((Ident ("false" | "true") | Int _ | String _ | Float _) as ct) ->
+				let e = Codegen.type_constant ctx.com ct p in
+				unify_expected e.etype;
+				let ct = match e.eexpr with TConst ct -> ct | _ -> assert false in
+				PatConstructor(ConConst ct,[])
+			| EConst (Ident i) ->
+				begin match i with
+					| "_" ->
+						begin match follow t with
+							| TFun(ta,tr) when tr == fake_tuple_type ->
+								PatTuple(List.map (fun (_,_,t) -> (PatAny,pos e)) ta)
+							| _ ->
+								PatAny
+						end
+					| _ ->
+						handle_ident i (pos e)
+				end
+			| EVars([(s,p),None,None]) ->
+				let v = add_local s p in
+				PatVariable v
+			| ECall(e1,el) ->
+				let t = tfun (List.map (fun _ -> mk_mono()) el) t in
+				let e1 = type_expr ctx e1 (WithType t) in
+				begin match e1.eexpr,follow e1.etype with
+					| TField(_, FEnum(en,ef)),TFun(_,TEnum(_,tl)) ->
+						let monos = List.map (fun _ -> mk_mono()) ef.ef_params in
+						let map t = apply_params en.e_params tl (apply_params ef.ef_params monos t) in
+						(* We cannot use e1.etype here because it has applied type parameters (issue #1310). *)
+						let args = match follow (map ef.ef_type) with
+							| TFun(args,r) ->
+								unify_expected r;
+								args
+							| _ -> assert false
+						in
+						let rec loop el tl = match el,tl with
+							| [EConst (Ident "_"),p],(_,_,t) :: tl ->
+								(* Allow using final _ to match "multiple" arguments *)
+								(PatAny,p) :: (match tl with [] -> [] | _ -> loop el tl)
+							| e :: el,(_,_,t) :: tl ->
+								make pctx t e :: loop el tl
+							| [],(_,true,t) :: tl ->
+								(PatAny,pos e) :: loop [] tl
+							| [],[] ->
+								[]
+							| [],_ ->
+								error "Not enough arguments" p
+							| _,[] ->
+								error "Too many arguments" p
+						in
+						let patterns = loop el args in
+						(* We want to change the original monomorphs back to type parameters, but we don't want to do that
+						   if they are bound to other monomorphs (issue #4578). *)
+						unapply_type_parameters ef.ef_params monos;
+						PatConstructor(ConEnum(en,ef),patterns)
+					| _ ->
+						fail()
+				end
+			| EField _ ->
+				begin try
+					try_typing e
+				with
+					| Exit -> fail()
+					| Bad_pattern s -> error s p
+				end
+			| EArrayDecl el ->
+				begin match follow t with
+					| TFun(tl,tr) when tr == fake_tuple_type ->
+						let rec loop el tl = match el,tl with
+							| e :: el,(_,_,t) :: tl ->
+								let pat = make pctx t e in
+								pat :: loop el tl
+							| [],[] -> []
+							| [],_ -> error "Not enough arguments" p
+							| (_,p) :: _,[] -> error "Too many arguments" p
+						in
+						let patterns = loop el tl in
+						PatTuple patterns
+					| TInst({cl_path=[],"Array"},[t2]) | (TDynamic _ as t2) ->
+						let patterns = ExtList.List.mapi (fun i e ->
+							make pctx t2 e
+						) el in
+						PatConstructor(ConArray (List.length patterns),patterns)
+					| _ ->
+						fail()
+				end
+			| EObjectDecl fl ->
+				let known_fields = match follow t with
+					| TAnon an ->
+						PMap.fold (fun cf acc -> (cf,cf.cf_type) :: acc) an.a_fields []
+					| TInst(c,tl) ->
+						let rec loop fields c tl =
+							let fields = List.fold_left (fun acc cf -> (cf,apply_params c.cl_params tl cf.cf_type) :: acc) fields c.cl_ordered_fields in
+							match c.cl_super with
+								| None -> fields
+								| Some (csup,tlsup) -> loop fields csup (List.map (apply_params c.cl_params tl) tlsup)
+						in
+						loop [] c tl
+					| TAbstract({a_impl = Some c} as a,tl) ->
+						let fields = List.fold_left (fun acc cf ->
+							if Meta.has Meta.Impl cf.cf_meta then
+								(cf,apply_params a.a_params tl cf.cf_type) :: acc
+							else
+								acc
+						) [] c.cl_ordered_statics in
+						fields
+					| _ ->
+						error (Printf.sprintf "Cannot field-match against %s" (s_type t)) (pos e)
+				in
+				let is_matchable cf =
+					match cf.cf_kind with Method _ -> false | _ -> true
+				in
+				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
+						make pctx t e1 :: patterns,cf.cf_name :: fields
+					with Not_found ->
+						if is_matchable cf then
+							(PatAny,cf.cf_pos) :: patterns,cf.cf_name :: fields
+						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;
+				PatConstructor(ConFields fields,patterns)
+			| EBinop(OpOr,e1,e2) ->
+				let pctx1 = {pctx with current_locals = PMap.empty} in
+				let pat1 = make pctx1 t e1 in
+				let pctx2 = {pctx with current_locals = PMap.empty; or_locals = Some (pctx1.current_locals)} in
+				let pat2 = make pctx2 t e2 in
+				PMap.iter (fun name (v,p) ->
+					if not (PMap.mem name pctx2.current_locals) then verror name p;
+					pctx.current_locals <- PMap.add name (v,p) pctx.current_locals
+				) pctx1.current_locals;
+				PatOr(pat1,pat2)
+			| EBinop(OpAssign,(EConst (Ident s),p),e2) ->
+				let pat = make pctx t e2 in
+				let v = add_local s p in
+				PatBind(v,pat)
+			| EBinop(OpArrow,e1,e2) ->
+				let v = add_local "_" null_pos in
+				let e1 = type_expr ctx e1 Value in
+				v.v_name <- "tmp";
+				let pat = make pctx e1.etype e2 in
+				PatExtractor(v,e1,pat)
+			| EDisplay(e,call) ->
+				let pat = loop e in
+				let _ = Typer.handle_display ctx e call (WithType t) in
+				pat
+			| _ ->
+				fail()
+		in
+		let pat = loop e in
+		pat,p
+
+	let make ctx t e =
+		let pctx = {
+			ctx = ctx;
+			current_locals = PMap.empty;
+			or_locals = None;
+			in_reification = false;
+		} in
+		make pctx t e
+end
+
+module Case = struct
+	open Typecore
+
+	type t = {
+		case_guard : texpr option;
+		case_expr : texpr option;
+		case_pos : pos;
+	}
+
+	let make ctx t el eg eo with_type =
+		let rec collapse_case el = match el with
+			| e :: [] ->
+				e
+			| e :: el ->
+				let e2 = collapse_case el in
+				EBinop(OpOr,e,e2),punion (pos e) (pos e2)
+			| [] ->
+				assert false
+		in
+		let e = collapse_case el in
+		let monos = List.map (fun _ -> mk_mono()) ctx.type_params in
+		let map = apply_params ctx.type_params monos in
+		let save = save_locals ctx in
+		let old_types = PMap.fold (fun v acc ->
+			let t_old = v.v_type in
+			v.v_type <- map v.v_type;
+			(v,t_old) :: acc
+		) ctx.locals [] in
+		let old_ret = ctx.ret in
+		ctx.ret <- map ctx.ret;
+		let pat = Pattern.make ctx (map t) e in
+		unapply_type_parameters ctx.type_params monos;
+		let eg = match eg with
+			| None -> None
+			| Some e -> Some (type_expr ctx e Value)
+		in
+		let eo = match eo,with_type with
+			| None,WithType t ->
+				unify ctx ctx.t.tvoid t (pos e);
+				None
+			| None,_ ->
+				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
+				Some e
+			| Some e,_ ->
+				let e = type_expr ctx e with_type in
+				Some e
+		in
+		ctx.ret <- old_ret;
+		List.iter (fun (v,t) -> v.v_type <- t) old_types;
+		save();
+		{
+			case_guard = eg;
+			case_expr = eo;
+			case_pos = pos e;
+		},[],pat
+end
+
+module Decision_tree = struct
+	open Case
+
+	type subject = texpr
+
+	type type_finiteness =
+		| Infinite          (* type has inifite constructors (e.g. Int, String) *)
+		| CompileTimeFinite (* type is considered finite only at compile-time but has inifite possible run-time values (enum abstracts) *)
+		| RunTimeFinite     (* type is truly finite (Bool, enums) *)
+
+	type t =
+		| Leaf of Case.t
+		| Switch of subject * (Constructor.t * bool * dt) list * dt
+		| Bind of (tvar * pos * texpr) list * dt
+		| Guard of texpr * dt * dt
+		| GuardNull of texpr * dt * dt
+		| Fail
+
+	and dt = {
+		dt_t : t;
+		dt_i : int;
+		dt_pos : pos;
+		mutable dt_goto_target : bool;
+	}
+
+	let s_case_expr tabs case = match case.case_expr with
+		| None -> ""
+		| Some e -> Type.s_expr_pretty false tabs s_type e
+
+	let rec to_string tabs dt = match dt.dt_t with
+		| Leaf case ->
+			s_case_expr tabs case
+		| Switch(e,cases,dt) ->
+			let s_case (con,b,dt) =
+				Printf.sprintf "\n\t%scase %s%s: %s" tabs (Constructor.to_string con) (if b then "(unguarded) " else "") (to_string (tabs ^ "\t") dt)
+			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
+		| 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
+		| Guard(e,dt1,dt2) ->
+			Printf.sprintf "if (%s) {\n\t%s%s\n%s} else {\n\t%s%s\n%s}" (s_expr_pretty e) tabs (to_string (tabs ^ "\t") dt1) tabs tabs (to_string (tabs ^ "\t") dt2) tabs
+		| GuardNull(e,dt1,dt2) ->
+			Printf.sprintf "if (%s == null) {\n\t%s%s\n%s} else {\n\t%s%s\n%s}" (s_expr_pretty e) tabs (to_string (tabs ^ "\t") dt1) tabs tabs (to_string (tabs ^ "\t") dt2) tabs
+		| Fail ->
+			"<fail>"
+
+	let equal_dt dt1 dt2 = dt1.dt_i = dt2.dt_i
+
+	let equal dt1 dt2 = match dt1,dt2 with
+		| Leaf case1,Leaf case2 ->
+			case1 == case2
+		| Switch(subject1,cases1,dt1),Switch(subject2,cases2,dt2) ->
+			subject1 == subject2 &&
+			safe_for_all2 (fun (con1,b1,dt1) (con2,b2,dt2) -> Constructor.equal con1 con2 && b1 = b2 && equal_dt dt1 dt2) cases1 cases2 &&
+			equal_dt dt1 dt2
+		| Bind(l1,dt1),Bind(l2,dt2) ->
+			safe_for_all2 (fun (v1,_,e1) (v2,_,e2) -> v1 == v2 && e1 == e2) l1 l2 &&
+			equal_dt dt1 dt2
+		| Fail,Fail ->
+			true
+		| (Guard(e1,dt11,dt12),Guard(e2,dt21,dt22)) | (GuardNull(e1,dt11,dt12),GuardNull(e2,dt21,dt22)) ->
+			e1 == e2 && equal_dt dt11 dt21 && equal_dt dt12 dt22
+		| _ ->
+			false
+
+	let hash = Hashtbl.hash
+end
+
+module ConTable = Hashtbl.Make(Constructor)
+
+(*
+	Implements checks for useless patterns based on http://moscova.inria.fr/~maranget/papers/warn/index.html.
+*)
+module Useless = struct
+	open Pattern
+	open Constructor
+	open Case
+
+	type useless =
+		| False
+		| Pos of pos
+		| True
+
+	(* U part *)
+
+	let specialize is_tuple con pM =
+		let rec loop acc pM = match pM with
+			| patterns :: pM ->
+				begin match patterns with
+					| (PatConstructor(con',patterns1),_) :: patterns2 when not is_tuple && Constructor.equal con con' ->
+						loop ((patterns1 @ patterns2) :: acc) pM
+					| (PatTuple patterns1,_) :: patterns2 when is_tuple ->
+						loop ((patterns1 @ patterns2) :: acc) pM
+					| (PatAny,p) :: patterns2 ->
+						let patterns1 = ExtList.List.make (arity con) (PatAny,p) in
+						loop ((patterns1 @ patterns2) :: acc) pM
+					| (PatBind(_,pat1),_) :: patterns2 ->
+						loop acc ((pat1 :: patterns2) :: pM)
+					| _ ->
+						loop acc pM
+				end
+			| [] ->
+				List.rev acc
+		in
+		loop [] pM
+
+	let default pM =
+		let rec loop acc pM = match pM with
+			| patterns :: pM ->
+				begin match patterns with
+					| ((PatConstructor _ | PatTuple _),_) :: _ ->
+						loop acc pM
+					| ((PatVariable _ | PatAny),_) :: patterns ->
+						loop (patterns :: acc) pM
+					| _ ->
+						loop acc pM
+				end
+			| [] ->
+				List.rev acc
+		in
+		loop [] pM
+
+	let rec u pM q =
+		match q,pM with
+		| [],[] -> true
+		| [],_ -> false
+		| (q1 :: ql),_ ->
+			let rec loop pat = match fst pat with
+				| PatConstructor(con,patterns) ->
+					let s = specialize false con pM in
+					u s (patterns @ ql)
+				| PatTuple patterns ->
+					let s = specialize true (ConConst TNull) pM in
+					u s (patterns @ ql)
+				| (PatVariable _ | PatAny) ->
+					let d = default pM in
+					u d ql
+				| PatOr(pat1,pat2) ->
+					u pM (pat1 :: ql) || u pM (pat2 :: ql)
+				| PatBind(_,pat1) ->
+					loop pat1
+				| PatExtractor _ ->
+					true (* ? *)
+			in
+			loop q1
+
+	(* U' part *)
+
+	let transfer_column source target =
+		let source,target = List.fold_left2 (fun (source,target) patterns1 patterns2 -> match patterns1 with
+			| pat :: patterns -> patterns :: source,(pat :: patterns2) :: target
+			| [] -> source,target
+		) ([],[]) source target in
+		List.rev source,List.rev target
+
+	let copy p = List.map (fun _ -> []) p
+
+	let rec specialize' is_tuple con pM qM rM =
+		let arity = arity con in
+		let rec loop pAcc qAcc rAcc pM qM rM = match pM,qM,rM with
+			| p1 :: pM,q1 :: qM,r1 :: rM ->
+				let rec loop2 p1 = match p1 with
+					| (PatConstructor(con',patterns1),_) :: patterns2 when not is_tuple && Constructor.equal con con' ->
+						loop ((patterns1 @ patterns2) :: pAcc) (q1 :: qAcc) (r1 :: rAcc) pM qM rM
+					| (PatTuple patterns1,_) :: patterns2 when is_tuple ->
+						loop ((patterns1 @ patterns2) :: pAcc) (q1 :: qAcc) (r1 :: rAcc) pM qM rM
+					| ((PatVariable _ | PatAny),p) :: patterns2 ->
+						let patterns1 = ExtList.List.make arity (PatAny,p) in
+						loop ((patterns1 @ patterns2) :: pAcc) (q1 :: qAcc) (r1 :: rAcc) pM qM rM
+					| ((PatOr(pat1,pat2)),_) :: patterns2 ->
+						specialize' is_tuple con (((pat1 :: patterns2) :: (pat2 :: patterns2) :: pAcc)) (q1 :: q1 :: qM @ qAcc) (r1 :: r1 :: rM @ rAcc)
+					| (PatBind(_,pat1),_) :: patterns2 ->
+						loop2 (pat1 :: patterns2)
+					| _ ->
+						loop pAcc qAcc rAcc pM qM rM
+				in
+				loop2 p1
+			| [],_,_ ->
+				List.rev pAcc,List.rev qAcc,List.rev rAcc
+			| _ ->
+				assert false
+		in
+		loop [] [] [] pM qM rM
+
+	let combine et1 et2 = match fst et1,fst et2 with
+		| True,True -> True
+		| False,False -> False
+		| True,False -> Pos (pos et2)
+		| False,True -> Pos (pos et1)
+		| True,Pos _ -> fst et2
+		| Pos _,True -> fst et1
+		| False,Pos _ -> Pos (pos et1)
+		| Pos _,_ -> fst et1
+
+	let rec u' pM qM rM p q r =
+		match p with
+		| [] ->
+			begin match r with
+				| [] -> if u qM q then True else False
+				| _ ->
+					snd (List.fold_left (fun (i,et) pat -> match fst pat with
+						| PatOr(pat1,pat2) ->
+						 	let process_row i l q =
+						 		let rec loop acc k l = match l with
+						 			| x :: l when i = k -> x,(List.rev acc) @ l @ q
+						 			| x :: l -> loop (x :: acc) (k + 1) l
+						 			| [] -> assert false
+						 		in
+						 		loop [] 0 l
+						 	in
+							let col,mat = List.fold_left2 (fun (col,mat) r q ->
+					 			let x,l = process_row i r q in
+					 			([x] :: col,l :: mat)
+					 		) ([],[]) rM qM in
+					 		let col,mat = List.rev col,List.rev mat in
+							let _,r = process_row i r q in
+							let et1 = u' col mat (copy mat) [pat1] r [] in
+							let qM = (mat @ [r]) in
+							let et2 = u' (col @ [[pat1]]) qM (copy qM) [pat2] r [] in
+							let et3 = combine (et1,pos pat1) (et2,pos pat2) in
+							let p = punion (pos pat1) (pos pat2) in
+							let et = combine (et,p) (et3,p) in
+							(i + 1,et)
+						| _ -> assert false
+					) (0,True) r)
+			end
+		| (pat :: pl) ->
+			let rec loop pat = match fst pat with
+				| PatConstructor(con,patterns) ->
+					let pM,qM,rM = specialize' false con pM qM rM in
+					u' pM qM rM (patterns @ pl) q r
+				| PatTuple patterns ->
+					let pM,qM,rM = specialize' true (ConConst TNull) pM qM rM in
+					u' pM qM rM (patterns @ pl) q r
+				| PatAny | PatVariable _ ->
+					let pM,qM = transfer_column pM qM in
+					u' pM qM rM pl (pat :: q) r
+				| PatOr _ ->
+					let pM,rM = transfer_column pM rM in
+					u' pM qM rM pl q (pat :: r)
+				| PatBind(_,pat1) ->
+					loop pat1
+				| PatExtractor _ ->
+					True
+			in
+			loop pat
+
+	(* Sane part *)
+
+	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
+			| Pos p -> com.warning "This pattern is unused" p
+			| True -> ()
+
+	let check com cases =
+		ignore(List.fold_left (fun acc (case,bindings,patterns) ->
+			check_case com acc (case,bindings,patterns);
+			if case.case_guard = None then acc @ [case,bindings,patterns] else acc
+		) [] cases)
+end
+
+module DtTable = Hashtbl.Make(Decision_tree)
+
+module Compile = struct
+	open Typecore
+	open Decision_tree
+	open Case
+	open Constructor
+	open Pattern
+
+	exception Extractor
+
+	type matcher_context = {
+		ctx : typer;
+		dt_table : dt DtTable.t;
+		match_pos : pos;
+		match_debug : bool;
+		mutable dt_count : int;
+	}
+
+	let rec hashcons mctx dt p =
+		try
+			DtTable.find mctx.dt_table dt
+		with Not_found ->
+			let dti = {dt_t = dt; dt_i = mctx.dt_count; dt_pos = p; dt_goto_target = false } in
+			DtTable.add mctx.dt_table dt dti;
+			mctx.dt_count <- mctx.dt_count + 1;
+			dti
+
+	let leaf mctx case = hashcons mctx (Leaf case) case.case_pos
+	let fail mctx p = hashcons mctx Fail p
+	let switch mctx subject cases default = hashcons mctx (Switch(subject,cases,default)) subject.epos
+	let bind mctx bindings dt = hashcons mctx (Bind(bindings,dt)) dt.dt_pos
+	let guard mctx e dt1 dt2 = hashcons mctx (Guard(e,dt1,dt2)) (punion dt1.dt_pos dt2.dt_pos)
+	let guard_null mctx e dt1 dt2 = hashcons mctx (GuardNull(e,dt1,dt2)) (punion dt1.dt_pos dt2.dt_pos)
+
+	let rec get_sub_subjects mctx e con =
+		match con with
+		| ConEnum(en,ef) ->
+			let tl = List.map (fun _ -> mk_mono()) en.e_params in
+			let t_en = TEnum(en,tl) in
+			let e = if not (type_iseq t_en e.etype) then mk (TCast(e,None)) t_en e.epos else e in
+			begin match follow ef.ef_type with
+				| TFun(args,_) ->
+					ExtList.List.mapi (fun i (_,_,t) -> mk (TEnumParameter(e,ef,i)) (apply_params en.e_params tl (monomorphs ef.ef_params t)) e.epos) args
+				| _ ->
+					[]
+			end
+		| ConFields sl ->
+			List.map (type_field_access mctx.ctx e) sl
+		| ConArray 0 -> []
+		| ConArray i ->
+			let t = match follow e.etype with TInst({cl_path=[],"Array"},[t]) -> t | TDynamic _ as t -> t | _ -> assert false in
+			ExtList.List.init i (fun i ->
+				let ei = Codegen.ExprBuilder.make_int mctx.ctx.com i e.epos in
+				mk (TArray(e,ei)) t e.epos
+			)
+		| ConConst _ | ConTypeExpr _ | ConStatic _ ->
+			[]
+
+	let specialize subject con cases =
+		let arity = arity con in
+		let rec loop acc cases = match cases with
+			| (case,bindings,patterns) :: cases ->
+				begin match patterns with
+					| (PatConstructor(con',patterns1),_) :: patterns2 when Constructor.equal con con' ->
+						loop ((case,bindings,patterns1 @ patterns2) :: acc) cases
+					| (PatVariable v,p) :: patterns2 ->
+						let patterns1 = ExtList.List.make arity (PatAny,p) in
+						loop ((case,((v,p,subject) :: bindings),patterns1 @ patterns2) :: acc) cases
+					| ((PatAny,_)) as pat :: patterns2 ->
+						let patterns1 = ExtList.List.make arity pat in
+						loop ((case,bindings,patterns1 @ patterns2) :: acc) cases
+					| ((PatBind(v,pat),p)) :: patterns ->
+						loop acc ((case,((v,p,subject) :: bindings),pat :: patterns) :: cases)
+					| _ ->
+						loop acc cases
+				end
+			| [] ->
+				List.rev acc
+		in
+		loop [] cases
+
+	let default subject cases =
+		let rec loop acc cases = match cases with
+			| (case,bindings,patterns) :: cases ->
+				begin match patterns with
+					| (PatConstructor _,_) :: _ ->
+						loop acc cases
+					| (PatVariable v,p) :: patterns ->
+						loop ((case,((v,p,subject) :: bindings),patterns) :: acc) cases
+					| (PatAny,_) :: patterns ->
+						loop ((case,bindings,patterns) :: acc) cases
+					| (PatBind(v,pat),p) :: patterns ->
+						loop acc ((case,((v,p,subject) :: bindings),pat :: patterns) :: cases)
+					| _ ->
+						loop acc cases
+				end
+			| [] ->
+				List.rev acc
+		in
+		loop [] cases
+
+	let rec is_wildcard_pattern pat = match fst pat with
+		| PatVariable _ | PatAny -> true
+		| _ -> false
+
+	let rec expand cases =
+		let changed,cases = List.fold_left (fun (changed,acc) (case,bindings,patterns) ->
+			let rec loop f patterns = match patterns with
+				| (PatOr(pat1,pat2),_) :: patterns ->
+					true,(case,bindings,f pat2 :: patterns) :: (case,bindings,f pat1 :: patterns) :: acc
+				| (PatBind(v,pat1),p) :: patterns ->
+					loop (fun pat2 -> f (PatBind(v,pat2),p)) (pat1 :: patterns)
+				| (PatTuple patterns1,_) :: patterns2 ->
+					loop f (patterns1 @ patterns2)
+				| pat :: patterns ->
+					changed,(case,bindings,f pat :: patterns) :: acc
+				| [] ->
+					changed,((case,bindings,patterns) :: acc)
+			in
+			loop (fun pat -> pat) patterns
+		) (false,[]) cases in
+		let cases = List.rev cases in
+		if changed then expand cases else cases
+
+	let s_subjects subjects =
+		String.concat " " (List.map s_expr_pretty subjects)
+
+	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
+		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 =
+		String.concat "\n" (List.map s_case cases)
+
+	let select_column subjects cases =
+		let rec loop i patterns = match patterns with
+			| ((PatVariable _ | PatAny | PatExtractor _),_) :: patterns -> loop (i + 1) patterns
+			| [] -> 0
+			| _ -> i
+		in
+		let _,_,patterns = List.hd cases in
+		let i = loop 0 patterns in
+		let subjects,cases = if i = 0 then
+			subjects,cases
+		else begin
+			let rec sort i cur acc l = match l with
+				| x :: l ->
+					if i = cur then x :: acc @ l
+					else sort i (cur + 1) (x :: acc) l
+				| [] ->
+					acc
+			in
+			let subjects = sort i 0 [] subjects in
+			let cases = List.map (fun (case,bindings,patterns) ->
+				let patterns = sort i 0 [] patterns in
+				case,bindings,patterns
+			) cases in
+			subjects,cases
+		end in
+		subjects,cases
+
+	let rec compile mctx subjects cases = match cases with
+		| [] ->
+			fail mctx (match subjects with e :: _ -> e.epos | _ -> mctx.match_pos);
+		| (_,_,patterns) as case :: cases when List.for_all is_wildcard_pattern patterns ->
+			compile_leaf mctx subjects case cases
+		| _ ->
+			let cases = expand cases in
+			let subjects,cases = select_column subjects cases in
+			let cases = expand cases in (* TODO: is this really necessary? *)
+			try
+				compile_switch mctx subjects cases
+			with Extractor ->
+				compile_extractors mctx subjects cases
+
+	and compile_leaf mctx subjects (case,bindings,patterns) cases =
+		if mctx.match_debug then print_endline (Printf.sprintf "compile_leaf:\n\tsubjects: %s\n\tcase: %s\n\tcases: %s" (s_subjects subjects) (s_case (case,bindings,patterns)) (s_cases cases));
+		let dt = leaf mctx case in
+		let dt = match case.case_guard with
+			| None ->
+				dt
+			| Some e ->
+				let dt2 = compile mctx subjects cases in
+				guard mctx e dt dt2
+		in
+		let rec loop patterns el = match patterns,el with
+			| [PatAny,_],_ ->
+				[]
+			| (PatVariable v,p) :: patterns,e :: el ->
+				(v,p,e) :: loop patterns el
+			| _ :: patterns,_ :: el ->
+				loop patterns el
+			| [],[] ->
+				[]
+			| [],e :: _ ->
+				error "Invalid match: Not enough patterns" e.epos
+			| (_,p) :: _,[] ->
+				error "Invalid match: Too many patterns" p
+		in
+		let bindings = bindings @ loop patterns subjects in
+		if bindings = [] then dt else bind mctx bindings dt
+
+	and compile_switch mctx subjects cases =
+		let subject,subjects = match subjects with
+			| [] -> raise Internal_match_failure
+			| subject :: subjects -> subject,subjects
+		in
+		let get_column_sigma cases =
+			let sigma = ConTable.create 0 in
+			let unguarded = ConTable.create 0 in
+			let null = ref [] in
+			List.iter (fun (case,bindings,patterns) ->
+				let rec loop pat = match fst pat with
+					| PatConstructor(ConConst TNull,_) ->
+						null := (case,bindings,List.tl patterns) :: !null;
+					| PatConstructor(con,_) ->
+						if case.case_guard = None then ConTable.replace unguarded con true;
+						ConTable.replace sigma con true;
+					| PatBind(_,pat) -> loop pat
+					| PatVariable _ | PatAny -> ()
+					| PatExtractor _ -> raise Extractor
+					| _ -> error ("Unexpected pattern: " ^ (Pattern.to_string pat)) case.case_pos;
+				in
+				loop (List.hd patterns)
+			) cases;
+			let sigma = ConTable.fold (fun con _ acc -> (con,ConTable.mem unguarded con) :: acc) sigma [] in
+			sigma,List.rev !null
+		in
+		let sigma,null = get_column_sigma cases in
+		if mctx.match_debug then print_endline (Printf.sprintf "compile_switch:\n\tsubject: %s\n\ttsubjects: %s\n\tcases: %s" (s_expr_pretty subject) (s_subjects subjects) (s_cases cases));
+		let switch_cases = List.map (fun (con,unguarded) ->
+			let subjects = get_sub_subjects mctx subject con @ subjects in
+			let spec = specialize subject con cases in
+			let dt = compile mctx subjects spec in
+			con,unguarded,dt
+		) sigma in
+		let default = default subject cases in
+		let switch_default = compile mctx subjects default in
+		let dt = if switch_cases = [] then switch_default else switch mctx subject switch_cases switch_default in
+		let null_guard dt_null =
+			guard_null mctx subject dt_null dt
+		in
+		match null with
+			| [] ->
+				if is_explicit_null subject.etype then null_guard switch_default else dt
+			| cases ->
+				let dt_null = compile mctx subjects (cases @ default) in
+				null_guard dt_null
+
+	and compile_extractors mctx subjects cases =
+		let subject,subjects = match subjects with
+			| [] -> raise Internal_match_failure
+			| subject :: subjects -> subject,subjects
+		in
+		if mctx.match_debug then print_endline (Printf.sprintf "compile_extractor:\n\tsubject: %s\n\ttsubjects: %s\n\tcases: %s" (s_expr_pretty subject) (s_subjects subjects) (s_cases cases));
+		let num_extractors,extractors = List.fold_left (fun (i,extractors) (_,_,patterns) ->
+			let rec loop bindings pat = match pat with
+				| (PatExtractor(v,e1,pat),_) -> i + 1,Some (v,e1,pat,bindings) :: extractors
+				| (PatBind(v,pat1),_) -> loop (v :: bindings) pat1
+				| _ -> i,None :: extractors
+			in
+			loop [] (List.hd patterns)
+		) (0,[]) cases in
+		let pat_any = (PatAny,null_pos) in
+		let _,_,ex_subjects,cases,bindings = List.fold_left2 (fun (left,right,subjects,cases,ex_bindings) (case,bindings,patterns) extractor -> match extractor,patterns with
+			| Some(v,e1,pat,vars), _ :: patterns ->
+				let patterns = make_offset_list (left + 1) (right - 1) pat pat_any @ patterns in
+				let rec loop e = match e.eexpr with
+					| TLocal v' when v' == v -> subject
+					| _ -> Type.map_expr loop e
+				in
+				let e1 = loop e1 in
+				let bindings = List.map (fun v -> v,subject.epos,subject) vars @ bindings in
+				let v,ex_bindings = try
+					let v,_,_ = List.find (fun (_,_,e2) -> Texpr.equal e1 e2) ex_bindings in
+					v,ex_bindings
+				with Not_found ->
+					let v = alloc_var "_hx_tmp" e1.etype e1.epos in
+					v,(v,e1.epos,e1) :: ex_bindings
+				in
+				let ev = mk (TLocal v) v.v_type e1.epos in
+				(left + 1, right - 1,ev :: subjects,((case,bindings,patterns) :: cases),ex_bindings)
+			| None,pat :: patterns ->
+				let patterns = make_offset_list 0 num_extractors pat pat_any @ patterns in
+				(left,right,subjects,((case,bindings,patterns) :: cases),ex_bindings)
+			| _,[] ->
+				assert false
+		) (0,num_extractors,[],[],[]) cases (List.rev extractors) in
+		let dt = compile mctx ((subject :: List.rev ex_subjects) @ subjects) (List.rev cases) in
+		bind mctx bindings dt
+
+	let compile ctx match_debug subjects cases p =
+		let mctx = {
+			ctx = ctx;
+			match_debug = match_debug;
+			dt_table = DtTable.create 7;
+			match_pos = p;
+			dt_count = 0;
+		} in
+		let subjects,vars = List.fold_left (fun (subjects,vars) e -> match e.eexpr with
+			| TConst _ | TLocal _ ->
+				(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
+		let dt = compile mctx subjects cases in
+		Useless.check mctx.ctx.com cases;
+		match vars with
+			| [] -> dt
+			| _ -> bind mctx vars dt
+end
+
+module TexprConverter = struct
+	open Typecore
+	open Decision_tree
+	open Constructor
+	open Case
+
+	type match_kind =
+		| SKValue
+		| SKEnum
+		| SKFakeEnum
+		| SKLength
+
+	exception Not_exhaustive
+
+	let s_subject s e =
+		let rec loop s e = match e.eexpr with
+			| TField(e1,fa) ->
+				loop (Printf.sprintf "{ %s: %s }" (field_name fa) s) e1
+			| TEnumParameter(e1,ef,i) ->
+				let arity = match follow ef.ef_type with TFun(args,_) -> List.length args | _ -> assert false in
+				let l = make_offset_list i (arity - i - 1) s "_" in
+				loop (Printf.sprintf "%s(%s)" ef.ef_name (String.concat ", " l)) e1
+			| _ ->
+				s
+		in
+		loop s e
+
+	let s_match_kind = function
+		| SKValue -> "value"
+		| SKEnum -> "enum"
+		| SKFakeEnum -> "fakeEnum"
+		| SKLength -> "length"
+
+	let unify_constructor ctx params t con =
+		match con with
+		| ConEnum(en,ef) ->
+			let t_ef = match follow ef.ef_type with TFun(_,t) -> t | _ -> ef.ef_type in
+			let t_ef = apply_params ctx.type_params params (monomorphs en.e_params (monomorphs ef.ef_params t_ef)) in
+			let monos = List.map (fun t -> match follow t with
+				| TInst({cl_kind = KTypeParameter _},_) -> mk_mono()
+				| _ -> t
+			) params in
+			let rec duplicate_monos t = match follow t with
+				| TMono _ -> mk_mono()
+				| _ -> Type.map duplicate_monos t
+			in
+			let t_e = apply_params ctx.type_params monos (duplicate_monos t) in
+			begin try
+				Type.unify t_ef t_e;
+				Some(con,monos)
+			with Unify_error _ ->
+				None
+			end
+		| _ ->
+			Some(con,params)
+
+	let all_ctors ctx e cases =
+		let infer_type() = match cases with
+			| [] -> e,e.etype,false
+			| (con,_,_) :: _ ->
+				let fail() =
+					(* error "Could not determine switch kind, make sure the type is known" e.epos; *)
+					t_dynamic
+				in
+				let t = match con with
+					| ConEnum(en,_) -> TEnum(en,List.map snd en.e_params)
+					| ConArray _ -> ctx.t.tarray t_dynamic
+					| ConConst ct ->
+						begin match ct with
+							| TString _ -> ctx.t.tstring
+							| TInt _ -> ctx.t.tint
+							| TFloat _ -> ctx.t.tfloat
+							| TBool _ -> ctx.t.tbool
+							| _ -> fail()
+						end
+					| ConStatic({cl_kind = KAbstractImpl a},_) -> (TAbstract(a,List.map snd a.a_params))
+					| ConTypeExpr mt -> get_general_module_type ctx mt e.epos
+					| ConFields _ | ConStatic _ -> fail()
+				in
+				mk (TCast(e,None)) t e.epos,t,true
+		in
+		let e,t,inferred = match follow e.etype with
+			| TDynamic _ | TMono _ ->
+				infer_type()
+			| _ ->
+				e,e.etype,false
+		in
+		let h = ConTable.create 0 in
+		let add constructor =
+			ConTable.replace h constructor true
+		in
+		let rec loop t = match follow t with
+			| TAbstract({a_path = [],"Bool"},_) ->
+				add (ConConst(TBool true));
+				add (ConConst(TBool false));
+				SKValue,RunTimeFinite
+			| TAbstract({a_impl = Some c} as a,pl) when Meta.has Meta.Enum a.a_meta ->
+				List.iter (fun cf ->
+					ignore(follow cf.cf_type);
+					if Meta.has Meta.Impl cf.cf_meta && Meta.has Meta.Enum cf.cf_meta then match cf.cf_expr with
+						| Some {eexpr = TConst ct | TCast ({eexpr = TConst ct},None)} ->
+							if ct != TNull then add (ConConst ct)
+						| _ -> add (ConStatic(c,cf))
+				) c.cl_ordered_statics;
+				SKValue,CompileTimeFinite
+			| TAbstract(a,pl) when not (Meta.has Meta.CoreType a.a_meta) ->
+				loop (Abstract.get_underlying_type a pl)
+			| TInst({cl_path=[],"String"},_)
+			| TInst({cl_kind = KTypeParameter _ },_) ->
+				SKValue,Infinite
+			| TInst({cl_path=[],"Array"},_) ->
+				SKLength,Infinite
+			| TEnum(en,pl) ->
+				PMap.iter (fun _ ef -> add (ConEnum(en,ef))) en.e_constrs;
+				if Meta.has Meta.FakeEnum en.e_meta then
+					SKFakeEnum,CompileTimeFinite
+				else
+					SKEnum,RunTimeFinite
+			| TAnon _ ->
+				SKValue,CompileTimeFinite
+			| TInst(_,_) ->
+				SKValue,CompileTimeFinite
+			| _ ->
+				SKValue,Infinite
+		in
+		let kind,finiteness = loop t in
+		let compatible_kind con = match con with
+			| ConEnum _ -> kind = SKEnum || kind = SKFakeEnum
+			| ConArray _ -> kind = SKLength
+			| _ -> kind = SKValue
+		in
+		List.iter (fun (con,unguarded,dt) ->
+			if not (compatible_kind con) then error "Incompatible pattern" dt.dt_pos;
+			if unguarded then ConTable.remove h con
+		) cases;
+		let unmatched = ConTable.fold (fun con _ acc -> con :: acc) h [] in
+		e,unmatched,kind,finiteness
+
+	let report_not_exhaustive e_subject unmatched =
+		let sl = match follow e_subject.etype with
+			| TAbstract({a_impl = Some c} as a,tl) when Meta.has Meta.Enum a.a_meta ->
+				List.map (fun (con,_) -> match con with
+					| ConConst ct1 ->
+						let cf = List.find (fun cf ->
+							match cf.cf_expr with
+							| Some ({eexpr = TConst ct2 | TCast({eexpr = TConst ct2},None)}) -> ct1 = ct2
+							| _ -> false
+						) c.cl_ordered_statics in
+						cf.cf_name
+					| _ ->
+						Constructor.to_string con
+				) unmatched
+			| _ ->
+				List.map (fun (con,_) -> Constructor.to_string con) unmatched
+		in
+		let s = match unmatched with
+			| [] -> "_"
+			| _ -> String.concat " | " (List.sort Pervasives.compare sl)
+		in
+		error (Printf.sprintf "Unmatched patterns: %s" (s_subject s e_subject)) e_subject.epos
+
+	let to_texpr ctx t_switch match_debug with_type dt =
+		let com = ctx.com in
+		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 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
+			else
+				let cf = PMap.find "enumIndex" c_type.cl_statics in
+				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
+				mk (TConst (TString "")) ctx.t.tstring e.epos
+			else
+				let cf = PMap.find "enumConstructor" c_type.cl_statics in
+				make_static_call ctx c_type cf (fun t -> t) [e] com.basic.tstring e.epos
+		in
+		let rec loop toplevel params dt = match dt.dt_t with
+			| Leaf case ->
+				begin match case.case_expr with
+					| Some e -> e
+					| None -> mk (TBlock []) ctx.t.tvoid case.case_pos
+				end
+			| Switch(_,[ConFields _,_,dt],_) -> (* TODO: Can we improve this by making it more general? *)
+				loop false params dt
+			| Switch(e_subject,cases,default) ->
+				let e_subject,unmatched,kind,finiteness = all_ctors ctx e_subject cases in
+				let unmatched = ExtList.List.filter_map (unify_constructor ctx params e_subject.etype) unmatched in
+				let loop toplevel params dt =
+					try Some (loop toplevel params dt)
+					with Not_exhaustive -> match with_type,finiteness with
+						| NoValue,Infinite -> None
+						| _,CompileTimeFinite when unmatched = [] -> None
+						| _ when ctx.com.display <> DMNone -> 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
+					| Some(_,params) -> Some (con,dt,params)
+					| None -> None
+				) cases in
+				let group cases =
+					let h = DtTable.create 0 in
+					List.iter (fun (con,dt,params) ->
+						let l,_,_ = try DtTable.find h dt.dt_t with Not_found -> [],dt,params in
+						DtTable.replace h dt.dt_t (con :: l,dt,params)
+					) cases;
+					DtTable.fold (fun _ (cons,dt,params) acc -> (cons,dt,params) :: acc) h []
+				in
+				let cases = group cases in
+				let cases = List.sort (fun (cons1,_,_) (cons2,_,_) -> match cons1,cons2 with
+					| (con1 :: _),con2 :: _ -> Constructor.compare con1 con2
+					| _ -> -1
+				) cases in
+				let e_default = match unmatched,finiteness with
+					| [],RunTimeFinite ->
+						None
+					| _ ->
+						loop false params default
+				in
+				let cases = ExtList.List.filter_map (fun (cons,dt,params) ->
+					let eo = loop false params dt in
+					begin match eo with
+						| None -> None
+						| Some e -> Some (List.map (Constructor.to_texpr ctx match_debug dt.dt_pos) (List.sort Constructor.compare cons),e)
+					end
+				) cases in
+				let e_subject = match kind with
+					| SKValue | SKFakeEnum -> e_subject
+					| SKEnum -> if match_debug then mk_name_call e_subject else mk_index_call e_subject
+					| SKLength -> type_field_access ctx e_subject "length"
+				in
+				begin match cases with
+					| [_,e2] when e_default = None && (match finiteness with RunTimeFinite -> true | _ -> false) ->
+						e2
+					| [[e1],e2] when (with_type = NoValue || e_default <> None) && ctx.com.platform <> Java (* TODO: problem with TestJava.hx:285 *) ->
+						let e_op = mk (TBinop(OpEq,e_subject,e1)) ctx.t.tbool e_subject.epos in
+						mk (TIf(e_op,e2,e_default)) t_switch dt.dt_pos
+					| _ ->
+						let e_subject = match finiteness with
+							| RunTimeFinite | CompileTimeFinite when e_default = None ->
+								let meta = (Meta.Exhaustive,[],dt.dt_pos) in
+								mk (TMeta(meta,e_subject)) e_subject.etype e_subject.epos
+							| _ ->
+								e_subject
+						in
+						mk (TSwitch(e_subject,cases,e_default)) t_switch dt.dt_pos
+				end
+			| Guard(e,dt1,dt2) ->
+				let e_then = loop false params dt1 in
+				begin try
+					let e_else = loop false params dt2 in
+					mk (TIf(e,e_then,Some e_else)) e_then.etype (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
+			| GuardNull(e,dt1,dt2) ->
+				let e_null = Codegen.ExprBuilder.make_null e.etype e.epos in
+				let f = try
+					let e_then = loop false params dt1 in
+					(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)
+					)
+				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 report_not_exhaustive e [ConConst TNull,dt.dt_pos]
+				in
+				f()
+			| Bind(bl,dt) ->
+				let el = List.rev_map (fun (v,p,e) ->
+					mk (TVar(v,Some e)) com.basic.tvoid p
+				) bl in
+				let e = loop toplevel params dt in
+				mk (TBlock (el @ [e])) e.etype dt.dt_pos
+			| Fail ->
+				raise Not_exhaustive
+		in
+		let params = List.map snd ctx.type_params in
+		let e = loop true params dt in
+		Texpr.duplicate_tvars e
+end
+
+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) ->
+				let el = List.map (fun e -> type_expr ctx e Value) el in
+				let t = tuple_type (List.map (fun e -> e.etype) el) in
+				t,el
+			| EParenthesis e1 ->
+				loop e1
+			| _ ->
+				let e = type_expr ctx e Value in
+				e.etype,[e]
+		in
+		let t,subjects = loop e in
+		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]
+		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
+			case,bindings,[pat]
+		) cases in
+		let infer_switch_type () =
+			match with_type with
+				| NoValue -> mk_mono()
+				| Value ->
+					let el = List.map (fun (case,_,_) -> match case.Case.case_expr with Some e -> e | None -> mk (TBlock []) ctx.t.tvoid p) cases in
+					unify_min ctx el
+				| WithType t -> t
+		in
+		if match_debug then begin
+			print_endline "CASES BEGIN";
+			List.iter (fun (case,_,patterns) ->
+				print_endline (String.concat "" (List.map (Pattern.to_string) patterns));
+			) cases;
+			print_endline "CASES END";
+		end;
+		let dt = Compile.compile ctx match_debug subjects cases p in
+		if match_debug then begin
+			print_endline "DECISION TREE BEGIN";
+			print_endline (Decision_tree.to_string "" dt);
+			print_endline "DECISION TREE END";
+		end;
+		let e = try
+			let t_switch = infer_switch_type() in
+			(match tmono with Some t -> unify ctx t_switch t p | _ -> ());
+			TexprConverter.to_texpr ctx t_switch match_debug with_type dt
+		with TexprConverter.Not_exhaustive ->
+			error "Unmatched patterns: _" p;
+		in
+		if match_debug then begin
+			print_endline "TEXPR BEGIN";
+			print_endline (s_expr_pretty e);
+			print_endline "TEXPR END";
+		end;
+		e
+end
+;;
+Typecore.match_expr_ref := Match.match_expr

+ 627 - 103
type.ml → src/typing/type.ml

@@ -1,23 +1,20 @@
 (*
- * Copyright (C)2005-2013 Haxe Foundation
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
+	The 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
@@ -72,13 +69,16 @@ and tconstant =
 	| TThis
 	| TSuper
 
+and tvar_extra = (type_params * texpr option) option
+
 and tvar = {
 	mutable v_id : int;
 	mutable v_name : string;
 	mutable v_type : t;
 	mutable v_capture : bool;
-	mutable v_extra : (type_params * texpr option) option;
+	mutable v_extra : tvar_extra;
 	mutable v_meta : metadata;
+	v_pos : pos;
 }
 
 and tfunc = {
@@ -204,7 +204,7 @@ and tclass = {
 	mutable cl_init : texpr option;
 	mutable cl_overrides : tclass_field list;
 
-	mutable cl_build : unit -> bool;
+	mutable cl_build : unit -> build_state;
 	mutable cl_restore : unit -> unit;
 }
 
@@ -263,6 +263,7 @@ and tabstract = {
 	mutable a_to : t list;
 	mutable a_to_field : (t * tclass_field) list;
 	mutable a_array : tclass_field list;
+	mutable a_resolve : tclass_field option;
 }
 
 and module_type =
@@ -301,29 +302,19 @@ and module_kind =
 	| MSub
 	| MExtern
 
-and dt =
-	| DTSwitch of texpr * (texpr * dt) list * dt option
-	| DTBind of ((tvar * pos) * texpr) list * dt
-	| DTGoto of int
-	| DTExpr of texpr
-	| DTGuard of texpr * dt * dt option
-
-and decision_tree = {
-	dt_dt_lookup : dt array;
-	dt_first : int;
-	dt_type : t;
-	dt_var_init : (tvar * texpr option) list;
-	dt_is_complex : bool;
-}
+and build_state =
+	| Built
+	| Building
+	| BuildMacro of (unit -> unit) list ref
 
 (* ======= General utility ======= *)
 
 let alloc_var =
 	let uid = ref 0 in
-	(fun n t -> incr uid; { v_name = n; v_type = t; v_id = !uid; v_capture = false; v_extra = None; v_meta = [] })
+	(fun n t p -> incr uid; { v_name = n; v_type = t; v_id = !uid; v_capture = false; v_extra = None; v_meta = []; v_pos = p })
 
-let alloc_unbound_var n t =
-	let v = alloc_var n t in
+let alloc_unbound_var n t p =
+	let v = alloc_var n t p in
 	v.v_meta <- [Meta.Unbound,[],null_pos];
 	v
 
@@ -338,6 +329,9 @@ let mk_block e =
 	| TBlock _ -> e
 	| _ -> mk (TBlock [e]) e.etype e.epos
 
+let is_unbound v =
+	Meta.has Meta.Unbound v.v_meta
+
 let mk_cast e t p = mk (TCast(e,None)) t p
 
 let null t p = mk (TConst TNull) t p
@@ -373,7 +367,7 @@ let mk_class m path pos =
 		cl_constructor = None;
 		cl_init = None;
 		cl_overrides = [];
-		cl_build = (fun() -> true);
+		cl_build = (fun() -> Built);
 		cl_restore = (fun() -> ());
 	}
 
@@ -439,6 +433,7 @@ let null_abstract = {
 	a_to = [];
 	a_to_field = [];
 	a_array = [];
+	a_resolve = None;
 }
 
 let add_dependency m mdep =
@@ -487,7 +482,7 @@ let map loop t =
 				a.a_fields <- fields;
 				t
 			| _ ->
-	 			TAnon {
+				TAnon {
 					a_fields = fields;
 					a_status = a.a_status;
 				}
@@ -499,6 +494,22 @@ let map loop t =
 	| TDynamic t2 ->
 		if t == t2 then	t else TDynamic (loop t2)
 
+let dup t =
+	let monos = ref [] in
+	let rec loop t =
+		match t with
+		| TMono { contents = None } ->
+			(try
+				List.assq t !monos
+			with Not_found ->
+				let m = mk_mono() in
+				monos := (t,m) :: !monos;
+				m)
+		| _ ->
+			map loop t
+	in
+	loop t
+
 (* substitute parameters with other types *)
 let apply_params cparams params t =
 	match cparams with
@@ -557,7 +568,7 @@ let apply_params cparams params t =
 					a.a_fields <- fields;
 					t
 				| _ ->
-		 			TAnon {
+					TAnon {
 						a_fields = fields;
 						a_status = a.a_status;
 					}
@@ -675,6 +686,37 @@ let type_of_module_type = function
 	| TTypeDecl t -> TType (t,List.map snd t.t_params)
 	| TAbstractDecl a -> TAbstract (a,List.map snd a.a_params)
 
+let rec module_type_of_type = function
+	| TInst(c,_) -> TClassDecl c
+	| TEnum(en,_) -> TEnumDecl en
+	| TType(t,_) -> TTypeDecl t
+	| TAbstract(a,_) -> TAbstractDecl a
+	| TLazy f -> module_type_of_type (!f())
+	| TMono r ->
+		(match !r with
+		| Some t -> module_type_of_type t
+		| _ -> raise Exit)
+	| _ ->
+		raise Exit
+
+let tconst_to_const = function
+	| TInt i -> Int (Int32.to_string i)
+	| TFloat s -> Float s
+	| TString s -> String s
+	| TBool b -> Ident (if b then "true" else "false")
+	| TNull -> Ident "null"
+	| TThis -> Ident "this"
+	| TSuper -> Ident "super"
+
+let has_ctor_constraint c = match c.cl_kind with
+	| KTypeParameter tl ->
+		List.exists (fun t -> match follow t with
+			| TAnon a when PMap.mem "new" a.a_fields -> true
+			| TAbstract({a_path=["haxe"],"Constructible"},_) -> true
+			| _ -> false
+		) tl;
+	| _ -> false
+
 (* ======= Field utility ======= *)
 
 let field_name f =
@@ -819,6 +861,12 @@ let rec s_type_kind t =
 	| TDynamic t2 -> "TDynamic"
 	| TLazy _ -> "TLazy"
 
+let s_module_type_kind = function
+	| TClassDecl c -> "TClassDecl(" ^ (s_type_path c.cl_path) ^ ")"
+	| TEnumDecl en -> "TEnumDecl(" ^ (s_type_path en.e_path) ^ ")"
+	| TAbstractDecl a -> "TAbstractDecl(" ^ (s_type_path a.a_path) ^ ")"
+	| TTypeDecl t -> "TTypeDecl(" ^ (s_type_path t.t_path) ^ ")"
+
 let rec s_type ctx t =
 	match t with
 	| TMono r ->
@@ -1004,29 +1052,14 @@ let rec s_expr s_type e =
 	) in
 	sprintf "(%s : %s)" str (s_type e.etype)
 
-and s_dt tabs tree =
-	let s_type = s_type (print_context()) in
-	tabs ^ match tree with
-	| DTSwitch (st,cl,dto) ->
-		"switch(" ^ (s_expr s_type st) ^ ") { \n" ^ tabs
-		^ (String.concat ("\n" ^ tabs) (List.map (fun (c,dt) ->
-			"case " ^ (s_expr s_type c) ^ ":\n" ^ (s_dt (tabs ^ "\t") dt)
-		) cl))
-		^ (match dto with None -> "" | Some dt -> tabs ^ "default: " ^ (s_dt (tabs ^ "\t") dt))
-		^ "\n" ^ (if String.length tabs = 0 then "" else (String.sub tabs 0 (String.length tabs - 1))) ^ "}"
-	| DTBind (bl, dt) -> "bind " ^ (String.concat "," (List.map (fun ((v,_),st) -> v.v_name ^ "(" ^ (string_of_int v.v_id) ^ ") =" ^ (s_expr s_type st)) bl)) ^ "\n" ^ (s_dt tabs dt)
-	| DTGoto i ->
-		"goto " ^ (string_of_int i)
-	| DTExpr e -> s_expr s_type e
-	| DTGuard (e,dt1,dt2) -> "if(" ^ (s_expr s_type e) ^ ") " ^ (s_dt tabs dt1) ^ (match dt2 with None -> "" | Some dt -> " else " ^ (s_dt tabs dt))
-
-let rec s_expr_pretty tabs s_type e =
+let rec s_expr_pretty print_var_ids tabs s_type e =
 	let sprintf = Printf.sprintf in
-	let loop = s_expr_pretty tabs s_type 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 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
-	| TLocal v -> v.v_name
+	| TLocal v -> local v
 	| TArray (e1,e2) -> sprintf "%s[%s]" (loop e1) (loop e2)
 	| TBinop (op,e1,e2) -> sprintf "%s %s %s" (loop e1) (s_binop op) (loop e2)
 	| TEnumParameter (e1,_,i) -> sprintf "%s[%i]" (loop e1) i
@@ -1043,16 +1076,16 @@ let rec s_expr_pretty tabs s_type e =
 		| 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" v.v_name (s_type v.v_type) (match o with None -> "" | Some c -> " = " ^ s_const c)) f.tf_args in
+		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)
 	| TVar (v,eo) ->
-		sprintf "var %s" (sprintf "%s%s" v.v_name (match eo with None -> "" | Some e -> " = " ^ loop e))
+		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 ntabs s_type e)) el)) 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 ^ "}"
 	| TFor (v,econd,e) ->
-		sprintf "for (%s in %s) %s" v.v_name (loop econd) (loop 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)
 	| TWhile (econd,e,flag) ->
@@ -1061,10 +1094,10 @@ let rec s_expr_pretty tabs s_type 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 ntabs s_type e)) cases) (match def with None -> "" | Some e -> ntabs ^ "default: " ^ (s_expr_pretty 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 (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
 		s ^ tabs ^ "}"
 	| TTry (e,cl) ->
-		sprintf "try %s%s" (loop e) (slist (fun (v,e) -> sprintf "catch( %s : %s ) %s" v.v_name (s_type v.v_type) (loop 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)
 	| TReturn None ->
 		"return"
 	| TReturn (Some e) ->
@@ -1100,12 +1133,12 @@ let rec s_expr_ast print_var_ids tabs s_type e =
 		sprintf "[%s:%s]%s" s st (tag_args (tabs ^ extra_tabs) sl)
 	in
 	let var_id v = if print_var_ids then v.v_id else 0 in
-	let const c = sprintf "[Const %s:%s]" (s_const c) (s_type e.etype) in
+	let const c t = tag "Const" ~t [s_const c] in
 	let local v = sprintf "[Local %s(%i):%s]" v.v_name (var_id v) (s_type v.v_type) in
 	let var v sl = sprintf "[Var %s(%i):%s]%s" v.v_name (var_id v) (s_type v.v_type) (tag_args tabs sl) in
 	let module_type mt = sprintf "[TypeExpr %s:%s]" (s_type_path (t_path mt)) (s_type e.etype) in
 	match e.eexpr with
-	| TConst c -> const c
+	| TConst c -> const c (Some e.etype)
 	| TLocal v -> local v
 	| TArray (e1,e2) -> tag "Array" [loop e1; loop e2]
 	| TBinop (op,e1,e2) -> tag "Binop" [loop e1; s_binop op; loop e2]
@@ -1129,7 +1162,7 @@ let rec s_expr_ast print_var_ids tabs s_type e =
 	| TNew (c,tl,el) -> tag "New" ((s_type (TInst(c,tl))) :: (List.map loop el))
 	| TFunction f ->
 		let arg (v,cto) =
-			tag "Arg" ~t:(Some v.v_type) ~extra_tabs:"\t" (match cto with None -> [local v] | Some ct -> [local v;const ct])
+			tag "Arg" ~t:(Some v.v_type) ~extra_tabs:"\t" (match cto with None -> [local v] | Some ct -> [local v;const ct None])
 		in
 		tag "Function" ((List.map arg f.tf_args) @ [loop f.tf_expr])
 	| TVar (v,eo) -> var v (match eo with None -> [] | Some e -> [loop e])
@@ -1191,6 +1224,147 @@ let s_class_kind = function
 	| KAbstractImpl a ->
 		Printf.sprintf "KAbstractImpl %s" (s_type_path a.a_path)
 
+module Printer = struct
+
+	let s_type =
+		s_type (print_context())
+
+	let s_record_field name value =
+		Printf.sprintf "%s = %s;" name value
+
+	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
+
+	let s_list sep f l =
+		"[" ^ (String.concat sep (List.map f l)) ^ "]"
+
+	let s_opt f o = match o with
+		| None -> "None"
+		| Some v -> f v
+
+	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))
+
+	let s_metadata metadata =
+		s_list " " s_metadata_entry metadata
+
+	let s_type_param (s,t) = match follow t with
+		| TInst({cl_kind = KTypeParameter tl1},tl2) ->
+			begin match tl1 with
+			| [] -> s
+			| _ -> Printf.sprintf "%s:%s" s (String.concat ", " (List.map s_type tl1))
+			end
+		| _ -> assert false
+
+	let s_type_params tl =
+		s_list ", " s_type_param tl
+
+	let s_tclass_field cf =
+		s_record_fields "\t" [
+			"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_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 "" [
+			"cl_path",s_type_path c.cl_path;
+			"cl_module",s_type_path c.cl_module.m_path;
+			"cl_private",string_of_bool c.cl_private;
+			"cl_doc",s_doc c.cl_doc;
+			"cl_meta",s_metadata c.cl_meta;
+			"cl_params",s_type_params c.cl_params;
+			"cl_kind",s_class_kind c.cl_kind;
+			"cl_extern",string_of_bool c.cl_extern;
+			"cl_interface",string_of_bool c.cl_interface;
+			"cl_super",s_opt (fun (c,tl) -> s_type (TInst(c,tl))) c.cl_super;
+			"cl_implements",s_list ", " (fun (c,tl) -> s_type (TInst(c,tl))) c.cl_implements;
+			"cl_dynamic",s_opt s_type c.cl_dynamic;
+			"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;
+		]
+
+	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_private",string_of_bool t.t_private;
+			"t_doc",s_doc t.t_doc;
+			"t_meta",s_metadata t.t_meta;
+			"t_params",s_type_params t.t_params;
+			"t_type",s_type_kind t.t_type
+		]
+
+	let s_tenum_field ef =
+		s_record_fields "\t" [
+			"ef_name",ef.ef_name;
+			"ef_doc",s_doc ef.ef_doc;
+			"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 "" [
+			"e_path",s_type_path en.e_path;
+			"e_module",s_type_path en.e_module.m_path;
+			"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_names",String.concat ", " en.e_names
+		]
+
+	let s_tabstract a =
+		s_record_fields "" [
+			"a_path",s_type_path a.a_path;
+			"a_modules",s_type_path a.a_module.m_path;
+			"a_private",string_of_bool a.a_private;
+			"a_doc",s_doc a.a_doc;
+			"a_meta",s_metadata a.a_meta;
+			"a_params",s_type_params a.a_params;
+			"a_ops",s_list ", " (fun (op,cf) -> Printf.sprintf "%s: %s" (s_binop op) cf.cf_name) a.a_ops;
+			"a_unops",s_list ", " (fun (op,flag,cf) -> Printf.sprintf "%s (%s): %s" (s_unop op) (if flag = Postfix then "postfix" else "prefix") cf.cf_name) a.a_unops;
+			"a_impl",s_opt (fun c -> s_type_path c.cl_path) a.a_impl;
+			"a_this",s_type_kind a.a_this;
+			"a_from",s_list ", " s_type_kind a.a_from;
+			"a_to",s_list ", " s_type_kind a.a_to;
+			"a_from_field",s_list ", " (fun (t,cf) -> Printf.sprintf "%s: %s" (s_type_kind t) cf.cf_name) a.a_from_field;
+			"a_to_field",s_list ", " (fun (t,cf) -> Printf.sprintf "%s: %s" (s_type_kind t) cf.cf_name) a.a_to_field;
+			"a_array",s_list ", " (fun cf -> cf.cf_name) a.a_array;
+			"a_resolve",s_opt (fun cf -> cf.cf_name) a.a_resolve;
+		]
+
+	let s_tvar_extra (tl,eo) =
+		Printf.sprintf "Some(%s, %s)" (s_type_params tl) (s_opt (s_expr_ast true "" s_type) eo)
+
+	let s_tvar v =
+		s_record_fields "" [
+			"v_id",string_of_int v.v_id;
+			"v_name",v.v_name;
+			"v_type",s_type v.v_type;
+			"v_capture",string_of_bool v.v_capture;
+			"v_extra",s_opt s_tvar_extra v.v_extra;
+			"v_meta",s_metadata v.v_meta;
+		]
+end
+
 (* ======= Unification ======= *)
 
 let rec link e a b =
@@ -1227,6 +1401,11 @@ let rec link e a b =
 		true
 	end
 
+let link_dynamic a b = match follow a,follow b with
+	| TMono r,TDynamic _ -> r := Some b
+	| TDynamic _,TMono r -> r := Some a
+	| _ -> ()
+
 let rec fast_eq a b =
 	if a == b then
 		true
@@ -1322,8 +1501,8 @@ let unify_kind k1 k2 =
 			| MethDynamic -> direct_access v.v_read && direct_access v.v_write
 			| MethMacro -> false
 			| MethNormal | MethInline ->
-				match v.v_write with
-				| AccNo | AccNever -> true
+				match v.v_read,v.v_write with
+				| AccNormal,(AccNo | AccNever) -> true
 				| _ -> false)
 		| Method m1, Method m2 ->
 			match m1,m2 with
@@ -1343,7 +1522,7 @@ type eq_kind =
 let rec type_eq param a b =
 	let can_follow t = match param with
 		| EqCoreType -> false
-		| EqDoNotFollowNull -> not (is_null t)
+		| EqDoNotFollowNull -> not (is_explicit_null t)
 		| _ -> true
 	in
 	if a == b then
@@ -1402,10 +1581,17 @@ let rec type_eq param a b =
 				try
 					let f2 = PMap.find n a2.a_fields in
 					if f1.cf_kind <> f2.cf_kind && (param = EqStrict || param = EqCoreType || not (unify_kind f1.cf_kind f2.cf_kind)) then error [invalid_kind n f1.cf_kind f2.cf_kind];
-					try
-						type_eq param f1.cf_type f2.cf_type
-					with
-						Unify_error l -> error (invalid_field n :: l)
+					let a = f1.cf_type and b = f2.cf_type in
+					if not (List.exists (fun (a2,b2) -> fast_eq a a2 && fast_eq b b2) (!eq_stack)) then begin
+						eq_stack := (a,b) :: !eq_stack;
+						try
+							type_eq param a b;
+							eq_stack := List.tl !eq_stack;
+						with
+							Unify_error l ->
+								eq_stack := List.tl !eq_stack;
+								error (invalid_field n :: l)
+					end;
 				with
 					Not_found ->
 						if is_closed a2 then error [has_no_field b n];
@@ -1436,6 +1622,13 @@ let type_iseq a b =
 	with
 		Unify_error _ -> false
 
+let type_iseq_strict a b =
+	try
+		type_eq EqDoNotFollowNull a b;
+		true
+	with Unify_error _ ->
+		false
+
 let unify_stack = ref []
 let abstract_cast_stack = ref []
 let unify_new_monos = ref []
@@ -1508,7 +1701,12 @@ let rec unify a b =
 				loop cs (List.map (apply_params c.cl_params tl) tls)
 			) c.cl_implements
 			|| (match c.cl_kind with
-			| KTypeParameter pl -> List.exists (fun t -> match follow t with TInst (cs,tls) -> loop cs (List.map (apply_params c.cl_params tl) tls) | _ -> false) pl
+			| KTypeParameter pl -> List.exists (fun t ->
+				match follow t with
+				| TInst (cs,tls) -> loop cs (List.map (apply_params c.cl_params tl) tls)
+				| TAbstract(aa,tl) -> List.exists (unify_to aa tl b) aa.a_to
+				| _ -> false
+			) pl
 			| _ -> false)
 		in
 		if not (loop c1 tl1) then error [cannot_unify a b]
@@ -1552,20 +1750,48 @@ let rec unify a b =
 				let ft = apply_params c.cl_params tl ft in
 				if not (unify_kind f1.cf_kind f2.cf_kind) then error [invalid_kind n f1.cf_kind f2.cf_kind];
 				if f2.cf_public && not f1.cf_public then error [invalid_visibility n];
-				let old_monos = !unify_new_monos in
-				unify_new_monos := !monos @ !unify_new_monos;
-				if not (List.exists (fun (a2,b2) -> fast_eq b2 f2.cf_type && fast_eq_mono !unify_new_monos ft a2) (!unify_stack)) then begin
-					unify_stack := (ft,f2.cf_type) :: !unify_stack;
-					(try
+
+				(match f2.cf_kind with
+				| Var { v_read = AccNo } | Var { v_read = AccNever } ->
+					(* we will do a recursive unification, so let's check for possible recursion *)
+					let old_monos = !unify_new_monos in
+					unify_new_monos := !monos @ !unify_new_monos;
+					if not (List.exists (fun (a2,b2) -> fast_eq b2 f2.cf_type && fast_eq_mono !unify_new_monos ft a2) (!unify_stack)) then begin
+						unify_stack := (ft,f2.cf_type) :: !unify_stack;
+						(try
+							unify_with_access ft f2
+						with
+							Unify_error l ->
+								unify_new_monos := old_monos;
+								unify_stack := List.tl !unify_stack;
+								error (invalid_field n :: l));
+						unify_stack := List.tl !unify_stack;
+					end;
+					unify_new_monos := old_monos;
+				| Method MethNormal | Method MethInline | Var { v_write = AccNo } | Var { v_write = AccNever } ->
+					(* same as before, but unification is reversed (read-only var) *)
+					let old_monos = !unify_new_monos in
+					unify_new_monos := !monos @ !unify_new_monos;
+					if not (List.exists (fun (a2,b2) -> fast_eq b2 ft && fast_eq_mono !unify_new_monos f2.cf_type a2) (!unify_stack)) then begin
+						unify_stack := (f2.cf_type,ft) :: !unify_stack;
+						(try
+							unify_with_access ft f2
+						with
+							Unify_error l ->
+								unify_new_monos := old_monos;
+								unify_stack := List.tl !unify_stack;
+								error (invalid_field n :: l));
+						unify_stack := List.tl !unify_stack;
+					end;
+					unify_new_monos := old_monos;
+				| _ ->
+					(* will use fast_eq, which have its own stack *)
+					try
 						unify_with_access ft f2
 					with
 						Unify_error l ->
-							unify_new_monos := old_monos;
-							unify_stack := List.tl !unify_stack;
 							error (invalid_field n :: l));
-					unify_stack := List.tl !unify_stack;
-				end;
-				unify_new_monos := old_monos;
+
 				List.iter (fun f2o ->
 					if not (List.exists (fun f1o -> type_iseq f1o.cf_type f2o.cf_type) (f1 :: f1.cf_overloads))
 					then error [Missing_overload (f1, f2o.cf_type)]
@@ -1599,6 +1825,21 @@ let rec unify a b =
 		()
 	| TFun _, TAbstract ({ a_path = ["haxe"],"Function" },[]) ->
 		()
+	| TInst(c,tl),TAbstract({a_path = ["haxe"],"Constructible"},[t1]) ->
+		begin try
+			begin match c.cl_kind with
+				| KTypeParameter tl ->
+					(* type parameters require an equal Constructible constraint *)
+					if not (List.exists (fun t -> match follow t with TAbstract({a_path = ["haxe"],"Constructible"},[t2]) -> type_iseq t1 t2 | _ -> false) tl) then error [cannot_unify a b]
+				| _ ->
+					let _,t,cf = class_field c tl "new" in
+					if not cf.cf_public then error [invalid_visibility "new"];
+					begin try unify t1 t
+					with Unify_error l -> error (cannot_unify a b :: l) end
+			end
+		with Not_found ->
+			error [has_no_field a "new"]
+		end
 	| TDynamic t , _ ->
 		if t == a then
 			()
@@ -1865,11 +2106,13 @@ module Abstract = struct
 
 	let rec get_underlying_type a pl =
 		let maybe_recurse t =
-			underlying_type_stack := a :: !underlying_type_stack;
+			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.mem a !underlying_type_stack then begin
-						let s = String.concat " -> " (List.map (fun a -> s_type_path a.a_path) (List.rev (a :: !underlying_type_stack))) in
+					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
@@ -1899,16 +2142,6 @@ end
 
 (* ======= Mapping and iterating ======= *)
 
-let iter_dt f dt = match dt with
-	| DTBind(_,dt) -> f dt
-	| DTSwitch(_,cl,dto) ->
-		List.iter (fun (_,dt) -> f dt) cl;
-		(match dto with None -> () | Some dt -> f dt)
-	| DTGuard(_,dt1,dt2) ->
-		f dt1;
-		(match dt2 with None -> () | Some dt -> f dt)
-	| DTGoto _ | DTExpr _ -> ()
-
 let iter f e =
 	match e.eexpr with
 	| TConst _
@@ -1997,7 +2230,8 @@ let map_expr f e =
 	| TObjectDecl el ->
 		{ e with eexpr = TObjectDecl (List.map (fun (v,e) -> v, f e) el) }
 	| TCall (e1,el) ->
-		{ e with eexpr = TCall (f e1, List.map f el) }
+		let e1 = f e1 in
+		{ e with eexpr = TCall (e1, List.map f el) }
 	| TVar (v,eo) ->
 		{ e with eexpr = TVar (v, match eo with None -> None | Some e -> Some (f e)) }
 	| TFunction fu ->
@@ -2111,5 +2345,295 @@ let map_expr_type f ft fv e =
 	| TMeta (m,e1) ->
 		{e with eexpr = TMeta(m, f e1); etype = ft e.etype }
 
-let print_if b e =
-	if b then print_endline (s_expr_pretty "" (s_type (print_context())) e)
+module TExprToExpr = struct
+	let tpath p mp pl =
+		if snd mp = snd p then
+			CTPath {
+				tpackage = fst p;
+				tname = snd p;
+				tparams = List.map (fun t -> TPType t) pl;
+				tsub = None;
+			}
+		else CTPath {
+				tpackage = fst mp;
+				tname = snd mp;
+				tparams = List.map (fun t -> TPType t) pl;
+				tsub = Some (snd p);
+			}
+
+	let rec convert_type = function
+		| TMono r ->
+			(match !r with
+			| None -> raise Exit
+			| Some t -> convert_type t)
+		| TInst ({cl_private = true; cl_path=_,name},tl)
+		| TEnum ({e_private = true; e_path=_,name},tl)
+		| TType ({t_private = true; t_path=_,name},tl)
+		| TAbstract ({a_private = true; a_path=_,name},tl) ->
+			CTPath {
+				tpackage = [];
+				tname = name;
+				tparams = List.map (fun t -> TPType (convert_type' t)) tl;
+				tsub = None;
+			}
+		| TEnum (e,pl) ->
+			tpath e.e_path e.e_module.m_path (List.map convert_type' pl)
+		| TInst({cl_kind = KTypeParameter _} as c,pl) ->
+			tpath ([],snd c.cl_path) ([],snd c.cl_path) (List.map convert_type' pl)
+		| TInst (c,pl) ->
+			tpath c.cl_path c.cl_module.m_path (List.map convert_type' pl)
+		| TType (t,pl) as tf ->
+			(* recurse on type-type *)
+			if (snd t.t_path).[0] = '#' then convert_type (follow tf) else tpath t.t_path t.t_module.m_path (List.map convert_type' pl)
+		| TAbstract (a,pl) ->
+			tpath a.a_path a.a_module.m_path (List.map convert_type' pl)
+		| TFun (args,ret) ->
+			CTFunction (List.map (fun (_,_,t) -> convert_type' t) args, (convert_type' ret))
+		| TAnon a ->
+			begin match !(a.a_status) with
+			| Statics c -> tpath ([],"Class") ([],"Class") [tpath c.cl_path c.cl_path [],null_pos]
+			| EnumStatics e -> tpath ([],"Enum") ([],"Enum") [tpath e.e_path e.e_path [],null_pos]
+			| _ ->
+				CTAnonymous (PMap.foldi (fun _ f acc ->
+					{
+						cff_name = f.cf_name,null_pos;
+						cff_kind = FVar (mk_type_hint f.cf_type null_pos,None);
+						cff_pos = f.cf_pos;
+						cff_doc = f.cf_doc;
+						cff_meta = f.cf_meta;
+						cff_access = [];
+					} :: acc
+				) a.a_fields [])
+			end
+		| (TDynamic t2) as t ->
+			tpath ([],"Dynamic") ([],"Dynamic") (if t == t_dynamic then [] else [convert_type' t2])
+		| TLazy f ->
+			convert_type ((!f)())
+
+	and convert_type' t =
+		convert_type t,null_pos
+
+	and mk_type_hint t p =
+		match follow t with
+		| TMono _ -> None
+		| _ -> (try Some (convert_type t,p) with Exit -> None)
+
+	let rec convert_expr e =
+		let full_type_path t =
+			let mp,p = match t with
+			| TClassDecl c -> c.cl_module.m_path,c.cl_path
+			| TEnumDecl en -> en.e_module.m_path,en.e_path
+			| TAbstractDecl a -> a.a_module.m_path,a.a_path
+			| TTypeDecl t -> t.t_module.m_path,t.t_path
+			in
+			if snd mp = snd p then p else (fst mp) @ [snd mp],snd p
+		in
+		let mk_path = expr_of_type_path in
+		let mk_ident = function
+			| "`trace" -> Ident "trace"
+			| n -> Ident n
+		in
+		let eopt = function None -> None | Some e -> Some (convert_expr e) in
+		((match e.eexpr with
+		| TConst c ->
+			EConst (tconst_to_const c)
+		| TLocal v -> EConst (mk_ident v.v_name)
+		| TArray (e1,e2) -> EArray (convert_expr e1,convert_expr e2)
+		| TBinop (op,e1,e2) -> EBinop (op, convert_expr e1, convert_expr e2)
+		| TField (e,f) -> EField (convert_expr e, field_name f)
+		| 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)
+		| 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)
+		| TUnop (op,p,e) -> EUnop (op,p,convert_expr e)
+		| TFunction f ->
+			let arg (v,c) = (v.v_name,v.v_pos), false, v.v_meta, mk_type_hint v.v_type null_pos, (match c with None -> None | Some c -> Some (EConst (tconst_to_const c),e.epos)) in
+			EFunction (None,{ f_params = []; f_args = List.map arg f.tf_args; f_type = mk_type_hint f.tf_type null_pos; f_expr = Some (convert_expr f.tf_expr) })
+		| TVar (v,eo) ->
+			EVars ([(v.v_name,v.v_pos), mk_type_hint v.v_type v.v_pos, eopt eo])
+		| TBlock el -> EBlock (List.map convert_expr el)
+		| TFor (v,it,e) ->
+			let ein = (EIn ((EConst (Ident v.v_name),it.epos),convert_expr it),it.epos) in
+			EFor (ein,convert_expr e)
+		| TIf (e,e1,e2) -> EIf (convert_expr e,convert_expr e1,eopt e2)
+		| 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))
+			) cases in
+			let def = match eopt def with None -> None | Some (EBlock [],_) -> Some None | e -> Some 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)
+		| TReturn e -> EReturn (eopt e)
+		| TBreak -> EBreak
+		| TContinue -> EContinue
+		| TThrow e -> EThrow (convert_expr e)
+		| TCast (e,t) ->
+			let t = (match t with
+				| None -> None
+				| Some t ->
+					let t = (match t with TClassDecl c -> TInst (c,[]) | TEnumDecl e -> TEnum (e,[]) | TTypeDecl t -> TType (t,[]) | TAbstractDecl a -> TAbstract (a,[])) in
+					Some (try convert_type t,null_pos with Exit -> assert false)
+			) in
+			ECast (convert_expr e,t)
+		| TMeta ((Meta.Ast,[e1,_],_),_) -> e1
+		| TMeta (m,e) -> EMeta(m,convert_expr e))
+		,e.epos)
+
+end
+
+module Texpr = struct
+	let equal_fa fa1 fa2 = match fa1,fa2 with
+		| FStatic(c1,cf1),FStatic(c2,cf2) -> c1 == c2 && cf1 == cf2
+		| FInstance(c1,tl1,cf1),FInstance(c2,tl2,cf2) -> c1 == c2 && safe_for_all2 type_iseq tl1 tl2 && cf1 == cf2
+		(* TODO: This is technically not correct but unfortunately the compiler makes a distinct tclass_field for each anon field access. *)
+		| FAnon cf1,FAnon cf2 -> cf1.cf_name = cf2.cf_name
+		| FDynamic s1,FDynamic s2 -> s1 = s2
+		| FClosure(None,cf1),FClosure(None,cf2) -> cf1 == cf2
+		| FClosure(Some(c1,tl1),cf1),FClosure(Some(c2,tl2),cf2) -> c1 == c2 && safe_for_all2 type_iseq tl1 tl2 && cf1 == cf2
+		| FEnum(en1,ef1),FEnum(en2,ef2) -> en1 == en2 && ef1 == ef2
+		| _ -> false
+
+	let rec equal e1 e2 = match e1.eexpr,e2.eexpr with
+		| TConst ct1,TConst ct2 -> ct1 = ct2
+		| TLocal v1,TLocal v2 -> v1 == v2
+		| TArray(eb1,ei1),TArray(eb2,ei2) -> equal eb1 eb2 && equal ei1 ei2
+		| TBinop(op1,lhs1,rhs1),TBinop(op2,lhs2,rhs2) -> op1 = op2 && equal lhs1 lhs2 && equal rhs1 rhs2
+		| TField(e1,fa1),TField(e2,fa2) -> equal e1 e2 && equal_fa fa1 fa2
+		| TTypeExpr mt1,TTypeExpr mt2 -> mt1 == mt2
+		| TParenthesis e1,TParenthesis e2 -> equal e1 e2
+		| TObjectDecl fl1,TObjectDecl fl2 -> safe_for_all2 (fun (s1,e1) (s2,e2) -> s1 = s2 && equal e1 e2) fl1 fl2
+		| (TArrayDecl el1,TArrayDecl el2) | (TBlock el1,TBlock el2) -> safe_for_all2 equal el1 el2
+		| TCall(e1,el1),TCall(e2,el2) -> equal e1 e2 && safe_for_all2 equal el1 el2
+		| TNew(c1,tl1,el1),TNew(c2,tl2,el2) -> c1 == c2 && safe_for_all2 type_iseq tl1 tl2 && safe_for_all2 equal el1 el2
+		| TUnop(op1,flag1,e1),TUnop(op2,flag2,e2) -> op1 = op2 && flag1 = flag2 && equal e1 e2
+		| TFunction tf1,TFunction tf2 -> tf1 == tf2
+		| TVar(v1,None),TVar(v2,None) -> v1 == v2
+		| TVar(v1,Some e1),TVar(v2,Some e2) -> v1 == v2 && equal e1 e2
+		| TFor(v1,ec1,eb1),TFor(v2,ec2,eb2) -> v1 == v2 && equal ec1 ec2 && equal eb1 eb2
+		| TIf(e1,ethen1,None),TIf(e2,ethen2,None) -> equal e1 e2 && equal ethen1 ethen2
+		| TIf(e1,ethen1,Some eelse1),TIf(e2,ethen2,Some eelse2) -> equal e1 e2 && equal ethen1 ethen2 && equal eelse1 eelse2
+		| TWhile(e1,eb1,flag1),TWhile(e2,eb2,flag2) -> equal e1 e2 && equal eb2 eb2 && flag1 = flag2
+		| TSwitch(e1,cases1,eo1),TSwitch(e2,cases2,eo2) ->
+			equal e1 e2 &&
+			safe_for_all2 (fun (el1,e1) (el2,e2) -> safe_for_all2 equal el1 el2 && equal e1 e2) cases1 cases2 &&
+			(match eo1,eo2 with None,None -> true | Some e1,Some e2 -> equal e1 e2 | _ -> false)
+		| TTry(e1,catches1),TTry(e2,catches2) -> equal e1 e2 && safe_for_all2 (fun (v1,e1) (v2,e2) -> v1 == v2 && equal e1 e2) catches1 catches2
+		| TReturn None,TReturn None -> true
+		| TReturn(Some e1),TReturn(Some e2) -> equal e1 e2
+		| TThrow e1,TThrow e2 -> equal e1 e2
+		| TCast(e1,None),TCast(e2,None) -> equal e1 e2
+		| TCast(e1,Some mt1),TCast(e2,Some mt2) -> equal e1 e2 && mt1 == mt2
+		| TMeta((m1,el1,_),e1),TMeta((m2,el2,_),e2) -> m1 = m2 && safe_for_all2 (fun e1 e2 -> (* TODO: cheating? *) (Ast.s_expr e1) = (Ast.s_expr e2)) el1 el2 && equal e1 e2
+		| (TBreak,TBreak) | (TContinue,TContinue) -> true
+		| TEnumParameter(e1,ef1,i1),TEnumParameter(e2,ef2,i2) -> equal e1 e2 && ef1 == ef2 && i1 = i2
+		| _ -> false
+
+	let duplicate_tvars e =
+		let vars = Hashtbl.create 0 in
+		let copy_var v =
+			let v2 = alloc_var v.v_name v.v_type v.v_pos in
+			v2.v_meta <- v.v_meta;
+			Hashtbl.add vars v.v_id v2;
+			v2;
+		in
+		let rec build_expr e =
+			match e.eexpr with
+			| TVar (v,eo) ->
+				let v2 = copy_var v in
+				{e with eexpr = TVar(v2, Option.map build_expr eo)}
+			| TFor (v,e1,e2) ->
+				let v2 = copy_var v in
+				{e with eexpr = TFor(v2, build_expr e1, build_expr e2)}
+			| TTry (e1,cl) ->
+				let cl = List.map (fun (v,e) ->
+					let v2 = copy_var v in
+					v2, build_expr e
+				) cl in
+				{e with eexpr = TTry(build_expr e1, cl)}
+			| TFunction f ->
+				let args = List.map (fun (v,c) -> copy_var v, c) f.tf_args in
+				let f = {
+					tf_args = args;
+					tf_type = f.tf_type;
+					tf_expr = build_expr f.tf_expr;
+				} in
+				{e with eexpr = TFunction f}
+			| TLocal v ->
+				(try
+					let v2 = Hashtbl.find vars v.v_id in
+					{e with eexpr = TLocal v2}
+				with _ ->
+					e)
+			| _ ->
+				map_expr build_expr e
+		in
+		build_expr e
+
+	let rec skip e = match e.eexpr with
+		| TParenthesis e1 | TMeta(_,e1) | TBlock [e1] | TCast(e1,None) -> skip e1
+		| _ -> e
+end
+
+module ExtType = struct
+	let is_void = function
+		| TAbstract({a_path=[],"Void"},_) -> true
+		| _ -> false
+end
+
+module StringError = struct
+	(* Source: http://en.wikibooks.org/wiki/Algorithm_implementation/Strings/Levenshtein_distance#OCaml *)
+	let levenshtein a b =
+		let x = Array.init (String.length a) (fun i -> a.[i]) in
+		let y = Array.init (String.length b) (fun i -> b.[i]) in
+		let minimum (x:int) y z =
+			let m' (a:int) b = if a < b then a else b in
+			m' (m' x y) z
+		in
+		let init_matrix n m =
+			let init_col = Array.init m in
+				Array.init n (function
+				| 0 -> init_col (function j -> j)
+				| i -> init_col (function 0 -> i | _ -> 0)
+			)
+		in
+		match Array.length x, Array.length y with
+			| 0, n -> n
+			| m, 0 -> m
+			| m, n ->
+				let matrix = init_matrix (m + 1) (n + 1) in
+				for i = 1 to m do
+					let s = matrix.(i) and t = matrix.(i - 1) in
+					for j = 1 to n do
+						let cost = abs (compare x.(i - 1) y.(j - 1)) in
+						s.(j) <- minimum (t.(j) + 1) (s.(j - 1) + 1) (t.(j - 1) + cost)
+					done
+				done;
+				matrix.(m).(n)
+
+	let filter_similar f cl =
+		let rec loop sl = match sl with
+			| (x,i) :: sl when f x i -> x :: loop sl
+			| _ -> []
+		in
+		loop cl
+
+	let string_error_raise s sl msg =
+		if sl = [] then msg else
+		let cl = List.map (fun s2 -> s2,levenshtein s s2) sl in
+		let cl = List.sort (fun (_,c1) (_,c2) -> compare c1 c2) cl in
+		let cl = filter_similar (fun s2 i -> i <= (min (String.length s) (String.length s2)) / 3) cl in
+		match cl with
+			| [] -> raise Not_found
+			| [s] -> Printf.sprintf "%s (Suggestion: %s)" msg s
+			| sl -> Printf.sprintf "%s (Suggestions: %s)" msg (String.concat ", " sl)
+
+	let string_error s sl msg =
+		try string_error_raise s sl msg
+		with Not_found -> msg
+end

+ 83 - 94
typecore.ml → src/typing/typecore.ml

@@ -1,23 +1,20 @@
 (*
- * Copyright (C)2005-2013 Haxe Foundation
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
+	The 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 Common
@@ -27,7 +24,6 @@ type with_type =
 	| NoValue
 	| Value
 	| WithType of t
-	| WithTypeResume of t
 
 type type_patch = {
 	mutable tp_type : Ast.complex_type option;
@@ -47,7 +43,7 @@ type macro_mode =
 	| MExpr
 	| MBuild
 	| MMacroType
-
+	| MDisplay
 
 type typer_pass =
 	| PBuildModule			(* build the module structure and setup module type parameters *)
@@ -57,6 +53,15 @@ type typer_pass =
 	| PForce				(* usually ensure that lazy have been evaluated *)
 	| PFinal				(* not used, only mark for finalize *)
 
+type typer_module = {
+	curmod : module_def;
+	mutable module_types : (module_type * pos) list;
+	mutable module_using : (tclass * pos) list;
+	mutable module_globals : (string, (module_type * string * pos)) PMap.t;
+	mutable wildcard_packages : (string list * pos) list;
+	mutable module_imports : Ast.import list;
+}
+
 type typer_globals = {
 	types_module : (path, path) Hashtbl.t;
 	modules : (path , module_def) Hashtbl.t;
@@ -71,9 +76,9 @@ type typer_globals = {
 	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;
 	delayed_macros : (unit -> unit) DynArray.t;
-	mutable global_using : tclass list;
+	mutable global_using : (tclass * Ast.pos) list;
 	(* api *)
-	do_inherit : typer -> Type.tclass -> Ast.pos -> Ast.class_flag -> bool;
+	do_inherit : typer -> Type.tclass -> Ast.pos -> (bool * Ast.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_load_module : typer -> path -> pos -> module_def;
@@ -81,14 +86,6 @@ type typer_globals = {
 	do_build_instance : typer -> module_type -> pos -> ((string * t) list * path * (t list -> t));
 }
 
-and typer_module = {
-	curmod : module_def;
-	mutable module_types : module_type list;
-	mutable module_using : tclass list;
-	mutable module_globals : (string, (module_type * string)) PMap.t;
-	mutable wildcard_packages : string list list;
-}
-
 and typer = {
 	(* shared *)
 	com : context;
@@ -102,6 +99,7 @@ and typer = {
 	mutable pass : typer_pass;
 	(* per-module *)
 	mutable m : typer_module;
+	mutable is_display_file : bool;
 	(* per-class *)
 	mutable curclass : tclass;
 	mutable tthis : t;
@@ -109,7 +107,6 @@ and typer = {
 	(* per-function *)
 	mutable curfield : tclass_field;
 	mutable untyped : bool;
-	mutable in_super_call : bool;
 	mutable in_loop : bool;
 	mutable in_display : bool;
 	mutable in_macro : bool;
@@ -119,12 +116,13 @@ and typer = {
 	mutable locals : (string, tvar) PMap.t;
 	mutable opened : anon_status ref list;
 	mutable vthis : tvar option;
+	mutable in_call_args : bool;
 	(* events *)
 	mutable on_error : typer -> string -> pos -> unit;
 }
 
 type call_error =
-	| Not_enough_arguments
+	| Not_enough_arguments of (string * bool * t) list
 	| Too_many_arguments
 	| Could_not_unify of error_msg
 	| Cannot_skip_non_nullable of string
@@ -144,65 +142,18 @@ exception Forbid_package of (string * path * pos) * pos list * string
 
 exception Error of error_msg * pos
 
-exception DisplayTypes of t list
-
-exception DisplayPosition of Ast.pos list
+exception WithTypeError of unify_error list * 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_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 -> decision_tree) 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 find_array_access_raise_ref : (typer -> tabstract -> tparams -> texpr -> texpr option -> pos -> (tclass_field * t * t * texpr * texpr option)) ref = ref (fun _ _ _ _ _ _ -> assert false)
-
-(* Source: http://en.wikibooks.org/wiki/Algorithm_implementation/Strings/Levenshtein_distance#OCaml *)
-let levenshtein a b =
-	let x = Array.init (String.length a) (fun i -> a.[i]) in
-	let y = Array.init (String.length b) (fun i -> b.[i]) in
-	let minimum (x:int) y z =
-		let m' (a:int) b = if a < b then a else b in
-		m' (m' x y) z
-	in
-	let init_matrix n m =
-		let init_col = Array.init m in
-			Array.init n (function
-			| 0 -> init_col (function j -> j)
-			| i -> init_col (function 0 -> i | _ -> 0)
-		)
-	in
-	match Array.length x, Array.length y with
-		| 0, n -> n
-		| m, 0 -> m
-		| m, n ->
-			let matrix = init_matrix (m + 1) (n + 1) in
-			for i = 1 to m do
-				let s = matrix.(i) and t = matrix.(i - 1) in
-				for j = 1 to n do
-					let cost = abs (compare x.(i - 1) y.(j - 1)) in
-					s.(j) <- minimum (t.(j) + 1) (s.(j - 1) + 1) (t.(j - 1) + cost)
-				done
-			done;
-			matrix.(m).(n)
-
-let string_error_raise s sl msg =
-	if sl = [] then msg else
-	let cl = List.map (fun s2 -> s2,levenshtein s s2) sl in
-	let cl = List.sort (fun (_,c1) (_,c2) -> compare c1 c2) cl in
-	let rec loop sl = match sl with
-		| (s2,i) :: sl when i <= (min (String.length s) (String.length s2)) / 3 -> s2 :: loop sl
-		| _ -> []
-	in
-	match loop cl with
-		| [] -> raise Not_found
-		| [s] -> Printf.sprintf "%s (Suggestion: %s)" msg s
-		| sl -> Printf.sprintf "%s (Suggestions: %s)" msg (String.concat ", " sl)
-
-let string_error s sl msg =
-	try string_error_raise s sl msg
-	with Not_found -> msg
+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
@@ -220,7 +171,7 @@ let unify_error_msg ctx = function
 	| Invalid_field_type s ->
 		"Invalid type for field " ^ s ^ " :"
 	| Has_no_field (t,n) ->
-		string_error n (string_source t) (short_type ctx t ^ " has no field " ^ 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) ->
@@ -265,7 +216,9 @@ let rec error_msg = function
 	| Call_error err -> s_call_error err
 
 and s_call_error = function
-	| Not_enough_arguments -> "Not enough arguments"
+	| 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
@@ -278,7 +231,9 @@ let pass_name = function
 	| PForce -> "force"
 	| PFinal -> "final"
 
-let display_error ctx msg p = ctx.on_error ctx msg p
+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))
 
@@ -290,20 +245,35 @@ let unify_min ctx el = (!unify_min_ref) ctx el
 
 let match_expr ctx e cases def with_type p = !match_expr_ref ctx e cases def with_type p
 
-let make_static_call ctx c cf map args t p =
+let make_static_this c p =
 	let ta = TAnon { a_fields = c.cl_statics; a_status = ref (Statics c) } in
-	let ethis = mk (TTypeExpr (TClassDecl c)) ta p in
+	mk (TTypeExpr (TClassDecl c)) ta p
+
+let make_static_field_access c cf t p =
+	let ethis = make_static_this c p in
+	mk (TField (ethis,(FStatic (c,cf)))) t p
+
+let make_static_call ctx c cf map args t p =
 	let monos = List.map (fun _ -> mk_mono()) cf.cf_params in
 	let map t = map (apply_params cf.cf_params monos t) in
-	let ef = mk (TField (ethis,(FStatic (c,cf)))) (map cf.cf_type) p in
+	let ef = make_static_field_access c cf (map cf.cf_type) p in
 	make_call ctx ef args (map 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 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))
+	else display_error ctx msg p
+
 let unify ctx t1 t2 p =
 	try
 		Type.unify t1 t2
 	with
 		Unify_error l ->
-			if not ctx.untyped then display_error ctx (error_msg (Unify l)) p
+			raise_or_display ctx l p
 
 let unify_raise ctx t1 t2 p =
 	try
@@ -317,14 +287,14 @@ let save_locals ctx =
 	let locals = ctx.locals in
 	(fun() -> ctx.locals <- locals)
 
-let add_local ctx n t =
-	let v = alloc_var n t in
+let add_local ctx n t p =
+	let v = alloc_var n t p in
 	ctx.locals <- PMap.add n v ctx.locals;
 	v
 
 let gen_local_prefix = "`"
 
-let gen_local ctx t =
+let gen_local ctx t p =
 	(* ensure that our generated local does not mask an existing one *)
 	let rec loop n =
 		let nv = (if n = 0 then gen_local_prefix else gen_local_prefix ^ string_of_int n) in
@@ -333,7 +303,7 @@ let gen_local ctx t =
 		else
 			nv
 	in
-	add_local ctx (loop 0) t
+	add_local ctx (loop 0) t p
 
 let is_gen_local v =
 	String.unsafe_get v.v_name 0 = String.unsafe_get gen_local_prefix 0
@@ -354,6 +324,17 @@ let delay ctx p f =
 	in
 	ctx.g.delayed <- loop ctx.g.delayed
 
+let delay_late ctx p f =
+	let rec loop = function
+		| [] -> [p,[f]]
+		| (p2,l) :: rest ->
+			if p2 <= p then
+				(p2,l) :: loop rest
+			else
+				(p,[f]) :: (p2,l) :: rest
+	in
+	ctx.g.delayed <- loop ctx.g.delayed
+
 let rec flush_pass ctx p (where:string) =
 	match ctx.g.delayed with
 	| (p2,l) :: rest when p2 <= p ->
@@ -398,6 +379,14 @@ let create_fake_module ctx file =
 	Hashtbl.replace ctx.g.modules mdep.m_path mdep;
 	mdep
 
+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 () -> ()
+	| _ ->
+		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
+		er,fun () -> ctx.this_stack <- List.tl ctx.this_stack
+
 (* -------------- debug functions to activate when debugging typer passes ------------------------------- *)
 (*/*
 

+ 4187 - 0
src/typing/typeload.ml

@@ -0,0 +1,4187 @@
+(*
+	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 Type
+open Common
+open Typecore
+
+exception Build_canceled of build_state
+
+let locate_macro_error = ref true
+
+let transform_abstract_field com this_t a_t a f =
+	let stat = List.mem AStatic f.cff_access in
+	let p = f.cff_pos in
+	match f.cff_kind with
+	| FProp (("get" | "never"),("set" | "never"),_,_) when not stat ->
+		(* TODO: hack to avoid issues with abstract property generation on As3 *)
+		if Common.defined com Define.As3 then f.cff_meta <- (Meta.Extern,[],p) :: f.cff_meta;
+		{ f with cff_access = AStatic :: f.cff_access; cff_meta = (Meta.Impl,[],p) :: f.cff_meta }
+	| FProp _ when not stat ->
+		error "Member property accessors must be get/set or never" p;
+	| FFun fu when fst f.cff_name = "new" && not stat ->
+		let init p = (EVars [("this",null_pos),Some this_t,None],p) in
+		let cast e = (ECast(e,None)),pos e in
+		let ret p = (EReturn (Some (cast (EConst (Ident "this"),p))),p) in
+		let meta = (Meta.Impl,[],p) :: f.cff_meta in
+		let meta = if Meta.has Meta.MultiType a.a_meta then begin
+			if List.mem AInline f.cff_access then error "MultiType constructors cannot be inline" f.cff_pos;
+			if fu.f_expr <> None then error "MultiType constructors cannot have a body" f.cff_pos;
+			(Meta.Extern,[],f.cff_pos) :: meta
+		end else
+			meta
+		in
+		(* We don't want the generated expression positions to shadow the real code. *)
+		let p = { p with pmax = p.pmin } in
+		let fu = {
+			fu with
+			f_expr = (match fu.f_expr with
+			| None -> if Meta.has Meta.MultiType a.a_meta then Some (EConst (Ident "null"),p) else None
+			| Some (EBlock el,_) -> Some (EBlock (init p :: el @ [ret p]),p)
+			| Some e -> Some (EBlock [init p;e;ret p],p)
+			);
+			f_type = Some a_t;
+		} in
+		{ f with cff_name = "_new",pos f.cff_name; cff_access = AStatic :: f.cff_access; cff_kind = FFun fu; cff_meta = meta }
+	| FFun fu when not stat ->
+		if Meta.has Meta.From f.cff_meta then error "@:from cast functions must be static" f.cff_pos;
+		let fu = { fu with f_args = (if List.mem AMacro f.cff_access then fu.f_args else (("this",null_pos),false,[],Some this_t,None) :: fu.f_args) } in
+		{ f with cff_kind = FFun fu; cff_access = AStatic :: f.cff_access; cff_meta = (Meta.Impl,[],p) :: f.cff_meta }
+	| _ ->
+		f
+
+let make_module ctx mpath file loadp =
+	let m = {
+		m_id = alloc_mid();
+		m_path = mpath;
+		m_types = [];
+		m_extra = module_extra (Common.unique_full_path file) (Common.get_signature ctx.com) (file_time file) (if ctx.in_macro then MMacro else MCode);
+	} in
+	m
+
+(*
+	Build module structure : should be atomic - no type loading is possible
+*)
+let module_pass_1 ctx m tdecls loadp =
+	let com = ctx.com in
+	let decls = ref [] in
+	let make_path name priv =
+		if List.exists (fun (t,_) -> snd (t_path t) = name) !decls then error ("Type name " ^ name ^ " is already defined in this module") loadp;
+		if priv then (fst m.m_path @ ["_" ^ snd m.m_path], name) else (fst m.m_path, name)
+	in
+	let pt = ref None in
+	let rec make_decl acc decl =
+		let p = snd decl in
+		let acc = (match fst decl with
+		| EImport _ | EUsing _ ->
+			(match !pt with
+			| None -> acc
+			| Some _ -> error "import and using may not appear after a type declaration" p)
+		| EClass d ->
+			let name = fst d.d_name in
+			if String.length name > 0 && name.[0] = '$' then error "Type names starting with a dollar are not allowed" p;
+			pt := Some p;
+			let priv = List.mem HPrivate d.d_flags in
+			let path = make_path name priv in
+			let c = mk_class m path p in
+			(* we shouldn't load any other type until we propertly set cl_build *)
+			c.cl_build <- (fun() -> assert false);
+			c.cl_module <- m;
+			c.cl_private <- priv;
+			c.cl_doc <- d.d_doc;
+			c.cl_meta <- d.d_meta;
+			decls := (TClassDecl c, decl) :: !decls;
+			acc
+		| EEnum d ->
+			let name = fst d.d_name in
+			if String.length name > 0 && name.[0] = '$' then error "Type names starting with a dollar are not allowed" p;
+			pt := Some p;
+			let priv = List.mem EPrivate d.d_flags in
+			let path = make_path name priv in
+			let e = {
+				e_path = path;
+				e_module = m;
+				e_pos = p;
+				e_doc = d.d_doc;
+				e_meta = d.d_meta;
+				e_params = [];
+				e_private = priv;
+				e_extern = List.mem EExtern d.d_flags;
+				e_constrs = PMap.empty;
+				e_names = [];
+				e_type = {
+					t_path = [], "Enum<" ^ (s_type_path path) ^ ">";
+					t_module = m;
+					t_doc = None;
+					t_pos = p;
+					t_type = mk_mono();
+					t_private = true;
+					t_params = [];
+					t_meta = [];
+				};
+			} in
+			decls := (TEnumDecl e, decl) :: !decls;
+			acc
+		| ETypedef d ->
+			let name = fst d.d_name in
+			if String.length name > 0 && name.[0] = '$' then error "Type names starting with a dollar are not allowed" p;
+			pt := Some p;
+			let priv = List.mem EPrivate d.d_flags in
+			let path = make_path name priv in
+			let t = {
+				t_path = path;
+				t_module = m;
+				t_pos = p;
+				t_doc = d.d_doc;
+				t_private = priv;
+				t_params = [];
+				t_type = mk_mono();
+				t_meta = d.d_meta;
+			} in
+			(* failsafe in case the typedef is not initialized (see #3933) *)
+			delay ctx PBuildModule (fun () ->
+				match t.t_type with
+				| TMono r -> (match !r with None -> r := Some com.basic.tvoid | _ -> ())
+				| _ -> ()
+			);
+			decls := (TTypeDecl t, decl) :: !decls;
+			acc
+		 | EAbstract d ->
+		 	let name = fst d.d_name in
+			if String.length name > 0 && name.[0] = '$' then error "Type names starting with a dollar are not allowed" p;
+			let priv = List.mem APrivAbstract d.d_flags in
+			let path = make_path name priv in
+			let a = {
+				a_path = path;
+				a_private = priv;
+				a_module = m;
+				a_pos = p;
+				a_doc = d.d_doc;
+				a_params = [];
+				a_meta = d.d_meta;
+				a_from = [];
+				a_to = [];
+				a_from_field = [];
+				a_to_field = [];
+				a_ops = [];
+				a_unops = [];
+				a_impl = None;
+				a_array = [];
+				a_this = mk_mono();
+				a_resolve = None;
+			} in
+			decls := (TAbstractDecl a, decl) :: !decls;
+			match d.d_data with
+			| [] when Meta.has Meta.CoreType a.a_meta ->
+				a.a_this <- t_dynamic;
+				acc
+			| fields ->
+				let a_t =
+					let params = List.map (fun t -> TPType (CTPath { tname = fst t.tp_name; tparams = []; tsub = None; tpackage = [] },null_pos)) d.d_params in
+					CTPath { tpackage = []; tname = fst d.d_name; tparams = params; tsub = None },null_pos
+				in
+				let rec loop = function
+					| [] -> a_t
+					| AIsType t :: _ -> t
+					| _ :: l -> loop l
+				in
+				let this_t = loop d.d_flags in
+				let fields = List.map (transform_abstract_field com this_t a_t a) fields in
+				let meta = ref [] in
+				if has_meta Meta.Dce a.a_meta then meta := (Meta.Dce,[],p) :: !meta;
+				let acc = make_decl acc (EClass { d_name = (fst d.d_name) ^ "_Impl_",snd d.d_name; d_flags = [HPrivate]; d_data = fields; d_doc = None; d_params = []; d_meta = !meta },p) in
+				(match !decls with
+				| (TClassDecl c,_) :: _ ->
+					List.iter (fun m -> match m with
+						| ((Meta.Build | Meta.CoreApi | Meta.Allow | Meta.Access | Meta.Enum | Meta.Dce | Meta.Native | Meta.JsRequire | Meta.PythonImport | Meta.Expose | Meta.Deprecated | Meta.PhpConstants | Meta.PhpGlobal),_,_) ->
+							c.cl_meta <- m :: c.cl_meta;
+						| _ ->
+							()
+					) a.a_meta;
+					a.a_impl <- Some c;
+					c.cl_kind <- KAbstractImpl a
+				| _ -> assert false);
+				acc
+		) in
+		decl :: acc
+	in
+	let tdecls = List.fold_left make_decl [] tdecls in
+	let decls = List.rev !decls in
+	decls, List.rev tdecls
+
+let parse_file_from_lexbuf com file p lexbuf =
+	let t = Common.timer "parsing" in
+	Lexer.init file true;
+	incr stats.s_files_parsed;
+	let data = (try Parser.parse com lexbuf with e -> t(); raise e) in
+	if com.display = DMModuleSymbols && Display.is_display_file file then
+		raise (Display.ModuleSymbols(Display.print_module_symbols data));
+	t();
+	Common.log com ("Parsed " ^ file);
+	data
+
+let parse_file_from_string com file p string =
+	parse_file_from_lexbuf com file p (Lexing.from_string string)
+
+let current_stdin = ref None (* TODO: we're supposed to clear this at some point *)
+
+let parse_file com file p =
+	let use_stdin = (Common.defined com Define.DisplayStdin) && Display.is_display_file file in
+	if use_stdin then
+		let s =
+			match !current_stdin with
+			| Some s ->
+				s
+			| None ->
+				let s = Std.input_all stdin in
+				close_in stdin;
+				current_stdin := Some s;
+				s
+		in
+		parse_file_from_string com file p s
+	else
+		let ch = try open_in_bin file with _ -> error ("Could not open " ^ file) p in
+		Std.finally (fun() -> close_in ch) (parse_file_from_lexbuf com file p) (Lexing.from_channel ch)
+
+let parse_hook = ref parse_file
+let type_module_hook = ref (fun _ _ _ -> None)
+let type_function_params_rec = ref (fun _ _ _ _ -> assert false)
+let return_partial_type = ref false
+
+let type_function_arg ctx t e opt p =
+	if opt then
+		let e = (match e with None -> Some (EConst (Ident "null"),p) | _ -> e) in
+		ctx.t.tnull t, e
+	else
+		let t = match e with Some (EConst (Ident "null"),p) -> ctx.t.tnull t | _ -> t in
+		t, e
+
+let type_var_field ctx t e stat do_display p =
+	if stat then ctx.curfun <- FunStatic else ctx.curfun <- FunMember;
+	let e = if do_display then Display.process_expr ctx.com e else e in
+	let e = type_expr ctx e (WithType t) in
+	let e = (!cast_or_unify_ref) ctx t e p in
+	match t with
+	| TType ({ t_path = ([],"UInt") },[]) | TAbstract ({ a_path = ([],"UInt") },[]) when stat -> { e with etype = t }
+	| _ -> e
+
+let apply_macro ctx mode path el p =
+	let cpath, meth = (match List.rev (ExtString.String.nsplit path ".") with
+		| meth :: name :: pack -> (List.rev pack,name), meth
+		| _ -> error "Invalid macro path" p
+	) in
+	ctx.g.do_macro ctx mode cpath meth el p
+
+(** since load_type_def and load_instance are used in PASS2, they should not access the structure of a type **)
+
+(*
+	load a type or a subtype definition
+*)
+let rec load_type_def ctx p t =
+	let no_pack = t.tpackage = [] in
+	let tname = (match t.tsub with None -> t.tname | Some n -> n) in
+	try
+		if t.tsub <> None then raise Not_found;
+		let path_matches t2 =
+			let tp = t_path t2 in
+			tp = (t.tpackage,tname) || (no_pack && snd tp = tname)
+		in
+		try
+			List.find path_matches ctx.m.curmod.m_types
+		with Not_found ->
+			let t,pi = List.find (fun (t2,pi) -> path_matches t2) ctx.m.module_types in
+			Display.mark_import_position ctx.com pi;
+			t
+	with
+		Not_found ->
+			let next() =
+				let t, m = (try
+					t, ctx.g.do_load_module ctx (t.tpackage,t.tname) p
+				with Error (Module_not_found _,p2) as e when p == p2 ->
+					match t.tpackage with
+					| "std" :: l ->
+						let t = { t with tpackage = l } in
+						t, ctx.g.do_load_module ctx (t.tpackage,t.tname) p
+					| _ -> raise e
+				) in
+				let tpath = (t.tpackage,tname) in
+				try
+					List.find (fun t -> not (t_infos t).mt_private && t_path t = tpath) m.m_types
+				with
+					Not_found -> raise (Error (Type_not_found (m.m_path,tname),p))
+			in
+			(* lookup in wildcard imported packages *)
+			try
+				if not no_pack then raise Exit;
+				let rec loop l = match l with
+					| [] -> raise Exit
+					| (wp,pi) :: l ->
+						try
+							let t = load_type_def ctx p { t with tpackage = wp } in
+							Display.mark_import_position ctx.com pi;
+							t
+						with
+							| Error (Module_not_found _,p2)
+							| Error (Type_not_found _,p2) when p == p2 -> loop l
+				in
+				loop ctx.m.wildcard_packages
+			with Exit ->
+			(* lookup in our own package - and its upper packages *)
+			let rec loop = function
+				| [] -> raise Exit
+				| (_ :: lnext) as l ->
+					try
+						load_type_def ctx p { t with tpackage = List.rev l }
+					with
+						| Error (Module_not_found _,p2)
+						| Error (Type_not_found _,p2) when p == p2 -> loop lnext
+			in
+			try
+				if not no_pack then raise Exit;
+				(match fst ctx.m.curmod.m_path with
+				| [] -> raise Exit
+				| x :: _ ->
+					(* this can occur due to haxe remoting : a module can be
+						already defined in the "js" package and is not allowed
+						to access the js classes *)
+					try
+						(match PMap.find x ctx.com.package_rules with
+						| Forbidden -> raise Exit
+						| _ -> ())
+					with Not_found -> ());
+				loop (List.rev (fst ctx.m.curmod.m_path));
+			with
+				Exit -> next()
+
+let resolve_position_by_path ctx path p =
+	let mt = load_type_def ctx p path in
+	let p = (t_infos mt).mt_pos in
+	raise (Display.DisplayPosition [p])
+
+let check_param_constraints ctx types t pl c p =
+	match follow t with
+	| TMono _ -> ()
+	| _ ->
+		let ctl = (match c.cl_kind with KTypeParameter l -> l | _ -> []) in
+		List.iter (fun ti ->
+			let ti = apply_params types pl ti in
+			let ti = (match follow ti with
+				| TInst ({ cl_kind = KGeneric } as c,pl) ->
+					(* if we solve a generic contraint, let's substitute with the actual generic instance before unifying *)
+					let _,_, f = ctx.g.do_build_instance ctx (TClassDecl c) p in
+					f pl
+				| _ -> ti
+			) in
+			try
+				unify_raise ctx t ti p
+			with Error(Unify l,p) ->
+				if not ctx.untyped then display_error ctx (error_msg (Unify (Constraint_failure (s_type_path c.cl_path) :: l))) p;
+		) ctl
+
+let requires_value_meta com co =
+	Common.defined com Define.DocGen || (match co with
+		| None -> false
+		| Some c -> c.cl_extern || Meta.has Meta.Rtti c.cl_meta)
+
+let generate_value_meta com co cf args =
+	if requires_value_meta com co then begin
+		let values = List.fold_left (fun acc ((name,_),_,_,_,eo) -> match eo with Some e -> (name,e) :: acc | _ -> acc) [] args in
+		match values with
+			| [] -> ()
+			| _ -> cf.cf_meta <- ((Meta.Value,[EObjectDecl values,cf.cf_pos],cf.cf_pos) :: cf.cf_meta)
+	end
+
+let pselect p1 p2 =
+	if p1 = null_pos then p2 else p1
+
+(* build an instance from a full type *)
+let rec load_instance ?(allow_display=false) ctx (t,pn) allow_no_params p =
+	let p = pselect pn p in
+	let t = try
+		if t.tpackage <> [] || t.tsub <> None then raise Not_found;
+		let pt = List.assoc t.tname ctx.type_params in
+		if t.tparams <> [] then error ("Class type parameter " ^ t.tname ^ " can't have parameters") p;
+		pt
+	with Not_found ->
+		let mt = load_type_def ctx p t in
+		let is_generic,is_generic_build = match mt with
+			| TClassDecl {cl_kind = KGeneric} -> true,false
+			| TClassDecl {cl_kind = KGenericBuild _} -> false,true
+			| _ -> false,false
+		in
+		let types , path , f = ctx.g.do_build_instance ctx mt p in
+		let is_rest = is_generic_build && (match types with ["Rest",_] -> true | _ -> false) in
+		if allow_no_params && t.tparams = [] && not is_rest then begin
+			let pl = ref [] in
+			pl := List.map (fun (name,t) ->
+				match follow t with
+				| TInst (c,_) ->
+					let t = mk_mono() in
+					if c.cl_kind <> KTypeParameter [] || is_generic then delay ctx PCheckConstraint (fun() -> check_param_constraints ctx types t (!pl) c p);
+					t;
+				| _ -> assert false
+			) types;
+			f (!pl)
+		end else if path = ([],"Dynamic") then
+			match t.tparams with
+			| [] -> t_dynamic
+			| [TPType t] -> TDynamic (load_complex_type ctx false p t)
+			| _ -> error "Too many parameters for Dynamic" p
+		else begin
+			if not is_rest && ctx.com.display = DMNone && List.length types <> List.length t.tparams then error ("Invalid number of type parameters for " ^ s_type_path path) p;
+			let tparams = List.map (fun t ->
+				match t with
+				| TPExpr e ->
+					let name = (match fst e with
+						| EConst (String s) -> "S" ^ s
+						| EConst (Int i) -> "I" ^ i
+						| EConst (Float f) -> "F" ^ f
+						| _ -> "Expr"
+					) in
+					let c = mk_class null_module ([],name) p in
+					c.cl_kind <- KExpr e;
+					TInst (c,[])
+				| TPType t -> load_complex_type ctx true p t
+			) t.tparams in
+			let rec loop tl1 tl2 is_rest = match tl1,tl2 with
+				| t :: tl1,(name,t2) :: tl2 ->
+					let check_const c =
+						let is_expression = (match t with TInst ({ cl_kind = KExpr _ },_) -> true | _ -> false) in
+						let expects_expression = name = "Const" || Meta.has Meta.Const c.cl_meta in
+						let accepts_expression = name = "Rest" in
+						if is_expression then begin
+							if not expects_expression && not accepts_expression then
+								error "Constant value unexpected here" p
+						end else if expects_expression then
+							error "Constant value excepted as type parameter" p
+					in
+					let is_rest = is_rest || name = "Rest" && is_generic_build in
+					let t = match follow t2 with
+						| TInst ({ cl_kind = KTypeParameter [] } as c, []) when not is_generic ->
+							check_const c;
+							t
+						| TInst (c,[]) ->
+							check_const c;
+							let r = exc_protect ctx (fun r ->
+								r := (fun() -> t);
+								delay ctx PCheckConstraint (fun() -> check_param_constraints ctx types t tparams c p);
+								t
+							) "constraint" in
+							delay ctx PForce (fun () -> ignore(!r()));
+							TLazy r
+						| _ -> assert false
+					in
+					t :: loop tl1 tl2 is_rest
+				| [],[] ->
+					[]
+				| [],["Rest",_] when is_generic_build ->
+					[]
+				| [],(_,t) :: tl when ctx.com.display <> DMNone ->
+					t :: loop [] tl is_rest
+				| [],_ ->
+					error ("Not enough type parameters for " ^ s_type_path path) p
+				| t :: tl,[] ->
+					if is_rest then
+						t :: loop tl [] true
+					else
+						error ("Too many parameters for " ^ s_type_path path) p
+			in
+			let params = loop tparams types false in
+			f params
+		end
+	in
+	if allow_display && ctx.is_display_file && Display.is_display_position pn then
+		Display.display_type ctx.com.display t pn;
+	t
+
+(*
+	build an instance from a complex type
+*)
+and load_complex_type ctx allow_display p (t,pn) =
+	let p = pselect pn p in
+	match t with
+	| CTParent t -> load_complex_type ctx allow_display p t
+	| CTPath t -> load_instance ~allow_display ctx (t,pn) false p
+	| CTOptional _ -> error "Optional type not allowed here" p
+	| CTExtend (tl,l) ->
+		(match load_complex_type ctx allow_display p (CTAnonymous l,p) with
+		| TAnon a as ta ->
+			let is_redefined cf1 a2 =
+				try
+					let cf2 = PMap.find cf1.cf_name a2.a_fields in
+					let st = s_type (print_context()) in
+					if not (type_iseq cf1.cf_type cf2.cf_type) then begin
+						display_error ctx ("Cannot redefine field " ^ cf1.cf_name ^ " with different type") p;
+						display_error ctx ("First type was " ^ (st cf1.cf_type)) cf1.cf_pos;
+						error ("Second type was " ^ (st cf2.cf_type)) cf2.cf_pos
+					end else
+						true
+				with Not_found ->
+					false
+			in
+			let mk_extension t =
+				match follow t with
+				| TInst ({cl_kind = KTypeParameter _},_) ->
+					error "Cannot structurally extend type parameters" p
+				| TInst (c,tl) ->
+					ctx.com.warning "Structurally extending classes is deprecated and will be removed" p;
+					let c2 = mk_class null_module (fst c.cl_path,"+" ^ snd c.cl_path) p in
+					c2.cl_private <- true;
+					PMap.iter (fun f _ ->
+						try
+							ignore(class_field c tl f);
+							error ("Cannot redefine field " ^ f) p
+						with
+							Not_found -> ()
+					) a.a_fields;
+					(* do NOT tag as extern - for protect *)
+					c2.cl_kind <- KExtension (c,tl);
+					c2.cl_super <- Some (c,tl);
+					c2.cl_fields <- a.a_fields;
+					TInst (c2,[])
+				| TMono _ ->
+					error "Loop found in cascading signatures definitions. Please change order/import" p
+				| TAnon a2 ->
+					PMap.iter (fun _ cf -> ignore(is_redefined cf a2)) a.a_fields;
+					TAnon { a_fields = (PMap.foldi PMap.add a.a_fields a2.a_fields); a_status = ref (Extend [t]); }
+				| _ -> error "Can only extend classes and structures" p
+			in
+			let loop t = match follow t with
+				| TAnon a2 ->
+					PMap.iter (fun f cf ->
+						if not (is_redefined cf a) then
+							a.a_fields <- PMap.add f cf a.a_fields
+					) a2.a_fields
+				| _ ->
+					error "Multiple structural extension is only allowed for structures" p
+			in
+			let il = List.map (fun (t,_) -> load_instance ctx ~allow_display (t,pn) false p) tl in
+			let tr = ref None in
+			let t = TMono tr in
+			let r = exc_protect ctx (fun r ->
+				r := (fun _ -> t);
+				tr := Some (match il with
+					| [i] ->
+						mk_extension i
+					| _ ->
+						List.iter loop il;
+						a.a_status := Extend il;
+						ta);
+				t
+			) "constraint" in
+			delay ctx PForce (fun () -> ignore(!r()));
+			TLazy r
+		| _ -> assert false)
+	| CTAnonymous l ->
+		let rec loop acc f =
+			let n = fst f.cff_name in
+			let p = f.cff_pos in
+			if PMap.mem n acc then error ("Duplicate field declaration : " ^ n) p;
+			let topt = function
+				| None -> error ("Explicit type required for field " ^ n) p
+				| Some t -> load_complex_type ctx allow_display p t
+			in
+			if n = "new" then ctx.com.warning "Structures with new are deprecated, use haxe.Constraints.Constructible instead" p;
+			let no_expr = function
+				| None -> ()
+				| Some (_,p) -> error "Expression not allowed here" p
+			in
+			let pub = ref true in
+			let dyn = ref false in
+			let params = ref [] in
+			List.iter (fun a ->
+				match a with
+				| APublic -> ()
+				| APrivate -> pub := false;
+				| ADynamic when (match f.cff_kind with FFun _ -> true | _ -> false) -> dyn := true
+				| AStatic | AOverride | AInline | ADynamic | AMacro -> error ("Invalid access " ^ Ast.s_access a) p
+			) f.cff_access;
+			let t , access = (match f.cff_kind with
+				| FVar (Some (CTPath({tpackage=[];tname="Void"}),_), _)  | FProp (_,_,Some (CTPath({tpackage=[];tname="Void"}),_),_) ->
+					error "Fields of type Void are not allowed in structures" p
+				| FVar (t, e) ->
+					no_expr e;
+					topt t, Var { v_read = AccNormal; v_write = AccNormal }
+				| FFun fd ->
+					params := (!type_function_params_rec) ctx fd (fst f.cff_name) p;
+					no_expr fd.f_expr;
+					let old = ctx.type_params in
+					ctx.type_params <- !params @ old;
+					let args = List.map (fun ((name,_),o,_,t,e) -> no_expr e; name, o, topt t) fd.f_args in
+					let t = TFun (args,topt fd.f_type), Method (if !dyn then MethDynamic else MethNormal) in
+					ctx.type_params <- old;
+					t
+				| FProp (i1,i2,t,e) ->
+					no_expr e;
+					let access m get =
+						match m with
+						| "null" -> AccNo
+						| "never" -> AccNever
+						| "default" -> AccNormal
+						| "dynamic" -> AccCall
+						| "get" when get -> AccCall
+						| "set" when not get -> AccCall
+						| x when get && x = "get_" ^ n -> AccCall
+						| x when not get && x = "set_" ^ n -> AccCall
+						| _ ->
+							error "Custom property access is no longer supported in Haxe 3" f.cff_pos;
+					in
+					let t = (match t with None -> error "Type required for structure property" p | Some t -> t) in
+					load_complex_type ctx allow_display p t, Var { v_read = access i1 true; v_write = access i2 false }
+			) in
+			let t = if Meta.has Meta.Optional f.cff_meta then ctx.t.tnull t else t in
+			let cf = {
+				cf_name = n;
+				cf_type = t;
+				cf_pos = p;
+				cf_public = !pub;
+				cf_kind = access;
+				cf_params = !params;
+				cf_expr = None;
+				cf_doc = f.cff_doc;
+				cf_meta = f.cff_meta;
+				cf_overloads = [];
+			} in
+			init_meta_overloads ctx None cf;
+			PMap.add n cf acc
+		in
+		mk_anon (List.fold_left loop PMap.empty l)
+	| CTFunction (args,r) ->
+		match args with
+		| [CTPath { tpackage = []; tparams = []; tname = "Void" },_] ->
+			TFun ([],load_complex_type ctx allow_display p r)
+		| _ ->
+			TFun (List.map (fun t ->
+				let t, opt = (match fst t with CTOptional t -> t, true | _ -> t,false) in
+				"",opt,load_complex_type ctx allow_display p t
+			) args,load_complex_type ctx allow_display p r)
+
+and init_meta_overloads ctx co cf =
+	let overloads = ref [] in
+	let filter_meta m = match m with
+		| ((Meta.Overload | Meta.Value),_,_) -> false
+		| _ -> true
+	in
+	let cf_meta = List.filter filter_meta cf.cf_meta in
+	cf.cf_meta <- List.filter (fun m ->
+		match m with
+		| (Meta.Overload,[(EFunction (fname,f),p)],_)  ->
+			if fname <> None then error "Function name must not be part of @:overload" p;
+			(match f.f_expr with Some (EBlock [], _) -> () | _ -> error "Overload must only declare an empty method body {}" p);
+			let old = ctx.type_params in
+			(match cf.cf_params with
+			| [] -> ()
+			| l -> ctx.type_params <- List.filter (fun t -> not (List.mem t l)) ctx.type_params);
+			let params = (!type_function_params_rec) ctx f cf.cf_name p in
+			ctx.type_params <- params @ ctx.type_params;
+			let topt = function None -> error "Explicit type required" p | Some t -> load_complex_type ctx true p t in
+			let args = List.map (fun ((a,_),opt,_,t,_) -> a,opt,topt t) f.f_args in
+			let cf = { cf with cf_type = TFun (args,topt f.f_type); cf_params = params; cf_meta = cf_meta} in
+			generate_value_meta ctx.com co cf f.f_args;
+			overloads := cf :: !overloads;
+			ctx.type_params <- old;
+			false
+		| (Meta.Overload,[],_) when ctx.com.config.pf_overload ->
+			let topt (n,_,t) = match t with | TMono t when !t = None -> error ("Explicit type required for overload functions\nFor function argument '" ^ n ^ "'") cf.cf_pos | _ -> () in
+			(match follow cf.cf_type with
+			| TFun (args,_) -> List.iter topt args
+			| _ -> () (* could be a variable *));
+			true
+		| (Meta.Overload,[],p) ->
+				error "This platform does not support this kind of overload declaration. Try @:overload(function()... {}) instead" p
+		| (Meta.Overload,_,p) ->
+				error "Invalid @:overload metadata format" p
+		| _ ->
+			true
+	) cf.cf_meta;
+	cf.cf_overloads <- (List.rev !overloads)
+
+let hide_params ctx =
+	let old_m = ctx.m in
+	let old_type_params = ctx.type_params in
+	let old_deps = ctx.g.std.m_extra.m_deps in
+	ctx.m <- {
+		curmod = ctx.g.std;
+		module_types = [];
+		module_using = [];
+		module_globals = PMap.empty;
+		wildcard_packages = [];
+		module_imports = [];
+	};
+	ctx.type_params <- [];
+	(fun() ->
+		ctx.m <- old_m;
+		ctx.type_params <- old_type_params;
+		(* restore dependencies that might be have been wronly inserted *)
+		ctx.g.std.m_extra.m_deps <- old_deps;
+	)
+
+(*
+	load a type while ignoring the current imports or local types
+*)
+let load_core_type ctx name =
+	let show = hide_params ctx in
+	let t = load_instance ctx ({ tpackage = []; tname = name; tparams = []; tsub = None; },null_pos) false null_pos in
+	show();
+	add_dependency ctx.m.curmod (match t with
+	| TInst (c,_) -> c.cl_module
+	| TType (t,_) -> t.t_module
+	| TAbstract (a,_) -> a.a_module
+	| TEnum (e,_) -> e.e_module
+	| _ -> assert false);
+	t
+
+let t_iterator ctx =
+	let show = hide_params ctx in
+	match load_type_def ctx null_pos { tpackage = []; tname = "Iterator"; tparams = []; tsub = None } with
+	| TTypeDecl t ->
+		show();
+		add_dependency ctx.m.curmod t.t_module;
+		if List.length t.t_params <> 1 then assert false;
+		let pt = mk_mono() in
+		apply_params t.t_params [pt] t.t_type, pt
+	| _ ->
+		assert false
+
+(*
+	load either a type t or Null<Unknown> if not defined
+*)
+let load_type_hint ?(opt=false) ctx pcur t =
+	let t = match t with
+		| None -> mk_mono()
+		| Some (t,p) -> load_complex_type ctx true pcur (t,p)
+	in
+	if opt then ctx.t.tnull t else t
+
+(* ---------------------------------------------------------------------- *)
+(* Structure check *)
+
+let valid_redefinition ctx f1 t1 f2 t2 =
+	let valid t1 t2 =
+		Type.unify t1 t2;
+		if is_null t1 <> is_null t2 || ((follow t1) == t_dynamic && (follow t2) != t_dynamic) then raise (Unify_error [Cannot_unify (t1,t2)]);
+	in
+	let t1, t2 = (match f1.cf_params, f2.cf_params with
+		| [], [] -> t1, t2
+		| l1, l2 when List.length l1 = List.length l2 ->
+			let to_check = ref [] in
+			let monos = List.map2 (fun (name,p1) (_,p2) ->
+				(match follow p1, follow p2 with
+				| TInst ({ cl_kind = KTypeParameter ct1 } as c1,pl1), TInst ({ cl_kind = KTypeParameter ct2 } as c2,pl2) ->
+					(match ct1, ct2 with
+					| [], [] -> ()
+					| _, _ when List.length ct1 = List.length ct2 ->
+						(* if same constraints, they are the same type *)
+						let check monos =
+							List.iter2 (fun t1 t2  ->
+								try
+									let t1 = apply_params l1 monos (apply_params c1.cl_params pl1 t1) in
+									let t2 = apply_params l2 monos (apply_params c2.cl_params pl2 t2) in
+									type_eq EqStrict t1 t2
+								with Unify_error l ->
+									raise (Unify_error (Unify_custom "Constraints differ" :: l))
+							) ct1 ct2
+						in
+						to_check := check :: !to_check;
+					| _ ->
+						raise (Unify_error [Unify_custom "Different number of constraints"]))
+				| _ -> ());
+				TInst (mk_class null_module ([],name) Ast.null_pos,[])
+			) l1 l2 in
+			List.iter (fun f -> f monos) !to_check;
+			apply_params l1 monos t1, apply_params l2 monos t2
+		| _  ->
+			(* ignore type params, will create other errors later *)
+			t1, t2
+	) in
+	match f1.cf_kind,f2.cf_kind with
+	| Method m1, Method m2 when not (m1 = MethDynamic) && not (m2 = MethDynamic) ->
+		begin match follow t1, follow t2 with
+		| TFun (args1,r1) , TFun (args2,r2) -> (
+			if not (List.length args1 = List.length args2) then raise (Unify_error [Unify_custom "Different number of function arguments"]);
+			try
+				List.iter2 (fun (n,o1,a1) (_,o2,a2) ->
+					if o1 <> o2 then raise (Unify_error [Not_matching_optional n]);
+					(try valid a2 a1 with Unify_error _ -> raise (Unify_error [Cannot_unify(a1,a2)]))
+				) args1 args2;
+				valid r1 r2
+			with Unify_error l ->
+				raise (Unify_error (Cannot_unify (t1,t2) :: l)))
+		| _ ->
+			assert false
+		end
+	| _,(Var { v_write = AccNo | AccNever }) ->
+		(* write variance *)
+		valid t1 t2
+	| _,(Var { v_read = AccNo | AccNever }) ->
+		(* read variance *)
+		valid t2 t1
+	| _ , _ ->
+		(* in case args differs, or if an interface var *)
+		type_eq EqStrict t1 t2;
+		if is_null t1 <> is_null t2 then raise (Unify_error [Cannot_unify (t1,t2)])
+
+let copy_meta meta_src meta_target sl =
+	let meta = ref meta_target in
+	List.iter (fun (m,e,p) ->
+		if List.mem m sl then meta := (m,e,p) :: !meta
+	) meta_src;
+	!meta
+
+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)
+
+
+let check_overloads ctx c =
+	(* check if field with same signature was declared more than once *)
+	List.iter (fun f ->
+		if Meta.has Meta.Overload f.cf_meta then
+			List.iter (fun f2 ->
+				try
+					ignore (List.find (fun f3 -> f3 != f2 && same_overload_args f2.cf_type f3.cf_type f2 f3) (f :: f.cf_overloads));
+					display_error ctx ("Another overloaded field of same signature was already declared : " ^ f2.cf_name) f2.cf_pos
+				with | Not_found -> ()
+		) (f :: f.cf_overloads)) (c.cl_ordered_fields @ c.cl_ordered_statics)
+
+let check_overriding ctx c =
+	match c.cl_super with
+	| None ->
+		(match c.cl_overrides with
+		| [] -> ()
+		| i :: _ ->
+			display_error ctx ("Field " ^ i.cf_name ^ " is declared 'override' but doesn't override any field") i.cf_pos)
+	| _ when c.cl_extern && Meta.has Meta.CsNative c.cl_meta -> () (* -net-lib specific: do not check overrides on extern CsNative classes *)
+	| Some (csup,params) ->
+		PMap.iter (fun i f ->
+			let p = f.cf_pos in
+			let check_field f get_super_field is_overload = try
+				(if is_overload && not (Meta.has Meta.Overload f.cf_meta) then
+					display_error ctx ("Missing @:overload declaration for field " ^ i) p);
+				let t, f2 = get_super_field csup i in
+				(* allow to define fields that are not defined for this platform version in superclass *)
+				(match f2.cf_kind with
+				| Var { v_read = AccRequire _ } -> raise Not_found;
+				| _ -> ());
+				if ctx.com.config.pf_overload && (Meta.has Meta.Overload f2.cf_meta && not (Meta.has Meta.Overload f.cf_meta)) then
+					display_error ctx ("Field " ^ i ^ " should be declared with @:overload since it was already declared as @:overload in superclass") p
+				else if not (List.memq f c.cl_overrides) then
+					display_error ctx ("Field " ^ i ^ " should be declared with 'override' since it is inherited from superclass " ^ Ast.s_type_path csup.cl_path) p
+				else if not f.cf_public && f2.cf_public then
+					display_error ctx ("Field " ^ i ^ " has less visibility (public/private) than superclass one") p
+				else (match f.cf_kind, f2.cf_kind with
+				| _, Method MethInline ->
+					display_error ctx ("Field " ^ i ^ " is inlined and cannot be overridden") p
+				| a, b when a = b -> ()
+				| Method MethInline, Method MethNormal ->
+					() (* allow to redefine a method as inlined *)
+				| _ ->
+					display_error ctx ("Field " ^ i ^ " has different property access than in superclass") p);
+				if has_meta Meta.Final f2.cf_meta then display_error ctx ("Cannot override @:final method " ^ i) p;
+				try
+					let t = apply_params csup.cl_params params t in
+					valid_redefinition ctx f f.cf_type f2 t
+				with
+					Unify_error l ->
+						display_error ctx ("Field " ^ i ^ " overloads parent class with different or incomplete type") p;
+						display_error ctx ("Base field is defined here") f2.cf_pos;
+						display_error ctx (error_msg (Unify l)) p;
+			with
+				Not_found ->
+					if List.memq f c.cl_overrides then
+						let msg = if is_overload then
+							("Field " ^ i ^ " is declared 'override' but no compatible overload was found")
+						else
+							("Field " ^ i ^ " is declared 'override' but doesn't override any field")
+						in
+						display_error ctx msg p
+			in
+			if ctx.com.config.pf_overload && Meta.has Meta.Overload f.cf_meta then begin
+				let overloads = get_overloads csup i in
+				List.iter (fun (t,f2) ->
+					(* check if any super class fields are vars *)
+					match f2.cf_kind with
+					| Var _ ->
+						display_error ctx ("A variable named '" ^ f2.cf_name ^ "' was already declared in a superclass") f.cf_pos
+					| _ -> ()
+				) overloads;
+				List.iter (fun f ->
+					(* find the exact field being overridden *)
+					check_field f (fun csup i ->
+						List.find (fun (t,f2) ->
+							same_overload_args f.cf_type (apply_params csup.cl_params params t) f f2
+						) overloads
+					) true
+				) (f :: f.cf_overloads)
+			end else
+				check_field f (fun csup i ->
+					let _, t, f2 = raw_class_field (fun f -> f.cf_type) csup params i in
+					t, f2) false
+		) c.cl_fields
+
+let class_field_no_interf c i =
+	try
+		let f = PMap.find i c.cl_fields in
+		f.cf_type , f
+	with Not_found ->
+		match c.cl_super with
+		| None ->
+			raise Not_found
+		| Some (c,tl) ->
+			(* rec over class_field *)
+			let _, t , f = raw_class_field (fun f -> f.cf_type) c tl i in
+			apply_params c.cl_params tl t , f
+
+let rec return_flow ctx e =
+	let error() =
+		display_error ctx (Printf.sprintf "Missing return: %s" (s_type (print_context()) ctx.ret)) e.epos; raise Exit
+	in
+	let return_flow = return_flow ctx in
+	let rec uncond e = match e.eexpr with
+		| TIf _ | TWhile _ | TSwitch _ | TTry _ | TFunction _ -> ()
+		| TReturn _ | TThrow _ -> raise Exit
+		| _ -> Type.iter uncond e
+	in
+	let has_unconditional_flow e = try uncond e; false with Exit -> true in
+	match e.eexpr with
+	| TReturn _ | TThrow _ -> ()
+	| TParenthesis e | TMeta(_,e) ->
+		return_flow e
+	| TBlock el ->
+		let rec loop = function
+			| [] -> error()
+			| [e] -> return_flow e
+			| e :: _ when has_unconditional_flow e -> ()
+			| _ :: l -> loop l
+		in
+		loop el
+	| TIf (_,e1,Some e2) ->
+		return_flow e1;
+		return_flow e2;
+	| TSwitch (v,cases,Some e) ->
+		List.iter (fun (_,e) -> return_flow e) cases;
+		return_flow e
+	| TSwitch ({eexpr = TMeta((Meta.Exhaustive,_,_),_)},cases,None) ->
+		List.iter (fun (_,e) -> return_flow e) cases;
+	| TTry (e,cases) ->
+		return_flow e;
+		List.iter (fun (_,e) -> return_flow e) cases;
+	| TWhile({eexpr = (TConst (TBool true))},e,_) ->
+		(* a special case for "inifite" while loops that have no break *)
+		let rec loop e = match e.eexpr with
+			(* ignore nested loops to not accidentally get one of its breaks *)
+			| TWhile _ | TFor _ -> ()
+			| TBreak -> error()
+			| _ -> Type.iter loop e
+		in
+		loop e
+	| _ ->
+		error()
+
+(* ---------------------------------------------------------------------- *)
+(* PASS 1 & 2 : Module and Class Structure *)
+
+let is_generic_parameter ctx c =
+	(* first check field parameters, then class parameters *)
+	try
+		ignore (List.assoc (snd c.cl_path) ctx.curfield.cf_params);
+		Meta.has Meta.Generic ctx.curfield.cf_meta
+	with Not_found -> try
+		ignore(List.assoc (snd c.cl_path) ctx.type_params);
+		(match ctx.curclass.cl_kind with | KGeneric -> true | _ -> false);
+	with Not_found ->
+		false
+
+let type_function_arg_value ctx t c =
+	match c with
+		| None -> None
+		| Some e ->
+			let p = pos e in
+			let e = ctx.g.do_optimize ctx (type_expr ctx e (WithType t)) in
+			unify ctx e.etype t p;
+			let rec loop e = match e.eexpr with
+				| TConst c -> Some c
+				| TCast(e,None) -> loop e
+				| _ ->
+					if ctx.com.display = DMNone then display_error ctx "Parameter default value should be constant" p;
+					None
+			in
+			loop e
+
+(**** strict meta ****)
+let get_native_repr md pos =
+	let path, meta = match md with
+		| TClassDecl cl -> cl.cl_path, cl.cl_meta
+		| TEnumDecl e -> e.e_path, e.e_meta
+		| TTypeDecl t -> t.t_path, t.t_meta
+		| TAbstractDecl a -> a.a_path, a.a_meta
+	in
+	let rec loop acc = function
+		| (Meta.JavaCanonical,[EConst(String pack),_; EConst(String name),_],_) :: _ ->
+			ExtString.String.nsplit pack ".", name
+		| (Meta.Native,[EConst(String name),_],_) :: meta ->
+			loop (Ast.parse_path name) meta
+		| _ :: meta ->
+			loop acc meta
+		| [] ->
+			acc
+	in
+	let pack, name = loop path meta in
+	match pack with
+		| [] ->
+			(EConst(Ident(name)), pos)
+		| hd :: tl ->
+			let rec loop pack expr = match pack with
+				| hd :: tl ->
+					loop tl (EField(expr,hd),pos)
+				| [] ->
+					(EField(expr,name),pos)
+			in
+			loop tl (EConst(Ident(hd)),pos)
+
+let rec process_meta_argument ?(toplevel=true) ctx expr = match expr.eexpr with
+	| TField(e,f) ->
+		(EField(process_meta_argument ~toplevel:false ctx e,field_name f),expr.epos)
+	| TConst(TInt i) ->
+		(EConst(Int (Int32.to_string i)), expr.epos)
+	| TConst(TFloat f) ->
+		(EConst(Float f), expr.epos)
+	| TConst(TString s) ->
+		(EConst(String s), expr.epos)
+	| TConst TNull ->
+		(EConst(Ident "null"), expr.epos)
+	| TConst(TBool b) ->
+		(EConst(Ident (string_of_bool b)), expr.epos)
+	| TCast(e,_) | TMeta(_,e) | TParenthesis(e) ->
+		process_meta_argument ~toplevel ctx e
+	| TTypeExpr md when toplevel ->
+		let p = expr.epos in
+		if ctx.com.platform = Cs then
+			(ECall( (EConst(Ident "typeof"), p), [get_native_repr md expr.epos] ), p)
+		else
+			(EField(get_native_repr md expr.epos, "class"), p)
+	| TTypeExpr md ->
+		get_native_repr md expr.epos
+	| _ ->
+		display_error ctx "This expression is too complex to be a strict metadata argument" expr.epos;
+		(EConst(Ident "null"), expr.epos)
+
+let make_meta ctx texpr extra =
+	match texpr.eexpr with
+		| TNew(c,_,el) ->
+			ECall(get_native_repr (TClassDecl c) texpr.epos, (List.map (process_meta_argument ctx) el) @ extra), texpr.epos
+		| TTypeExpr(md) ->
+			ECall(get_native_repr md texpr.epos, extra), texpr.epos
+		| _ ->
+			display_error ctx "Unexpected expression" texpr.epos; assert false
+
+let field_to_type_path ctx e =
+	let rec loop e pack name = match e with
+		| EField(e,f),p when Char.lowercase (String.get f 0) <> String.get f 0 -> (match name with
+			| [] | _ :: [] ->
+				loop e pack (f :: name)
+			| _ -> (* too many name paths *)
+				display_error ctx ("Unexpected " ^ f) p;
+				raise Exit)
+		| EField(e,f),_ ->
+			loop e (f :: pack) name
+		| EConst(Ident f),_ ->
+			let pack, name, sub = match name with
+				| [] ->
+					let fchar = String.get f 0 in
+					if Char.uppercase fchar = fchar then
+						pack, f, None
+					else begin
+						display_error ctx "A class name must start with an uppercase character" (snd e);
+						raise Exit
+					end
+				| [name] ->
+					f :: pack, name, None
+				| [name; sub] ->
+					f :: pack, name, Some sub
+				| _ ->
+					assert false
+			in
+			{ tpackage=pack; tname=name; tparams=[]; tsub=sub }
+		| _,pos ->
+			display_error ctx "Unexpected expression when building strict meta" pos;
+			raise Exit
+	in
+	loop e [] []
+
+let handle_fields ctx fields_to_check with_type_expr =
+	List.map (fun (name,expr) ->
+		let pos = snd expr in
+		let field = (EField(with_type_expr,name), pos) in
+		let fieldexpr = (EConst(Ident name),pos) in
+		let left_side = match ctx.com.platform with
+			| Cs -> field
+			| Java -> (ECall(field,[]),pos)
+			| _ -> assert false
+		in
+
+		let left = type_expr ctx left_side NoValue in
+		let right = type_expr ctx expr (WithType left.etype) in
+		unify ctx left.etype right.etype (snd expr);
+		(EBinop(Ast.OpAssign,fieldexpr,process_meta_argument ctx right), pos)
+	) fields_to_check
+
+let get_strict_meta ctx params pos =
+	let pf = ctx.com.platform in
+	let changed_expr, fields_to_check, ctype = match params with
+		| [ECall(ef, el),p] ->
+			(* check last argument *)
+			let el, fields = match List.rev el with
+				| (EObjectDecl(decl),_) :: el ->
+					List.rev el, decl
+				| _ ->
+					el, []
+			in
+			let tpath = field_to_type_path ctx ef in
+			if pf = Cs then
+				(ENew((tpath,snd ef), el), p), fields, CTPath tpath
+			else
+				ef, fields, CTPath tpath
+		| [EConst(Ident i),p as expr] ->
+			let tpath = { tpackage=[]; tname=i; tparams=[]; tsub=None } in
+			if pf = Cs then
+				(ENew((tpath,p), []), p), [], CTPath tpath
+			else
+				expr, [], CTPath tpath
+		| [ (EField(_),p as field) ] ->
+			let tpath = field_to_type_path ctx field in
+			if pf = Cs then
+				(ENew((tpath,p), []), p), [], CTPath tpath
+			else
+				field, [], CTPath tpath
+		| _ ->
+			display_error ctx "A @:strict metadata must contain exactly one parameter. Please check the documentation for more information" pos;
+			raise Exit
+	in
+	let texpr = type_expr ctx changed_expr NoValue in
+	let with_type_expr = (ECheckType( (EConst (Ident "null"), pos), (ctype,null_pos) ), pos) in
+	let extra = handle_fields ctx fields_to_check with_type_expr in
+	Meta.Meta, [make_meta ctx texpr extra], pos
+
+let check_strict_meta ctx metas =
+	let pf = ctx.com.platform in
+	match pf with
+		| Cs | Java ->
+			let ret = ref [] in
+			List.iter (function
+				| Meta.Strict,params,pos -> (try
+					ret := get_strict_meta ctx params pos :: !ret
+				with | Exit -> ())
+				| _ -> ()
+			) metas;
+			!ret
+		| _ -> []
+
+(**** end of strict meta handling *****)
+
+let add_constructor ctx c force_constructor p =
+	match c.cl_constructor, c.cl_super with
+	| None, Some ({ cl_constructor = Some cfsup } as csup,cparams) when not c.cl_extern ->
+		let cf = {
+			cfsup with
+			cf_pos = p;
+			cf_meta = List.filter (fun (m,_,_) -> m = Meta.CompilerGenerated) cfsup.cf_meta;
+			cf_doc = None;
+			cf_expr = None;
+		} in
+		let r = exc_protect ctx (fun r ->
+			let t = mk_mono() in
+			r := (fun() -> t);
+			let ctx = { ctx with
+				curfield = cf;
+				pass = PTypeField;
+			} in
+			ignore (follow cfsup.cf_type); (* make sure it's typed *)
+			(if ctx.com.config.pf_overload then List.iter (fun cf -> ignore (follow cf.cf_type)) cf.cf_overloads);
+			let map_arg (v,def) =
+				(*
+					let's optimize a bit the output by not always copying the default value
+					into the inherited constructor when it's not necessary for the platform
+				*)
+				match ctx.com.platform, def with
+				| _, Some _ when not ctx.com.config.pf_static -> v, (Some TNull)
+				| Flash, Some (TString _) -> v, (Some TNull)
+				| Cpp, Some (TString _) -> v, def
+				| Cpp, Some _ -> { v with v_type = ctx.t.tnull v.v_type }, (Some TNull)
+				| _ -> v, def
+			in
+			let args = (match cfsup.cf_expr with
+				| Some { eexpr = TFunction f } ->
+					List.map map_arg f.tf_args
+				| _ ->
+					let values = get_value_meta cfsup.cf_meta in
+					match follow cfsup.cf_type with
+					| TFun (args,_) ->
+						List.map (fun (n,o,t) ->
+							let def = try type_function_arg_value ctx t (Some (PMap.find n values)) with Not_found -> if o then Some TNull else None in
+							map_arg (alloc_var n (if o then ctx.t.tnull t else t) p,def) (* TODO: var pos *)
+						) args
+					| _ -> assert false
+			) in
+			let p = c.cl_pos in
+			let vars = List.map (fun (v,def) -> alloc_var v.v_name (apply_params csup.cl_params cparams v.v_type) v.v_pos, def) args in
+			let super_call = mk (TCall (mk (TConst TSuper) (TInst (csup,cparams)) p,List.map (fun (v,_) -> mk (TLocal v) v.v_type p) vars)) ctx.t.tvoid p in
+			let constr = mk (TFunction {
+				tf_args = vars;
+				tf_type = ctx.t.tvoid;
+				tf_expr = super_call;
+			}) (TFun (List.map (fun (v,c) -> v.v_name, c <> None, v.v_type) vars,ctx.t.tvoid)) p in
+			cf.cf_expr <- Some constr;
+			cf.cf_type <- t;
+			unify ctx t constr.etype p;
+			t
+		) "add_constructor" in
+		cf.cf_type <- TLazy r;
+		c.cl_constructor <- Some cf;
+		delay ctx PForce (fun() -> ignore((!r)()));
+	| None,_ when force_constructor ->
+		let constr = mk (TFunction {
+			tf_args = [];
+			tf_type = ctx.t.tvoid;
+			tf_expr = mk (TBlock []) ctx.t.tvoid p;
+		}) (tfun [] ctx.t.tvoid) p in
+		let cf = mk_field "new" constr.etype p in
+		cf.cf_expr <- Some constr;
+		cf.cf_type <- constr.etype;
+		cf.cf_meta <- [Meta.CompilerGenerated,[],p];
+		cf.cf_kind <- Method MethNormal;
+		c.cl_constructor <- Some cf;
+	| _ ->
+		(* nothing to do *)
+		()
+
+let check_struct_init_constructor ctx c p = match c.cl_constructor with
+	| Some _ ->
+		()
+	| None ->
+		let params = List.map snd c.cl_params in
+		let ethis = mk (TConst TThis) (TInst(c,params)) p in
+		let args,el,tl = List.fold_left (fun (args,el,tl) cf -> match cf.cf_kind with
+			| Var _ ->
+				let opt = Meta.has Meta.Optional cf.cf_meta in
+				let t = if opt then ctx.t.tnull cf.cf_type else cf.cf_type in
+				let v = alloc_var cf.cf_name t p in
+				let ef = mk (TField(ethis,FInstance(c,params,cf))) t p in
+				let ev = mk (TLocal v) v.v_type p in
+				let e = mk (TBinop(OpAssign,ef,ev)) ev.etype p in
+				(v,None) :: args,e :: el,(cf.cf_name,opt,t) :: tl
+			| Method _ ->
+				args,el,tl
+		) ([],[],[]) (List.rev c.cl_ordered_fields) in
+		let tf = {
+			tf_args = args;
+			tf_type = ctx.t.tvoid;
+			tf_expr = mk (TBlock el) ctx.t.tvoid p
+		} in
+		let e = mk (TFunction tf) (TFun(tl,ctx.t.tvoid)) p in
+		let cf = mk_field "new" e.etype p in
+		cf.cf_expr <- Some e;
+		cf.cf_type <- e.etype;
+		cf.cf_meta <- [Meta.CompilerGenerated,[],p];
+		cf.cf_kind <- Method MethNormal;
+		c.cl_constructor <- Some cf
+
+module Inheritance = struct
+	let check_extends ctx c t p = match follow t with
+		| TInst ({ cl_path = [],"Array"; cl_extern = basic_extern },_)
+		| TInst ({ cl_path = [],"String"; cl_extern = basic_extern },_)
+		| TInst ({ cl_path = [],"Date"; cl_extern = basic_extern },_)
+		| TInst ({ cl_path = [],"Xml"; cl_extern = basic_extern },_) when not (c.cl_extern && basic_extern) ->
+			error "Cannot extend basic class" p;
+		| TInst (csup,params) ->
+			if is_parent c csup then error "Recursive class" p;
+			begin match csup.cl_kind with
+				| KTypeParameter _ when not (is_generic_parameter ctx csup) -> error "Cannot extend non-generic type parameters" p
+				| _ -> csup,params
+			end
+		| _ -> error "Should extend by using a class" p
+
+	let rec check_interface ctx c intf params =
+		let p = c.cl_pos in
+		let rec check_field i f =
+			(if ctx.com.config.pf_overload then
+				List.iter (function
+					| f2 when f != f2 ->
+							check_field i f2
+					| _ -> ()) f.cf_overloads);
+			let is_overload = ref false in
+			try
+				let t2, f2 = class_field_no_interf c i in
+				let t2, f2 =
+					if ctx.com.config.pf_overload && (f2.cf_overloads <> [] || Meta.has Meta.Overload f2.cf_meta) then
+						let overloads = get_overloads c i in
+						is_overload := true;
+						let t = (apply_params intf.cl_params params f.cf_type) in
+						List.find (fun (t1,f1) -> same_overload_args t t1 f f1) overloads
+					else
+						t2, f2
+				in
+
+				ignore(follow f2.cf_type); (* force evaluation *)
+				let p = (match f2.cf_expr with None -> p | Some e -> e.epos) in
+				let mkind = function
+					| MethNormal | MethInline -> 0
+					| MethDynamic -> 1
+					| MethMacro -> 2
+				in
+				if f.cf_public && not f2.cf_public && not (Meta.has Meta.CompilerGenerated f.cf_meta) then
+					display_error ctx ("Field " ^ i ^ " should be public as requested by " ^ s_type_path intf.cl_path) p
+				else if not (unify_kind f2.cf_kind f.cf_kind) || not (match f.cf_kind, f2.cf_kind with Var _ , Var _ -> true | Method m1, Method m2 -> mkind m1 = mkind m2 | _ -> false) then
+					display_error ctx ("Field " ^ i ^ " has different property access than in " ^ s_type_path intf.cl_path ^ " (" ^ s_kind f2.cf_kind ^ " should be " ^ s_kind f.cf_kind ^ ")") p
+				else try
+					valid_redefinition ctx f2 t2 f (apply_params intf.cl_params params f.cf_type)
+				with
+					Unify_error l ->
+						if not (Meta.has Meta.CsNative c.cl_meta && c.cl_extern) then begin
+							display_error ctx ("Field " ^ i ^ " has different type than in " ^ s_type_path intf.cl_path) p;
+							display_error ctx ("Interface field is defined here") f.cf_pos;
+							display_error ctx (error_msg (Unify l)) p;
+						end
+			with
+				| Not_found when not c.cl_interface ->
+					let msg = if !is_overload then
+						let ctx = print_context() in
+						let args = match follow f.cf_type with | TFun(args,_) -> String.concat ", " (List.map (fun (n,o,t) -> (if o then "?" else "") ^ n ^ " : " ^ (s_type ctx t)) args) | _ -> assert false in
+						"No suitable overload for " ^ i ^ "( " ^ args ^ " ), as needed by " ^ s_type_path intf.cl_path ^ " was found"
+					else
+						("Field " ^ i ^ " needed by " ^ s_type_path intf.cl_path ^ " is missing")
+					in
+					display_error ctx msg p
+				| Not_found -> ()
+		in
+		PMap.iter check_field intf.cl_fields;
+		List.iter (fun (i2,p2) ->
+			check_interface ctx c i2 (List.map (apply_params intf.cl_params params) p2)
+		) intf.cl_implements
+
+	let check_interfaces ctx c =
+		match c.cl_path with
+		| "Proxy" :: _ , _ -> ()
+		| _ when c.cl_extern && Meta.has Meta.CsNative c.cl_meta -> ()
+		| _ ->
+		List.iter (fun (intf,params) -> check_interface ctx c intf params) c.cl_implements
+
+	let set_heritance ctx c herits p =
+		let is_lib = Meta.has Meta.LibType c.cl_meta in
+		let ctx = { ctx with curclass = c; type_params = c.cl_params; } in
+		let old_meta = c.cl_meta in
+		let process_meta csup =
+			List.iter (fun m ->
+				match m with
+				| Meta.Final, _, _ -> if not (Meta.has Meta.Hack c.cl_meta || (match c.cl_kind with KTypeParameter _ -> true | _ -> false)) then error "Cannot extend a final class" p;
+				| Meta.AutoBuild, el, p -> c.cl_meta <- (Meta.Build,el,p) :: m :: c.cl_meta
+				| _ -> ()
+			) csup.cl_meta
+		in
+		let check_cancel_build csup =
+			match csup.cl_build() with
+			| Built -> ()
+			| state ->
+				(* for macros reason, our super class is not yet built - see #2177 *)
+				(* let's reset our build and delay it until we are done *)
+				c.cl_meta <- old_meta;
+				raise (Build_canceled state)
+		in
+		let has_interf = ref false in
+		(*
+			resolve imports before calling build_inheritance, since it requires full paths.
+			that means that typedefs are not working, but that's a fair limitation
+		*)
+		let resolve_imports (t,p) =
+			match t.tpackage with
+			| _ :: _ -> t,p
+			| [] ->
+				try
+					let path_matches lt = snd (t_path lt) = t.tname in
+					let lt = try
+						List.find path_matches ctx.m.curmod.m_types
+					with Not_found ->
+						let t,pi = List.find (fun (lt,_) -> path_matches lt) ctx.m.module_types in
+						Display.mark_import_position ctx.com pi;
+						t
+					in
+					{ t with tpackage = fst (t_path lt) },p
+				with
+					Not_found -> t,p
+		in
+		let herits = ExtList.List.filter_map (function
+			| HExtends t -> Some(true,resolve_imports t)
+			| HImplements t -> Some(false,resolve_imports t)
+			| t -> None
+		) herits in
+		let herits = List.filter (ctx.g.do_inherit ctx c p) herits in
+		(* Pass 1: Check and set relations *)
+		let fl = List.map (fun (is_extends,t) ->
+			let t = load_instance ~allow_display:true ctx t false p in
+			if is_extends then begin
+				if c.cl_super <> None then error "Cannot extend several classes" p;
+				let csup,params = check_extends ctx c t p in
+				if c.cl_interface then begin
+					if not csup.cl_interface then error "Cannot extend by using a class" p;
+					c.cl_implements <- (csup,params) :: c.cl_implements;
+					if not !has_interf then begin
+						if not is_lib then delay ctx PForce (fun() -> check_interfaces ctx c);
+						has_interf := true;
+					end
+				end else begin
+					if csup.cl_interface then error "Cannot extend by using an interface" p;
+					c.cl_super <- Some (csup,params)
+				end;
+				(fun () ->
+					check_cancel_build csup;
+					process_meta csup;
+				)
+			end else begin match follow t with
+				| TInst ({ cl_path = [],"ArrayAccess"; cl_extern = true; },[t]) ->
+					if c.cl_array_access <> None then error "Duplicate array access" p;
+					c.cl_array_access <- Some t;
+					(fun () -> ())
+				| TInst (intf,params) ->
+					if is_parent c intf then error "Recursive class" p;
+					if c.cl_interface then error "Interfaces cannot implement another interface (use extends instead)" p;
+					if not intf.cl_interface then error "You can only implement an interface" p;
+					c.cl_implements <- (intf, params) :: c.cl_implements;
+					if not !has_interf && not is_lib && not (Meta.has (Meta.Custom "$do_not_check_interf") c.cl_meta) then begin
+						delay ctx PForce (fun() -> check_interfaces ctx c);
+						has_interf := true;
+					end;
+					(fun () ->
+						check_cancel_build intf;
+						process_meta intf;
+					)
+				| TDynamic t ->
+					if c.cl_dynamic <> None then error "Cannot have several dynamics" p;
+					c.cl_dynamic <- Some t;
+					(fun () -> ())
+				| _ ->
+					error "Should implement by using an interface" p
+			end
+		) herits in
+		fl
+end
+
+let rec type_type_param ?(enum_constructor=false) ctx path get_params p tp =
+	let n = fst tp.tp_name in
+	let c = mk_class ctx.m.curmod (fst path @ [snd path],n) (pos tp.tp_name) in
+	c.cl_params <- type_type_params ctx c.cl_path get_params p tp.tp_params;
+	c.cl_kind <- KTypeParameter [];
+	c.cl_meta <- tp.Ast.tp_meta;
+	if enum_constructor then c.cl_meta <- (Meta.EnumConstructorParam,[],c.cl_pos) :: c.cl_meta;
+	let t = TInst (c,List.map snd c.cl_params) in
+	if ctx.is_display_file && Display.is_display_position (pos tp.tp_name) then
+		Display.display_type ctx.com.display t (pos tp.tp_name);
+	match tp.tp_constraints with
+	| [] ->
+		n, t
+	| _ ->
+		let r = exc_protect ctx (fun r ->
+			r := (fun _ -> t);
+			let ctx = { ctx with type_params = ctx.type_params @ get_params() } in
+			let constr = List.map (load_complex_type ctx true p) tp.tp_constraints in
+			(* check against direct recursion *)
+			let rec loop t =
+				match follow t with
+				| TInst (c2,_) when c == c2 -> error "Recursive constraint parameter is not allowed" p
+				| TInst ({ cl_kind = KTypeParameter cl },_) ->
+					List.iter loop cl
+				| _ ->
+					()
+			in
+			List.iter loop constr;
+			c.cl_kind <- KTypeParameter constr;
+			t
+		) "constraint" in
+		delay ctx PForce (fun () -> ignore(!r()));
+		n, TLazy r
+
+and type_type_params ?(enum_constructor=false) ctx path get_params p tpl =
+	let names = ref [] in
+	List.map (fun tp ->
+		if List.exists (fun name -> name = fst tp.tp_name) !names then display_error ctx ("Duplicate type parameter name: " ^ fst tp.tp_name) (pos tp.tp_name);
+		names := (fst tp.tp_name) :: !names;
+		type_type_param ~enum_constructor ctx path get_params p tp
+	) tpl
+
+let type_function_params ctx fd fname p =
+	let params = ref [] in
+	params := type_type_params ctx ([],fname) (fun() -> !params) p fd.f_params;
+	!params
+
+let type_function ctx args ret fmode f do_display p =
+	let locals = save_locals ctx in
+	let fargs = List.map2 (fun (n,c,t) ((_,pn),_,m,_,_) ->
+		if n.[0] = '$' then error "Function argument names starting with a dollar are not allowed" p;
+		let c = type_function_arg_value ctx t c in
+		let v,c = add_local ctx n t pn, c in
+		v.v_meta <- m;
+		if do_display && Display.is_display_position pn then
+			Display.display_variable ctx.com.display v pn;
+		if n = "this" then v.v_meta <- (Meta.This,[],p) :: v.v_meta;
+		v,c
+	) args f.f_args in
+	let old_ret = ctx.ret in
+	let old_fun = ctx.curfun in
+	let old_opened = ctx.opened in
+	ctx.curfun <- fmode;
+	ctx.ret <- ret;
+	ctx.opened <- [];
+	let e = match f.f_expr with None -> error "Function body required" p | Some e -> e in
+	let e = if not do_display then
+		type_expr ctx e NoValue
+	else begin
+		let e = Display.process_expr ctx.com e in
+		try
+			if Common.defined ctx.com Define.NoCOpt then raise Exit;
+			type_expr ctx (Optimizer.optimize_completion_expr e) NoValue
+		with
+		| Parser.TypePath (_,None,_) | Exit ->
+			type_expr ctx e NoValue
+		| Display.DisplayType (t,_) | Display.DisplaySignatures [(t,_)] when (match follow t with TMono _ -> true | _ -> false) ->
+			type_expr ctx (if ctx.com.display = DMToplevel then Display.find_enclosing ctx.com e else e) NoValue
+	end in
+	let e = match e.eexpr with
+		| TMeta((Meta.MergeBlock,_,_), ({eexpr = TBlock el} as e1)) -> e1
+		| _ -> e
+	in
+	let has_return e =
+		let rec loop e =
+			match e.eexpr with
+			| TReturn (Some _) -> raise Exit
+			| TFunction _ -> ()
+			| _ -> Type.iter loop e
+		in
+		try loop e; false with Exit -> true
+	in
+	begin match follow ret with
+		| TAbstract({a_path=[],"Void"},_) -> ()
+		(* We have to check for the presence of return expressions here because
+		   in the case of Dynamic ctx.ret is still a monomorph. If we indeed
+		   don't have a return expression we can link the monomorph to Void. We
+		   can _not_ use type_iseq to avoid the Void check above because that
+		   would turn Dynamic returns to Void returns. *)
+		| TMono t when not (has_return e) -> ignore(link t ret ctx.t.tvoid)
+		| _ when ctx.com.display <> DMNone -> ()
+		| _ -> (try return_flow ctx e with Exit -> ())
+	end;
+	let rec loop e =
+		match e.eexpr with
+		| TCall ({ eexpr = TConst TSuper },_) -> raise Exit
+		| TFunction _ -> ()
+		| _ -> Type.iter loop e
+	in
+	let has_super_constr() =
+		match ctx.curclass.cl_super with
+		| None ->
+			None
+		| Some (csup,tl) ->
+			try
+				let _,cf = get_constructor (fun f->f.cf_type) csup in
+				Some (Meta.has Meta.CompilerGenerated cf.cf_meta,TInst(csup,tl))
+			with Not_found ->
+				None
+	in
+	let e = if fmode <> FunConstructor then
+		e
+	else match has_super_constr() with
+		| Some (was_forced,t_super) ->
+			(try
+				loop e;
+				if was_forced then
+					let e_super = mk (TConst TSuper) t_super e.epos in
+					let e_super_call = mk (TCall(e_super,[])) ctx.t.tvoid e.epos in
+					concat e_super_call e
+				else begin
+					display_error ctx "Missing super constructor call" p;
+					e
+				end
+			with
+				Exit -> e);
+		| None ->
+			e
+	in
+	locals();
+	let e = match ctx.curfun, ctx.vthis with
+		| (FunMember|FunConstructor), Some v ->
+			let ev = mk (TVar (v,Some (mk (TConst TThis) ctx.tthis p))) ctx.t.tvoid p in
+			(match e.eexpr with
+			| TBlock l -> { e with eexpr = TBlock (ev::l) }
+			| _ -> mk (TBlock [ev;e]) e.etype p)
+		| _ -> e
+	in
+	List.iter (fun r -> r := Closed) ctx.opened;
+	ctx.ret <- old_ret;
+	ctx.curfun <- old_fun;
+	ctx.opened <- old_opened;
+	e , fargs
+
+let load_core_class ctx c =
+	let ctx2 = (match ctx.g.core_api with
+		| None ->
+			let com2 = Common.clone ctx.com in
+			com2.defines <- PMap.empty;
+			Common.define com2 Define.CoreApi;
+			Common.define com2 Define.Sys;
+			if ctx.in_macro then Common.define com2 Define.Macro;
+			com2.class_path <- ctx.com.std_path;
+			let ctx2 = ctx.g.do_create com2 in
+			ctx.g.core_api <- Some ctx2;
+			ctx2
+		| Some c ->
+			c
+	) in
+	let tpath = match c.cl_kind with
+		| KAbstractImpl a -> { tpackage = fst a.a_path; tname = snd a.a_path; tparams = []; tsub = None; }
+		| _ -> { tpackage = fst c.cl_path; tname = snd c.cl_path; tparams = []; tsub = None; }
+	in
+	let t = load_instance ctx2 (tpath,c.cl_pos) true c.cl_pos in
+	flush_pass ctx2 PFinal "core_final";
+	match t with
+	| TInst (ccore,_) | TAbstract({a_impl = Some ccore}, _) ->
+		ccore
+	| _ ->
+		assert false
+
+let init_core_api ctx c =
+	let ccore = load_core_class ctx c in
+	begin try
+		List.iter2 (fun (n1,t1) (n2,t2) -> match follow t1, follow t2 with
+			| TInst({cl_kind = KTypeParameter l1},_),TInst({cl_kind = KTypeParameter l2},_) ->
+				begin try
+					List.iter2 (fun t1 t2 -> type_eq EqCoreType t2 t1) l1 l2
+				with
+					| Invalid_argument _ ->
+						error "Type parameters must have the same number of constraints as core type" c.cl_pos
+					| Unify_error l ->
+						display_error ctx ("Type parameter " ^ n2 ^ " has different constraint than in core type") c.cl_pos;
+						display_error ctx (error_msg (Unify l)) c.cl_pos
+				end
+			| t1,t2 ->
+				Printf.printf "%s %s" (s_type (print_context()) t1) (s_type (print_context()) t2);
+				assert false
+		) ccore.cl_params c.cl_params;
+	with Invalid_argument _ ->
+		error "Class must have the same number of type parameters as core type" c.cl_pos
+	end;
+	(match c.cl_doc with
+	| None -> c.cl_doc <- ccore.cl_doc
+	| Some _ -> ());
+	let compare_fields f f2 =
+		let p = (match f2.cf_expr with None -> c.cl_pos | Some e -> e.epos) in
+		(try
+			type_eq EqCoreType (apply_params ccore.cl_params (List.map snd c.cl_params) f.cf_type) f2.cf_type
+		with Unify_error l ->
+			display_error ctx ("Field " ^ f.cf_name ^ " has different type than in core type") p;
+			display_error ctx (error_msg (Unify l)) p);
+		if f2.cf_public <> f.cf_public then error ("Field " ^ f.cf_name ^ " has different visibility than core type") p;
+		(match f2.cf_doc with
+		| None -> f2.cf_doc <- f.cf_doc
+		| Some _ -> ());
+		if f2.cf_kind <> f.cf_kind then begin
+			match f2.cf_kind, f.cf_kind with
+			| Method MethInline, Method MethNormal -> () (* allow to add 'inline' *)
+			| Method MethNormal, Method MethInline -> () (* allow to disable 'inline' *)
+			| _ ->
+				error ("Field " ^ f.cf_name ^ " has different property access than core type") p;
+		end;
+		(match follow f.cf_type, follow f2.cf_type with
+		| TFun (pl1,_), TFun (pl2,_) ->
+			if List.length pl1 != List.length pl2 then error "Argument count mismatch" p;
+			List.iter2 (fun (n1,_,_) (n2,_,_) ->
+				if n1 <> n2 then error ("Method parameter name '" ^ n2 ^ "' should be '" ^ n1 ^ "'") p;
+			) pl1 pl2;
+		| _ -> ());
+	in
+	let check_fields fcore fl =
+		PMap.iter (fun i f ->
+			if not f.cf_public then () else
+			let f2 = try PMap.find f.cf_name fl with Not_found -> error ("Missing field " ^ i ^ " required by core type") c.cl_pos in
+			compare_fields f f2;
+		) fcore;
+		PMap.iter (fun i f ->
+			let p = (match f.cf_expr with None -> c.cl_pos | Some e -> e.epos) in
+			if f.cf_public && not (Meta.has Meta.Hack f.cf_meta) && not (PMap.mem f.cf_name fcore) && not (List.memq f c.cl_overrides) then error ("Public field " ^ i ^ " is not part of core type") p;
+		) fl;
+	in
+	check_fields ccore.cl_fields c.cl_fields;
+	check_fields ccore.cl_statics c.cl_statics;
+	(match ccore.cl_constructor, c.cl_constructor with
+	| None, None -> ()
+	| Some { cf_public = false }, _ -> ()
+	| Some f, Some f2 -> compare_fields f f2
+	| None, Some { cf_public = false } -> ()
+	| _ -> error "Constructor differs from core type" c.cl_pos)
+
+let check_global_metadata ctx f_add mpath tpath so =
+	let sl1 = full_dot_path mpath tpath in
+	let sl1,field_mode = match so with None -> sl1,false | Some s -> sl1 @ [s],true in
+	List.iter (fun (sl2,m,(recursive,to_types,to_fields)) ->
+		let add = ((field_mode && to_fields) || (not field_mode && to_types)) && (match_path recursive sl1 sl2) in
+		if add then f_add m
+	) ctx.g.global_metadata
+
+let patch_class ctx c fields =
+	let path = match c.cl_kind with
+		| KAbstractImpl a -> a.a_path
+		| _ -> c.cl_path
+	in
+	let h = (try Some (Hashtbl.find ctx.g.type_patches path) with Not_found -> None) in
+	match h with
+	| None -> fields
+	| Some (h,hcl) ->
+		c.cl_meta <- c.cl_meta @ hcl.tp_meta;
+		let rec loop acc = function
+			| [] -> acc
+			| f :: l ->
+				(* patch arguments types *)
+				(match f.cff_kind with
+				| FFun ff ->
+					let param (((n,pn),opt,m,_,e) as p) =
+						try
+							let t2 = (try Hashtbl.find h (("$" ^ (fst f.cff_name) ^ "__" ^ n),false) with Not_found -> Hashtbl.find h (("$" ^ n),false)) in
+							(n,pn), opt, m, (match t2.tp_type with None -> None | Some t -> Some (t,null_pos)), e
+						with Not_found ->
+							p
+					in
+					f.cff_kind <- FFun { ff with f_args = List.map param ff.f_args }
+				| _ -> ());
+				(* other patches *)
+				match (try Some (Hashtbl.find h (fst f.cff_name,List.mem AStatic f.cff_access)) with Not_found -> None) with
+				| None -> loop (f :: acc) l
+				| Some { tp_remove = true } -> loop acc l
+				| Some p ->
+					f.cff_meta <- f.cff_meta @ p.tp_meta;
+					(match p.tp_type with
+					| None -> ()
+					| Some t ->
+						f.cff_kind <- match f.cff_kind with
+						| FVar (_,e) -> FVar (Some (t,null_pos),e)
+						| FProp (get,set,_,eo) -> FProp (get,set,Some (t,null_pos),eo)
+						| FFun f -> FFun { f with f_type = Some (t,null_pos) });
+					loop (f :: acc) l
+		in
+		List.rev (loop [] fields)
+
+let string_list_of_expr_path (e,p) =
+	try string_list_of_expr_path_raise (e,p)
+	with Exit -> error "Invalid path" p
+
+let build_enum_abstract ctx c a fields p =
+	List.iter (fun field ->
+		match field.cff_kind with
+		| FVar(ct,eo) when not (List.mem AStatic field.cff_access) ->
+			field.cff_access <- [AStatic; if (List.mem APrivate field.cff_access) then APrivate else APublic];
+			field.cff_meta <- (Meta.Enum,[],field.cff_pos) :: (Meta.Impl,[],field.cff_pos) :: field.cff_meta;
+			let ct = match ct with
+				| Some _ -> ct
+				| None -> Some (TExprToExpr.convert_type (TAbstract(a,List.map snd a.a_params)),null_pos)
+			in
+			begin match eo with
+				| None ->
+					if not c.cl_extern then error "Value required" field.cff_pos
+					else field.cff_kind <- FProp("default","never",ct,None)
+				| Some e ->
+					field.cff_access <- AInline :: field.cff_access;
+					let e = (ECast(e,None),(pos e)) in
+					field.cff_kind <- FVar(ct,Some e)
+			end
+		| _ ->
+			()
+	) fields;
+	EVars [("",null_pos),Some (CTAnonymous fields,p),None],p
+
+let is_java_native_function meta = try
+	match Meta.get Meta.Native meta with
+		| (Meta.Native,[],_) -> true
+		| _ -> false
+	with | Not_found -> false
+
+let build_module_def ctx mt meta fvars context_init fbuild =
+	let loop (f_build,f_enum) = function
+		| Meta.Build,args,p -> (fun () ->
+				let epath, el = (match args with
+					| [ECall (epath,el),p] -> epath, el
+					| _ -> error "Invalid build parameters" p
+				) in
+				let s = try String.concat "." (List.rev (string_list_of_expr_path epath)) with Error (_,p) -> error "Build call parameter must be a class path" p in
+				if ctx.in_macro then error "You cannot use @:build inside a macro : make sure that your type is not used in macro" p;
+				let old = ctx.g.get_build_infos in
+				ctx.g.get_build_infos <- (fun() -> Some (mt, List.map snd (t_infos mt).mt_params, fvars()));
+				context_init();
+				let r = try apply_macro ctx MBuild s el p with e -> ctx.g.get_build_infos <- old; raise e in
+				ctx.g.get_build_infos <- old;
+				(match r with
+				| None -> error "Build failure" p
+				| Some e -> fbuild e)
+			) :: f_build,f_enum
+		| Meta.Enum,_,p -> f_build,Some (fun () ->
+				begin match mt with
+					| TClassDecl ({cl_kind = KAbstractImpl a} as c) ->
+						context_init();
+						let e = build_enum_abstract ctx c a (fvars()) p in
+						fbuild e;
+					| _ ->
+						()
+				end
+			)
+		| _ ->
+			f_build,f_enum
+	in
+	(* let errors go through to prevent resume if build fails *)
+	let f_build,f_enum = List.fold_left loop ([],None) meta in
+	List.iter (fun f -> f()) (List.rev f_build);
+	(match f_enum with None -> () | Some f -> f())
+
+module ClassInitializer = struct
+	type class_init_ctx = {
+		tclass : tclass; (* I don't trust ctx.curclass because it's mutable. *)
+		is_lib : bool;
+		is_native : bool;
+		is_core_api : bool;
+		extends_public : bool;
+		abstract : tabstract option;
+		context_init : unit -> unit;
+		completion_position : pos;
+		mutable delayed_expr : (typer * (unit -> t) ref option) list;
+		mutable force_constructor : bool;
+	}
+
+	type field_kind =
+		| FKNormal
+		| FKConstructor
+		| FKInit
+
+	type field_init_ctx = {
+		is_inline : bool;
+		is_static : bool;
+		is_override : bool;
+		is_extern : bool;
+		is_macro : bool;
+		is_abstract_member : bool;
+		is_display_field : bool;
+		field_kind : field_kind;
+		mutable do_bind : bool;
+		mutable do_add : bool;
+	}
+
+	let create_class_context ctx c context_init p =
+		locate_macro_error := true;
+		incr stats.s_classes_built;
+		let abstract = match c.cl_kind with
+			| KAbstractImpl a -> Some a
+			| _ -> None
+		in
+		let ctx = {
+			ctx with
+			curclass = c;
+			type_params = c.cl_params;
+			pass = PBuildClass;
+			tthis = (match abstract with
+				| Some a ->
+					(match a.a_this with
+					| TMono r when !r = None -> TAbstract (a,List.map snd c.cl_params)
+					| t -> t)
+				| None -> TInst (c,List.map snd c.cl_params));
+			on_error = (fun ctx msg ep ->
+				ctx.com.error msg ep;
+				(* macros expressions might reference other code, let's recall which class we are actually compiling *)
+				if !locate_macro_error && (ep.pfile <> c.cl_pos.pfile || ep.pmax < c.cl_pos.pmin || ep.pmin > c.cl_pos.pmax) then ctx.com.error "Defined in this class" c.cl_pos
+			);
+		} in
+		(* a lib type will skip most checks *)
+		let is_lib = Meta.has Meta.LibType c.cl_meta in
+		if is_lib && not c.cl_extern then ctx.com.error "@:libType can only be used in extern classes" c.cl_pos;
+		(* a native type will skip one check: the static vs non-static field *)
+		let is_native = Meta.has Meta.JavaNative c.cl_meta || Meta.has Meta.CsNative c.cl_meta in
+		if Meta.has Meta.Macro c.cl_meta then display_error ctx "Macro classes are no longer allowed in haxe 3" c.cl_pos;
+		let rec extends_public c =
+			Meta.has Meta.PublicFields c.cl_meta ||
+			match c.cl_super with
+			| None -> false
+			| Some (c,_) -> extends_public c
+		in
+		let cctx = {
+			tclass = c;
+			is_lib = is_lib;
+			is_native = is_native;
+			is_core_api = Meta.has Meta.CoreApi c.cl_meta;
+			extends_public = extends_public c;
+			abstract = abstract;
+			context_init = context_init;
+			completion_position = !Parser.resume_display;
+			force_constructor = false;
+			delayed_expr = [];
+		} in
+		ctx,cctx
+
+	let create_field_context (ctx,cctx) c cff =
+		let ctx = {
+			ctx with
+			pass = PBuildClass; (* will be set later to PTypeExpr *)
+		} in
+		let is_static = List.mem AStatic cff.cff_access in
+		let is_extern = Meta.has Meta.Extern cff.cff_meta || c.cl_extern in
+		let allow_inline = cctx.abstract <> None || match cff.cff_kind with
+			| FFun _ -> ctx.g.doinline || is_extern
+			| _ -> true
+		in
+		let is_inline = allow_inline && List.mem AInline cff.cff_access in
+		let is_override = List.mem AOverride cff.cff_access in
+		let is_macro = List.mem AMacro cff.cff_access in
+		let field_kind = match fst cff.cff_name with
+			| "new" -> FKConstructor
+			| "__init__" when is_static -> FKInit
+			| _ -> FKNormal
+		in
+		let fctx = {
+			is_inline = is_inline;
+			is_static = is_static;
+			is_override = is_override;
+			is_macro = is_macro;
+			is_extern = is_extern;
+			is_display_field = ctx.is_display_file && Display.is_display_position cff.cff_pos;
+			is_abstract_member = cctx.abstract <> None && Meta.has Meta.Impl cff.cff_meta;
+			field_kind = field_kind;
+			do_bind = (((not c.cl_extern || is_inline) && not c.cl_interface) || field_kind = FKInit);
+			do_add = true;
+		} in
+		ctx,fctx
+
+	let is_public (ctx,cctx) access parent =
+		let c = cctx.tclass in
+		if List.mem APrivate access then
+			false
+		else if List.mem APublic access then
+			true
+		else match parent with
+			| Some { cf_public = p } -> p
+			| _ -> c.cl_extern || c.cl_interface || cctx.extends_public
+
+	let rec get_parent c name =
+		match c.cl_super with
+		| None -> None
+		| Some (csup,_) ->
+			try
+				Some (PMap.find name csup.cl_fields)
+			with
+				Not_found -> get_parent csup name
+
+	let add_field c cf is_static =
+		if is_static then begin
+			c.cl_statics <- PMap.add cf.cf_name cf c.cl_statics;
+			c.cl_ordered_statics <- cf :: c.cl_ordered_statics;
+		end else begin
+			c.cl_fields <- PMap.add cf.cf_name cf c.cl_fields;
+			c.cl_ordered_fields <- cf :: c.cl_ordered_fields;
+		end
+
+	let type_opt (ctx,cctx) p t =
+		let c = cctx.tclass in
+		match t with
+		| None when c.cl_extern || c.cl_interface ->
+			display_error ctx "Type required for extern classes and interfaces" p;
+			t_dynamic
+		| None when cctx.is_core_api ->
+			display_error ctx "Type required for core api classes" p;
+			t_dynamic
+		| _ ->
+			load_type_hint ctx p t
+
+	let build_fields (ctx,cctx) c fields =
+		let fields = ref fields in
+		let get_fields() = !fields in
+		let pending = ref [] in
+		c.cl_build <- (fun() -> BuildMacro pending);
+		build_module_def ctx (TClassDecl c) c.cl_meta get_fields cctx.context_init (fun (e,p) ->
+			match e with
+			| EVars [_,Some (CTAnonymous f,p),None] ->
+				let f = List.map (fun f ->
+					let f = match cctx.abstract with
+						| Some a ->
+							let a_t = TExprToExpr.convert_type' (TAbstract(a,List.map snd a.a_params)) in
+							let this_t = TExprToExpr.convert_type' a.a_this in (* TODO: better pos? *)
+							transform_abstract_field ctx.com this_t a_t a f
+						| None ->
+							f
+					in
+					if List.mem AMacro f.cff_access then
+						(match ctx.g.macros with
+						| Some (_,mctx) when Hashtbl.mem mctx.g.types_module c.cl_path ->
+							(* assume that if we had already a macro with the same name, it has not been changed during the @:build operation *)
+							if not (List.exists (fun f2 -> f2.cff_name = f.cff_name && List.mem AMacro f2.cff_access) (!fields)) then
+								error "Class build macro cannot return a macro function when the class has already been compiled into the macro context" p
+						| _ -> ());
+					f
+				) f in
+				fields := f
+			| _ -> error "Class build macro must return a single variable with anonymous fields" p
+		);
+		c.cl_build <- (fun() -> Building);
+		List.iter (fun f -> f()) !pending;
+		!fields
+
+	let bind_type (ctx,cctx,fctx) cf r p =
+		let c = cctx.tclass in
+		let rec is_full_type t =
+			match t with
+			| TFun (args,ret) -> is_full_type ret && List.for_all (fun (_,_,t) -> is_full_type t) args
+			| TMono r -> (match !r with None -> false | Some t -> is_full_type t)
+			| TAbstract _ | TInst _ | TEnum _ | TLazy _ | TDynamic _ | TAnon _ | TType _ -> true
+		in
+		let handle_display_field () =
+			if fctx.is_macro && not ctx.in_macro then
+				(* force macro system loading of this class in order to get completion *)
+				delay ctx PTypeField (fun() -> try ignore(ctx.g.do_macro ctx MDisplay c.cl_path cf.cf_name [] p) with Exit -> ())
+			else begin
+				cf.cf_type <- TLazy r;
+				cctx.delayed_expr <- (ctx,Some r) :: cctx.delayed_expr;
+			end
+		in
+		begin match ctx.com.display with
+			| DMNone | DMUsage ->
+				if fctx.is_macro && not ctx.in_macro then
+					()
+				else begin
+					cf.cf_type <- TLazy r;
+					(* is_lib ? *)
+					cctx.delayed_expr <- (ctx,Some r) :: cctx.delayed_expr;
+				end
+			| DMDiagnostics ->
+				handle_display_field()
+			| _ ->
+				if fctx.is_display_field then begin
+					handle_display_field()
+				end else begin
+					if not (is_full_type cf.cf_type) then begin
+						cctx.delayed_expr <- (ctx, None) :: cctx.delayed_expr;
+						cf.cf_type <- TLazy r;
+					end;
+				end
+		end
+
+	let bind_var (ctx,cctx,fctx) cf e =
+		let c = cctx.tclass in
+		let p = cf.cf_pos in
+		let rec get_declared f = function
+			| None -> None
+			| Some (c,a) when PMap.exists f c.cl_fields ->
+				Some (c,a)
+			| Some (c,_) ->
+				let ret = get_declared f c.cl_super in
+				match ret with
+					| Some r -> Some r
+					| None ->
+						let rec loop ifaces = match ifaces with
+							| [] -> None
+							| i :: ifaces -> match get_declared f (Some i) with
+								| Some r -> Some r
+								| None -> loop ifaces
+						in
+						loop c.cl_implements
+		in
+		if not fctx.is_static && not cctx.is_lib then begin match get_declared cf.cf_name c.cl_super with
+				| None -> ()
+				| Some (csup,_) ->
+					(* this can happen on -net-lib generated classes if a combination of explicit interfaces and variables with the same name happens *)
+					if not (csup.cl_interface && Meta.has Meta.CsNative c.cl_meta) then
+						error ("Redefinition of variable " ^ cf.cf_name ^ " in subclass is not allowed. Previously declared at " ^ (Ast.s_type_path csup.cl_path) ) p
+		end;
+		let t = cf.cf_type in
+
+		match e with
+		| None ->
+			()
+		| Some e ->
+			if requires_value_meta ctx.com (Some c) then cf.cf_meta <- ((Meta.Value,[e],cf.cf_pos) :: cf.cf_meta);
+			let check_cast e =
+				(* insert cast to keep explicit field type (issue #1901) *)
+				if type_iseq e.etype cf.cf_type then
+					e
+				else begin match e.eexpr,follow cf.cf_type with
+					| TConst (TInt i),TAbstract({a_path=[],"Float"},_) ->
+						(* turn int constant to float constant if expected type is float *)
+						{e with eexpr = TConst (TFloat (Int32.to_string i))}
+					| _ ->
+						mk_cast e cf.cf_type e.epos
+				end
+			in
+			let r = exc_protect ctx (fun r ->
+				(* type constant init fields (issue #1956) *)
+				if not !return_partial_type || (match fst e with EConst _ -> true | _ -> false) then begin
+					r := (fun() -> t);
+					cctx.context_init();
+					if ctx.com.verbose then Common.log ctx.com ("Typing " ^ (if ctx.in_macro then "macro " else "") ^ s_type_path c.cl_path ^ "." ^ cf.cf_name);
+					let e = type_var_field ctx t e fctx.is_static fctx.is_display_field p in
+					let maybe_run_analyzer e = match e.eexpr with
+						| TConst _ | TLocal _ | TFunction _ -> e
+						| _ -> !analyzer_run_on_expr_ref ctx.com e
+					in
+					let require_constant_expression e msg =
+						if ctx.com.display <> DMNone then
+							e
+						else match Optimizer.make_constant_expression ctx (maybe_run_analyzer e) with
+						| Some e -> e
+						| None -> display_error ctx msg p; e
+					in
+					let e = (match cf.cf_kind with
+					| Var v when c.cl_extern || Meta.has Meta.Extern cf.cf_meta ->
+						if not fctx.is_static then begin
+							display_error ctx "Extern non-static variables may not be initialized" p;
+							e
+						end else if not fctx.is_inline then begin
+							display_error ctx "Extern non-inline variables may not be initialized" p;
+							e
+						end else require_constant_expression e "Extern variable initialization must be a constant value"
+					| Var v when is_extern_field cf ->
+						(* disallow initialization of non-physical fields (issue #1958) *)
+						display_error ctx "This field cannot be initialized because it is not a real variable" p; e
+					| Var v when not fctx.is_static ->
+						let e = match ctx.com.display with
+							| DMNone ->
+								begin match Optimizer.make_constant_expression ctx (maybe_run_analyzer e) with
+									| Some e -> e
+									| None ->
+										let rec has_this e = match e.eexpr with
+											| TConst TThis ->
+												display_error ctx "Cannot access this or other member field in variable initialization" e.epos;
+											| TLocal v when (match ctx.vthis with Some v2 -> v == v2 | None -> false) ->
+												display_error ctx "Cannot access this or other member field in variable initialization" e.epos;
+											| _ ->
+											Type.iter has_this e
+										in
+										has_this e;
+										e
+								end
+							| _ ->
+								e
+						in
+						e
+					| Var v when v.v_read = AccInline ->
+						let e = require_constant_expression e "Inline variable initialization must be a constant value" in
+						begin match c.cl_kind with
+							| KAbstractImpl a when Meta.has Meta.Enum cf.cf_meta && Meta.has Meta.Enum a.a_meta ->
+								unify ctx t (TAbstract(a,(List.map (fun _ -> mk_mono()) a.a_params))) p;
+								begin match e.eexpr with
+									| TCast(e1,None) -> unify ctx e1.etype a.a_this e1.epos
+									| _ -> assert false
+								end
+							| _ ->
+								()
+						end;
+						e
+					| _ ->
+						e
+					) in
+					let e = check_cast e in
+					cf.cf_expr <- Some e;
+					cf.cf_type <- t;
+				end;
+				t
+			) "bind_var" in
+			if not fctx.is_static then cctx.force_constructor <- true;
+			bind_type (ctx,cctx,fctx) cf r (snd e)
+
+	let check_field_display com p cf =
+ 		if Display.is_display_position p then
+			Display.display_field com.display cf p
+
+	let create_variable (ctx,cctx,fctx) c f t eo p =
+		if not fctx.is_static && cctx.abstract <> None then error (fst f.cff_name ^ ": Cannot declare member variable in abstract") p;
+		if fctx.is_inline && not fctx.is_static then error (fst f.cff_name ^ ": Inline variable must be static") p;
+		if fctx.is_inline && eo = None then error (fst f.cff_name ^ ": Inline variable must be initialized") p;
+
+		let t = (match t with
+			| None when not fctx.is_static && eo = None ->
+				error ("Type required for member variable " ^ fst f.cff_name) p;
+			| None ->
+				mk_mono()
+			| Some t ->
+				(* TODO is_lib: only load complex type if needed *)
+				let old = ctx.type_params in
+				if fctx.is_static then ctx.type_params <- (match cctx.abstract with
+					| Some a -> a.a_params
+					| _ -> []
+				);
+				let t = load_complex_type ctx true p t in
+				if fctx.is_static then ctx.type_params <- old;
+				t
+		) in
+		let cf = {
+			cf_name = fst f.cff_name;
+			cf_doc = f.cff_doc;
+			cf_meta = f.cff_meta;
+			cf_type = t;
+			cf_pos = f.cff_pos;
+			cf_kind = Var (if fctx.is_inline then { v_read = AccInline ; v_write = AccNever } else { v_read = AccNormal; v_write = AccNormal });
+			cf_expr = None;
+			cf_public = is_public (ctx,cctx) f.cff_access None;
+			cf_params = [];
+			cf_overloads = [];
+		} in
+		ctx.curfield <- cf;
+		bind_var (ctx,cctx,fctx) cf eo;
+		if fctx.is_display_field then check_field_display ctx.com (pos f.cff_name) cf;
+		cf
+
+	let check_abstract (ctx,cctx,fctx) c cf fd t ret p =
+		match cctx.abstract with
+			| Some a ->
+				let m = mk_mono() in
+				let ta = TAbstract(a, List.map (fun _ -> mk_mono()) a.a_params) in
+				let tthis = if fctx.is_abstract_member || Meta.has Meta.To cf.cf_meta then monomorphs a.a_params a.a_this else a.a_this in
+				let allows_no_expr = ref (Meta.has Meta.CoreType a.a_meta) in
+				let rec loop ml = match ml with
+					| (Meta.From,_,_) :: _ ->
+						let r = fun () ->
+							(* the return type of a from-function must be the abstract, not the underlying type *)
+							if not fctx.is_macro then (try type_eq EqStrict ret ta with Unify_error l -> error (error_msg (Unify l)) p);
+							match t with
+								| TFun([_,_,t],_) -> t
+								| _ -> error (cf.cf_name ^ ": @:from cast functions must accept exactly one argument") p
+						in
+						a.a_from_field <- (TLazy (ref r),cf) :: a.a_from_field;
+					| (Meta.To,_,_) :: _ ->
+						if fctx.is_macro then error (cf.cf_name ^ ": Macro cast functions are not supported") p;
+						(* TODO: this doesn't seem quite right... *)
+						if not (Meta.has Meta.Impl cf.cf_meta) then cf.cf_meta <- (Meta.Impl,[],cf.cf_pos) :: cf.cf_meta;
+						let resolve_m args =
+							(try unify_raise ctx t (tfun (tthis :: args) m) cf.cf_pos with Error (Unify l,p) -> error (error_msg (Unify l)) p);
+							match follow m with
+								| TMono _ when (match t with TFun(_,r) -> r == t_dynamic | _ -> false) -> t_dynamic
+								| m -> m
+						in
+							let r = exc_protect ctx (fun r ->
+								let args = if Meta.has Meta.MultiType a.a_meta then begin
+								let ctor = try
+									PMap.find "_new" c.cl_statics
+								with Not_found ->
+									error "Constructor of multi-type abstract must be defined before the individual @:to-functions are" cf.cf_pos
+								in
+								(* delay ctx PFinal (fun () -> unify ctx m tthis f.cff_pos); *)
+								let args = match follow (monomorphs a.a_params ctor.cf_type) with
+									| TFun(args,_) -> List.map (fun (_,_,t) -> t) args
+									| _ -> assert false
+								in
+								args
+							end else
+								[]
+							in
+							let t = resolve_m args in
+							r := (fun() -> t);
+							t
+						) "@:to" in
+						delay ctx PForce (fun() -> ignore ((!r)()));
+						a.a_to_field <- (TLazy r, cf) :: a.a_to_field
+					| ((Meta.ArrayAccess,_,_) | (Meta.Op,[(EArrayDecl _),_],_)) :: _ ->
+						if fctx.is_macro then error (cf.cf_name ^ ": Macro array-access functions are not supported") p;
+						a.a_array <- cf :: a.a_array;
+					| (Meta.Op,[EBinop(op,_,_),_],_) :: _ ->
+						if fctx.is_macro then error (cf.cf_name ^ ": Macro operator functions are not supported") p;
+						let targ = if fctx.is_abstract_member then tthis else ta in
+						let left_eq,right_eq = match follow t with
+							| TFun([(_,_,t1);(_,_,t2)],_) ->
+								type_iseq targ t1,type_iseq targ t2
+							| _ ->
+								if fctx.is_abstract_member then
+									error (cf.cf_name ^ ": Member @:op functions must accept exactly one argument") cf.cf_pos
+								else
+									error (cf.cf_name ^ ": Static @:op functions must accept exactly two arguments") cf.cf_pos
+						in
+						if not (left_eq || right_eq) then error (cf.cf_name ^ ": The left or right argument type must be " ^ (s_type (print_context()) targ)) cf.cf_pos;
+						if right_eq && Meta.has Meta.Commutative cf.cf_meta then error (cf.cf_name ^ ": @:commutative is only allowed if the right argument is not " ^ (s_type (print_context()) targ)) cf.cf_pos;
+						a.a_ops <- (op,cf) :: a.a_ops;
+						allows_no_expr := true;
+					| (Meta.Op,[EUnop(op,flag,_),_],_) :: _ ->
+						if fctx.is_macro then error (cf.cf_name ^ ": Macro operator functions are not supported") p;
+						let targ = if fctx.is_abstract_member then tthis else ta in
+						(try type_eq EqStrict t (tfun [targ] (mk_mono())) with Unify_error l -> raise (Error ((Unify l),cf.cf_pos)));
+						a.a_unops <- (op,flag,cf) :: a.a_unops;
+						allows_no_expr := true;
+					| (Meta.Impl,_,_) :: ml when cf.cf_name <> "_new" && not fctx.is_macro ->
+						begin match follow t with
+							| TFun((_,_,t1) :: _, _) when type_iseq tthis t1 ->
+								()
+							| _ ->
+								display_error ctx ("First argument of implementation function must be " ^ (s_type (print_context()) tthis)) cf.cf_pos
+						end;
+						loop ml
+					| ((Meta.Resolve,_,_) | (Meta.Op,[EField _,_],_)) :: _ ->
+						if a.a_resolve <> None then error "Multiple resolve methods are not supported" cf.cf_pos;
+						let targ = if fctx.is_abstract_member then tthis else ta in
+						begin match follow t with
+							| TFun([(_,_,t1);(_,_,t2)],_) ->
+								if not fctx.is_macro then begin
+									if not (type_iseq targ t1) then error ("First argument type must be " ^ (s_type (print_context()) targ)) cf.cf_pos;
+									if not (type_iseq ctx.t.tstring t2) then error ("Second argument type must be String") cf.cf_pos
+								end
+							| _ ->
+								error ("Field type of resolve must be " ^ (s_type (print_context()) targ) ^ " -> String -> T") cf.cf_pos
+						end;
+						a.a_resolve <- Some cf;
+					| _ :: ml ->
+						loop ml
+					| [] ->
+						()
+				in
+				loop cf.cf_meta;
+				let check_bind () =
+					if fd.f_expr = None then begin
+						if fctx.is_inline then error (cf.cf_name ^ ": Inline functions must have an expression") cf.cf_pos;
+						begin match fd.f_type with
+							| None -> error (cf.cf_name ^ ": Functions without expressions must have an explicit return type") cf.cf_pos
+							| Some _ -> ()
+						end;
+						cf.cf_meta <- (Meta.NoExpr,[],cf.cf_pos) :: cf.cf_meta;
+						fctx.do_bind <- false;
+						if not (Meta.has Meta.CoreType a.a_meta) then fctx.do_add <- false;
+					end
+				in
+				if cf.cf_name = "_new" && Meta.has Meta.MultiType a.a_meta then fctx.do_bind <- false;
+				if !allows_no_expr then check_bind()
+			| _ ->
+				()
+
+	let create_method (ctx,cctx,fctx) c f fd p =
+		let params = type_function_params ctx fd (fst f.cff_name) p in
+		if Meta.has Meta.Generic f.cff_meta then begin
+			if params = [] then error (fst f.cff_name ^ ": Generic functions must have type parameters") p;
+		end;
+		let fd = if fctx.is_macro && not ctx.in_macro && not fctx.is_static then
+			(* remove display of first argument which will contain the "this" expression *)
+			{ fd with f_args = match fd.f_args with [] -> [] | _ :: l -> l }
+		else
+			fd
+		in
+		let fd = if not fctx.is_macro then
+			fd
+		else begin
+			if ctx.in_macro then begin
+				(* a class with a macro cannot be extern in macro context (issue #2015) *)
+				c.cl_extern <- false;
+				let texpr = CTPath { tpackage = ["haxe";"macro"]; tname = "Expr"; tparams = []; tsub = None } in
+				(* ExprOf type parameter might contain platform-specific type, let's replace it by Expr *)
+				let no_expr_of (t,p) = match t with
+					| CTPath { tpackage = ["haxe";"macro"]; tname = "Expr"; tsub = Some ("ExprOf"); tparams = [TPType _] }
+					| CTPath { tpackage = []; tname = ("ExprOf"); tsub = None; tparams = [TPType _] } -> Some (texpr,p)
+					| t -> Some (t,p)
+				in
+				{
+					f_params = fd.f_params;
+					f_type = (match fd.f_type with None -> Some (texpr,null_pos) | Some t -> no_expr_of t);
+					f_args = List.map (fun (a,o,m,t,e) -> a,o,m,(match t with None -> Some (texpr,null_pos) | Some t -> no_expr_of t),e) fd.f_args;
+					f_expr = fd.f_expr;
+				}
+			end else
+				let tdyn = Some (CTPath { tpackage = []; tname = "Dynamic"; tparams = []; tsub = None },null_pos) in
+				let to_dyn p t = match t with
+					| { tpackage = ["haxe";"macro"]; tname = "Expr"; tsub = Some ("ExprOf"); tparams = [TPType t] } -> Some t
+					| { tpackage = []; tname = ("ExprOf"); tsub = None; tparams = [TPType t] } -> Some t
+					| { tpackage = ["haxe"]; tname = ("PosInfos"); tsub = None; tparams = [] } -> error "haxe.PosInfos is not allowed on macro functions, use Context.currentPos() instead" p
+					| _ -> tdyn
+				in
+				{
+					f_params = fd.f_params;
+					f_type = (match fd.f_type with Some (CTPath t,p) -> to_dyn p t | _ -> tdyn);
+					f_args = List.map (fun (a,o,m,t,_) -> a,o,m,(match t with Some (CTPath t,p) -> to_dyn p t | _ -> tdyn),None) fd.f_args;
+					f_expr = None;
+				}
+		end in
+		begin match c.cl_interface,fctx.field_kind with
+			| true,FKConstructor ->
+				error "An interface cannot have a constructor" p;
+			| true,_ ->
+				if not fctx.is_static && fd.f_expr <> None then error (fst f.cff_name ^ ": An interface method cannot have a body") p;
+				if fctx.is_inline && c.cl_interface then error (fst f.cff_name ^ ": You can't declare inline methods in interfaces") p;
+			| false,FKConstructor ->
+				if fctx.is_static then error "A constructor must not be static" p;
+				begin match fd.f_type with
+					| None | Some (CTPath { tpackage = []; tname = "Void" },_) -> ()
+					| _ -> error "A class constructor can't have a return value" p;
+				end
+			| false,_ ->
+				()
+		end;
+		let parent = (if not fctx.is_static then get_parent c (fst f.cff_name) else None) in
+		let dynamic = List.mem ADynamic f.cff_access || (match parent with Some { cf_kind = Method MethDynamic } -> true | _ -> false) in
+		if fctx.is_inline && dynamic then error (fst f.cff_name ^ ": You can't have both 'inline' and 'dynamic'") p;
+		ctx.type_params <- (match cctx.abstract with
+			| Some a when fctx.is_abstract_member ->
+				params @ a.a_params
+			| _ ->
+				if fctx.is_static then params else params @ ctx.type_params);
+		(* TODO is_lib: avoid forcing the return type to be typed *)
+		let ret = if fctx.field_kind = FKConstructor then ctx.t.tvoid else type_opt (ctx,cctx) p fd.f_type in
+		let rec loop args = match args with
+			| ((name,_),opt,m,t,ct) :: args ->
+				(* TODO is_lib: avoid forcing the field to be typed *)
+				let t, ct = type_function_arg ctx (type_opt (ctx,cctx) p t) ct opt p in
+				delay ctx PTypeField (fun() -> match follow t with
+					| TAbstract({a_path = ["haxe";"extern"],"Rest"},_) ->
+						if not c.cl_extern then error "Rest argument are only supported for extern methods" p;
+						if opt then error "Rest argument cannot be optional" p;
+						if ct <> None then error "Rest argument cannot have default value" p;
+						if args <> [] then error "Rest should only be used for the last function argument" p;
+					| _ ->
+						()
+				);
+				(name, ct, t) :: (loop args)
+			| [] ->
+				[]
+		in
+		let args = loop fd.f_args in
+		let t = TFun (fun_args args,ret) in
+		let cf = {
+			cf_name = fst f.cff_name;
+			cf_doc = f.cff_doc;
+			cf_meta = f.cff_meta;
+			cf_type = t;
+			cf_pos = f.cff_pos;
+			cf_kind = Method (if fctx.is_macro then MethMacro else if fctx.is_inline then MethInline else if dynamic then MethDynamic else MethNormal);
+			cf_expr = None;
+			cf_public = is_public (ctx,cctx) f.cff_access parent;
+			cf_params = params;
+			cf_overloads = [];
+		} in
+		cf.cf_meta <- List.map (fun (m,el,p) -> match m,el with
+			| Meta.AstSource,[] -> (m,(match fd.f_expr with None -> [] | Some e -> [e]),p)
+			| _ -> m,el,p
+		) cf.cf_meta;
+		generate_value_meta ctx.com (Some c) cf fd.f_args;
+		check_abstract (ctx,cctx,fctx) c cf fd t ret p;
+		init_meta_overloads ctx (Some c) cf;
+		ctx.curfield <- cf;
+		let r = exc_protect ctx (fun r ->
+			if not !return_partial_type then begin
+				r := (fun() -> t);
+				cctx.context_init();
+				incr stats.s_methods_typed;
+				if ctx.com.verbose then Common.log ctx.com ("Typing " ^ (if ctx.in_macro then "macro " else "") ^ s_type_path c.cl_path ^ "." ^ fst f.cff_name);
+				let fmode = (match cctx.abstract with
+					| Some _ ->
+						(match args with
+						| ("this",_,_) :: _ -> FunMemberAbstract
+						| _ when fst f.cff_name = "_new" -> FunMemberAbstract
+						| _ -> FunStatic)
+					| None ->
+						if fctx.field_kind = FKConstructor then FunConstructor else if fctx.is_static then FunStatic else FunMember
+				) in
+				match ctx.com.platform with
+					| Java when is_java_native_function cf.cf_meta ->
+						if fd.f_expr <> None then
+							ctx.com.warning "@:native function definitions shouldn't include an expression. This behaviour is deprecated." cf.cf_pos;
+						cf.cf_expr <- None;
+						cf.cf_type <- t
+					| _ ->
+						let e , fargs = type_function ctx args ret fmode fd fctx.is_display_field p in
+						let tf = {
+							tf_args = fargs;
+							tf_type = ret;
+							tf_expr = e;
+						} in
+						if fctx.field_kind = FKInit then
+							(match e.eexpr with
+							| TBlock [] | TBlock [{ eexpr = TConst _ }] | TConst _ | TObjectDecl [] -> ()
+							| _ -> c.cl_init <- Some e);
+						cf.cf_expr <- Some (mk (TFunction tf) t p);
+						cf.cf_type <- t;
+			end;
+			t
+		) "type_fun" in
+		if fctx.do_bind then bind_type (ctx,cctx,fctx) cf r (match fd.f_expr with Some e -> snd e | None -> f.cff_pos);
+		if fctx.is_display_field then check_field_display ctx.com (pos f.cff_name) cf;
+		cf
+
+	let create_property (ctx,cctx,fctx) c f (get,set,t,eo) p =
+		let name = fst f.cff_name in
+		(match cctx.abstract with
+		| Some a when fctx.is_abstract_member ->
+			ctx.type_params <- a.a_params;
+		| _ -> ());
+		(* TODO is_lib: lazify load_complex_type *)
+		let ret = (match t, eo with
+			| None, None -> error (name ^ ": Property must either define a type or a default value") p;
+			| None, _ -> mk_mono()
+			| Some t, _ -> load_complex_type ctx true p t
+		) in
+		let t_get,t_set = match cctx.abstract with
+			| Some a when fctx.is_abstract_member ->
+				if Meta.has Meta.IsVar f.cff_meta then error (name ^ ": Abstract properties cannot be real variables") f.cff_pos;
+				let ta = apply_params a.a_params (List.map snd a.a_params) a.a_this in
+				tfun [ta] ret, tfun [ta;ret] ret
+			| _ -> tfun [] ret, TFun(["value",false,ret],ret)
+		in
+		let check_method m t req_name =
+			if ctx.com.display <> DMNone then () else
+			try
+				let overloads =
+					(* on pf_overload platforms, the getter/setter may have been defined as an overloaded function; get all overloads *)
+					if ctx.com.config.pf_overload then
+						if fctx.is_static then
+							let f = PMap.find m c.cl_statics in
+							(f.cf_type, f) :: (List.map (fun f -> f.cf_type, f) f.cf_overloads)
+						else
+							get_overloads c m
+					else
+						[ if fctx.is_static then
+							let f = PMap.find m c.cl_statics in
+							f.cf_type, f
+						else match class_field c (List.map snd c.cl_params) m with
+							| _, t,f -> t,f ]
+				in
+				(* choose the correct overload if and only if there is more than one overload found *)
+				let rec get_overload overl = match overl with
+					| [tf] -> tf
+					| (t2,f2) :: overl ->
+						if type_iseq t t2 then
+							(t2,f2)
+						else
+							get_overload overl
+					| [] ->
+						if c.cl_interface then
+							raise Not_found
+						else
+							raise (Error (Custom
+								(Printf.sprintf "No overloaded method named %s was compatible with the property %s with expected type %s" m (name) (s_type (print_context()) t)
+							), p))
+				in
+				let t2, f2 = get_overload overloads in
+				(* accessors must be public on As3 (issue #1872) *)
+				if Common.defined ctx.com Define.As3 then f2.cf_meta <- (Meta.Public,[],p) :: f2.cf_meta;
+				(match f2.cf_kind with
+					| Method MethMacro ->
+						display_error ctx (f2.cf_name ^ ": Macro methods cannot be used as property accessor") p;
+						display_error ctx (f2.cf_name ^ ": Accessor method is here") f2.cf_pos;
+					| _ -> ());
+				unify_raise ctx t2 t f2.cf_pos;
+				if (fctx.is_abstract_member && not (Meta.has Meta.Impl f2.cf_meta)) || (Meta.has Meta.Impl f2.cf_meta && not (fctx.is_abstract_member)) then
+					display_error ctx "Mixing abstract implementation and static properties/accessors is not allowed" f2.cf_pos;
+				(match req_name with None -> () | Some n -> display_error ctx ("Please use " ^ n ^ " to name your property access method") f2.cf_pos);
+				f2.cf_meta <- List.fold_left (fun acc ((m,_,_) as meta) -> match m with
+					| Meta.Deprecated -> meta :: acc
+					| _ -> acc
+				) f2.cf_meta f.cff_meta;
+			with
+				| Error (Unify l,p) -> raise (Error (Stack (Custom ("In method " ^ m ^ " required by property " ^ name),Unify l),p))
+				| Not_found ->
+					if req_name <> None then display_error ctx (name ^ ": Custom property accessor is no longer supported, please use get/set") p else
+					if c.cl_interface then begin
+						let cf = mk_field m t p in
+						cf.cf_meta <- [Meta.CompilerGenerated,[],p];
+						cf.cf_kind <- Method MethNormal;
+						c.cl_fields <- PMap.add cf.cf_name cf c.cl_fields;
+						c.cl_ordered_fields <- cf :: c.cl_ordered_fields;
+					end else if not c.cl_extern then begin
+						try
+							let _, _, f2 = (if not fctx.is_static then let f = PMap.find m c.cl_statics in None, f.cf_type, f else class_field c (List.map snd c.cl_params) m) in
+							display_error ctx (Printf.sprintf "Method %s is no valid accessor for %s because it is %sstatic" m (name) (if fctx.is_static then "not " else "")) f2.cf_pos
+						with Not_found ->
+							display_error ctx ("Method " ^ m ^ " required by property " ^ name ^ " is missing") p
+					end
+		in
+		let get = (match get with
+			| "null" -> AccNo
+			| "dynamic" -> AccCall
+			| "never" -> AccNever
+			| "default" -> AccNormal
+			| _ ->
+				let get = if get = "get" then "get_" ^ name else get in
+				if not cctx.is_lib then delay ctx PTypeField (fun() -> check_method get t_get (if get <> "get" && get <> "get_" ^ name then Some ("get_" ^ name) else None));
+				AccCall
+		) in
+		let set = (match set with
+			| "null" ->
+				(* standard flash library read-only variables can't be accessed for writing, even in subclasses *)
+				if c.cl_extern && (match c.cl_path with "flash" :: _	, _ -> true | _ -> false) && ctx.com.platform = Flash then
+					AccNever
+				else
+					AccNo
+			| "never" -> AccNever
+			| "dynamic" -> AccCall
+			| "default" -> AccNormal
+			| _ ->
+				let set = if set = "set" then "set_" ^ name else set in
+				if not cctx.is_lib then delay ctx PTypeField (fun() -> check_method set t_set (if set <> "set" && set <> "set_" ^ name then Some ("set_" ^ name) else None));
+				AccCall
+		) in
+		if set = AccNormal && (match get with AccCall -> true | _ -> false) then error (name ^ ": Unsupported property combination") p;
+		let cf = {
+			cf_name = name;
+			cf_doc = f.cff_doc;
+			cf_meta = f.cff_meta;
+			cf_pos = f.cff_pos;
+			cf_kind = Var { v_read = get; v_write = set };
+			cf_expr = None;
+			cf_type = ret;
+			cf_public = is_public (ctx,cctx) f.cff_access None;
+			cf_params = [];
+			cf_overloads = [];
+		} in
+		ctx.curfield <- cf;
+		bind_var (ctx,cctx,fctx) cf eo;
+		if fctx.is_display_field then check_field_display ctx.com (pos f.cff_name) cf;
+		cf
+
+	let init_field (ctx,cctx,fctx) f =
+		let c = cctx.tclass in
+		let name = fst f.cff_name in
+		check_global_metadata ctx (fun m -> f.cff_meta <- m :: f.cff_meta) c.cl_module.m_path c.cl_path (Some name);
+		let p = f.cff_pos in
+		if name.[0] = '$' && ctx.com.display = DMNone then error "Field names starting with a dollar are not allowed" p;
+		List.iter (fun acc ->
+			match (acc, f.cff_kind) with
+			| APublic, _ | APrivate, _ | AStatic, _ -> ()
+			| ADynamic, FFun _ | AOverride, FFun _ | AMacro, FFun _ | AInline, FFun _ | AInline, FVar _ -> ()
+			| _, FVar _ -> error ("Invalid accessor '" ^ Ast.s_access acc ^ "' for variable " ^ name) p
+			| _, FProp _ -> error ("Invalid accessor '" ^ Ast.s_access acc ^ "' for property " ^ name) p
+		) f.cff_access;
+		if fctx.is_override then (match c.cl_super with None -> error ("Invalid override on field '" ^ name ^ "': class has no super class") p | _ -> ());
+		match f.cff_kind with
+		| FVar (t,e) ->
+			create_variable (ctx,cctx,fctx) c f t e p
+		| FFun fd ->
+			create_method (ctx,cctx,fctx) c f fd p
+		| FProp (get,set,t,eo) ->
+			create_property (ctx,cctx,fctx) c f (get,set,t,eo) p
+
+	let init_class ctx c p context_init herits fields =
+		let ctx,cctx = create_class_context ctx c context_init p in
+		let fields = patch_class ctx c fields in
+		let fields = build_fields (ctx,cctx) c fields in
+		if cctx.is_core_api && ctx.com.display = DMNone then delay ctx PForce (fun() -> init_core_api ctx c);
+		if not cctx.is_lib then begin
+			(match c.cl_super with None -> () | Some _ -> delay ctx PForce (fun() -> check_overriding ctx c));
+			if ctx.com.config.pf_overload then delay ctx PForce (fun() -> check_overloads ctx c)
+		end;
+		let rec has_field f = function
+			| None -> false
+			| Some (c,_) ->
+				PMap.exists f c.cl_fields || has_field f c.cl_super || List.exists (fun i -> has_field f (Some i)) c.cl_implements
+		in
+		let rec check_require = function
+			| [] -> None
+			| (Meta.Require,conds,_) :: l ->
+				let rec loop = function
+					| [] -> check_require l
+					| e :: l ->
+						let sc = match fst e with
+							| EConst (Ident s) -> s
+							| EBinop ((OpEq|OpNotEq|OpGt|OpGte|OpLt|OpLte) as op,(EConst (Ident s),_),(EConst ((Int _ | Float _ | String _) as c),_)) -> s ^ s_binop op ^ s_constant c
+							| _ -> ""
+						in
+						if not (Parser.is_true (Parser.eval ctx.com e)) then
+							Some (sc,(match List.rev l with (EConst (String msg),_) :: _ -> Some msg | _ -> None))
+						else
+							loop l
+				in
+				loop conds
+			| _ :: l ->
+				check_require l
+		in
+		let rec check_if_feature = function
+			| [] -> []
+			| (Meta.IfFeature,el,_) :: _ -> List.map (fun (e,p) -> match e with EConst (String s) -> s | _ -> error "String expected" p) el
+			| _ :: l -> check_if_feature l
+		in
+		let cl_if_feature = check_if_feature c.cl_meta in
+		let cl_req = check_require c.cl_meta in
+		List.iter (fun f ->
+			let p = f.cff_pos in
+			try
+				let ctx,fctx = create_field_context (ctx,cctx) c f in
+				let cf = init_field (ctx,cctx,fctx) f in
+				if fctx.is_static && c.cl_interface && fctx.field_kind <> FKInit && not cctx.is_lib then error "You can't declare static fields in interfaces" p;
+				let set_feature s =
+					ctx.m.curmod.m_extra.m_if_feature <- (s,(c,cf,fctx.is_static)) :: ctx.m.curmod.m_extra.m_if_feature
+				in
+				List.iter set_feature cl_if_feature;
+				List.iter set_feature (check_if_feature cf.cf_meta);
+				let req = check_require f.cff_meta in
+				let req = (match req with None -> if fctx.is_static || fctx.field_kind = FKConstructor then cl_req else None | _ -> req) in
+				(match req with
+				| None -> ()
+				| Some r -> cf.cf_kind <- Var { v_read = AccRequire (fst r, snd r); v_write = AccRequire (fst r, snd r) });
+				begin match fctx.field_kind with
+				| FKConstructor ->
+					begin match c.cl_constructor with
+					| None ->
+							c.cl_constructor <- Some cf
+					| Some ctor when ctx.com.config.pf_overload ->
+							if Meta.has Meta.Overload cf.cf_meta && Meta.has Meta.Overload ctor.cf_meta then
+								ctor.cf_overloads <- cf :: ctor.cf_overloads
+							else
+								display_error ctx ("If using overloaded constructors, all constructors must be declared with @:overload") (if Meta.has Meta.Overload cf.cf_meta then ctor.cf_pos else cf.cf_pos)
+					| Some ctor ->
+								display_error ctx "Duplicate constructor" p
+					end
+				| FKInit ->
+					()
+				| FKNormal ->
+					let dup = if fctx.is_static then PMap.exists cf.cf_name c.cl_fields || has_field cf.cf_name c.cl_super else PMap.exists cf.cf_name c.cl_statics in
+					if not cctx.is_native && not c.cl_extern && dup then error ("Same field name can't be use for both static and instance : " ^ cf.cf_name) p;
+					if List.mem AOverride f.cff_access then c.cl_overrides <- cf :: c.cl_overrides;
+					let is_var f = match cf.cf_kind with | Var _ -> true | _ -> false in
+					if PMap.mem cf.cf_name (if fctx.is_static then c.cl_statics else c.cl_fields) then
+						if ctx.com.config.pf_overload && Meta.has Meta.Overload cf.cf_meta && not (is_var f) then
+							let mainf = PMap.find cf.cf_name (if fctx.is_static then c.cl_statics else c.cl_fields) in
+							if is_var mainf then display_error ctx "Cannot declare a variable with same name as a method" mainf.cf_pos;
+							(if not (Meta.has Meta.Overload mainf.cf_meta) then display_error ctx ("Overloaded methods must have @:overload metadata") mainf.cf_pos);
+							mainf.cf_overloads <- cf :: mainf.cf_overloads
+						else
+							display_error ctx ("Duplicate class field declaration : " ^ cf.cf_name) p
+					else
+					if fctx.do_add then add_field c cf (fctx.is_static || fctx.is_macro && ctx.in_macro)
+				end
+			with Error (Custom str,p2) when p = p2 ->
+				display_error ctx str p
+		) fields;
+		(match cctx.abstract with
+		| Some a ->
+			a.a_to_field <- List.rev a.a_to_field;
+			a.a_from_field <- List.rev a.a_from_field;
+			a.a_ops <- List.rev a.a_ops;
+			a.a_unops <- List.rev a.a_unops;
+			a.a_array <- List.rev a.a_array;
+		| None -> ());
+		c.cl_ordered_statics <- List.rev c.cl_ordered_statics;
+		c.cl_ordered_fields <- List.rev c.cl_ordered_fields;
+		(*
+			make sure a default contructor with same access as super one will be added to the class structure at some point.
+		*)
+		(* add_constructor does not deal with overloads correctly *)
+		if not ctx.com.config.pf_overload then add_constructor ctx c cctx.force_constructor p;
+		if Meta.has Meta.StructInit c.cl_meta then check_struct_init_constructor ctx c p;
+		(* check overloaded constructors *)
+		(if ctx.com.config.pf_overload && not cctx.is_lib then match c.cl_constructor with
+		| Some ctor ->
+			delay ctx PTypeField (fun() ->
+				List.iter (fun f ->
+					try
+						(* TODO: consider making a broader check, and treat some types, like TAnon and type parameters as Dynamic *)
+						ignore(List.find (fun f2 -> f != f2 && same_overload_args f.cf_type f2.cf_type f f2) (ctor :: ctor.cf_overloads));
+						display_error ctx ("Another overloaded field of same signature was already declared : " ^ f.cf_name) f.cf_pos;
+					with Not_found -> ()
+				) (ctor :: ctor.cf_overloads)
+			)
+		| _ -> ());
+		(* push delays in reverse order so they will be run in correct order *)
+		List.iter (fun (ctx,r) ->
+			init_class_done ctx;
+			(match r with
+			| None -> ()
+			| Some r -> delay ctx PTypeField (fun() -> ignore((!r)())))
+		) cctx.delayed_expr
+end
+
+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
+
+let check_module_types ctx m p t =
+	let t = t_infos t in
+	try
+		let m2 = Hashtbl.find ctx.g.types_module t.mt_path in
+		if m.m_path <> m2 && String.lowercase (s_type_path m2) = String.lowercase (s_type_path m.m_path) then error ("Module " ^ s_type_path m2 ^ " is loaded with a different case than " ^ s_type_path m.m_path) p;
+		error ("Type name " ^ s_type_path t.mt_path ^ " is redefined from module " ^ s_type_path m2) p
+	with
+		Not_found ->
+			Hashtbl.add ctx.g.types_module t.mt_path m.m_path
+
+let add_module ctx m p =
+	List.iter (check_module_types ctx m p) m.m_types;
+	Hashtbl.add ctx.g.modules m.m_path m
+
+let handle_path_display ctx path p =
+	match Display.convert_import_to_something_usable path,ctx.com.display with
+		| (Display.IDKPackage sl,_),_ ->
+			raise (Parser.TypePath(sl,None,true))
+		| (Display.IDKModule(sl,s),_),DMPosition ->
+			(* We assume that we want to go to the module file, not a specific type
+			   which might not even exist anyway. *)
+			let mt = ctx.g.do_load_module ctx (sl,s) p in
+			let p = { pfile = mt.m_extra.m_file; pmin = 0; pmax = 0} in
+			raise (Display.DisplayPosition [p])
+		| (Display.IDKModule(sl,s),_),_ ->
+			(* TODO: wait till nadako requests @type display for these, then implement it somehow *)
+			raise (Parser.TypePath(sl,Some(s,false),true))
+		| (Display.IDKSubType(sl,sm,st),p),DMPosition ->
+			resolve_position_by_path ctx { tpackage = sl; tname = sm; tparams = []; tsub = Some st} p
+		| (Display.IDKSubType(sl,sm,st),_),_ ->
+			raise (Parser.TypePath(sl @ [sm],Some(st,false),true))
+		| ((Display.IDKSubTypeField(sl,sm,st,sf) | Display.IDKModuleField(sl,(sm as st),sf)),p),_ ->
+			let m = ctx.g.do_load_module ctx (sl,sm) p in
+			List.iter (fun t -> match t with
+				| TClassDecl c when snd c.cl_path = st ->
+					ignore(c.cl_build());
+					let cf = PMap.find sf c.cl_statics in
+					Display.display_field ctx.com.display cf p
+				| _ ->
+					()
+			) m.m_types;
+		| (Display.IDK,_),_ ->
+			()
+
+(*
+	In this pass, we can access load and access other modules types, but we cannot follow them or access their structure
+	since they have not been setup. We also build a context_init list that will be evaluated the first time we evaluate
+	an expression into the context
+*)
+let init_module_type ctx context_init do_init (decl,p) =
+	let get_type name =
+		try List.find (fun t -> snd (t_infos t).mt_path = name) ctx.m.curmod.m_types with Not_found -> assert false
+	in
+	match decl with
+	| EImport (path,mode) ->
+		ctx.m.module_imports <- (path,mode) :: ctx.m.module_imports;
+		(* We cannot use ctx.is_display_file because the import could come from an import.hx file. *)
+		if Display.is_display_file p.pfile then begin
+			Display.add_import_position ctx.com p;
+			handle_path_display ctx path p;
+		end;
+		let rec loop acc = function
+			| x :: l when is_lower_ident (fst x) -> loop (x::acc) l
+			| rest -> List.rev acc, rest
+		in
+		let pack, rest = loop [] path in
+		(match rest with
+		| [] ->
+			(match mode with
+			| IAll ->
+				ctx.m.wildcard_packages <- (List.map fst pack,p) :: ctx.m.wildcard_packages
+			| _ ->
+				(match List.rev path with
+				| [] -> assert false
+				| (_,p) :: _ -> error "Module name must start with an uppercase letter" p))
+		| (tname,p2) :: rest ->
+			let p1 = (match pack with [] -> p2 | (_,p1) :: _ -> p1) in
+			let p_type = punion p1 p2 in
+			let md = ctx.g.do_load_module ctx (List.map fst pack,tname) p_type in
+			let types = md.m_types in
+			let no_private (t,_) = not (t_infos t).mt_private in
+			let chk_private t p = if (t_infos t).mt_private then error "You can't import a private type" p in
+			let has_name name t = snd (t_infos t).mt_path = name in
+			let get_type tname =
+				let t = (try List.find (has_name tname) types with Not_found -> error (StringError.string_error tname (List.map (fun mt -> snd (t_infos mt).mt_path) types) ("Module " ^ s_type_path md.m_path ^ " does not define type " ^ tname)) p_type) in
+				chk_private t p_type;
+				t
+			in
+			let rebind t name =
+				if not (name.[0] >= 'A' && name.[0] <= 'Z') then
+					error "Type aliases must start with an uppercase letter" p;
+				let _, _, f = ctx.g.do_build_instance ctx t p_type in
+				(* create a temp private typedef, does not register it in module *)
+				TTypeDecl {
+					t_path = (fst md.m_path @ ["_" ^ snd md.m_path],name);
+					t_module = md;
+					t_pos = p;
+					t_private = true;
+					t_doc = None;
+					t_meta = [];
+					t_params = (t_infos t).mt_params;
+					t_type = f (List.map snd (t_infos t).mt_params);
+				}
+			in
+			let add_static_init t name s =
+				let name = (match name with None -> s | Some n -> n) in
+				match resolve_typedef t with
+				| TClassDecl c ->
+					ignore(c.cl_build());
+					ignore(PMap.find s c.cl_statics);
+					ctx.m.module_globals <- PMap.add name (TClassDecl c,s,p) ctx.m.module_globals
+				| TEnumDecl e ->
+					ignore(PMap.find s e.e_constrs);
+					ctx.m.module_globals <- PMap.add name (TEnumDecl e,s,p) ctx.m.module_globals
+				| _ ->
+					raise Not_found
+			in
+			(match mode with
+			| INormal | IAsName _ ->
+				let name = (match mode with IAsName n -> Some n | _ -> None) in
+				(match rest with
+				| [] ->
+					(match name with
+					| None ->
+						ctx.m.module_types <- List.filter no_private (List.map (fun t -> t,p) types) @ ctx.m.module_types
+					| Some newname ->
+						ctx.m.module_types <- (rebind (get_type tname) newname,p) :: ctx.m.module_types);
+				| [tsub,p2] ->
+					let pu = punion p1 p2 in
+					(try
+						let tsub = List.find (has_name tsub) types in
+						chk_private tsub pu;
+						ctx.m.module_types <- ((match name with None -> tsub | Some n -> rebind tsub n),p) :: ctx.m.module_types
+					with Not_found ->
+						(* this might be a static property, wait later to check *)
+						let tmain = get_type tname in
+						context_init := (fun() ->
+							try
+								add_static_init tmain name tsub
+							with Not_found ->
+								error (s_type_path (t_infos tmain).mt_path ^ " has no field or subtype " ^ tsub) p
+						) :: !context_init)
+				| (tsub,p2) :: (fname,p3) :: rest ->
+					(match rest with
+					| [] -> ()
+					| (n,p) :: _ -> error ("Unexpected " ^ n) p);
+					let tsub = get_type tsub in
+					context_init := (fun() ->
+						try
+							add_static_init tsub name fname
+						with Not_found ->
+							error (s_type_path (t_infos tsub).mt_path ^ " has no field " ^ fname) (punion p p3)
+					) :: !context_init;
+				)
+			| IAll ->
+				let t = (match rest with
+					| [] -> get_type tname
+					| [tsub,_] -> get_type tsub
+					| _ :: (n,p) :: _ -> error ("Unexpected " ^ n) p
+				) in
+				context_init := (fun() ->
+					match resolve_typedef t with
+					| TClassDecl c
+					| TAbstractDecl {a_impl = Some c} ->
+						ignore(c.cl_build());
+						PMap.iter (fun _ cf -> if not (has_meta Meta.NoImportGlobal cf.cf_meta) then ctx.m.module_globals <- PMap.add cf.cf_name (TClassDecl c,cf.cf_name,p) ctx.m.module_globals) c.cl_statics
+					| TEnumDecl e ->
+						PMap.iter (fun _ c -> if not (has_meta Meta.NoImportGlobal c.ef_meta) then ctx.m.module_globals <- PMap.add c.ef_name (TEnumDecl e,c.ef_name,p) ctx.m.module_globals) e.e_constrs
+					| _ ->
+						error "No statics to import from this type" p
+				) :: !context_init
+			))
+	| EUsing path ->
+		if Display.is_display_file p.pfile then begin
+			Display.add_import_position ctx.com p;
+			handle_path_display ctx path p;
+		end;
+		let t = match List.rev path with
+			| (s1,_) :: (s2,_) :: sl ->
+				if is_lower_ident s2 then { tpackage = (List.rev (s2 :: List.map fst sl)); tname = s1; tsub = None; tparams = [] }
+				else { tpackage = List.rev (List.map fst sl); tname = s2; tsub = Some s1; tparams = [] }
+			| (s1,_) :: sl ->
+				{ tpackage = List.rev (List.map fst sl); tname = s1; tsub = None; tparams = [] }
+			| [] ->
+				assert false
+		in
+		(* do the import first *)
+		let types = (match t.tsub with
+			| None ->
+				let md = ctx.g.do_load_module ctx (t.tpackage,t.tname) p in
+				let types = List.filter (fun t -> not (t_infos t).mt_private) md.m_types in
+				ctx.m.module_types <- (List.map (fun t -> t,p) types) @ ctx.m.module_types;
+				types
+			| Some _ ->
+				let t = load_type_def ctx p t in
+				ctx.m.module_types <- (t,p) :: ctx.m.module_types;
+				[t]
+		) in
+		(* delay the using since we need to resolve typedefs *)
+		let filter_classes types =
+			let rec loop acc types = match types with
+				| td :: l ->
+					(match resolve_typedef td with
+					| TClassDecl c | TAbstractDecl({a_impl = Some c}) ->
+						loop ((c,p) :: acc) l
+					| td ->
+						loop acc l)
+				| [] ->
+					acc
+			in
+			loop [] types
+		in
+		context_init := (fun() -> ctx.m.module_using <- filter_classes types @ ctx.m.module_using) :: !context_init
+	| EClass d ->
+		let c = (match get_type (fst d.d_name) with TClassDecl c -> c | _ -> assert false) in
+		if ctx.is_display_file && Display.is_display_position (pos d.d_name) then
+			Display.display_module_type ctx.com.display (match c.cl_kind with KAbstractImpl a -> TAbstractDecl a | _ -> TClassDecl c) (pos d.d_name);
+		check_global_metadata ctx (fun m -> c.cl_meta <- m :: c.cl_meta) c.cl_module.m_path c.cl_path None;
+		let herits = d.d_flags in
+		c.cl_extern <- List.mem HExtern herits;
+		c.cl_interface <- List.mem HInterface herits;
+		let build() =
+			let fl = Inheritance.set_heritance ctx c herits p in
+			let rec build() =
+				c.cl_build <- (fun()-> Building);
+				try
+					List.iter (fun f -> f()) fl;
+					ClassInitializer.init_class ctx c p do_init d.d_flags d.d_data;
+					c.cl_build <- (fun()-> Built);
+					List.iter (fun (_,t) -> ignore(follow t)) c.cl_params;
+					Built;
+				with Build_canceled state ->
+					c.cl_build <- make_pass ctx build;
+					let rebuild() =
+						delay_late ctx PBuildClass (fun() -> ignore(c.cl_build()));
+					in
+					(match state with
+					| Built -> assert false
+					| Building -> rebuild()
+					| BuildMacro f -> f := rebuild :: !f);
+					state
+				| exn ->
+					c.cl_build <- (fun()-> Built);
+					raise exn
+			in
+			build()
+		in
+		ctx.pass <- PBuildClass;
+		ctx.curclass <- c;
+		c.cl_build <- make_pass ctx build;
+		ctx.pass <- PBuildModule;
+		ctx.curclass <- null_class;
+		delay ctx PBuildClass (fun() -> ignore(c.cl_build()));
+		if (ctx.com.platform = Java || ctx.com.platform = Cs) && not c.cl_extern then
+			delay ctx PTypeField (fun () ->
+				let metas = check_strict_meta ctx c.cl_meta in
+				if metas <> [] then c.cl_meta <- metas @ c.cl_meta;
+				let rec run_field cf =
+					let metas = check_strict_meta ctx cf.cf_meta in
+					if metas <> [] then cf.cf_meta <- metas @ cf.cf_meta;
+					List.iter run_field cf.cf_overloads
+				in
+				List.iter run_field c.cl_ordered_statics;
+				List.iter run_field c.cl_ordered_fields;
+				match c.cl_constructor with
+					| Some f -> run_field f
+					| _ -> ()
+			);
+	| EEnum d ->
+		let e = (match get_type (fst d.d_name) with TEnumDecl e -> e | _ -> assert false) in
+		if ctx.is_display_file && Display.is_display_position (pos d.d_name) then
+			Display.display_module_type ctx.com.display (TEnumDecl e) (pos d.d_name);
+		let ctx = { ctx with type_params = e.e_params } in
+		let h = (try Some (Hashtbl.find ctx.g.type_patches e.e_path) with Not_found -> None) in
+		check_global_metadata ctx (fun m -> e.e_meta <- m :: e.e_meta) e.e_module.m_path e.e_path None;
+		(match h with
+		| None -> ()
+		| Some (h,hcl) ->
+			Hashtbl.iter (fun _ _ -> error "Field type patch not supported for enums" e.e_pos) h;
+			e.e_meta <- e.e_meta @ hcl.tp_meta);
+		let constructs = ref d.d_data in
+		let get_constructs() =
+			List.map (fun c ->
+				{
+					cff_name = c.ec_name;
+					cff_doc = c.ec_doc;
+					cff_meta = c.ec_meta;
+					cff_pos = c.ec_pos;
+					cff_access = [];
+					cff_kind = (match c.ec_args, c.ec_params with
+						| [], [] -> FVar (c.ec_type,None)
+						| _ -> FFun { f_params = c.ec_params; f_type = c.ec_type; f_expr = None; f_args = List.map (fun (n,o,t) -> (n,null_pos),o,[],Some t,None) c.ec_args });
+				}
+			) (!constructs)
+		in
+		let init () = List.iter (fun f -> f()) !context_init in
+		build_module_def ctx (TEnumDecl e) e.e_meta get_constructs init (fun (e,p) ->
+			match e with
+			| EVars [_,Some (CTAnonymous fields,p),None] ->
+				constructs := List.map (fun f ->
+					let args, params, t = (match f.cff_kind with
+					| FVar (t,None) -> [], [], t
+					| FFun { f_params = pl; f_type = t; f_expr = (None|Some (EBlock [],_)); f_args = al } ->
+						let al = List.map (fun ((n,_),o,_,t,_) -> match t with None -> error "Missing function parameter type" f.cff_pos | Some t -> n,o,t) al in
+						al, pl, t
+					| _ ->
+						error "Invalid enum constructor in @:build result" p
+					) 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;
+					}
+				) fields
+			| _ -> error "Enum build macro must return a single variable with anonymous object fields" p
+		);
+		let et = TEnum (e,List.map snd e.e_params) in
+		let names = ref [] in
+		let index = ref 0 in
+		let is_flat = ref true in
+		let fields = ref PMap.empty in
+		List.iter (fun c ->
+			let p = c.ec_pos in
+			let params = ref [] in
+			params := type_type_params ~enum_constructor:true ctx ([],fst c.ec_name) (fun() -> !params) c.ec_pos c.ec_params;
+			let params = !params in
+			let ctx = { ctx with type_params = params @ ctx.type_params } in
+			let rt = (match c.ec_type with
+				| None -> et
+				| Some t ->
+					let t = load_complex_type ctx true p t in
+					(match follow t with
+					| TEnum (te,_) when te == e ->
+						()
+					| _ ->
+						error "Explicit enum type must be of the same enum type" p);
+					t
+			) in
+			let t = (match c.ec_args with
+				| [] -> rt
+				| l ->
+					is_flat := false;
+					let pnames = ref PMap.empty in
+					TFun (List.map (fun (s,opt,(t,tp)) ->
+						(match t with CTPath({tpackage=[];tname="Void"}) -> error "Arguments of type Void are not allowed in enum constructors" c.ec_pos | _ -> ());
+						if PMap.mem s (!pnames) then error ("Duplicate parameter '" ^ s ^ "' in enum constructor " ^ fst c.ec_name) p;
+						pnames := PMap.add s () (!pnames);
+						s, opt, load_type_hint ~opt ctx p (Some (t,tp))
+					) l, rt)
+			) in
+			if PMap.mem (fst c.ec_name) e.e_constrs then error ("Duplicate constructor " ^ fst c.ec_name) p;
+			let f = {
+				ef_name = fst c.ec_name;
+				ef_type = t;
+				ef_pos = p;
+				ef_doc = c.ec_doc;
+				ef_index = !index;
+				ef_params = params;
+				ef_meta = c.ec_meta;
+			} in
+			let cf = {
+				cf_name = f.ef_name;
+				cf_public = true;
+				cf_type = f.ef_type;
+				cf_kind = (match follow f.ef_type with
+					| TFun _ -> Method MethNormal
+					| _ -> Var { v_read = AccNormal; v_write = AccNo }
+				);
+				cf_pos = p;
+				cf_doc = f.ef_doc;
+				cf_meta = no_meta;
+				cf_expr = None;
+				cf_params = f.ef_params;
+				cf_overloads = [];
+			} in
+ 			if ctx.is_display_file && Display.is_display_position p then
+ 				Display.display_enum_field ctx.com.display f p;
+			e.e_constrs <- PMap.add f.ef_name f e.e_constrs;
+			fields := PMap.add cf.cf_name cf !fields;
+			incr index;
+			names := (fst c.ec_name) :: !names;
+		) (!constructs);
+		e.e_names <- List.rev !names;
+		e.e_extern <- e.e_extern;
+		e.e_type.t_params <- e.e_params;
+		e.e_type.t_type <- TAnon {
+			a_fields = !fields;
+			a_status = ref (EnumStatics e);
+		};
+		if !is_flat then e.e_meta <- (Meta.FlatEnum,[],e.e_pos) :: e.e_meta;
+
+		if (ctx.com.platform = Java || ctx.com.platform = Cs) && not e.e_extern then
+			delay ctx PTypeField (fun () ->
+				let metas = check_strict_meta ctx e.e_meta in
+				e.e_meta <- metas @ e.e_meta;
+				PMap.iter (fun _ ef ->
+					let metas = check_strict_meta ctx ef.ef_meta in
+					if metas <> [] then ef.ef_meta <- metas @ ef.ef_meta
+				) e.e_constrs
+			);
+	| ETypedef d ->
+		let t = (match get_type (fst d.d_name) with TTypeDecl t -> t | _ -> assert false) in
+		if ctx.is_display_file && Display.is_display_position (pos d.d_name) then
+			Display.display_module_type ctx.com.display (TTypeDecl t) (pos d.d_name);
+		check_global_metadata ctx (fun m -> t.t_meta <- m :: t.t_meta) t.t_module.m_path t.t_path None;
+		let ctx = { ctx with type_params = t.t_params } in
+		let tt = load_complex_type ctx true p d.d_data in
+		let tt = (match fst d.d_data with
+		| CTExtend _ -> tt
+		| CTPath { tpackage = ["haxe";"macro"]; tname = "MacroType" } ->
+			(* we need to follow MacroType immediately since it might define other module types that we will load afterwards *)
+			if t.t_type == follow tt then error "Recursive typedef is not allowed" p;
+			tt
+		| _ ->
+			if (Meta.has Meta.Eager d.d_meta) then
+				follow tt
+			else begin
+				let f r =
+					if t.t_type == follow tt then error "Recursive typedef is not allowed" p;
+					r := (fun() -> tt);
+					tt
+				in
+				let r = exc_protect ctx f "typedef_rec_check" in
+				delay ctx PForce (fun () -> ignore(!r()));
+				TLazy r
+			end
+		) in
+		(match t.t_type with
+		| TMono r ->
+			(match !r with
+			| None -> r := Some tt;
+			| Some _ -> assert false);
+		| _ -> assert false);
+		if ctx.com.platform = Cs && t.t_meta <> [] then
+			delay ctx PTypeField (fun () ->
+				let metas = check_strict_meta ctx t.t_meta in
+				if metas <> [] then t.t_meta <- metas @ t.t_meta;
+			);
+	| EAbstract d ->
+		let a = (match get_type (fst d.d_name) with TAbstractDecl a -> a | _ -> assert false) in
+		if ctx.is_display_file && Display.is_display_position (pos d.d_name) then
+			Display.display_module_type ctx.com.display (TAbstractDecl a) (pos d.d_name);
+		check_global_metadata ctx (fun m -> a.a_meta <- m :: a.a_meta) a.a_module.m_path a.a_path None;
+		let ctx = { ctx with type_params = a.a_params } in
+		let is_type = ref false in
+		let load_type t from =
+			let t = load_complex_type ctx true p t in
+			let t = if not (Meta.has Meta.CoreType a.a_meta) then begin
+				if !is_type then begin
+					let r = exc_protect ctx (fun r ->
+						r := (fun() -> t);
+						let at = monomorphs a.a_params a.a_this in
+						(try (if from then Type.unify t at else Type.unify at t) with Unify_error _ -> error "You can only declare from/to with compatible types" p);
+						t
+					) "constraint" in
+					delay ctx PForce (fun () -> ignore(!r()));
+					TLazy r
+				end else
+					error "Missing underlying type declaration or @:coreType declaration" p;
+			end else begin
+				if Meta.has Meta.Callable a.a_meta then
+					error "@:coreType abstracts cannot be @:callable" p;
+				t
+			end in
+			t
+		in
+		List.iter (function
+			| AFromType t -> a.a_from <- (load_type t true) :: a.a_from
+			| AToType t -> a.a_to <- (load_type t false) :: a.a_to
+			| AIsType t ->
+				if a.a_impl = None then error "Abstracts with underlying type must have an implementation" a.a_pos;
+				if Meta.has Meta.CoreType a.a_meta then error "@:coreType abstracts cannot have an underlying type" p;
+				let at = load_complex_type ctx true p t in
+				delay ctx PForce (fun () ->
+					begin match follow at with
+						| TAbstract(a2,_) when a == a2 -> error "Abstract underlying type cannot be recursive" a.a_pos
+						| _ -> ()
+					end;
+				);
+				a.a_this <- at;
+				is_type := true;
+			| AExtern ->
+				(match a.a_impl with Some c -> c.cl_extern <- true | None -> (* Hmmmm.... *) ())
+			| APrivAbstract -> ()
+		) d.d_flags;
+		if not !is_type then begin
+			if Meta.has Meta.CoreType a.a_meta then
+				a.a_this <- TAbstract(a,List.map snd a.a_params)
+			else
+				error "Abstract is missing underlying type declaration" a.a_pos
+		end
+
+let module_pass_2 ctx m decls tdecls p =
+	(* here is an additional PASS 1 phase, which define the type parameters for all module types.
+		 Constraints are handled lazily (no other type is loaded) because they might be recursive anyway *)
+	List.iter (fun d ->
+		match d with
+		| (TClassDecl c, (EClass d, p)) ->
+			c.cl_params <- type_type_params ctx c.cl_path (fun() -> c.cl_params) p d.d_params;
+			if Meta.has Meta.Generic c.cl_meta && c.cl_params <> [] then c.cl_kind <- KGeneric;
+			if Meta.has Meta.GenericBuild c.cl_meta then begin
+				if ctx.in_macro then error "@:genericBuild cannot be used in macros" c.cl_pos;
+				c.cl_kind <- KGenericBuild d.d_data;
+			end;
+			if c.cl_path = (["haxe";"macro"],"MacroType") then c.cl_kind <- KMacroType;
+		| (TEnumDecl e, (EEnum d, p)) ->
+			e.e_params <- type_type_params ctx e.e_path (fun() -> e.e_params) p d.d_params;
+		| (TTypeDecl t, (ETypedef d, p)) ->
+			t.t_params <- type_type_params ctx t.t_path (fun() -> t.t_params) p d.d_params;
+		| (TAbstractDecl a, (EAbstract d, p)) ->
+			a.a_params <- type_type_params ctx a.a_path (fun() -> a.a_params) p d.d_params;
+		| _ ->
+			assert false
+	) decls;
+	(* setup module types *)
+	let context_init = ref [] in
+	let do_init() =
+		match !context_init with
+		| [] -> ()
+		| l -> context_init := []; List.iter (fun f -> f()) (List.rev l)
+	in
+	List.iter (init_module_type ctx context_init do_init) tdecls
+
+(*
+	Creates a module context for [m] and types [tdecls] using it.
+*)
+let type_types_into_module ctx m tdecls p =
+	let decls, tdecls = module_pass_1 ctx m tdecls p in
+	let types = List.map fst decls in
+	List.iter (check_module_types ctx m p) types;
+	m.m_types <- m.m_types @ types;
+	(* define the per-module context for the next pass *)
+	let ctx = {
+		com = ctx.com;
+		g = ctx.g;
+		t = ctx.t;
+		m = {
+			curmod = m;
+			module_types = List.map (fun t -> t,null_pos) ctx.g.std.m_types;
+			module_using = [];
+			module_globals = PMap.empty;
+			wildcard_packages = [];
+			module_imports = [];
+		};
+		is_display_file = (match ctx.com.display with DMNone -> false | _ -> Display.is_display_file m.m_extra.m_file);
+		meta = [];
+		this_stack = [];
+		with_type_stack = [];
+		call_argument_stack = [];
+		pass = PBuildModule;
+		on_error = (fun ctx msg p -> ctx.com.error msg p);
+		macro_depth = ctx.macro_depth;
+		curclass = null_class;
+		curfield = null_field;
+		tthis = ctx.tthis;
+		ret = ctx.ret;
+		locals = PMap.empty;
+		type_params = [];
+		curfun = FunStatic;
+		untyped = false;
+		in_macro = ctx.in_macro;
+		in_display = false;
+		in_loop = false;
+		opened = [];
+		in_call_args = false;
+		vthis = None;
+	} in
+	if ctx.g.std != null_module then begin
+		add_dependency m ctx.g.std;
+		(* this will ensure both String and (indirectly) Array which are basic types which might be referenced *)
+		ignore(load_core_type ctx "String");
+	end;
+	module_pass_2 ctx m decls tdecls p;
+	ctx
+
+let handle_import_hx ctx m decls p =
+	let path_split = List.tl (List.rev (get_path_parts m.m_extra.m_file)) in
+	let join l = String.concat (if Sys.os_type = "Win32" || Sys.os_type = "Cygwin" then "\\" else "/") (List.rev ("import.hx" :: l)) in
+	let rec loop path pack = match path,pack with
+		| _,[] -> [join path]
+		| (p :: path),(_ :: pack) -> (join (p :: path)) :: (loop path pack)
+		| _ -> []
+	in
+	let candidates = loop path_split (fst m.m_path) in
+	let make_import_module path r =
+		Hashtbl.replace ctx.com.parser_cache path r;
+		(* We use the file path as module name to make it unique. This may or may not be a good idea... *)
+		let m_import = make_module ctx ([],path) path p in
+		add_module ctx m_import p;
+		m_import
+	in
+	List.fold_left (fun acc path ->
+		let decls = try
+			let r = Hashtbl.find ctx.com.parser_cache path in
+			let mimport = Hashtbl.find ctx.g.modules ([],path) in
+			if mimport.m_extra.m_kind <> MFake then add_dependency m mimport;
+			r
+		with Not_found ->
+			if Sys.file_exists path then begin
+				let _,r = parse_file ctx.com path p in
+				List.iter (fun (d,p) -> match d with EImport _ | EUsing _ -> () | _ -> error "Only import and using is allowed in import.hx files" p) r;
+				add_dependency m (make_import_module path r);
+				r
+			end else begin
+				let r = [] in
+				(* Add empty decls so we don't check the file system all the time. *)
+				(make_import_module path r).m_extra.m_kind <- MFake;
+				r
+			end
+		in
+		decls @ acc
+	) decls candidates
+
+(*
+	Creates a new module and types [tdecls] into it.
+*)
+let type_module ctx mpath file ?(is_extern=false) tdecls p =
+	let m = make_module ctx mpath file p in
+	Hashtbl.add ctx.g.modules m.m_path m;
+	let tdecls = handle_import_hx ctx m tdecls p in
+	let ctx = type_types_into_module ctx m tdecls p in
+	if is_extern then m.m_extra.m_kind <- MExtern;
+	begin if ctx.is_display_file then match ctx.com.display with
+		| DMDiagnostics ->
+			flush_pass ctx PBuildClass "diagnostics";
+			List.iter (fun mt -> match mt with
+				| TClassDecl c | TAbstractDecl({a_impl = Some c}) ->
+					ignore(c.cl_build());
+					let field cf = match cf.cf_kind with
+						| Method MethMacro ->
+							(try ignore (ctx.g.do_macro ctx MDisplay c.cl_path cf.cf_name [] p) with Exit -> ())
+						| _ ->
+							ignore(follow cf.cf_type);
+					in
+					List.iter field c.cl_ordered_fields;
+					List.iter field c.cl_ordered_statics;
+					(match c.cl_constructor with None -> () | Some cf -> field cf);
+				| _ ->
+					()
+			) m.m_types;
+			raise (Display.Diagnostics (Display.Diagnostics.print_diagnostics ctx.com))
+		| DMResolve s ->
+			resolve_position_by_path ctx {tname = s; tpackage = []; tsub = None; tparams = []} p
+		| _ ->
+			()
+	end;
+	m
+
+let resolve_module_file com m remap p =
+	let forbid = ref false in
+	let file = (match m with
+		| [] , name -> name
+		| x :: l , name ->
+			let x = (try
+				match PMap.find x com.package_rules with
+				| Forbidden -> forbid := true; x
+				| Directory d -> d
+				| Remap d -> remap := d :: l; d
+				with Not_found -> x
+			) in
+			String.concat "/" (x :: l) ^ "/" ^ name
+	) ^ ".hx" in
+	let file = Common.find_file com file in
+	let file = (match String.lowercase (snd m) with
+	| "con" | "aux" | "prn" | "nul" | "com1" | "com2" | "com3" | "lpt1" | "lpt2" | "lpt3" when Sys.os_type = "Win32" ->
+		(* these names are reserved by the OS - old DOS legacy, such files cannot be easily created but are reported as visible *)
+		if (try (Unix.stat file).Unix.st_size with _ -> 0) > 0 then file else raise Not_found
+	| _ -> file
+	) in
+	(* if we try to load a std.xxxx class and resolve a real std file, the package name is not valid, ignore *)
+	(match fst m with
+	| "std" :: _ ->
+		let file = Common.unique_full_path file in
+		if List.exists (fun path -> ExtString.String.starts_with file (try Common.unique_full_path path with _ -> path)) com.std_path then raise Not_found;
+	| _ -> ());
+	if !forbid then begin
+		let _, decls = (!parse_hook) com file p in
+		let rec loop decls = match decls with
+			| ((EImport _,_) | (EUsing _,_)) :: decls -> loop decls
+			| (EClass d,_) :: _ -> d.d_meta
+			| (EEnum d,_) :: _ -> d.d_meta
+			| (EAbstract d,_) :: _ -> d.d_meta
+			| (ETypedef d,_) :: _ -> d.d_meta
+			| [] -> []
+		in
+		let meta = loop decls in
+		if not (Meta.has Meta.NoPackageRestrict meta) then begin
+			let x = (match fst m with [] -> assert false | x :: _ -> x) in
+			raise (Forbid_package ((x,m,p),[],if Common.defined com Define.Macro then "macro" else platform_name com.platform));
+		end;
+	end;
+	file
+
+let parse_module ctx m p =
+	let remap = ref (fst m) in
+	let file = resolve_module_file ctx.com m remap p in
+	let pack, decls = (!parse_hook) ctx.com file p in
+	if pack <> !remap then begin
+		let spack m = if m = [] then "<empty>" else String.concat "." m in
+		if p == Ast.null_pos then
+			display_error ctx ("Invalid commandline class : " ^ s_type_path m ^ " should be " ^ s_type_path (pack,snd m)) p
+		else
+			display_error ctx ("Invalid package : " ^ spack (fst m) ^ " should be " ^ spack pack) p
+	end;
+	file, if !remap <> fst m then
+		(* build typedefs to redirect to real package *)
+		List.rev (List.fold_left (fun acc (t,p) ->
+			let build f d =
+				let priv = List.mem f d.d_flags in
+				(ETypedef {
+					d_name = d.d_name;
+					d_doc = None;
+					d_meta = [];
+					d_params = d.d_params;
+					d_flags = if priv then [EPrivate] else [];
+					d_data = CTPath (if priv then { tpackage = []; tname = "Dynamic"; tparams = []; tsub = None; } else
+						{
+							tpackage = !remap;
+							tname = fst d.d_name;
+							tparams = List.map (fun tp ->
+								TPType (CTPath { tpackage = []; tname = fst tp.tp_name; tparams = []; tsub = None; },null_pos)
+							) d.d_params;
+							tsub = None;
+						}),null_pos;
+				},p) :: acc
+			in
+			match t with
+			| EClass d -> build HPrivate d
+			| EEnum d -> build EPrivate d
+			| ETypedef d -> build EPrivate d
+			| EAbstract d -> build APrivAbstract d
+			| EImport _ | EUsing _ -> acc
+		) [(EImport (List.map (fun s -> s,null_pos) (!remap @ [snd m]),INormal),null_pos)] decls)
+	else
+		decls
+
+let load_module ctx m p =
+	let m2 = (try
+		Hashtbl.find ctx.g.modules m
+	with
+		Not_found ->
+			match !type_module_hook ctx m p with
+			| Some m -> m
+			| None ->
+			let is_extern = ref false in
+			let file, decls = (try
+				parse_module ctx m p
+			with Not_found ->
+				let rec loop = function
+					| [] ->
+						raise (Error (Module_not_found m,p))
+					| load :: l ->
+						match load m p with
+						| None -> loop l
+						| Some (file,(_,a)) -> file, a
+				in
+				is_extern := true;
+				loop ctx.com.load_extern_type
+			) in
+			let is_extern = !is_extern in
+			try
+				type_module ctx m file ~is_extern decls p
+			with Forbid_package (inf,pl,pf) when p <> Ast.null_pos ->
+				raise (Forbid_package (inf,p::pl,pf))
+	) in
+	add_dependency ctx.m.curmod m2;
+	if ctx.pass = PTypeField then flush_pass ctx PBuildClass "load_module";
+	m2
+
+;;
+type_function_params_rec := type_function_params
+
+(* former codegen.ml stuff starting here *)
+
+(* -------------------------------------------------------------------------- *)
+(* REMOTING PROXYS *)
+
+let extend_remoting ctx c t p async prot =
+	if c.cl_super <> None then error "Cannot extend several classes" p;
+	(* remove forbidden packages *)
+	let rules = ctx.com.package_rules in
+	ctx.com.package_rules <- PMap.foldi (fun key r acc -> match r with Forbidden -> acc | _ -> PMap.add key r acc) rules PMap.empty;
+	(* parse module *)
+	let path = (t.tpackage,t.tname) in
+	let new_name = (if async then "Async_" else "Remoting_") ^ t.tname in
+	(* check if the proxy already exists *)
+	let t = (try
+		load_type_def ctx p { tpackage = fst path; tname = new_name; tparams = []; tsub = None }
+	with
+		Error (Module_not_found _,p2) when p == p2 ->
+	(* build it *)
+	Common.log ctx.com ("Building proxy for " ^ s_type_path path);
+	let file, decls = (try
+		parse_module ctx path p
+	with
+		| Not_found -> ctx.com.package_rules <- rules; error ("Could not load proxy module " ^ s_type_path path ^ (if fst path = [] then " (try using absolute path)" else "")) p
+		| e -> ctx.com.package_rules <- rules; raise e) in
+	ctx.com.package_rules <- rules;
+	let base_fields = [
+		{ cff_name = "__cnx",null_pos; cff_pos = p; cff_doc = None; cff_meta = []; cff_access = []; cff_kind = FVar (Some (CTPath { tpackage = ["haxe";"remoting"]; tname = if async then "AsyncConnection" else "Connection"; tparams = []; tsub = None },null_pos),None) };
+		{ cff_name = "new",null_pos; cff_pos = p; cff_doc = None; cff_meta = []; cff_access = [APublic]; cff_kind = FFun { f_args = [("c",null_pos),false,[],None,None]; f_type = None; f_expr = Some (EBinop (OpAssign,(EConst (Ident "__cnx"),p),(EConst (Ident "c"),p)),p); f_params = [] } };
+	] in
+	let tvoid = CTPath { tpackage = []; tname = "Void"; tparams = []; tsub = None } in
+	let build_field is_public acc f =
+		if fst f.cff_name = "new" then
+			acc
+		else match f.cff_kind with
+		| FFun fd when (is_public || List.mem APublic f.cff_access) && not (List.mem AStatic f.cff_access) ->
+			if List.exists (fun (_,_,_,t,_) -> t = None) fd.f_args then error ("Field " ^ fst f.cff_name ^ " type is not complete and cannot be used by RemotingProxy") p;
+			let eargs = [EArrayDecl (List.map (fun ((a,_),_,_,_,_) -> (EConst (Ident a),p)) fd.f_args),p] in
+			let ftype = (match fd.f_type with Some (CTPath { tpackage = []; tname = "Void" },_) -> None | _ -> fd.f_type) in
+			let fargs, eargs = if async then match ftype with
+				| Some (tret,_) -> fd.f_args @ [("__callb",null_pos),true,[],Some (CTFunction ([tret,null_pos],(tvoid,null_pos)),null_pos),None], eargs @ [EConst (Ident "__callb"),p]
+				| _ -> fd.f_args, eargs @ [EConst (Ident "null"),p]
+			else
+				fd.f_args, eargs
+			in
+			let id = (EConst (String (fst f.cff_name)), p) in
+			let id = if prot then id else ECall ((EConst (Ident "__unprotect__"),p),[id]),p in
+			let expr = ECall (
+				(EField (
+					(ECall ((EField ((EConst (Ident "__cnx"),p),"resolve"),p),[id]),p),
+					"call")
+				,p),eargs),p
+			in
+			let expr = if async || ftype = None then expr else (EReturn (Some expr),p) in
+			let fd = {
+				f_params = fd.f_params;
+				f_args = fargs;
+				f_type = if async then None else ftype;
+				f_expr = Some (EBlock [expr],p);
+			} in
+			{ cff_name = f.cff_name; cff_pos = f.cff_pos; cff_doc = None; cff_meta = []; cff_access = [APublic]; cff_kind = FFun fd } :: acc
+		| _ -> acc
+	in
+	let decls = List.map (fun d ->
+		match d with
+		| EClass c, p when fst c.d_name = t.tname ->
+			let is_public = List.mem HExtern c.d_flags || List.mem HInterface c.d_flags in
+			let fields = List.rev (List.fold_left (build_field is_public) base_fields c.d_data) in
+			(EClass { c with d_flags = []; d_name = new_name,pos c.d_name; d_data = fields },p)
+		| _ -> d
+	) decls in
+	let m = type_module ctx (t.tpackage,new_name) file decls p in
+	add_dependency ctx.m.curmod m;
+	try
+		List.find (fun tdecl -> snd (t_path tdecl) = new_name) m.m_types
+	with Not_found ->
+		error ("Module " ^ s_type_path path ^ " does not define type " ^ t.tname) p
+	) in
+	match t with
+	| TClassDecl c2 when c2.cl_params = [] -> ignore(c2.cl_build()); c.cl_super <- Some (c2,[]);
+	| _ -> error "Remoting proxy must be a class without parameters" p
+
+(* -------------------------------------------------------------------------- *)
+(* HAXE.RTTI.GENERIC *)
+
+exception Generic_Exception of string * Ast.pos
+
+type generic_context = {
+	ctx : typer;
+	subst : (t * t) list;
+	name : string;
+	p : pos;
+	mutable mg : module_def option;
+}
+
+let make_generic ctx ps pt p =
+	let rec loop l1 l2 =
+		match l1, l2 with
+		| [] , [] -> []
+		| (x,TLazy f) :: l1, _ -> loop ((x,(!f)()) :: l1) l2
+		| (_,t1) :: l1 , t2 :: l2 -> (t1,t2) :: loop l1 l2
+		| _ -> assert false
+	in
+	let name =
+		String.concat "_" (List.map2 (fun (s,_) t ->
+			let s_type_path_underscore (p,s) = match p with [] -> s | _ -> String.concat "_" p ^ "_" ^ s in
+			let rec loop top t = match follow t with
+				| TInst(c,tl) -> (s_type_path_underscore c.cl_path) ^ (loop_tl tl)
+				| TEnum(en,tl) -> (s_type_path_underscore en.e_path) ^ (loop_tl tl)
+				| TAbstract(a,tl) -> (s_type_path_underscore a.a_path) ^ (loop_tl tl)
+				| _ when not top -> "_" (* allow unknown/incompatible types as type parameters to retain old behavior *)
+				| TMono _ -> raise (Generic_Exception (("Could not determine type for parameter " ^ s), p))
+				| TDynamic _ -> "Dynamic"
+				| t -> raise (Generic_Exception (("Type parameter must be a class or enum instance (found " ^ (s_type (print_context()) t) ^ ")"), p))
+			and loop_tl tl = match tl with
+				| [] -> ""
+				| tl -> "_" ^ String.concat "_" (List.map (loop false) tl)
+			in
+			loop true t
+		) ps pt)
+	in
+	{
+		ctx = ctx;
+		subst = loop ps pt;
+		name = name;
+		p = p;
+		mg = None;
+	}
+
+let rec generic_substitute_type gctx t =
+	match t with
+	| TInst ({ cl_kind = KGeneric } as c2,tl2) ->
+		(* maybe loop, or generate cascading generics *)
+		let _, _, f = gctx.ctx.g.do_build_instance gctx.ctx (TClassDecl c2) gctx.p in
+		let t = f (List.map (generic_substitute_type gctx) tl2) in
+		(match follow t,gctx.mg with TInst(c,_), Some m -> add_dependency m c.cl_module | _ -> ());
+		t
+	| _ ->
+		try
+			generic_substitute_type gctx (List.assq t gctx.subst)
+		with Not_found ->
+			Type.map (generic_substitute_type gctx) t
+
+let generic_substitute_expr gctx e =
+	let vars = Hashtbl.create 0 in
+	let build_var v =
+		try
+			Hashtbl.find vars v.v_id
+		with Not_found ->
+			let v2 = alloc_var v.v_name (generic_substitute_type gctx v.v_type) v.v_pos in
+			v2.v_meta <- v.v_meta;
+			Hashtbl.add vars v.v_id v2;
+			v2
+	in
+	let rec build_expr e =
+		match e.eexpr with
+		| TField(e1, FInstance({cl_kind = KGeneric} as c,tl,cf)) ->
+			let _, _, f = gctx.ctx.g.do_build_instance gctx.ctx (TClassDecl c) gctx.p in
+			let t = f (List.map (generic_substitute_type gctx) tl) in
+			let fa = try
+				quick_field t cf.cf_name
+			with Not_found ->
+				error (Printf.sprintf "Type %s has no field %s (possible typing order issue)" (s_type (print_context()) t) cf.cf_name) e.epos
+			in
+			build_expr {e with eexpr = TField(e1,fa)}
+		| TTypeExpr (TClassDecl ({cl_kind = KTypeParameter _;} as c)) when Meta.has Meta.Const c.cl_meta ->
+			let rec loop subst = match subst with
+				| (t1,t2) :: subst ->
+					begin match follow t1 with
+						| TInst(c2,_) when c == c2 -> t2
+						| _ -> loop subst
+					end
+				| [] -> raise Not_found
+			in
+			begin try
+				let t = loop gctx.subst in
+				begin match follow t with
+					| TInst({cl_kind = KExpr e},_) -> type_expr gctx.ctx e Value
+					| _ -> error "Only Const type parameters can be used as value" e.epos
+				end
+			with Not_found ->
+				e
+			end
+		| _ ->
+			map_expr_type build_expr (generic_substitute_type gctx) build_var e
+	in
+	build_expr e
+
+let get_short_name =
+	let i = ref (-1) in
+	(fun () ->
+		incr i;
+		Printf.sprintf "Hx___short___hx_type_%i" !i
+	)
+
+let rec build_generic ctx c p tl =
+	let pack = fst c.cl_path in
+	let recurse = ref false in
+	let rec check_recursive t =
+		match follow t with
+		| TInst (c2,tl) ->
+			(match c2.cl_kind with
+			| KTypeParameter tl ->
+				if not (is_generic_parameter ctx c2) && has_ctor_constraint c2 then
+					error "Type parameters with a constructor cannot be used non-generically" p;
+				recurse := true
+			| _ -> ());
+			List.iter check_recursive tl;
+		| _ ->
+			()
+	in
+	List.iter check_recursive tl;
+	if !recurse || ctx.com.display <> DMNone then begin
+		TInst (c,tl) (* build a normal instance *)
+	end else begin
+	let gctx = make_generic ctx c.cl_params tl p in
+	let name = (snd c.cl_path) ^ "_" ^ gctx.name in
+	try
+		load_instance ctx ({ tpackage = pack; tname = name; tparams = []; tsub = None },p) false p
+	with Error(Module_not_found path,_) when path = (pack,name) ->
+		let m = (try Hashtbl.find ctx.g.modules (Hashtbl.find ctx.g.types_module c.cl_path) with Not_found -> assert false) in
+		(* let ctx = { ctx with m = { ctx.m with module_types = m.m_types @ ctx.m.module_types } } in *)
+		ignore(c.cl_build()); (* make sure the super class is already setup *)
+		let mg = {
+			m_id = alloc_mid();
+			m_path = (pack,name);
+			m_types = [];
+			m_extra = module_extra (s_type_path (pack,name)) m.m_extra.m_sign 0. MFake;
+		} in
+		gctx.mg <- Some mg;
+		let cg = mk_class mg (pack,name) c.cl_pos in
+		mg.m_types <- [TClassDecl cg];
+		Hashtbl.add ctx.g.modules mg.m_path mg;
+		add_dependency mg m;
+		add_dependency ctx.m.curmod mg;
+		(* ensure that type parameters are set in dependencies *)
+		let dep_stack = ref [] in
+		let rec loop t =
+			if not (List.memq t !dep_stack) then begin
+			dep_stack := t :: !dep_stack;
+			match t with
+			| TInst (c,tl) -> add_dep c.cl_module tl
+			| TEnum (e,tl) -> add_dep e.e_module tl
+			| TType (t,tl) -> add_dep t.t_module tl
+			| TAbstract (a,tl) -> add_dep a.a_module tl
+			| TMono r ->
+				(match !r with
+				| None -> ()
+				| Some t -> loop t)
+			| TLazy f ->
+				loop ((!f)());
+			| TDynamic t2 ->
+				if t == t2 then () else loop t2
+			| TAnon a ->
+				PMap.iter (fun _ f -> loop f.cf_type) a.a_fields
+			| TFun (args,ret) ->
+				List.iter (fun (_,_,t) -> loop t) args;
+				loop ret
+			end
+		and add_dep m tl =
+			add_dependency mg m;
+			List.iter loop tl
+		in
+		List.iter loop tl;
+		let build_field cf_old =
+			(* We have to clone the type parameters (issue #4672). We cannot substitute the constraints immediately because
+		       we need the full substitution list first. *)
+			let param_subst,params = List.fold_left (fun (subst,params) (s,t) -> match follow t with
+				| TInst(c,tl) as t ->
+					let t2 = TInst({c with cl_pos = c.cl_pos;},tl) in
+					(t,t2) :: subst,(s,t2) :: params
+				| _ -> assert false
+			) ([],[]) cf_old.cf_params in
+			let gctx = {gctx with subst = param_subst @ gctx.subst} in
+			let cf_new = {cf_old with cf_pos = cf_old.cf_pos} in (* copy *)
+			(* Type parameter constraints are substituted here. *)
+			cf_new.cf_params <- List.rev_map (fun (s,t) -> match follow t with
+				| TInst({cl_kind = KTypeParameter tl1} as c,_) ->
+					let tl1 = List.map (generic_substitute_type gctx) tl1 in
+					c.cl_kind <- KTypeParameter tl1;
+					s,t
+				| _ -> assert false
+			) params;
+			let f () =
+				let t = generic_substitute_type gctx cf_old.cf_type in
+				ignore (follow t);
+				begin try (match cf_old.cf_expr with
+					| None ->
+						begin match cf_old.cf_kind with
+							| Method _ when not c.cl_interface && not c.cl_extern ->
+								display_error ctx (Printf.sprintf "Field %s has no expression (possible typing order issue)" cf_new.cf_name) cf_new.cf_pos;
+								display_error ctx (Printf.sprintf "While building %s" (s_type_path cg.cl_path)) p;
+							| _ ->
+								()
+						end
+					| Some e ->
+						cf_new.cf_expr <- Some (generic_substitute_expr gctx e)
+				) with Unify_error l ->
+					error (error_msg (Unify l)) cf_new.cf_pos
+				end;
+				t
+			in
+			let r = exc_protect ctx (fun r ->
+				let t = mk_mono() in
+				r := (fun() -> t);
+				unify_raise ctx (f()) t p;
+				t
+			) "build_generic" in
+			delay ctx PForce (fun() -> ignore ((!r)()));
+			cf_new.cf_type <- TLazy r;
+			cf_new
+		in
+		if c.cl_init <> None || c.cl_dynamic <> None then error "This class can't be generic" p;
+		List.iter (fun cf -> match cf.cf_kind with
+			| Method MethMacro when not ctx.in_macro -> ()
+			| _ -> error "A generic class can't have static fields" cf.cf_pos
+		) c.cl_ordered_statics;
+		cg.cl_super <- (match c.cl_super with
+			| None -> None
+			| Some (cs,pl) ->
+				let find_class subst =
+					let rec loop subst = match subst with
+						| (TInst(c,[]),t) :: subst when c == cs -> t
+						| _ :: subst -> loop subst
+						| [] -> raise Not_found
+					in
+					try
+						if pl <> [] then raise Not_found;
+						let t = loop subst in
+						(* extended type parameter: concrete type must have a constructor, but generic base class must not have one *)
+						begin match follow t,c.cl_constructor with
+							| TInst(cs,_),None ->
+								ignore(cs.cl_build());
+								begin match cs.cl_constructor with
+									| None -> error ("Cannot use " ^ (s_type_path cs.cl_path) ^ " as type parameter because it is extended and has no constructor") p
+									| _ -> ()
+								end;
+							| _,Some cf -> error "Generics extending type parameters cannot have constructors" cf.cf_pos
+							| _ -> ()
+						end;
+						t
+					with Not_found ->
+						apply_params c.cl_params tl (TInst(cs,pl))
+				in
+				let ts = follow (find_class gctx.subst) in
+				let cs,pl = Inheritance.check_extends ctx c ts p in
+				match cs.cl_kind with
+				| KGeneric ->
+					(match build_generic ctx cs p pl with
+					| TInst (cs,pl) -> Some (cs,pl)
+					| _ -> assert false)
+				| _ -> Some(cs,pl)
+		);
+		add_constructor ctx cg false p;
+		cg.cl_kind <- KGenericInstance (c,tl);
+		cg.cl_meta <- (Meta.NoDoc,[],p) :: cg.cl_meta;
+		if has_meta Meta.Keep c.cl_meta then cg.cl_meta <- (Meta.Keep,[],p) :: cg.cl_meta;
+		cg.cl_interface <- c.cl_interface;
+		cg.cl_constructor <- (match cg.cl_constructor, c.cl_constructor, c.cl_super with
+			| _, Some cf, _ -> Some (build_field cf)
+			| Some ctor, _, _ -> Some ctor
+			| None, None, None -> None
+			| _ -> error "Please define a constructor for this class in order to use it as generic" c.cl_pos
+		);
+		cg.cl_implements <- List.map (fun (i,tl) ->
+			(match follow (generic_substitute_type gctx (TInst (i, List.map (generic_substitute_type gctx) tl))) with
+			| TInst (i,tl) -> i, tl
+			| _ -> assert false)
+		) c.cl_implements;
+		cg.cl_ordered_fields <- List.map (fun f ->
+			let f = build_field f in
+			cg.cl_fields <- PMap.add f.cf_name f cg.cl_fields;
+			f
+		) c.cl_ordered_fields;
+		cg.cl_overrides <- List.map (fun f ->
+			try PMap.find f.cf_name cg.cl_fields with Not_found -> assert false
+		) c.cl_overrides;
+		(* In rare cases the class name can become too long, so let's shorten it (issue #3090). *)
+		if String.length (snd cg.cl_path) > 254 then begin
+			let n = get_short_name () in
+			cg.cl_meta <- (Meta.Native,[EConst(String (n)),p],p) :: cg.cl_meta;
+		end;
+		TInst (cg,[])
+	end
+
+(* -------------------------------------------------------------------------- *)
+(* HAXE.XML.PROXY *)
+
+let extend_xml_proxy ctx c t file p =
+	let t = load_complex_type ctx false p (t,p) in
+	let file = (try Common.find_file ctx.com file with Not_found -> file) in
+	add_dependency c.cl_module (create_fake_module ctx file);
+	let used = ref PMap.empty in
+	let print_results() =
+		PMap.iter (fun id used ->
+			if not used then ctx.com.warning (id ^ " is not used") p;
+		) (!used)
+	in
+	let check_used = Common.defined ctx.com Define.CheckXmlProxy in
+	if check_used then ctx.g.hook_generate <- print_results :: ctx.g.hook_generate;
+	try
+		let rec loop = function
+			| Xml.Element (_,attrs,childs) ->
+				(try
+					let id = List.assoc "id" attrs in
+					if PMap.mem id c.cl_fields then error ("Duplicate id " ^ id) p;
+					let t = if not check_used then t else begin
+						used := PMap.add id false (!used);
+						let ft() = used := PMap.add id true (!used); t in
+						TLazy (ref ft)
+					end in
+					let f = {
+						cf_name = id;
+						cf_type = t;
+						cf_public = true;
+						cf_pos = p;
+						cf_doc = None;
+						cf_meta = no_meta;
+						cf_kind = Var { v_read = AccResolve; v_write = AccNo };
+						cf_params = [];
+						cf_expr = None;
+						cf_overloads = [];
+					} in
+					c.cl_fields <- PMap.add id f c.cl_fields;
+				with
+					Not_found -> ());
+				List.iter loop childs;
+			| Xml.PCData _ -> ()
+		in
+		loop (Xml.parse_file file)
+	with
+		| Xml.Error e -> error ("XML error " ^ Xml.error e) p
+		| Xml.File_not_found f -> error ("XML File not found : " ^ f) p
+
+(* -------------------------------------------------------------------------- *)
+(* MACRO TYPE *)
+
+let get_macro_path ctx e args p =
+	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 = match e with
+		| (EConst(Ident i)),_ ->
+			let path = try
+				if not (PMap.mem i ctx.curclass.cl_statics) then raise Not_found;
+				ctx.curclass.cl_path
+			with Not_found -> try
+				(t_infos (let path,_,_ = PMap.find i ctx.m.module_globals in path)).mt_path
+			with Not_found ->
+				error "Invalid macro call" p
+			in
+			i :: (snd path) :: (fst path)
+		| _ ->
+			loop e
+	in
+	(match path with
+	| meth :: cl :: path -> (List.rev path,cl), meth, args
+	| _ -> error "Invalid macro call" p)
+
+let build_macro_type ctx pl p =
+	let path, field, args = (match pl with
+		| [TInst ({ cl_kind = KExpr (ECall (e,args),_) },_)]
+		| [TInst ({ cl_kind = KExpr (EArrayDecl [ECall (e,args),_],_) },_)] ->
+			get_macro_path ctx e args p
+		| _ ->
+			error "MacroType requires a single expression call parameter" p
+	) in
+	let old = ctx.ret in
+	let t = (match ctx.g.do_macro ctx MMacroType path field args p with
+		| None -> mk_mono()
+		| Some _ -> ctx.ret
+	) in
+	ctx.ret <- old;
+	t
+
+let build_macro_build ctx c pl cfl p =
+	let path, field, args = match Meta.get Meta.GenericBuild c.cl_meta with
+		| _,[ECall(e,args),_],_ -> get_macro_path ctx e args p
+		| _ -> error "genericBuild requires a single expression call parameter" p
+	in
+	let old = ctx.ret,ctx.g.get_build_infos in
+	ctx.g.get_build_infos <- (fun() -> Some (TClassDecl c, pl, cfl));
+	let t = (match ctx.g.do_macro ctx MMacroType path field args p with
+		| None -> mk_mono()
+		| Some _ -> ctx.ret
+	) in
+	ctx.ret <- fst old;
+	ctx.g.get_build_infos <- snd old;
+	t
+
+(* -------------------------------------------------------------------------- *)
+(* API EVENTS *)
+
+let build_instance ctx mtype p =
+	match mtype with
+	| TClassDecl c ->
+		if ctx.pass > PBuildClass then ignore(c.cl_build());
+		let build f s =
+			let r = exc_protect ctx (fun r ->
+				let t = mk_mono() in
+				r := (fun() -> t);
+				let tf = (f()) in
+				unify_raise ctx tf t p;
+				link_dynamic t tf;
+				t
+			) s in
+			delay ctx PForce (fun() -> ignore ((!r)()));
+			TLazy r
+		in
+		let ft = (fun pl ->
+			match c.cl_kind with
+			| KGeneric ->
+				build (fun () -> build_generic ctx c p pl) "build_generic"
+			| KMacroType ->
+				build (fun () -> build_macro_type ctx pl p) "macro_type"
+			| KGenericBuild cfl ->
+				build (fun () -> build_macro_build ctx c pl cfl p) "generic_build"
+			| _ ->
+				TInst (c,pl)
+		) in
+		c.cl_params , c.cl_path , ft
+	| TEnumDecl e ->
+		e.e_params , e.e_path , (fun t -> TEnum (e,t))
+	| TTypeDecl t ->
+		t.t_params , t.t_path , (fun tl -> TType(t,tl))
+	| TAbstractDecl a ->
+		a.a_params, a.a_path, (fun tl -> TAbstract(a,tl))
+
+let on_inherit ctx c p (is_extends,tp) =
+	if not is_extends then
+		true
+	else match fst tp with
+	| { tpackage = ["haxe";"remoting"]; tname = "Proxy"; tparams = [TPType(CTPath t,null_pos)] } ->
+		extend_remoting ctx c t p false true;
+		false
+	| { tpackage = ["haxe";"remoting"]; tname = "AsyncProxy"; tparams = [TPType(CTPath t,null_pos)] } ->
+		extend_remoting ctx c t p true true;
+		false
+	| { tpackage = ["haxe";"xml"]; tname = "Proxy"; tparams = [TPExpr(EConst (String file),p);TPType (t,_)] } ->
+		extend_xml_proxy ctx c t file p;
+		true
+	| _ ->
+		true

文件差异内容过多而无法显示
+ 366 - 293
src/typing/typer.ml


+ 5 - 2
std/Array.hx

@@ -1,5 +1,5 @@
 /*
- * Copyright (C)2005-2012 Haxe Foundation
+ * 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"),
@@ -22,6 +22,9 @@
 /**
 	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
 **/
 extern class Array<T> {
 
@@ -173,7 +176,7 @@ extern class Array<T> {
 
 		The result will include the individual elements' String representations
 		separated by comma. The enclosing [ ] may be missing on some platforms,
-		use Std.string() to get a String representation that is consistent
+		use `Std.string()` to get a String representation that is consistent
 		across platforms.
 	**/
 	function toString() : String;

+ 3 - 1
std/Class.hx

@@ -1,5 +1,5 @@
 /*
- * Copyright (C)2005-2012 Haxe Foundation
+ * 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"),
@@ -23,6 +23,8 @@
 	An abstract type that represents a Class.
 
 	See `Type` for the Haxe Reflection API.
+
+	@see http://haxe.org/manual/types-class-instance.html
 **/
 @:coreType @:runtimeValue abstract Class<T> {
 }

+ 3 - 3
std/Date.hx

@@ -1,5 +1,5 @@
 /*
- * Copyright (C)2005-2013 Haxe Foundation
+ * 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"),
@@ -29,9 +29,9 @@
 	- `Date.fromTime()` with a given timestamp or
 	- `Date.fromString()` by parsing from a String.
 
-	There is some extra functions available in the `DateTools` class.
+	There are some extra functions available in the `DateTools` class.
 
-	In the context of haxe dates, a timestamp is defined as the number of
+	In the context of Haxe dates, a timestamp is defined as the number of
 	milliseconds elapsed since 1st January 1970.
 **/
 extern class Date

+ 3 - 3
std/DateTools.hx

@@ -1,5 +1,5 @@
 /*
- * Copyright (C)2005-2013 Haxe Foundation
+ * 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"),
@@ -24,7 +24,7 @@
 	The DateTools class contains some extra functionalities for handling `Date`
 	instances and timestamps.
 
-	In the context of haxe dates, a timestamp is defined as the number of
+	In the context of Haxe dates, a timestamp is defined as the number of
 	milliseconds elapsed since 1st January 1970.
 **/
 class DateTools {
@@ -161,7 +161,7 @@ class DateTools {
 	/**
 		Converts a number of minutes to a timestamp.
 	**/
-	public static inline function minutes( n : Float ) : Float {
+	#if as3 @:extern #end public static inline function minutes( n : Float ) : Float {
 		return n * 60.0 * 1000.0;
 	}
 

+ 4 - 4
std/EReg.hx

@@ -1,5 +1,5 @@
 /*
- * Copyright (C)2005-2012 Haxe Foundation
+ * 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"),
@@ -28,13 +28,13 @@
 	off to retain a certain level of performance.
 
 	EReg instances can be created by calling the constructor, or with the
-	special syntax ~/pattern/modifier
+	special syntax `~/pattern/modifier`
 
 	EReg instances maintain an internal state, which is affected by several of
 	its methods.
 
 	A detailed explanation of the supported operations is available at
-	http://haxe.org/manual/std-regex.html
+	<http://haxe.org/manual/std-regex.html>
 **/
 class EReg {
 
@@ -145,7 +145,7 @@ class EReg {
 		empty String "" entry.
 
 		If two matching substrings appear next to each other, the result
-		contains the empty String "" between them.
+		contains the empty String `""` between them.
 
 		By default, this method splits `s` into two parts at the first matched
 		substring. If the global g modifier is in place, `s` is split at each

+ 3 - 1
std/Enum.hx

@@ -1,5 +1,5 @@
 /*
- * Copyright (C)2005-2012 Haxe Foundation
+ * 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"),
@@ -26,6 +26,8 @@
 	The corresponding enum instance type is `EnumValue`.
 
 	See `Type` for the Haxe Reflection API.
+
+	@see http://haxe.org/manual/types-enum-instance.html
 **/
 @:coreType @:runtimeValue abstract Enum<T> {
 }

+ 3 - 1
std/EnumValue.hx

@@ -1,5 +1,5 @@
 /*
- * Copyright (C)2005-2012 Haxe Foundation
+ * 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"),
@@ -23,6 +23,8 @@
 /**
 	An abstract type that represents any enum value.
 	See `Type` for the Haxe Reflection API.
+
+	@see http://haxe.org/manual/types-enum-instance.html
 **/
 @:coreType abstract EnumValue {
 }

+ 3 - 1
std/IntIterator.hx

@@ -1,5 +1,5 @@
 /*
- * Copyright (C)2005-2013 Haxe Foundation
+ * 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"),
@@ -30,6 +30,8 @@
 	field, it is worth noting that IntIterator does not reset after being used
 	in a for-loop. Subsequent uses of the same instance will then have no
 	effect.
+
+	@see http://haxe.org/manual/lf-iterators.html
 **/
 class IntIterator {
 

+ 28 - 2
std/Lambda.hx

@@ -1,5 +1,5 @@
 /*
- * Copyright (C)2005-2012 Haxe Foundation
+ * 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"),
@@ -22,7 +22,7 @@
 
 /**
 	The `Lambda` class is a collection of methods to support functional
-	programming. It is ideally used with 'using Lambda' and then acts as an
+	programming. It is ideally used with `using Lambda` and then acts as an
 	extension to Iterable types.
 
 	On static platforms, working with the Iterable structure might be slower
@@ -31,6 +31,8 @@
 
 	If the first argument to any of the methods is null, the result is
 	unspecified.
+
+	@see http://haxe.org/manual/std-Lambda.html
 **/
 class Lambda {
 
@@ -87,6 +89,30 @@ class Lambda {
 		return l;
 	}
 
+	/**
+		Concatenate a list of lists.
+
+		The order of elements is preserved.
+	**/
+	public static function flatten<A>( it : Iterable<Iterable<A>> ) : List<A> {
+		var l = new List<A>();
+		for (e in it)
+			for (x in e)
+				l.add(x);
+		return l;
+	}
+
+	/**
+		A composition of map and flatten.
+
+		The order of elements is preserved.
+
+		If `f` is null, the result is unspecified.
+	**/
+	public static function flatMap<A,B>( it : Iterable<A>, f: A -> Iterable<B> ) : List<B> {
+		return Lambda.flatten(Lambda.map(it, f));
+	}
+
 	/**
 		Tells if `it` contains `elt`.
 

+ 58 - 35
std/List.hx

@@ -1,5 +1,5 @@
 /*
- * Copyright (C)2005-2012 Haxe Foundation
+ * 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"),
@@ -19,15 +19,18 @@
  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
  * DEALINGS IN THE SOFTWARE.
  */
+
 /**
-	A linked-list of elements. The list is composed of two-elements arrays
+	A linked-list of elements. The list is composed of element container objects
 	that are chained together. It is optimized so that adding or removing an
-	element does not imply copying the whole array content every time.
+	element does not imply copying the whole list content every time.
+
+	@see http://haxe.org/manual/std-List.html
 **/
 class List<T> {
 
-	private var h : Array<Dynamic>;
-	private var q : Array<Dynamic>;
+	private var h : ListNode<T>;
+	private var q : ListNode<T>;
 
 	/**
 		The length of `this` List.
@@ -47,11 +50,11 @@ class List<T> {
 		`this.length` increases by 1.
 	**/
 	public function add( item : T ) {
-		var x:Array<Dynamic> = #if neko untyped __dollar__array(item,null) #else [item] #end;
+		var x = ListNode.create(item, null);
 		if( h == null )
 			h = x;
 		else
-			q[1] = x;
+			q.next = x;
 		q = x;
 		length++;
 	}
@@ -62,11 +65,7 @@ class List<T> {
 		`this.length` increases by 1.
 	**/
 	public function push( item : T ) {
-		var x : Array<Dynamic> = #if neko
-			untyped __dollar__array(item,h)
-		#else
-			[item,h]
-		#end;
+		var x = ListNode.create(item, h);
 		h = x;
 		if( q == null )
 			q = x;
@@ -79,7 +78,7 @@ class List<T> {
 		This function does not modify `this` List.
 	**/
 	public function first() : Null<T> {
-		return if( h == null ) null else h[0];
+		return if( h == null ) null else h.item;
 	}
 
 	/**
@@ -88,7 +87,7 @@ class List<T> {
 		This function does not modify `this` List.
 	**/
 	public function last() : Null<T> {
-		return if( q == null ) null else q[0];
+		return if( q == null ) null else q.item;
 	}
 
 
@@ -100,8 +99,8 @@ class List<T> {
 	public function pop() : Null<T> {
 		if( h == null )
 			return null;
-		var x = h[0];
-		h = h[1];
+		var x = h.item;
+		h = h.next;
 		if( h == null )
 			q = null;
 		length--;
@@ -136,21 +135,21 @@ class List<T> {
 		Otherwise, false is returned.
 	**/
 	public function remove( v : T ) : Bool {
-		var prev = null;
+		var prev:ListNode<T> = null;
 		var l = h;
 		while( l != null ) {
-			if( l[0] == v ) {
+			if( l.item == v ) {
 				if( prev == null )
-					h = l[1];
+					h = l.next;
 				else
-					prev[1] = l[1];
+					prev.next = l.next;
 				if( q == l )
 					q = prev;
 				length--;
 				return true;
 			}
 			prev = l;
-			l = l[1];
+			l = l.next;
 		}
 		return false;
 	}
@@ -178,8 +177,8 @@ class List<T> {
 				first = false;
 			else
 				s.add(", ");
-			s.add(Std.string(l[0]));
-			l = l[1];
+			s.add(Std.string(l.item));
+			l = l.next;
 		}
 		s.add("}");
 		return s.toString();
@@ -198,8 +197,8 @@ class List<T> {
 				first = false;
 			else
 				s.add(sep);
-			s.add(l[0]);
-			l = l[1];
+			s.add(l.item);
+			l = l.next;
 		}
 		return s.toString();
 	}
@@ -212,8 +211,8 @@ class List<T> {
 		var l2 = new List();
 		var l = h;
 		while( l != null ) {
-			var v = l[0];
-			l = l[1];
+			var v = l.item;
+			l = l.next;
 			if( f(v) )
 				l2.add(v);
 		}
@@ -228,8 +227,8 @@ class List<T> {
 		var b = new List();
 		var l = h;
 		while( l != null ) {
-			var v = l[0];
-			l = l[1];
+			var v = l.item;
+			l = l.next;
 			b.add(f(v));
 		}
 		return b;
@@ -237,13 +236,37 @@ class List<T> {
 
 }
 
+#if neko
+private extern class ListNode<T> extends neko.NativeArray<Dynamic> {
+	var item(get,set):T;
+	var next(get,set):ListNode<T>;
+	private inline function get_item():T return this[0];
+	private inline function set_item(v:T):T return this[0] = v;
+	private inline function get_next():ListNode<T> return this[1];
+	private inline function set_next(v:ListNode<T>):ListNode<T> return this[1] = v;
+	inline static function create<T>(item:T, next:ListNode<T>):ListNode<T> {
+		return untyped __dollar__array(item, next);
+	}
+}
+#else
+private class ListNode<T> {
+	public var item:T;
+	public var next:ListNode<T>;
+	public function new(item:T, next:ListNode<T>) {
+		this.item = item;
+		this.next = next;
+	}
+	@:extern public inline static function create<T>(item:T, next:ListNode<T>):ListNode<T> {
+		return new ListNode(item, next);
+	}
+}
+#end
+
 private class ListIterator<T> {
-	var head:Array<Dynamic>;
-	var val:Dynamic;
+	var head:ListNode<T>;
 
-	public inline function new(head:Array<Dynamic>) {
+	public inline function new(head:ListNode<T>) {
 		this.head = head;
-		this.val = null;
 	}
 
 	public inline function hasNext():Bool {
@@ -251,8 +274,8 @@ private class ListIterator<T> {
 	}
 
 	public inline function next():T {
-		val = head[0];
-		head = head[1];
+		var val = head.item;
+		head = head.next;
 		return val;
 	}
 }

+ 5 - 2
std/Map.hx

@@ -1,5 +1,5 @@
 /*
- * Copyright (C)2005-2013 Haxe Foundation
+ * 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"),
@@ -41,8 +41,10 @@ import haxe.Constraints.IMap;
 	Maps can also be created with `key1 => value1, key2 => value2` syntax.
 
 	Map is an abstract type, it is not available at runtime.
+
+	@see http://haxe.org/manual/std-Map.html
 **/
-@:multiType(K)
+@:multiType(@:followWithAbstracts K)
 abstract Map<K,V>(IMap<K,V> ) {
 
 	/**
@@ -163,5 +165,6 @@ abstract Map<K,V>(IMap<K,V> ) {
 	}
 }
 
+@:dox(hide)
 @:deprecated
 typedef IMap<K, V> = haxe.Constraints.IMap<K, V>;

+ 4 - 2
std/Math.hx

@@ -1,5 +1,5 @@
 /*
- * Copyright (C)2005-2012 Haxe Foundation
+ * 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"),
@@ -21,6 +21,8 @@
  */
 /**
 	This class defines mathematical functions and constants.
+
+	@see http://haxe.org/manual/std-math.html
 **/
 #if cpp @:include("hxMath.h") #end
 extern class Math
@@ -73,7 +75,7 @@ extern class Math
 
 		@php In PHP versions prior to 5.3.1 VC 9 there may be unexpected
 		results when performing arithmetic operations with `NaN` on Windows,
-		see [https://bugs.php.net/bug.php?id=42143]
+		see <https://bugs.php.net/bug.php?id=42143>
 	**/
 	static var NaN(default, null) : Float;
 

+ 5 - 3
std/Reflect.hx

@@ -1,5 +1,5 @@
 /*
- * Copyright (C)2005-2012 Haxe Foundation
+ * 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"),
@@ -22,6 +22,8 @@
 /**
 	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
 **/
 extern class Reflect {
 
@@ -148,8 +150,8 @@ extern class Reflect {
 
 		- class instance
 		- structure
-		- Class<T>
-		- Enum<T>
+		- `Class<T>`
+		- `Enum<T>`
 
 		Otherwise, including if `v` is null, the result is false.
 	**/

+ 1 - 1
std/Std.hx

@@ -1,5 +1,5 @@
 /*
- * Copyright (C)2005-2012 Haxe Foundation
+ * 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"),

+ 60 - 25
std/StdTypes.hx

@@ -1,5 +1,5 @@
 /*
- * Copyright (C)2005-2013 Haxe Foundation
+ * 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"),
@@ -22,98 +22,133 @@
 // standard Haxe types
 
 /**
-	The standard Void type. Only `null` values can be of the type `Void`.
+	The standard `Void` type. Only `null` values can be of the type `Void`.
+
+	@see http://haxe.org/manual/types-void.html
 **/
 @:coreType abstract Void { }
 
 /**
-	The standard Float type, this is a double-precision IEEE 64bit float.
+	The standard `Float` type, this is a double-precision IEEE 64bit float.
 
-	On static targets, null cannot be assigned to Float. If this is necessary,
+	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.parseFloat` converts a `String` to a `Float`.
+
+	@see http://haxe.org/manual/types-basic-types.html
+	@see http://haxe.org/manual/types-nullability.html
 **/
 @:coreType @:notNull @:runtimeValue abstract Float { }
 
 /**
-	The standard Int type. Its precision depends on the platform.
+	The standard `Int` type. Its precision depends on the platform.
 
-	On static targets, null cannot be assigned to Int. If this is necessary,
+	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.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
 **/
 @:coreType @:notNull @:runtimeValue abstract Int to Float { }
 
-#if (java || cs)
+#if (java || cs || hl)
+/**
+	Single-precision IEEE 32bit float (4-byte).
+**/
 @:coreType @:notNull @:runtimeValue abstract Single to Float from Float {}
 #end
 
 /**
 	`Null` can be useful in two cases. In order to document some methods
-	that accepts or can return a `null` value, or for the Flash compiler and AS3
-	generator to distinguish between base values that can be null and others that
+	that accept or can return a `null` value, or for the Flash compiler and AS3
+	generator to distinguish between base values that can be `null` and others that
 	can't.
+
+	@see http://haxe.org/manual/types-nullability.html
 **/
 typedef Null<T> = T
 
 /**
-	The standard Boolean type, which can either be true or false.
+	The standard Boolean type, which can either be `true` or `false`.
 
-	On static targets, null cannot be assigned to Bool. If this is necessary,
+	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
 **/
 @:coreType @:notNull @:runtimeValue abstract Bool {
 }
 
 /**
-	Dynamic is a special type which is compatible with all other types.
+	`Dynamic` is a special type which is compatible with all other types.
 
-	Use of Dynamic should be minimized as it prevents several compiler
+	Use of `Dynamic` should be minimized as it prevents several compiler
 	checks and optimizations.
+
+	@see http://haxe.org/manual/types-dynamic.html
 **/
 @:coreType @:runtimeValue abstract Dynamic<T> {
 }
 
 /**
-	An Iterator is a structure that permits iteration over elements of type T.
+	An `Iterator` is a structure that permits iteration over elements of type `T`.
 
-	Any class with matching hasNext and next fields is considered an Iterator
-	and can then be used e.g. in for-loops. This makes it easy to implement
+	Any class with matching `hasNext()` and `next()` fields is considered an `Iterator`
+	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
 **/
 typedef Iterator<T> = {
 
 	/**
-		Returns false if the iteration is complete, true otherwise.
+		Returns `false` if the iteration is complete, `true` otherwise.
 
 		Usually iteration is considered to be complete if all elements of the
-		underlying data structure were handled through calls to next(). However,
+		underlying data structure were handled through calls to `next()`. However,
 		in custom iterators any logic may be used to determine the completion
 		state.
 	**/
 	function hasNext() : Bool;
 
 	/**
-		Returns the current item of the Iterator and advances to the next one.
+		Returns the current item of the `Iterator` and advances to the next one.
 
-		This method is not required to check `hasNext` first. A call to this
-		method while `hasNext` is false yields unspecified behavior.
+		This method is not required to check `hasNext()` first. A call to this
+		method while `hasNext()` is `false` yields unspecified behavior.
 
-		On the other hand iterators should not require a call to `hasNext`
-		before the first call to `next` if an element is available.
+		On the other hand, iterators should not require a call to `hasNext()`
+		before the first call to `next()` if an element is available.
 	**/
 	function next() : T;
 
 }
 
 /**
-	An Iterable is a data structure which has an iterator() method.
+	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
 **/
 typedef Iterable<T> = {
 	function iterator() : Iterator<T>;
 }
 
 /**
-	ArrayAccess is used to indicate a class that can be accessed using brackets.
+	`ArrayAccess` is used to indicate a class that can be accessed using brackets.
 	The type parameter represents the type of the elements stored.
+
+	This interface should be used for externs only. Haxe does not support custom
+	array access on classes. However, array access can be implemented for
+	abstract types.
+
+	@see http://haxe.org/manual/types-abstract-array-access.html
 **/
 extern interface ArrayAccess<T> { }

+ 20 - 18
std/String.hx

@@ -1,5 +1,5 @@
 /*
- * Copyright (C)2005-2012 Haxe Foundation
+ * 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"),
@@ -22,13 +22,15 @@
 /**
 	The basic String class.
 
-	A haxe String is immutable, it is not possible to modify individual
+	A Haxe String is immutable, it is not possible to modify individual
 	characters. No method of this class changes the state of `this` String.
 
-	Strings can be constructed using the string literal syntax "string value".
+	Strings can be constructed using the String literal syntax `"string value"`.
 
-	String can be concatenated by using the + operator. If an operand is not a
-	String, it is passed through Std.string() first.
+	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
 **/
 extern class String {
 
@@ -59,7 +61,7 @@ extern class String {
 	/**
 		Returns the character at position `index` of `this` String.
 
-		If `index` is negative or exceeds `this.length`, the empty String ""
+		If `index` is negative or exceeds `this.length`, the empty String `""`
 		is returned.
 	**/
 	function charAt(index : Int) : String;
@@ -67,11 +69,11 @@ extern class String {
 	/**
 		Returns the character code at position `index` of `this` String.
 
-		If `index` is negative or exceeds `this.length`, null is returned.
+		If `index` is negative or exceeds `this.length`, `null` is returned.
 
-		To obtain the character code of a single character, "x".code can be used
-		instead to inline the character code at compile time. Note that this
-		only works on String literals of length 1.
+		To obtain the character code of a single character, `"x".code` can be
+		used instead to inline the character code at compile time. Note that
+		this only works on String literals of length 1.
 	**/
 	function charCodeAt( index : Int) : Null<Int>;
 
@@ -104,10 +106,10 @@ extern class String {
 	/**
 		Splits `this` String at each occurence of `delimiter`.
 
-		If `this` String is the empty String "", the result is not consistent
+		If `this` String is the empty String `""`, the result is not consistent
 		across targets and may either be `[]` (on Js, Cpp) or `[""]`.
 
-		If `delimiter` is the empty String "", `this` String is split into an
+		If `delimiter` is the empty String `""`, `this` String is split into an
 		Array of `this.length` elements, where the elements correspond to the
 		characters of `this` String.
 
@@ -117,9 +119,9 @@ extern class String {
 		If `delimiter` is null, the result is unspecified.
 
 		Otherwise, `this` String is split into parts at each occurence of
-		`delimiter`. If `this` String starts (or ends) with [delimiter}, the
-		result Array contains a leading (or trailing) empty String "" element.
-		Two subsequent delimiters also result in an empty String "" element.
+		`delimiter`. If `this` String starts (or ends) with `delimiter`, the
+		result `Array` contains a leading (or trailing) empty String `""` element.
+		Two subsequent delimiters also result in an empty String `""` element.
 	**/
 	function split( delimiter : String ) : Array<String>;
 
@@ -151,7 +153,7 @@ extern class String {
 		`this.length`, `this.length` is used instead.
 
 		If the (possibly swapped) `startIndex` exceeds `this.length`, the empty
-		String "" is returned.
+		String `""` is returned.
 	**/
 	function substring( startIndex : Int, ?endIndex : Int ) : String;
 
@@ -166,5 +168,5 @@ extern class String {
 		If `code` is negative or has another invalid value, the result is
 		unspecified.
 	**/
-	static function fromCharCode( code : Int ) : String;
-}
+	@:pure static function fromCharCode( code : Int ) : String;
+}

+ 1 - 1
std/StringBuf.hx

@@ -1,5 +1,5 @@
 /*
- * Copyright (C)2005-2012 Haxe Foundation
+ * 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"),

+ 197 - 31
std/StringTools.hx

@@ -1,5 +1,5 @@
 /*
- * Copyright (C)2005-2012 Haxe Foundation
+ * 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"),
@@ -21,7 +21,8 @@
  */
 /**
 	This class provides advanced methods on Strings. It is ideally used with
-	'using StringTools' and then acts as an extension to the String class.
+	`using StringTools` and then acts as an [extension](http://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
 	unspecified.
@@ -33,7 +34,7 @@ class StringTools {
 	/**
 		Encode an URL by using the standard format.
 	**/
-	#if (!java && !cpp) inline #end public static function urlEncode( s : String ) : String {
+	#if (!java && !cpp && !lua) inline #end public static function urlEncode( s : String ) : String {
 		#if flash
 			return untyped __global__["encodeURIComponent"](s);
 		#elseif neko
@@ -43,22 +44,67 @@ class StringTools {
 		#elseif cpp
 			return untyped s.__URLEncode();
 		#elseif java
-			try
-				return untyped __java__("java.net.URLEncoder.encode(s, \"UTF-8\")")
-			catch (e:Dynamic) throw e;
+			return postProcessUrlEncode(java.net.URLEncoder.encode(s, "UTF-8"));
 		#elseif cs
 			return untyped cs.system.Uri.EscapeDataString(s);
 		#elseif python
 			return python.lib.urllib.Parse.quote(s, "");
+		#elseif hl
+			var len = 0;
+			var b = @:privateAccess s.bytes.urlEncode(len);
+			return @:privateAccess String.__alloc__(b,len);
+		#elseif lua
+			s = lua.NativeStringTools.gsub(s, "\n", "\r\n");
+			s = lua.NativeStringTools.gsub(s, "([^%w %-%_%.%~])", function (c) {
+				return lua.NativeStringTools.format("%%%02X", lua.NativeStringTools.byte(c) + '');
+			});
+			s = lua.NativeStringTools.gsub(s, " ", "+");
+			return s;
 		#else
 			return null;
 		#end
 	}
 
+#if java
+	private static function postProcessUrlEncode( s : String ) : String {
+		var ret = new StringBuf();
+		var i = 0,
+		    len = s.length;
+		while (i < len) {
+			switch(_charAt(s, i++)) {
+			case '+'.code:
+				ret.add('%20');
+			case '%'.code if (i <= len - 2):
+				var c1 = _charAt(s, i++),
+				    c2 = _charAt(s, i++);
+				switch[c1, c2] {
+				case ['2'.code, '1'.code]:
+					ret.addChar('!'.code);
+				case ['2'.code, '7'.code]:
+					ret.addChar('\''.code);
+				case ['2'.code, '8'.code]:
+					ret.addChar('('.code);
+				case ['2'.code, '9'.code]:
+					ret.addChar(')'.code);
+				case ['7'.code, 'E'.code]:
+					ret.addChar('-'.code);
+				case _:
+					ret.addChar('%'.code);
+					ret.addChar(cast c1);
+					ret.addChar(cast c2);
+				}
+			case chr:
+				ret.addChar(cast chr);
+			}
+		}
+		return ret.toString();
+	}
+#end
+
 	/**
 		Decode an URL using the standard format.
 	**/
-	#if (!java && !cpp) inline #end public static function urlDecode( s : String ) : String {
+	#if (!java && !cpp && !lua) inline #end public static function urlDecode( s : String ) : String {
 		#if flash
 			return untyped __global__["decodeURIComponent"](s.split("+").join(" "));
 		#elseif neko
@@ -75,6 +121,16 @@ class StringTools {
 			return untyped cs.system.Uri.UnescapeDataString(s);
 		#elseif python
 			return python.lib.urllib.Parse.unquote(s);
+		#elseif hl
+			var len = 0;
+			var b = @:privateAccess s.bytes.urlDecode(len);
+			return @:privateAccess String.__alloc__(b,len);
+		#elseif lua
+			s = lua.NativeStringTools.gsub (s, "+", " ");
+			s = lua.NativeStringTools.gsub (s, "%%(%x%x)",
+				function(h) {return lua.NativeStringTools.char(lua.Lua.tonumber(h,16));});
+			s = lua.NativeStringTools.gsub (s, "\r\n", "\n");
+			return s;
 		#else
 			return null;
 		#end
@@ -103,7 +159,7 @@ class StringTools {
 		Unescapes HTML special characters of the string `s`.
 
 		This is the inverse operation to htmlEscape, i.e. the following always
-		holds: htmlUnescape(htmlEscape(s)) == s
+		holds: `htmlUnescape(htmlEscape(s)) == s`
 
 		The replacements follow:
 
@@ -120,9 +176,9 @@ class StringTools {
 	/**
 		Tells if the string `s` starts with the string `start`.
 
-		If `start` is null, the result is unspecified.
+		If `start` is `null`, the result is unspecified.
 
-		If `start` is the empty String "", the result is true.
+		If `start` is the empty String `""`, the result is true.
 	**/
 	public static #if (cs || java) inline #end function startsWith( s : String, start : String ) : Bool {
 		#if java
@@ -138,6 +194,8 @@ class StringTools {
 			if ( p0.at(i) != p1.at(i) )
 				return false;
 		return true;
+		#elseif hl
+		return @:privateAccess (s.length >= start.length && s.bytes.compare(0,start.bytes,0,start.length<<1) == 0);
 		#else
 		return( s.length >= start.length && s.substr(0, start.length) == start );
 		#end
@@ -146,9 +204,9 @@ class StringTools {
 	/**
 		Tells if the string `s` ends with the string `end`.
 
-		If `end` is null, the result is unspecified.
+		If `end` is `null`, the result is unspecified.
 
-		If `end` is the empty String "", the result is true.
+		If `end` is the empty String `""`, the result is true.
 	**/
 	public static #if (cs || java) inline #end function endsWith( s : String, end : String ) : Bool {
 		#if java
@@ -164,6 +222,10 @@ class StringTools {
 			if ( p0.at(i) != p1.at(i) )
 				return false;
 		return true;
+		#elseif hl
+		var elen = end.length;
+		var slen = s.length;
+		return @:privateAccess (slen >= elen && s.bytes.compare((slen - elen) << 1, end.bytes, 0, elen << 1) == 0);
 		#else
 		var elen = end.length;
 		var slen = s.length;
@@ -177,11 +239,11 @@ class StringTools {
 		A character is considered to be a space character if its character code
 		is 9,10,11,12,13 or 32.
 
-		If `s` is the empty String "", or if pos is not a valid position within
+		If `s` is the empty String `""`, or if pos is not a valid position within
 		`s`, the result is false.
 	**/
 	public static function isSpace( s : String, pos : Int ) : Bool {
-		#if python
+		#if (python || lua)
 		if (s.length == 0 || pos < 0 || pos >= s.length) return false;
 		#end
 		var c = s.charCodeAt( pos );
@@ -191,11 +253,11 @@ class StringTools {
 	/**
 		Removes leading space characters of `s`.
 
-		This function internally calls isSpace() to decide which characters to
+		This function internally calls `isSpace()` to decide which characters to
 		remove.
 
-		If `s` is the empty String "" or consists only of space characters, the
-		result is the empty String "".
+		If `s` is the empty String `""` or consists only of space characters, the
+		result is the empty String `""`.
 	**/
 	public #if cs inline #end static function ltrim( s : String ) : String {
 		#if cs
@@ -216,11 +278,11 @@ class StringTools {
 	/**
 		Removes trailing space characters of `s`.
 
-		This function internally calls isSpace() to decide which characters to
+		This function internally calls `isSpace()` to decide which characters to
 		remove.
 
-		If `s` is the empty String "" or consists only of space characters, the
-		result is the empty String "".
+		If `s` is the empty String `""` or consists only of space characters, the
+		result is the empty String `""`.
 	**/
 	public #if cs inline #end static function rtrim( s : String ) : String {
 		#if cs
@@ -242,7 +304,7 @@ class StringTools {
 	/**
 		Removes leading and trailing space characters of `s`.
 
-		This is a convenience function for ltrim(rtrim(s)).
+		This is a convenience function for `ltrim(rtrim(s))`.
 	**/
 	public #if (cs || java) inline #end static function trim( s : String ) : String {
 		#if cs
@@ -257,7 +319,7 @@ class StringTools {
 	/**
 		Concatenates `c` to `s` until `s.length` is at least `l`.
 
-		If `c` is the empty String "" or if `l` does not exceed `s.length`,
+		If `c` is the empty String `""` or if `l` does not exceed `s.length`,
 		`s` is returned unchanged.
 
 		If `c.length` is 1, the resulting String length is exactly `l`.
@@ -279,7 +341,7 @@ class StringTools {
 	/**
 		Appends `c` to `s` until `s.length` is at least `l`.
 
-		If `c` is the empty String "" or if `l` does not exceed `s.length`,
+		If `c` is the empty String `""` or if `l` does not exceed `s.length`,
 		`s` is returned unchanged.
 
 		If `c.length` is 1, the resulting String length is exactly `l`.
@@ -302,8 +364,8 @@ class StringTools {
 		Replace all occurences of the String `sub` in the String `s` by the
 		String `by`.
 
-		If `sub` is the empty String "", `by` is inserted after each character
-		of `s`. If `by` is also the empty String "", `s` remains unchanged.
+		If `sub` is the empty String `""`, `by` is inserted after each character
+		of `s`. If `by` is also the empty String `""`, `s` remains unchanged.
 
 		This is a convenience function for `s.split(sub).join(by)`.
 
@@ -329,7 +391,7 @@ class StringTools {
 		Encodes `n` into a hexadecimal representation.
 
 		If `digits` is specified, the resulting String is padded with "0" until
-		its length equals `digits`.
+		its `length` equals `digits`.
 	**/
 	public static function hex( n : Int, ?digits : Int ) {
 		#if flash
@@ -363,14 +425,14 @@ class StringTools {
 		Returns the character code at position `index` of String `s`, or an
 		end-of-file indicator at if `position` equals `s.length`.
 
-		This method is faster than String.charCodeAt() on some platforms, but
+		This method is faster than `String.charCodeAt()` on some platforms, but
 		the result is unspecified if `index` is negative or greater than
 		`s.length`.
 
-		End of file status can be checked by calling `StringTools.isEof` with
+		End of file status can be checked by calling `StringTools.isEof()` with
 		the returned value as argument.
 
-		This operation is not guaranteed to work if `s` contains the \0
+		This operation is not guaranteed to work if `s` contains the `\0`
 		character.
 	**/
 	public static inline function fastCodeAt( s : String, index : Int ) : Int {
@@ -388,6 +450,10 @@ class StringTools {
 		return (untyped s).charCodeAt(index);
 		#elseif python
 		return if (index >= s.length) -1 else python.internal.UBuiltins.ord(python.Syntax.arrayAccess(s, index));
+		#elseif hl
+		return @:privateAccess s.bytes.getUI16(index << 1);
+		#elseif lua
+		return lua.NativeStringTools.byte(s,index+1);
 		#else
 		return untyped s.cca(index);
 		#end
@@ -397,11 +463,11 @@ class StringTools {
 		Tells if `c` represents the end-of-file (EOF) character.
 	*/
 	@:noUsing public static inline function isEof( c : Int ) : Bool {
-		#if (flash || cpp)
+		#if (flash || cpp || hl)
 		return c == 0;
 		#elseif js
 		return c != c; // fast NaN
-		#elseif neko
+		#elseif (neko || lua)
 		return c == null;
 		#elseif cs
 		return c == -1;
@@ -414,6 +480,106 @@ class StringTools {
 		#end
 	}
 
+	/**
+		Returns a String that can be used as a single command line argument
+		on Unix.
+		The input will be quoted, or escaped if necessary.
+	*/
+	public static function quoteUnixArg(argument:String):String {
+		// Based on cpython's shlex.quote().
+		// https://hg.python.org/cpython/file/a3f076d4f54f/Lib/shlex.py#l278
+
+		if (argument == "")
+			return "''";
+
+		if (!~/[^a-zA-Z0-9_@%+=:,.\/-]/.match(argument))
+			return argument;
+
+		// use single quotes, and put single quotes into double quotes
+		// the string $'b is then quoted as '$'"'"'b'
+		return "'" + replace(argument, "'", "'\"'\"'") + "'";
+	}
+
+	/**
+		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];
+
+	/**
+		Returns a String that can be used as a single command line argument
+		on Windows.
+		The input will be quoted, or escaped if necessary, such that the output
+		will be parsed as a single argument using the rule specified in
+		http://msdn.microsoft.com/en-us/library/ms880421
+
+		Examples:
+		```
+		quoteWinArg("abc") == "abc";
+		quoteWinArg("ab c") == '"ab c"';
+		```
+	*/
+	public static function quoteWinArg(argument:String, escapeMetaCharacters:Bool):String {
+		// If there is no space, tab, back-slash, or double-quotes, and it is not an empty string.
+		if (!~/^[^ \t\\"]+$/.match(argument)) {
+
+			// Based on cpython's subprocess.list2cmdline().
+			// https://hg.python.org/cpython/file/50741316dd3a/Lib/subprocess.py#l620
+
+			var result = new StringBuf();
+			var needquote = argument.indexOf(" ") != -1 || argument.indexOf("\t") != -1 || argument == "";
+
+			if (needquote)
+				result.add('"');
+
+			var bs_buf = new StringBuf();
+			for (i in 0...argument.length) {
+				switch (argument.charCodeAt(i)) {
+					case "\\".code:
+						// Don't know if we need to double yet.
+						bs_buf.add("\\");
+					case '"'.code:
+						// Double backslashes.
+						var bs = bs_buf.toString();
+						result.add(bs);
+						result.add(bs);
+						bs_buf = new StringBuf();
+						result.add('\\"');
+					case c:
+						// Normal char
+						if (bs_buf.length > 0) {
+							result.add(bs_buf.toString());
+							bs_buf = new StringBuf();
+						}
+						result.addChar(c);
+				}
+			}
+
+			// Add remaining backslashes, if any.
+			result.add(bs_buf.toString());
+
+			if (needquote) {
+				result.add(bs_buf.toString());
+				result.add('"');
+			}
+
+			argument = result.toString();
+		}
+
+		if (escapeMetaCharacters) {
+			var result = new StringBuf();
+			for (i in 0...argument.length) {
+				var c = argument.charCodeAt(i);
+				if (winMetaCharacters.indexOf(c) >= 0) {
+					result.addChar("^".code);
+				}
+				result.addChar(c);
+			}
+			return result.toString();
+		} else {
+			return argument;
+		}
+	}
+
 	#if java
 	private static inline function _charAt(str:String, idx:Int):java.StdTypes.Char16 return untyped str._charAt(idx);
 	#end

+ 25 - 9
std/Sys.hx

@@ -1,5 +1,5 @@
 /*
- * Copyright (C)2005-2012 Haxe Foundation
+ * 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"),
@@ -20,7 +20,7 @@
  * DEALINGS IN THE SOFTWARE.
  */
 /**
-	This class gives you access to many base functionalities of system platforms. Looks in [sys] sub packages for more system APIs.
+	This class gives you access to many base functionalities of system platforms. Looks in `sys` sub packages for more system APIs.
 **/
 @:require(sys)
 extern class Sys {
@@ -31,7 +31,7 @@ extern class Sys {
 	static function print( v : Dynamic ) : Void;
 
 	/**
-		Print any value on the standard output, followed by a newline
+		Print any value on the standard output, followed by a newline.
 	**/
 	static function println( v : Dynamic ) : Void;
 
@@ -61,7 +61,7 @@ extern class Sys {
 	static function sleep( seconds : Float ) : Void;
 
 	/**
-		Change the current time locale, which will affect [DateTools.format] date formating.
+		Change the current time locale, which will affect `DateTools.format` date formating.
 		Returns true if the locale was successfully changed
 	**/
 	static function setTimeLocale( loc : String ) : Bool;
@@ -83,9 +83,18 @@ extern class Sys {
 	static function systemName() : String;
 
 	/**
-		Run the given command with the list of arguments. The command output will be printed on the same output as the current process.
+		Run the given command. The command output will be printed on the same output as the current process.
 		The current process will block until the command terminates and it will return the command result (0 if there was no error).
-		Read the [sys.io.Process] api for a more complete way to start background processes.
+
+		Command arguments can be passed in two ways: 1. using `args`, 2. appending to `cmd` and leaving `args` as `null`.
+
+		 1. When using `args` to pass command arguments, each argument will be automatically quoted, and shell meta-characters will be escaped if needed.
+		`cmd` should be an executable name that can be located in the `PATH` environment variable, or a path to an executable.
+
+		 2. When `args` is not given or is `null`, command arguments can be appended to `cmd`. No automatic quoting/escaping will be performed. `cmd` should be formatted exactly as it would be when typed at the command line.
+		It can run executables, as well as shell commands that are not executables (e.g. on Windows: `dir`, `cd`, `echo` etc).
+
+		Read the `sys.io.Process` api for a more complete way to start background processes.
 	**/
 	static function command( cmd : String, ?args : Array<String> ) : Int;
 
@@ -107,15 +116,22 @@ extern class Sys {
 	/**
 		Returns the path to the current executable that we are running.
 	**/
-	static function executablePath() : String;
+	@:deprecated("Use programPath instead") static function executablePath() : String;
+
+	/**
+		Returns the absolute path to the current program file that we are running.
+		Concretely, for an executable binary, it returns the path to the binary.
+		For a script (e.g. a PHP file), it returns the path to the script.
+	**/
+	static function programPath() : String;
 
 	/**
-		Read a single input character from the standard input (without blocking) and returns it. Setting [echo] to true will also display it on the output.
+		Read a single input character from the standard input (without blocking) and returns it. Setting `echo` to true will also display it on the output.
 	**/
 	static function getChar( echo : Bool ) : Int;
 
 	/**
-		Returns the process standard input, from which you can read what user enters. Usually it will block until the user send a full input line. See [getChar] for an alternative.
+		Returns the process standard input, from which you can read what user enters. Usually it will block until the user send a full input line. See `getChar` for an alternative.
 	**/
 	static function stdin() : haxe.io.Input;
 

+ 24 - 20
std/Type.hx

@@ -1,5 +1,5 @@
 /*
- * Copyright (C)2005-2012 Haxe Foundation
+ * 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"),
@@ -19,26 +19,15 @@
  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
  * DEALINGS IN THE SOFTWARE.
  */
-/**
-	The diffent possible runtime types of a value.
-**/
-enum ValueType {
-	TNull;
-	TInt;
-	TFloat;
-	TBool;
-	TObject;
-	TFunction;
-	TClass( c : Class<Dynamic> );
-	TEnum( e : Enum<Dynamic> );
-	TUnknown;
-}
 
 /**
-	The haxe Reflection API allows retrieval of type information at runtime.
+	The Haxe Reflection API allows retrieval of type information at runtime.
 
 	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
 **/
 extern class Type {
 
@@ -82,7 +71,7 @@ extern class Type {
 		If `c` is inside a package, the package structure is returned dot-
 		separated, with another dot separating the class name:
 		`pack1.pack2.(...).packN.ClassName`
-		If `c` is a sub-type of a haxe module, that module is not part of the
+		If `c` is a sub-type of a Haxe module, that module is not part of the
 		package structure.
 
 		If `c` has no package, the class name is returned.
@@ -99,7 +88,7 @@ extern class Type {
 		If `e` is inside a package, the package structure is returned dot-
 		separated, with another dot separating the enum name:
 		`pack1.pack2.(...).packN.EnumName`
-		If `e` is a sub-type of a haxe module, that module is not part of the
+		If `e` is a sub-type of a Haxe module, that module is not part of the
 		package structure.
 
 		If `e` has no package, the enum name is returned.
@@ -182,7 +171,7 @@ extern class Type {
 		Creates an instance of enum `e` by calling its constructor number
 		`index` with arguments `params`.
 
-		The constructor indices are preserved from haxe syntax, so the first
+		The constructor indices are preserved from Haxe syntax, so the first
 		declared is index 0, the next index 1 etc.
 
 		If `e` or `constr` is null, or if enum `e` has no constructor named
@@ -284,7 +273,7 @@ extern class Type {
 		Returns a list of all constructors of enum `e` that require no
 		arguments.
 
-		This may return the empty Array [] if all constructors of `e` require
+		This may return the empty Array `[]` if all constructors of `e` require
 		arguments.
 
 		Otherwise an instance of `e` constructed through each of its non-
@@ -297,3 +286,18 @@ extern class Type {
 
 }
 
+
+/**
+	The different possible runtime types of a value.
+**/
+enum ValueType {
+	TNull;
+	TInt;
+	TFloat;
+	TBool;
+	TObject;
+	TFunction;
+	TClass( c : Class<Dynamic> );
+	TEnum( e : Enum<Dynamic> );
+	TUnknown;
+}

+ 19 - 15
std/UInt.hx

@@ -1,5 +1,5 @@
 /*
- * Copyright (C)2005-2013 Haxe Foundation
+ * 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"),
@@ -20,10 +20,12 @@
  * DEALINGS IN THE SOFTWARE.
  */
 
-#if ((flash || flash9doc || cs) && !doc_gen)
+#if ((flash || flash9doc || cs || hl) && !doc_gen)
 /**
-	The unsigned Int type is only defined for Flash and C#. It's currently
+	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
 **/
 @:coreType
 @:notNull
@@ -61,7 +63,7 @@ abstract UInt to Int from Int
 	@:op(A&B) private static function and(lhs:UInt, rhs:UInt):UInt;
 
 	@:op(A<<B) private static function shl(lhs:UInt, rhs:Int):UInt;
-	@:op(A>>B) private static function shr(lhs:UInt, rhs:Int):UInt;
+	@:op(A>>B) private static inline function shr(lhs:UInt, rhs:Int):UInt return lhs >>> rhs;
 	@:op(A>>>B) private static function ushr(lhs:UInt, rhs:Int):UInt;
 
 	@:op(A>B) private static function gt(lhs:UInt, rhs:UInt):Bool;
@@ -70,13 +72,13 @@ abstract UInt to Int from Int
 	@:op(A<=B) private static function lte(lhs:UInt, rhs:UInt):Bool;
 
 	@:op(A>B) private static function gtf(lhs:UInt, rhs:Float):Bool;
-	@:op(A>B) private static function gtf(lhs:Float, rhs:UInt):Bool;
+	@:op(A>B) private static function gtf2(lhs:Float, rhs:UInt):Bool;
 	@:op(A>=B) private static function gtef(lhs:UInt, rhs:Float):Bool;
-	@:op(A>=B) private static function gtef(lhs:Float, rhs:UInt):Bool;
+	@:op(A>=B) private static function gtef2(lhs:Float, rhs:UInt):Bool;
 	@:op(A<B) private static function ltf(lhs:UInt, rhs:Float):Bool;
-	@:op(A<B) private static function ltf(lhs:Float, rhs:UInt):Bool;
+	@:op(A<B) private static function ltf2(lhs:Float, rhs:UInt):Bool;
 	@:op(A<=B) private static function ltef(lhs:UInt, rhs:Float):Bool;
-	@:op(A<=B) private static function ltef(lhs:Float, rhs:UInt):Bool;
+	@:op(A<=B) private static function ltef2(lhs:Float, rhs:UInt):Bool;
 
 	@:op(~A) private static function bneg(t:UInt):UInt;
 
@@ -92,8 +94,10 @@ abstract UInt to Int from Int
 }
 #else
 /**
-	The unsigned Int type is only defined for Flash and C#.
+	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
 **/
 abstract UInt(Int) from Int to Int {
 
@@ -154,7 +158,7 @@ abstract UInt(Int) from Int to Int {
 	}
 
 	@:op(A >> B) private static inline function shr(a:UInt, b:Int):UInt {
-		return a.toInt() >> b;
+		return a.toInt() >>> b;
 	}
 
 	@:op(A >>> B) private static inline function ushr(a:UInt, b:Int):UInt {
@@ -202,12 +206,12 @@ abstract UInt(Int) from Int to Int {
 	}
 
 	@:commutative @:op(A == B) private static inline function equalsFloat<T:Float>(a:UInt, b:T):Bool {
-        return a.toFloat() == b;
-    }
+		return a.toFloat() == b;
+	}
 
-    @:commutative @:op(A != B) private static inline function notEqualsFloat<T:Float>(a:UInt, b:T):Bool {
-        return a.toFloat() != b;
-    }
+	@:commutative @:op(A != B) private static inline function notEqualsFloat<T:Float>(a:UInt, b:T):Bool {
+		return a.toFloat() != b;
+	}
 
 	@:op(A >= B) private static inline function gteFloat(a:UInt, b:Float):Bool {
 		return a.toFloat() >= b;

+ 74 - 20
std/Xml.hx

@@ -1,5 +1,5 @@
 /*
- * Copyright (C)2005-2015 Haxe Foundation
+ * 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"),
@@ -20,26 +20,80 @@
  * DEALINGS IN THE SOFTWARE.
  */
 
+/**
+	Xml node types.
+
+	@see http://haxe.org/manual/std-Xml.html
+**/
 @:enum abstract XmlType(Int) {
+	/**
+		Represents an XML element type.
+	**/
 	var Element = 0;
+	/**
+		Represents XML parsed character data type.
+	**/
 	var PCData = 1;
+	/**
+		Represents XML character data type.
+	**/
 	var CData = 2;
+	/**
+		Represents an XML comment type.
+	**/
 	var Comment = 3;
+	/**
+		Represents an XML doctype element type.
+	**/
 	var DocType = 4;
+	/**
+	 	Represents an XML processing instruction type.
+	**/
 	var ProcessingInstruction = 5;
+	/**
+		Represents an XML document type.
+	**/
 	var Document = 6;
 }
 
-class Xml {
+/**
+	Crossplatform Xml API.
 
-	static public var Element(default,null) = XmlType.Element;
-	static public var PCData(default,null) = XmlType.PCData;
-	static public var CData(default,null) = XmlType.CData;
-	static public var Comment(default,null) = XmlType.Comment;
-	static public var DocType(default,null) = XmlType.DocType;
-	static public var ProcessingInstruction(default,null) = XmlType.ProcessingInstruction;
-	static public var Document(default,null) = XmlType.Document;
+	@see http://haxe.org/manual/std-Xml.html
+**/
+class Xml {
+	/**
+		XML element type.
+	**/
+	static public var Element(default,never) = XmlType.Element;
+	/**
+		XML parsed character data type.
+	**/
+	static public var PCData(default,never) = XmlType.PCData;
+	/**
+		XML character data type.
+	**/
+	static public var CData(default,never) = XmlType.CData;
+	/**
+		XML comment type.
+	**/
+	static public var Comment(default,never) = XmlType.Comment;
+	/**
+		XML doctype element type.
+	**/
+	static public var DocType(default,never) = XmlType.DocType;
+	/**
+	 	XML processing instruction type.
+	**/
+	static public var ProcessingInstruction(default,never) = XmlType.ProcessingInstruction;
+	/**
+		XML document type.
+	**/
+	static public var Document(default,never) = XmlType.Document;
 
+	/**
+		Parses the String into an Xml document.
+	**/
 	static public function parse( str : String ) : Xml {
 		return haxe.xml.Parser.parse(str);
 	}
@@ -63,35 +117,35 @@ class Xml {
 
 	/**
 		Returns the parent object in the Xml hierarchy.
-		The parent can be [null], an Element or a Document.
+		The parent can be `null`, an Element or a Document.
 	**/
 	public var parent(default, null) : Xml;
 
 	var children:Array<Xml>;
 	var attributeMap:Map<String, String>;
 
-	inline function get_nodeName() {
+	#if !cppia inline #end function get_nodeName() {
 		if (nodeType != Element) {
 			throw 'Bad node type, expected Element but found $nodeType';
 		}
 		return nodeName;
 	}
 
-	inline function set_nodeName(v) {
+	#if !cppia inline #end function set_nodeName(v) {
 		if (nodeType != Element) {
 			throw 'Bad node type, expected Element but found $nodeType';
 		}
 		return this.nodeName = v;
 	}
 
-	inline function get_nodeValue() {
+	#if !cppia inline #end function get_nodeValue() {
 		if (nodeType == Document || nodeType == Element) {
 			throw 'Bad node type, unexpected $nodeType';
 		}
 		return nodeValue;
 	}
 
-	inline function set_nodeValue(v) {
+	#if !cppia inline #end function set_nodeValue(v) {
 		if (nodeType == Document || nodeType == Element) {
 			throw 'Bad node type, unexpected $nodeType';
 		}
@@ -160,7 +214,7 @@ class Xml {
 	}
 
 	/**
-		Get the given attribute of an Element node. Returns [null] if not found.
+		Get the given attribute of an Element node. Returns `null` if not found.
 		Attributes are case-sensitive.
 	**/
 	public function get( att : String ) : String {
@@ -204,7 +258,7 @@ class Xml {
 	}
 
 	/**
-		Returns an [Iterator] on all the attribute names.
+		Returns an `Iterator` on all the attribute names.
 	**/
 	public function attributes() : Iterator<String> {
 		if (nodeType != Element) {
@@ -217,7 +271,7 @@ class Xml {
 		Returns an iterator of all child nodes.
 		Only works if the current node is an Element or a Document.
 	**/
-	public inline function iterator() : Iterator<Xml> {
+	public #if !cppia inline #end function iterator() : Iterator<Xml> {
 		ensureElementType();
 		return children.iterator();
 	}
@@ -245,7 +299,7 @@ class Xml {
 	/**
 		Returns the first child node.
 	**/
-	public inline function firstChild() : Xml {
+	public #if !cppia inline #end function firstChild() : Xml {
 		ensureElementType();
 		return children[0];
 	}
@@ -265,7 +319,7 @@ class Xml {
 
 	/**
 		Adds a child node to the Document or Element.
-		A child node can only be inside one given parent node, which is indicated by the [parent] property.
+		A child node can only be inside one given parent node, which is indicated by the `parent` property.
 		If the child is already inside this Document or Element, it will be moved to the last position among the Document or Element's children.
 		If the child node was previously inside a different node, it will be moved to this Document or Element.
 	**/
@@ -309,7 +363,7 @@ class Xml {
 	/**
 		Returns a String representation of the Xml node.
 	**/
-	public inline function toString() : String {
+	public #if !cppia inline #end function toString() : String {
 		return haxe.xml.Printer.print(this);
 	}
 

+ 22 - 1
std/cpp/ArrayBase.hx

@@ -1,4 +1,25 @@
-package cpp;
+/*
+ * 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;
 
 extern class ArrayBase
 {

+ 18 - 0
std/cpp/AtomicInt.hx

@@ -0,0 +1,18 @@
+package cpp;
+
+@:scalar @:coreType
+extern abstract AtomicInt from(Int) to(Int)
+{
+   // returns true if exchange took place
+   @:native("_hx_atomic_exchange_if")
+   public static function exchangeIf(ioValue:Pointer<AtomicInt>, test:Int, newVal:Int) : Bool;
+
+   // returns value before increment
+   @:native("_hx_atomic_inc")
+   public static function atomicInc(ioValue:Pointer<AtomicInt>) : Int;
+
+   // returns value before decrement
+   @:native("_hx_atomic_dec")
+   public static function atomicDec(ioValue:Pointer<AtomicInt>) : Int;
+}
+

+ 43 - 10
std/cpp/Callable.hx

@@ -1,22 +1,55 @@
+/*
+ * 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;
 
-#if cpp
 
-typedef Callable<T> = Function<T, cpp.abi.Abi >
-
-#else
-
-@:noPackageRestrict
+// 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)
 {
+   inline public function new(inValue:T) this = inValue;
    public var call(get,never):T;
+   inline function get_call():T return this;
 
-   inline public function new(inValue:T) this = inValue;
+   #if cpp
+   @:from
+   inline static public function fromFunction<F>( func:Function<F,cpp.abi.Abi> ) : Callable<F>
+       return new Callable<F>(cast func);
+   @:to
+   inline public function toFunction() : Function<T,cpp.abi.Abi> return cast this;
 
-   inline function get_call():T return this;
-}
 
+   inline public static function getProcAddress<T,ABI:cpp.abi.Abi>(inModule:String, inFunction:String) : Function<T,ABI>
+      return Function.getProcAddress(inModule, inFunction);
 
-#end
+   inline public static function fromStaticFunction<T>(inStaticFunction:T) : Callable<T>
+      return Function.fromStaticFunction(inStaticFunction);
+
+   inline public function lt(inOther:Callable<T>):Bool return toFunction().lt(inOther.toFunction());
+   inline public function leq(inOther:Callable<T>):Bool return toFunction().leq(inOther.toFunction());
+   inline public function gt(inOther:Callable<T>):Bool return toFunction().gt(inOther.toFunction());
+   inline public function geq(inOther:Callable<T>):Bool return toFunction().geq(inOther.toFunction());
+   #end
+}
 
 

+ 22 - 1
std/cpp/CastCharStar.hx

@@ -1,4 +1,25 @@
-package cpp;
+/*
+ * 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;
 
 abstract CastCharStar( RawPointer<Char> ) to(RawPointer<Char>)
 {

+ 22 - 1
std/cpp/Char.hx

@@ -1,3 +1,24 @@
-package cpp;
+/*
+ * 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;
 
 @:coreType @:notNull @:runtimeValue abstract Char from Int to Int {}

+ 23 - 2
std/cpp/ConstCharStar.hx

@@ -1,6 +1,27 @@
-package cpp;
+/*
+ * 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;
 
-abstract ConstCharStar( RawConstPointer<Char> ) to(RawConstPointer<Char>)
+@:extern abstract ConstCharStar( RawConstPointer<Char> ) to(RawConstPointer<Char>)
 {
    inline function new(s:String) this = untyped s.__s;
 

+ 50 - 22
std/cpp/ConstPointer.hx

@@ -1,41 +1,69 @@
-package cpp;
+/*
+ * 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;
 
 @:coreType @:include("cpp/Pointer.h") @:native("cpp.Pointer")
-@:analyzer(no_simplification)
 extern class ConstPointer<T>
 {
    // ptr actually returns the pointer - not strictly a 'T' - for pointers to smart pointers
    // Use value or ref to get dereferenced value
-	public var ptr:T;
+   public var ptr:Star<T>;
 
-   @:analyzer(no_simplification)
-	public var value(get,never):T;
-	public var raw(get,never):RawConstPointer<T>;
+   public var value(get,never):T;
 
-   @:analyzer(no_simplification)
-   public function get_value() : T;
+   // Typecast to non-const
+   public var raw(get,never):RawPointer<T>;
 
-	public function lt(inOther:Pointer<T>):Bool;
-	public function leq(inOther:Pointer<T>):Bool;
-	public function gt(inOther:Pointer<T>):Bool;
-	public function geq(inOther:Pointer<T>):Bool;
+   // const version
+   public var constRaw(get,never):RawConstPointer<T>;
+
+   public function get_value() : Reference<T>;
+
+   public function get_constRaw() : RawConstPointer<T>;
+   public function get_raw() : RawPointer<T>;
+
+
+   public function lt(inOther:ConstPointer<T>):Bool;
+   public function leq(inOther:ConstPointer<T>):Bool;
+   public function gt(inOther:ConstPointer<T>):Bool;
+   public function geq(inOther:ConstPointer<T>):Bool;
+
+   public function setRaw<O>(ptr:RawPointer<O>) : Void;
 
    public static function fromRaw<T>(ptr:RawConstPointer<T>) : ConstPointer<T>;
 
+
    public static function fromPointer<T>(inNativePointer:Dynamic) : ConstPointer<T>;
 
-	public function reinterpret<Other>():Pointer<Other>;
-	public function rawCast<Other>():RawPointer<Other>;
+   public function reinterpret<Other>():Pointer<Other>;
+   public function rawCast<Other>():RawPointer<Other>;
 
-   @:analyzer(no_simplification)
-	public function at(inIndex:Int):T;
+   public function at(inIndex:Int):Reference<T>;
 
-	public function inc():ConstPointer<T>;
-	public function dec():ConstPointer<T>;
-   @:analyzer(no_simplification)
-	public function postIncVal():T;
-	public function incBy(inT:Int):ConstPointer<T>;
-	public function add(inT:Int):ConstPointer<T>;
+   public function inc():ConstPointer<T>;
+   public function dec():ConstPointer<T>;
+   public function postIncVal():Reference<T>;
+   public function incBy(inT:Int):ConstPointer<T>;
+   public function add(inT:Int):ConstPointer<T>;
 
 }
 

+ 31 - 0
std/cpp/EnumBase.hx

@@ -0,0 +1,31 @@
+package cpp;
+
+@:native("hx.EnumBase")
+extern class EnumBase
+{
+   #if (hxcpp_api_level >= 330)
+   public function _hx_getIndex():Int;
+   public function _hx_getTag():String;
+   public function _hx_getParamCount():Int;
+   public function _hx_getParamI(inIndex:Int):Dynamic;
+   public function _hx_getParameters():Array<Dynamic>;
+
+   inline public function getIndex():Int return _hx_getIndex();
+   inline public function getTag():String return _hx_getTag();
+   inline public function getParamCount():Int return _hx_getParamCount();
+   inline public function getParamI(inIndex:Int):Dynamic return _hx_getParamI(inIndex);
+   inline public function getParameters():Array<Dynamic> return _hx_getParameters();
+   #else
+   public function __EnumParams():Array<Dynamic>;
+   public function __Tag():String;
+   public function __Index():Int;
+
+   inline public function _hx_getIndex():Int  return untyped __Index();
+   inline public function _hx_getTag():String  return untyped __Tag();
+   inline public function _hx_getParamCount():Int return untyped __EnumParams()==null ? 0 : __EnumParams().length;
+   inline public function _hx_getParamI(inIndex:Int):Dynamic return untyped __EnumParams()[inIndex];
+   inline public function _hx_getParameters():Array<Dynamic> return __EnumParams()==null ? [] : __EnumParams();
+
+   #end
+}
+

+ 1 - 1
std/cpp/FastIterator.hx

@@ -1,5 +1,5 @@
 /*
- * Copyright (C)2005-2012 Haxe Foundation
+ * 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"),

+ 14 - 0
std/cpp/Finalizable.hx

@@ -0,0 +1,14 @@
+package cpp;
+
+// This is just a helper class.  You do not actually need to inherit from this to use
+//  NativeGc.addFinalizable(this,inPin), you just need a function called "finalize"
+class Finalizable
+{
+   public function new(inPin = false)
+   {
+      NativeGc.addFinalizable(this,inPin);
+   }
+
+   public function finalize():Void { }
+}
+

部分文件因为文件数量过多而无法显示