瀏覽代碼

Merge branch 'development'

Simon Krajewski 8 年之前
父節點
當前提交
6e3d9dcbfe
共有 100 個文件被更改,包括 4750 次插入265 次删除
  1. 5 1
      .gitignore
  2. 17 1
      .travis.yml
  3. 5 3
      Makefile
  4. 7 7
      README.md
  5. 5 1
      appveyor.yml
  6. 34 0
      extra/CHANGES.txt
  7. 7 2
      extra/ImportAll.hx
  8. 6 0
      extra/all.hxml
  9. 4 5
      extra/release-checklist.txt
  10. 4 0
      haxe.hxproj
  11. 14 5
      src/context/common.ml
  12. 5 1
      src/context/meta.ml
  13. 2 0
      src/display/displayOutput.ml
  14. 37 36
      src/generators/gencpp.ml
  15. 2 7
      src/generators/genhl.ml
  16. 3 3
      src/generators/genjs.ml
  17. 31 4
      src/generators/genphp.ml
  18. 3330 0
      src/generators/genphp7.ml
  19. 2 2
      src/generators/genpy.ml
  20. 27 1
      src/generators/hlinterp.ml
  21. 2 1
      src/macro/interp.ml
  22. 12 2
      src/main.ml
  23. 1 0
      src/optimization/analyzerConfig.ml
  24. 20 7
      src/optimization/analyzerTexpr.ml
  25. 28 20
      src/optimization/analyzerTexprTransformer.ml
  26. 1 1
      src/optimization/filters.ml
  27. 1 1
      src/optimization/optimizer.ml
  28. 14 11
      src/syntax/parser.ml
  29. 1 1
      src/typing/matcher.ml
  30. 1 0
      src/typing/typeload.ml
  31. 19 20
      src/typing/typer.ml
  32. 17 4
      std/DateTools.hx
  33. 1 7
      std/cpp/ConstPointer.hx
  34. 12 13
      std/cpp/NativeArray.hx
  35. 2 1
      std/cpp/Pointer.hx
  36. 1 1
      std/cpp/_std/haxe/ds/IntMap.hx
  37. 1 1
      std/cpp/_std/haxe/ds/StringMap.hx
  38. 2 1
      std/cpp/_std/sys/io/Process.hx
  39. 2 0
      std/cs/_std/sys/FileSystem.hx
  40. 3 1
      std/cs/_std/sys/io/Process.hx
  41. 7 0
      std/haxe/CallStack.hx
  42. 1 1
      std/haxe/Http.hx
  43. 1 1
      std/haxe/Int64.hx
  44. 4 2
      std/haxe/Log.hx
  45. 7 2
      std/haxe/Serializer.hx
  46. 1 1
      std/haxe/Timer.hx
  47. 1 0
      std/haxe/extern/AsVar.hx
  48. 2 0
      std/haxe/io/BytesData.hx
  49. 20 17
      std/haxe/io/Input.hx
  50. 3 5
      std/haxe/macro/CompilationServer.hx
  51. 1 1
      std/haxe/macro/Compiler.hx
  52. 5 3
      std/haxe/rtti/Meta.hx
  53. 8 8
      std/haxe/xml/Parser.hx
  54. 2 1
      std/hl/Api.hx
  55. 35 1
      std/hl/Bytes.hx
  56. 1 1
      std/hl/Type.hx
  57. 1 1
      std/hl/_std/String.hx
  58. 3 3
      std/hl/_std/Type.hx
  59. 8 4
      std/hl/_std/haxe/io/Bytes.hx
  60. 2 2
      std/hl/_std/sys/db/Sqlite.hx
  61. 4 0
      std/hl/_std/sys/io/File.hx
  62. 56 7
      std/hl/_std/sys/io/Process.hx
  63. 10 2
      std/hl/_std/sys/net/Socket.hx
  64. 130 0
      std/hl/_std/sys/ssl/Certificate.hx
  65. 27 0
      std/hl/_std/sys/ssl/Digest.hx
  66. 36 0
      std/hl/_std/sys/ssl/Key.hx
  67. 10 0
      std/hl/_std/sys/ssl/Lib.hx
  68. 251 0
      std/hl/_std/sys/ssl/Socket.hx
  69. 4 0
      std/hl/types/ArrayBase.hx
  70. 1 1
      std/hl/types/ArrayBytes.hx
  71. 5 1
      std/hl/types/ArrayObj.hx
  72. 9 1
      std/java/_std/sys/io/Process.hx
  73. 1 1
      std/js/html/AlignSetting.hx
  74. 56 1
      std/js/html/AnchorElement.hx
  75. 72 1
      std/js/html/Animation.hx
  76. 16 1
      std/js/html/AnimationEffectReadOnly.hx
  77. 8 1
      std/js/html/AnimationEffectTiming.hx
  78. 37 0
      std/js/html/AnimationEffectTimingProperties.hx
  79. 40 1
      std/js/html/AnimationEffectTimingReadOnly.hx
  80. 20 1
      std/js/html/AnimationEvent.hx
  81. 1 1
      std/js/html/AnimationEventInit.hx
  82. 1 1
      std/js/html/AnimationPlayState.hx
  83. 12 1
      std/js/html/AnimationTimeline.hx
  84. 1 1
      std/js/html/AppletElement.hx
  85. 1 1
      std/js/html/ApplicationCache.hx
  86. 40 1
      std/js/html/AreaElement.hx
  87. 1 1
      std/js/html/ArrayBuffer.hx
  88. 8 1
      std/js/html/ArrayBufferView.hx
  89. 8 1
      std/js/html/Attr.hx
  90. 1 1
      std/js/html/Audio.hx
  91. 1 1
      std/js/html/AudioChannel.hx
  92. 1 1
      std/js/html/AudioContextState.hx
  93. 8 1
      std/js/html/AudioElement.hx
  94. 1 1
      std/js/html/AudioStreamTrack.hx
  95. 1 1
      std/js/html/AudioTrack.hx
  96. 1 1
      std/js/html/AudioTrackList.hx
  97. 12 1
      std/js/html/BRElement.hx
  98. 1 1
      std/js/html/BarProp.hx
  99. 16 1
      std/js/html/BaseElement.hx
  100. 40 1
      std/js/html/BatteryManager.hx

+ 5 - 1
.gitignore

@@ -63,6 +63,7 @@
 /tests/unit/node_modules/
 
 /haxe.sublime*
+.idea
 build.bat
 /.vscode
 tests/unit/compile.php.hxml
@@ -73,6 +74,7 @@ tests/unit/unit.py
 tests/unit/unit.py.res1.txt
 tests/unit/unit.py.res2.bin
 tests/sys/bin/
+/tests/sys/dump/
 tests/optimization/dump/
 tests/misc/projects/*/*.n
 tests/unit/bin/
@@ -88,4 +90,6 @@ tests/misc/projects/Issue4070/cpp/
 /tests/misc/eventLoop/dump
 /tests/misc/eventLoop/eventLoop.py
 /tests/misc/eventLoop/php
-*.vscode/
+*.vscode/
+
+/tests/sys/temp

+ 17 - 1
.travis.yml

@@ -29,7 +29,10 @@ addons: &addons
 
 install_linux: &install_linux
   # Install neko and haxe dependencies
+  - sudo add-apt-repository ppa:haxe/ocaml -y
+  - sudo apt-get update
   - sudo apt-get install -y
+      ocaml
       ocaml-native-compilers
       camlp4
       pkg-config
@@ -49,10 +52,13 @@ install_linux: &install_linux
   - sudo make install
   - popd
   # Setup database
-  - travis_retry sudo apt-get install mysql-server -y
+  - travis_retry sudo apt-get install mysql-server-5.6 -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;"
+  # Setup JDK
+  - jdk_switcher use oraclejdk7
+  - java -version
   # Build haxe
   - make package_src -s
   - make -s
@@ -128,6 +134,15 @@ matrix:
             g++-multilib
       install: *install_linux
 
+    - os: linux
+      env:
+        - TEST=php7
+      before_install:
+        - phpenv global "7.0"
+        # - sudo apt-get install php7-cli php7-mysql php7-sqlite -y || (sudo add-apt-repository ppa:ondrej/php -y && sudo apt-get update -y && sudo apt-get install  php7.0-cli php7.0-mysql php7.0-sqlite -y)
+        - php -v || true
+      install: *install_linux
+
     #######
     # osx #
     #######
@@ -146,6 +161,7 @@ script:
   - eval `ssh-agent -s` # for deployment to haxe.org
   - pushd tests
   -   mkdir ~/haxelib && haxelib setup ~/haxelib
+  -   haxelib install record-macros
   -   haxe -version
   -   haxe RunCi.hxml
   -   neko RunCi.n

+ 5 - 3
Makefile

@@ -58,7 +58,7 @@ MODULES=json version globals path context/meta syntax/ast display/displayTypes t
 	syntax/lexer context/common generators/genxml \
 	syntax/parser typing/abstract typing/typecore display/display optimization/optimizerTexpr \
 	optimization/optimizer typing/overloads typing/typeload generators/codegen generators/gencommon generators/genas3 \
-	generators/gencpp generators/genjs generators/genneko generators/genphp generators/genswf9 \
+	generators/gencpp generators/genjs generators/genneko generators/genphp generators/genphp7 generators/genswf9 \
 	generators/genswf generators/genjava generators/gencs generators/genpy macro/macroApi macro/interp generators/hlcode generators/hlopt generators/hlinterp generators/hl2c \
 	generators/genlua \
 	optimization/dce optimization/analyzerConfig optimization/analyzerTypes optimization/analyzerTexpr \
@@ -134,7 +134,7 @@ uninstall:
 	else \
 		rm -rf $(INSTALL_LIB_DIR); \
 	fi
-	
+
 
 # Modules
 
@@ -180,6 +180,8 @@ src/generators/genswf.$(MODULE_EXT): src/globals.$(MODULE_EXT) src/typing/type.$
 
 src/generators/hlinterp.$(MODULE_EXT): src/context/common.$(MODULE_EXT) src/generators/hlcode.$(MODULE_EXT) src/macro/interp.$(MODULE_EXT) src/generators/hlopt.$(MODULE_EXT) src/macro/macroApi.$(MODULE_EXT)
 
+src/generators/genphp7.$(MODULE_EXT): src/typing/abstract.$(MODULE_EXT) src/globals.$(MODULE_EXT) src/context/meta.$(MODULE_EXT) src/path.$(MODULE_EXT) src/typing/type.$(MODULE_EXT) src/syntax/lexer.$(MODULE_EXT) src/context/common.$(MODULE_EXT) src/generators/codegen.$(MODULE_EXT) src/syntax/ast.$(MODULE_EXT)
+
 src/generators/hl2c.$(MODULE_EXT): src/generators/hlcode.$(MODULE_EXT)
 
 src/generators/hlopt.$(MODULE_EXT): src/generators/hlcode.$(MODULE_EXT)
@@ -249,7 +251,7 @@ src/typing/typer.$(MODULE_EXT): src/typing/abstract.$(MODULE_EXT) src/context/me
 
 # main
 
-src/main.$(MODULE_EXT): src/context/meta.$(MODULE_EXT) src/globals.$(MODULE_EXT) src/typing/error.$(MODULE_EXT) src/globals.$(MODULE_EXT) src/path.$(MODULE_EXT) src/optimization/filters.$(MODULE_EXT) src/typing/matcher.$(MODULE_EXT) src/typing/typer.$(MODULE_EXT) src/typing/typeload.$(MODULE_EXT) src/typing/typecore.$(MODULE_EXT) src/typing/type.$(MODULE_EXT) src/syntax/parser.$(MODULE_EXT) src/optimization/optimizer.$(MODULE_EXT) src/syntax/lexer.$(MODULE_EXT) src/macro/interp.$(MODULE_EXT) src/generators/genxml.$(MODULE_EXT) src/generators/genswf.$(MODULE_EXT) src/generators/genphp.$(MODULE_EXT) src/generators/genneko.$(MODULE_EXT) src/generators/genjs.$(MODULE_EXT) src/generators/genlua.$(MODULE_EXT) src/generators/gencpp.$(MODULE_EXT) src/generators/genas3.$(MODULE_EXT) src/context/common.$(MODULE_EXT) src/generators/codegen.$(MODULE_EXT) src/generators/genjava.$(MODULE_EXT) src/generators/gencs.$(MODULE_EXT) src/generators/genpy.$(MODULE_EXT) src/generators/genhl.$(MODULE_EXT) src/display/display.$(MODULE_EXT) src/server.$(MODULE_EXT) src/display/displayOutput.$(MODULE_EXT) libs/ilib/il.$(LIB_EXT)
+src/main.$(MODULE_EXT): src/context/meta.$(MODULE_EXT) src/globals.$(MODULE_EXT) src/typing/error.$(MODULE_EXT) src/globals.$(MODULE_EXT) src/path.$(MODULE_EXT) src/optimization/filters.$(MODULE_EXT) src/typing/matcher.$(MODULE_EXT) src/typing/typer.$(MODULE_EXT) src/typing/typeload.$(MODULE_EXT) src/typing/typecore.$(MODULE_EXT) src/typing/type.$(MODULE_EXT) src/syntax/parser.$(MODULE_EXT) src/optimization/optimizer.$(MODULE_EXT) src/syntax/lexer.$(MODULE_EXT) src/macro/interp.$(MODULE_EXT) src/generators/genxml.$(MODULE_EXT) src/generators/genswf.$(MODULE_EXT) src/generators/genphp.$(MODULE_EXT) src/generators/genphp7.$(MODULE_EXT) src/generators/genneko.$(MODULE_EXT) src/generators/genjs.$(MODULE_EXT) src/generators/genlua.$(MODULE_EXT) src/generators/gencpp.$(MODULE_EXT) src/generators/genas3.$(MODULE_EXT) src/context/common.$(MODULE_EXT) src/generators/codegen.$(MODULE_EXT) src/generators/genjava.$(MODULE_EXT) src/generators/gencs.$(MODULE_EXT) src/generators/genpy.$(MODULE_EXT) src/generators/genhl.$(MODULE_EXT) src/display/display.$(MODULE_EXT) src/server.$(MODULE_EXT) src/display/displayOutput.$(MODULE_EXT) libs/ilib/il.$(LIB_EXT)
 
 src/globals.$(MODULE_EXT): src/version.$(MODULE_EXT)
 

+ 7 - 7
README.md

@@ -40,15 +40,15 @@ For the complete Haxe licenses, please see https://haxe.org/foundation/open-sour
 
 ## Installing Haxe
 
-The latest stable release is [Haxe 3.4.0-rc.1](https://haxe.org/download/version/3.4.0-rc.1/). Pre-built binaries are available for your platform:
+The latest stable release is [Haxe 3.4.0-rc.2](https://haxe.org/download/version/3.4.0-rc.2/). Pre-built binaries are available for your platform:
 
- * **[Windows installer](https://haxe.org/download/file/3.4.0-rc.1/haxe-3.4.0-rc.1-win.exe)**
- * **[Windows binaries](https://haxe.org/download/file/3.4.0-rc.1/haxe-3.4.0-rc.1-win.zip)**
- * **[OSX installer](https://haxe.org/download/file/3.4.0-rc.1/haxe-3.4.0-rc.1-osx-installer.pkg)**
- * **[OSX binaries](https://haxe.org/download/file/3.4.0-rc.1/haxe-3.4.0-rc.1-osx.tar.gz)**
+ * **[Windows installer](https://haxe.org/download/file/3.4.0-rc.2/haxe-3.4.0-rc.2-win.exe)**
+ * **[Windows binaries](https://haxe.org/download/file/3.4.0-rc.2/haxe-3.4.0-rc.2-win.zip)**
+ * **[OSX installer](https://haxe.org/download/file/3.4.0-rc.2/haxe-3.4.0-rc.2-osx-installer.pkg)**
+ * **[OSX binaries](https://haxe.org/download/file/3.4.0-rc.2/haxe-3.4.0-rc.2-osx.tar.gz)**
  * **[Linux Software Packages](https://haxe.org/download/linux)**
- * **[Linux 32-bit binaries](https://haxe.org/download/file/3.4.0-rc.1/haxe-3.4.0-rc.1-linux32.tar.gz)**
- * **[Linux 64-bit binaries](https://haxe.org/download/file/3.4.0-rc.1/haxe-3.4.0-rc.1-linux64.tar.gz)**
+ * **[Linux 32-bit binaries](https://haxe.org/download/file/3.4.0-rc.2/haxe-3.4.0-rc.2-linux32.tar.gz)**
+ * **[Linux 64-bit binaries](https://haxe.org/download/file/3.4.0-rc.2/haxe-3.4.0-rc.2-linux64.tar.gz)**
 
 Automated development builds are available from [build.haxe.org](http://build.haxe.org).
 

+ 5 - 1
appveyor.yml

@@ -10,7 +10,7 @@ environment:
         MYSQL_USER: root
         MYSQL_PASSWORD: Password12!
     matrix:
-        - TEST: "neko,python,cs,java,macro"
+        - TEST: "neko,python,cs,java,macro,php7"
         - TEST: "cpp"
 
 services:
@@ -36,6 +36,10 @@ install:
     - '%CYG_ROOT%/bin/bash -lc "opam install camlp4 --yes"'
     # Install neko
     - choco install neko --prerelease --ignore-dependencies -s 'https://ci.appveyor.com/nuget/neko' -y
+    - choco install php --ignore-dependencies
+    - echo extension=php_mysqli.dll >> C:\tools\php\php.ini
+    - echo extension=php_sqlite3.dll >> C:\tools\php\php.ini
+    - echo extension=php_openssl.dll >> C:\tools\php\php.ini
     - RefreshEnv
     # do not use chocolatey's shim, which is buggy when processing arguments
     # see https://github.com/chocolatey/shimgen/issues/27

+ 34 - 0
extra/CHANGES.txt

@@ -1,3 +1,37 @@
+2016-12-24: 3.4.0-RC2
+
+	New features:
+
+	js : added API documentation to HTML externs (#5868)
+	php : added php7 target, enabled with -D php7
+
+	Bugfixes:
+
+	all : fixed top-down inference infinite recursion issue (#5848)
+	all : fixed regression in Compiler.include (#5847)
+	all : fixed Not_found exception related to try/catch (#5851)
+	all : fixed metadata completion showing up in trace arguments (#5775)
+	all : fixed problem with useless pattern detection (#5873)
+	all : fixed issue with toString handling in trace arguments (#5858)
+	all : fixed inline constructor scoping (#5855)
+	cpp : fixed issue with cpp.Pointer variables being eliminated (#5850)
+	js : added Notification API to HTML externs (#5852)
+	js : fixed several options structures in HTML externs (#5849)
+	php/cs : FileSystem.deleteFile() and FileSystem.deleteDirectory() now throw on non-existent path (#5742)
+	php/lua : fixed field access on `null` (#4988)
+	php : fixed static field access on a `Class<T>` stored to a field (#5383)
+	php : fixed invalid detection of `NativeArray` by `Std.is()` (#5565)
+	php : fixed `stdin()`, `stdout()`, `stderr()` of `Sys` to use predefined constants for corresponding channels (#5733)
+	php : fixed Std.parseInt() on hexstrings for PHP7+ (#5521)
+	php : fixed typed cast in assign operations (#5135)
+	php : fixed exception thrown by `Reflect.fields(o)` when `o` is `Class<T>` (#5608)
+	php : fixed json encoding of empty objects (#5015)
+	php : fixed checking floats for equality (#4260)
+	php : throw if invalid json string supplied to Json.parse() (#4592)
+	php : fixed ssl connections (#4581)
+	php : fixed writing bytes containing zero byte to MySQL & SQLite (#4489)
+	php : fixed call()() cases for PHP5 (#5569)
+
 2016-11-29: 3.4.0-RC1
 
 	New features:

+ 7 - 2
extra/ImportAll.hx

@@ -29,8 +29,10 @@ class ImportAll {
 			haxe.macro.Compiler.define("doc_gen");
 		}
 		switch( pack ) {
+		case "php7":
+			if( !Context.defined("php7") ) return;
 		case "php":
-			if( !Context.defined("php") ) return;
+			if( !Context.defined("php") || Context.defined("php7") ) return;
 		case "neko":
 			if( !Context.defined("neko") ) return;
 		case "js":
@@ -71,7 +73,7 @@ class ImportAll {
 				if( file == ".svn" || file == "_std" )
 					continue;
 				var full = (pack == "") ? file : pack + "." + file;
-				if( StringTools.endsWith(file, ".hx") && file.indexOf(".") < 0 ) {
+				if( StringTools.endsWith(file, ".hx") && file.substr(0, file.length - 3).indexOf(".") < 0 ) {
 					var cl = full.substr(0, full.length - 3);
 					switch( cl ) {
 					case "ImportAll", "neko.db.MacroManager": continue;
@@ -83,6 +85,9 @@ class ImportAll {
 					case "haxe.remoting.SyncSocketConnection": if( !(Context.defined("neko") || Context.defined("php") || Context.defined("cpp")) ) continue;
 					case "sys.db.Sqlite" | "sys.db.Mysql" | "cs.db.AdoNet": continue;
 					}
+					if( Context.defined("php7") && cl.indexOf("php7.") == 0 ) {
+						cl = "php." + cl.substr("php7.".length);
+					}
 					Context.getModule(cl);
 				} else if( sys.FileSystem.isDirectory(p + "/" + file) )
 					run(full);

+ 6 - 0
extra/all.hxml

@@ -19,6 +19,12 @@
 
 --next
 
+-D php7
+-php all_php7
+-xml php7.xml
+
+--next
+
 -php all_php
 -xml php.xml
 

+ 4 - 5
extra/release-checklist.txt

@@ -20,14 +20,13 @@
 
 # Making the release
 
-- Copy relevant changelog part to CHANGES.md.
+- Copy relevant changelog part to downloads/$version/CHANGES.md.
 - Write announcement post.
-- Copy announcement post to RELEASE.md.
-- Update versions.json
-- Push the generated binaries and installers to haxe.org.
+- Copy announcement post to downloads/$version/RELEASE.md.
+- Update downloads/versions.json
+- Push the generated binaries and installers to haxe.org, requires git-lfs
 
 # Announcing the release
 
 - Regenerate and upload API documentation (check --title and -D version values).
-- Update http://haxe.org/file/CHANGES.txt
 - Post announcement post to haxelang.

+ 4 - 0
haxe.hxproj

@@ -416,6 +416,10 @@
     <hidden path="src\macro\macroApi.cmt" />
     <hidden path="src\macro\macroApi.cmx" />
     <hidden path="src\macro\macroApi.o" />
+    <hidden path="src\generators\genphp7.cmi" />
+    <hidden path="src\generators\genphp7.cmt" />
+    <hidden path="src\generators\genphp7.cmx" />
+    <hidden path="src\generators\genphp7.o" />
   </hiddenPaths>
   <!-- Executed before build -->
   <preBuildCommand>make -j4 FD_OUTPUT=1 -f Makefile.win kill haxe</preBuildCommand>

+ 14 - 5
src/context/common.ml

@@ -23,6 +23,7 @@ open Globals
 
 type package_rule =
 	| Forbidden
+	| Directory of string
 	| Remap of string
 
 type pos = Globals.pos
@@ -315,6 +316,8 @@ let get_signature com =
 		com.defines_signature <- Some s;
 		s
 
+let php7 com = com.platform = Php && PMap.exists "php7" com.defines
+
 module CompilationServer = struct
 	type cache = {
 		c_haxelib : (string list, string list) Hashtbl.t;
@@ -704,11 +707,17 @@ let get_config com =
 			pf_reserved_type_paths = [([],"Object");([],"Error")];
 		}
 	| Php ->
-		{
-			default_config with
-			pf_static = false;
-			pf_pad_nulls = true;
-		}
+		if php7 com then
+			{
+				default_config with
+				pf_static = false;
+			}
+		else
+			{
+				default_config with
+				pf_static = false;
+				pf_pad_nulls = true;
+			}
 	| Cpp ->
 		{
 			default_config with

+ 5 - 1
src/context/meta.ml

@@ -117,6 +117,8 @@ type strict_meta =
 	| Overload
 	| PhpConstants
 	| PhpGlobal
+	| PhpClassConst
+	| PhpMagic
 	| PrivateAccess
 	| Property
 	| Protected
@@ -307,7 +309,9 @@ let get_info = function
 	| 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])
+	| PhpGlobal -> ":phpGlobal",("(php7) Puts the static fields of a class in the global PHP namespace",[Platforms [Php;Php];UsedOn TClass])
+	| PhpClassConst -> ":phpClassConst",("(php7)  Generate static var of an extern class as a PHP class constant",[Platform Php;UsedOn TClass])
+	| PhpMagic -> ":phpMagic",("(php7) Treat annotated field as special PHP magic field",[Platform Php;UsedOn TClassField])
 	| Public -> ":public",("Marks a class field as being public",[UsedOn TClassField;UsedInternally])
 	| PublicFields -> ":publicFields",("Forces all class fields of inheriting classes to be public",[UsedOn TClass])
 	| QuotedField -> ":quotedField",("Used internally to mark structure fields which are quoted in syntax",[UsedInternally])

+ 2 - 0
src/display/displayOutput.ml

@@ -247,6 +247,7 @@ module TypePathHandler = struct
 			| x :: l ->
 				(try
 					match PMap.find x com.package_rules with
+					| Directory d -> d :: l
 					| Remap s -> s :: l
 					| _ -> p
 				with
@@ -267,6 +268,7 @@ module TypePathHandler = struct
 									match PMap.find f com.package_rules with
 									| Forbidden -> ()
 									| Remap f -> packages := f :: !packages
+									| Directory _ -> raise Not_found
 								with Not_found ->
 									packages := f :: !packages
 						else

+ 37 - 36
src/generators/gencpp.ml

@@ -1325,36 +1325,31 @@ let strip_file ctx file = (match Common.defined ctx Common.Define.AbsolutePath w
 ;;
 
 let hx_stack_push ctx output clazz func_name pos gc_stack =
-   let has_stackframe = ref false in
    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
-
-         let lineName  = (string_of_int (Lexer.get_error_line pos) ) in
-         incr ctx.ctx_file_id;
-         let classId = hash64 (clazz ^ "." ^ stripped_file) in
-         let varName = "_hx_pos_" ^ classId ^ "_" ^ lineName ^ "_" ^func_name in
-         let decl = ( varName ^ ",\"" ^ clazz ^ "\",\"" ^ func_name ^ "\"," ^ hash_class_func ^ ",\"" ^
-                 full_name ^ "\",\"" ^ esc_file ^ "\"," ^ lineName ^  "," ^ hash_file ) in
-         if ctx.ctx_is_header then
-            ctx.ctx_writer#write_h_unique ("HX_DECLARE_STACK_FRAME" ^ "(" ^ varName ^ ")\n")
-         else
-            ctx.ctx_writer#write_h_unique ("HX_DEFINE_STACK_FRAME" ^ "(" ^ decl ^ ")\n");
-         output ( (if gc_stack then "HX_GC_STACKFRAME" else "HX_STACKFRAME") ^ "(&" ^ varName ^ ")\n");
-         has_stackframe := true;
-      end
-   end;
-   if gc_stack && not !has_stackframe then
-      output ("HX_JUST_GC_STACKFRAME\n");
+      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
+
+      let lineName  = (string_of_int (Lexer.get_error_line pos) ) in
+      incr ctx.ctx_file_id;
+      let classId = hash64 (clazz ^ "." ^ stripped_file) in
+      let varName = "_hx_pos_" ^ classId ^ "_" ^ lineName ^ "_" ^func_name in
+      let decl = ( varName ^ ",\"" ^ clazz ^ "\",\"" ^ func_name ^ "\"," ^ hash_class_func ^ ",\"" ^
+              full_name ^ "\",\"" ^ esc_file ^ "\"," ^ lineName ^  "," ^ hash_file ) in
+      if ctx.ctx_is_header then
+         ctx.ctx_writer#write_h_unique ("HX_DECLARE_STACK_FRAME" ^ "(" ^ varName ^ ")\n")
+      else
+         ctx.ctx_writer#write_h_unique ("HX_DEFINE_STACK_FRAME" ^ "(" ^ decl ^ ")\n");
+      output ( (if gc_stack then "HX_GC_STACKFRAME" else "HX_STACKFRAME") ^ "(&" ^ varName ^ ")\n");
+   end else if gc_stack then
+      output ("HX_JUST_GC_STACKFRAME\n")
 ;;
 
 
@@ -2972,7 +2967,8 @@ let retype_expression ctx request_type function_args expression_tree forInjectio
                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
+               | TCppPointer(_,_)
+               | TCppInst(_) -> CppCast(baseCpp,return_type), return_type
                | TCppString -> CppCastScalar(baseCpp,"::String"), return_type
                | TCppCode(t) when baseStr <> (tcpp_to_string t)  ->
                      CppCast(baseCpp, t),  t
@@ -3924,8 +3920,8 @@ let gen_cpp_ast_expression_tree ctx class_name func_name function_args injection
 
       let prologue = function gc_stack ->
           cpp_gen_default_values ctx closure.close_args "__o_";
+          hx_stack_push ctx output_i class_name func_name closure.close_expr.cpppos gc_stack;
           if (ctx.ctx_debug_level>0) then begin
-             hx_stack_push ctx output_i class_name func_name closure.close_expr.cpppos gc_stack;
              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") )
@@ -3955,11 +3951,11 @@ let gen_cpp_function_body ctx clazz is_static func_name function_def head_code t
    let dot_name = join_class_path clazz.cl_path "." in
    if no_debug then ctx.ctx_debug_level <- 0;
    let prologue = function gc_stack ->
-      let spacer = "            \t" in
+      let spacer = if no_debug then "\t" else "            \t" in
       let output_i = fun s -> output (spacer ^ s) in
       ctx_default_values ctx function_def.tf_args "__o_";
+      hx_stack_push ctx output_i dot_name func_name function_def.tf_expr.epos gc_stack;
       if ctx.ctx_debug_level >0 then begin
-         hx_stack_push ctx output_i dot_name func_name function_def.tf_expr.epos gc_stack;
          if (not is_static)
             then output_i ("HX_STACK_THIS(" ^ (if ctx.ctx_real_this_ptr then "this" else "__this") ^")\n");
          List.iter (fun (v,_) -> if not (cpp_no_debug_synbol ctx v) then
@@ -3977,11 +3973,9 @@ let gen_cpp_function_body ctx clazz is_static func_name function_def head_code t
 let gen_cpp_init ctx dot_name func_name var_name expr =
    let output = ctx.ctx_output in
    let prologue = function gc_stack ->
-      if ctx.ctx_debug_level >0 then begin
-      let spacer = "            \t" in
+      let spacer = if ctx.ctx_debug_level > 0 then "            \t" else "\t" in
       let output_i = fun s -> output (spacer ^ s) in
          hx_stack_push ctx output_i dot_name func_name expr.epos gc_stack;
-      end
    in
    let injection = mk_injection prologue var_name "" in
    gen_cpp_ast_expression_tree ctx dot_name func_name [] injection (mk_block expr);
@@ -5082,8 +5076,11 @@ let access_str a = match a with
    | AccRequire(_,_) -> "AccRequire" ;;
 
 
-let script_type t optional = if optional then "Object" else
+let script_type t optional = if optional then begin
    match type_string t with
+   | "::String" -> "String"
+   | _ -> "Object"
+   end else match type_string t with
    | "bool" -> "Int"
    | "int" -> "Int"
    | "Float" -> "Float"
@@ -6534,6 +6531,7 @@ type cppia_op =
 	| IaToInterfaceArray
 	| IaFun
 	| IaCast
+	| IaTCast
 	| IaBlock
 	| IaBreak
 	| IaContinue
@@ -6738,6 +6736,7 @@ let cppia_op_info = function
 	| IaBinOp OpAssignOp OpLt
 	| IaBinOp OpAssignOp OpAssignOp _
 	| IaBinOp OpAssignOp OpArrow -> assert false
+	| IaTCast -> ("TCAST", 221)
 ;;
 
 class script_writer ctx filename asciiOut =
@@ -7217,8 +7216,10 @@ class script_writer ctx filename asciiOut =
             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;
+   | TCast (cast,Some (TClassDecl t)) ->
+         this#write ((this#op IaTCast) ^ (this#typeText (TInst(t,[])) ) ^ "\n");
+         this#gen_expression cast;
+   | TCast (cast,_) -> this#checkCast expression.etype cast true true;
    | TParenthesis _ -> abort "Unexpected parens" expression.epos
    | TMeta(_,_) -> abort "Unexpected meta" expression.epos
    );

+ 2 - 7
src/generators/genhl.ml

@@ -2820,12 +2820,7 @@ and make_fun ?gen_content ctx name fidx f cthis cparent =
 				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));
+				op ctx (OMov (r, make_string ctx s f.tf_expr.epos))
 			);
 			j();
 		);
@@ -3530,7 +3525,7 @@ let add_types ctx types =
 						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 _ })}]) } } } ->
+							| Some { eexpr = TFunction { tf_expr = { eexpr = TBlock ([] | [{ eexpr = TReturn (Some { eexpr = TConst _ })}]) } } } | None ->
 								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;
 							| _ -> ())

+ 3 - 3
src/generators/genjs.ml

@@ -650,7 +650,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' : " (Ast.s_escape f);
+			| TMeta((Meta.QuotedField,_,_),e) -> print ctx "\"%s\" : " (Ast.s_escape f);
 			| _ -> print ctx "%s : " (anon_field f));
 			gen_value ctx e
 		) fields;
@@ -941,9 +941,9 @@ and gen_value ctx e =
 			| _ -> cond
 		) in
 		gen_value ctx cond;
-		spr ctx "?";
+		spr ctx " ? ";
 		gen_value ctx e;
-		spr ctx ":";
+		spr ctx " : ";
 		(match eo with
 		| None -> spr ctx "null"
 		| Some e -> gen_value ctx e);

+ 31 - 4
src/generators/genphp.ml

@@ -71,6 +71,21 @@ type context = {
 
 let follow = Abstract.follow_with_abstracts
 
+(**
+	Check if specified expression is of `Float` type
+*)
+let is_float expr = match follow expr.etype with TAbstract ({ a_path = ([], "Float") }, _) -> true | _ -> false
+
+(**
+	If `expr` is a TCast or TMeta, then returns underlying expression (recursively bypassing nested casts).
+	Otherwise returns `expr` as is.
+*)
+let rec reveal_expr expr =
+	match expr.eexpr with
+		| TCast (e, _) -> reveal_expr e
+		| TMeta (_, e) -> reveal_expr e
+		| _ -> expr
+
 let join_class_path path separator =
 	let result = match fst path, snd path with
 	| [], s -> s
@@ -536,7 +551,7 @@ and gen_call ctx e el =
 			spr ctx "]";
 			genargs t)
 	in
-	match e.eexpr , el with
+	match (reveal_expr e).eexpr , el with
 	| TConst TSuper , params ->
 		(match ctx.curclass.cl_super with
 		| None -> assert false
@@ -790,10 +805,16 @@ and gen_member_access ctx isvar e s =
 		| EnumStatics _ ->
 			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 (sep, no_dollar) = if Meta.has Meta.PhpGlobal sta.cl_meta then
+					("", false)
+				else
+					match e.eexpr with
+						| TField _ -> ("->", true)
+						| _ -> ("::", false)
+			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)
+			print ctx "%s%s%s" sep (if isvar && not isconst && not no_dollar 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)
@@ -833,6 +854,9 @@ and gen_field_access ctx isvar e s =
 		gen_value ctx e;
 		spr ctx ")";
 		gen_member_access ctx isvar e s
+	| TConst TNull ->
+		spr ctx "_hx_deref(null)";
+		gen_member_access ctx isvar e s
 	| _ ->
 		gen_expr ctx e;
 		gen_member_access ctx isvar e s
@@ -1203,6 +1227,7 @@ and gen_expr ctx e =
 			end else if
 				   ((se1 = "Int" || se1 = "Null<Int>") && (se2 = "Int" || se2 = "Null<Int>"))
 				|| ((se1 = "Float" || se1 = "Null<Float>") && (se2 = "Float" || se2 = "Null<Float>"))
+				&& not (is_float e1 && is_float e2)
 			then begin
 				gen_field_op ctx e1;
 				spr ctx s_phop;
@@ -1775,7 +1800,9 @@ and gen_value ctx e =
 		gen_value ctx e1
 	| TBlock [] ->
 		()
-	| TCast (e, _)
+	| TCast (_, Some _) ->
+		gen_expr ctx e
+	| TCast (e, None)
 	| TBlock [e] ->
 		gen_value ctx e
 	| TIf (cond,e,eelse) when (cangen_ternary e eelse) ->

+ 3330 - 0
src/generators/genphp7.ml

@@ -0,0 +1,3330 @@
+(**
+	Compatible with PHP 7+
+*)
+
+open Ast
+open Type
+open Common
+open Meta
+open Globals
+
+let debug = ref false
+
+(**
+	Escape string for constant strings generation.
+	Copy-pasted from genphp.
+*)
+let escape_bin s =
+	let b = Buffer.create 0 in
+	for i = 0 to String.length s - 1 do
+		match Char.code (String.unsafe_get s i) with
+		| c when c = Char.code('\\') || c = Char.code('"') || c = Char.code('$') ->
+			Buffer.add_string b "\\";
+			Buffer.add_char b (Char.chr c)
+		| c when c < 32 ->
+			Buffer.add_string b (Printf.sprintf "\\x%.2X" c)
+		| c ->
+			Buffer.add_char b (Char.chr c)
+	done;
+	Buffer.contents b
+
+(**
+	Write resources passed to compiler via `-resource` flag
+	Copy-pasted from genphp
+*)
+let write_resource dir name data =
+	let rdir = dir ^ "/res" in
+	if not (Sys.file_exists dir) then Unix.mkdir dir 0o755;
+	if not (Sys.file_exists rdir) then Unix.mkdir rdir 0o755;
+	let name = Codegen.escape_res_name name false in
+	let ch = open_out_bin (rdir ^ "/" ^ name) in
+	output_string ch data;
+	close_out ch
+
+(**
+	Get list of keys in Hashtbl
+*)
+let hashtbl_keys tbl = Hashtbl.fold (fun key _ lst -> key :: lst) tbl []
+
+(**
+	@return List of items in `list1` which `list2` does not contain
+*)
+let diff_lists list1 list2 = List.filter (fun x -> not (List.mem x list2)) list1
+
+(**
+	Type path for native PHP Exception class
+*)
+let native_exception_path = ([], "Throwable")
+(**
+	Type path for Haxe exceptions wrapper
+*)
+let hxexception_type_path = (["php"; "_Boot"], "HxException")
+(**
+	Type path of `php.Boot`
+*)
+let boot_type_path = (["php"], "Boot")
+(**
+	Type path of the base class for all enums: `php.Boot.HxEnum`
+*)
+let hxenum_type_path = (["php"; "_Boot"], "HxEnum")
+(**
+	Type path of the implementation class for `Class<Dynamic>`
+*)
+let hxclass_type_path = (["php"; "_Boot"], "HxClass")
+(**
+	Type path of the implementation class for `String`
+*)
+let hxstring_type_path = (["php"; "_Boot"], "HxString")
+(**
+	Type path of the special implementation class for `String`
+	which is used when Dynamic value is suspected to be a string
+*)
+let hxdynamicstr_type_path = (["php"; "_Boot"], "HxDynamicStr")
+(**
+	Type path of the implementation class for anonymous objects
+*)
+let hxanon_type_path = (["php"; "_Boot"], "HxAnon")
+(**
+	Type path of the implementation class for closures
+*)
+let hxclosure_type_path = (["php"; "_Boot"], "HxClosure")
+(**
+	Type path for special PHP extern class to support specific language expressions
+*)
+let syntax_type_path = (["php"], "Syntax")
+(**
+	Special abstract which enables passing function arguments and return value by reference
+*)
+let ref_type_path = (["php"], "Ref")
+(**
+	Type path of the implementation class for `Array<T>`
+*)
+let array_type_path = ([], "Array")
+(**
+	Type path of the implementation class for `Array<T>`
+*)
+let native_array_type_path = (["php"], "NativeArray")
+(**
+	Type path of the `Void`
+*)
+let void_type_path = ([], "Void")
+(**
+	Type path of the `Bool`
+*)
+let bool_type_path = ([], "Bool")
+
+(**
+	Stub to use when you need a `Ast.pos` instance, but don't have one
+*)
+let dummy_pos = { pfile = ""; pmin = 0; pmax = 0 }
+
+(**
+	Check if specified string is a reserved word in PHP
+*)
+let is_keyword str =
+	match String.lowercase str with
+		| "__halt_compiler" | "abstract" | "and" | "array" | "as" | "break" | "callable" | "case" | "catch" | "class"
+		| "clone" | "const" | "continue" | "declare" | "default" | "die" | "do" | "echo" | "else" | "elseif" | "empty"
+		| "enddeclare" | "endfor" | "endforeach" | "endif" | "endswitch" | "endwhile" | "eval" | "exit" | "extends"
+		| "final" | "finally" | "for" | "foreach" | "function" | "global" | "goto" | "if" | "implements" | "include"
+		| "include_once" | "instanceof" | "insteadof" | "interface" | "isset" | "list" | "namespace" | "new" | "or"
+		| "print" | "private" | "protected" | "public" | "require" | "require_once" | "return" | "static" | "switch"
+		| "throw" | "trait" | "try" | "unset" | "use" | "var" | "while" | "xor" | "yield" | "__class__" | "__dir__"
+		| "__file__" | "__function__" | "__line__" | "__method__" | "__trait__" | "__namespace__" | "int" | "float"
+		| "bool" | "string" | "true" | "false" | "null" | "parent" | "void" | "iterable"
+			-> true
+		| _ -> false
+
+(**
+	Check if specified type is Void
+*)
+let is_void_type t = match follow t with TAbstract ({ a_path = void_type_path }, _) -> true | _ -> false
+
+(**
+	Check if specified type is Bool
+*)
+let is_bool_type t = match follow t with TAbstract ({ a_path = bool_type_path }, _) -> true | _ -> false
+
+(**
+	Check if specified type is php.NativeArray
+*)
+let is_native_array_type t = match follow t with TAbstract ({ a_path = native_array_type_path }, _) -> true | _ -> false
+
+(**
+	If `name` is not a reserved word in PHP then `name` is returned as-is.
+	Otherwise this method returns another string, which can be used instead of `name`
+*)
+let get_real_name name = if is_keyword name then name ^ "_hx" else name
+
+(**
+	If `path` contains some reserved in PHP words, they will be replaced with allowed words.
+*)
+let get_real_path path = List.map get_real_name path
+
+(**
+	Resolve real type (bypass abstracts and typedefs)
+*)
+let rec follow = Abstract.follow_with_abstracts
+
+let prefix = ref None
+(**
+	Returns value of `--php-prefix` compiler flag
+*)
+let get_php_prefix ctx =
+	match !prefix with
+		| Some prefix -> prefix
+		| None ->
+			let lst =
+				match ctx.php_prefix with
+					| None -> []
+					| Some str ->
+						if String.length str = 0 then
+							[]
+						else
+							Str.split (Str.regexp "\\.") str
+			in
+			prefix := Some lst;
+			lst
+
+(**
+	Adds packages specified by `--php-prefix` to `type_path`.
+	E.g. if `--php-prefix some.sub` and `type_path` is `(["pack"], "MyClass")`, then this function
+	will return `(["some", "sub", "pack"], "MyClass")`
+*)
+let add_php_prefix ctx type_path =
+	match type_path with
+		| (pack, name) -> ((get_php_prefix ctx) @ pack, name)
+
+(**
+	If `expr` is a TCast or TMeta, then returns underlying expression (recursively bypassing nested casts).
+	Otherwise returns `expr` as is.
+*)
+let rec reveal_expr expr =
+	match expr.eexpr with
+		| TCast (e, _) -> reveal_expr e
+		| TMeta (_, e) -> reveal_expr e
+		| _ -> expr
+
+(**
+	@return Error message with position information
+*)
+let error_message pos message = (Lexer.get_error_pos (Printf.sprintf "%s:%d:") pos) ^ ": " ^ message
+
+(**
+	Terminates compiler process and prints user-friendly instructions about filing an issue in compiler repo.
+*)
+let fail hxpos mlpos =
+	match mlpos with
+		| (file, line, _) ->
+			Printf.printf "%s\n" (error_message hxpos "Unexpected expression. Please submit an issue with expression example and following information:");
+			Printf.printf "%s:%d\n" file line;
+			assert false
+
+(**
+	Print compilation error message and abort compilation process.
+*)
+let error_and_exit pos message =
+	Printf.printf "%s" (error_message pos message);
+	exit 1
+
+(**
+	Check if `target` is a `Dynamic` type
+*)
+let rec is_dynamic_type (target:Type.t) = match follow target with TDynamic _ -> true | _ -> false
+
+(**
+	Check if `target` is `php.Ref`
+*)
+let is_ref (target:Type.t) = match target with TAbstract ({ a_path = type_path }, _) -> type_path = ref_type_path | _ -> false
+
+(**
+	Check if `field` is a `dynamic function`
+*)
+let rec is_dynamic_method (field:tclass_field) =
+	match field.cf_kind with
+		| Method MethDynamic -> true
+		| _ -> false
+
+(**
+	Check if specified expression is of `Dynamic` type
+*)
+let is_dynamic expr = is_dynamic_type expr.etype
+
+(**
+	Check if specified expression is of `Int` type
+*)
+let is_int expr = match follow expr.etype with TAbstract ({ a_path = ([], "Int") }, _) -> true | _ -> false
+
+(**
+	Check if specified expression is of `Float` type
+*)
+let is_float expr = match follow expr.etype with TAbstract ({ a_path = ([], "Float") }, _) -> true | _ -> false
+
+(**
+	Check if specified type is String
+*)
+let is_string_type t = match follow t with TInst ({ cl_path = ([], "String") }, _) -> true | _ -> false
+
+(**
+	Check if specified expression is of String type
+*)
+let is_string expr = is_string_type expr.etype
+
+(**
+	Check if `expr` is an access to a method of special `php.PHP` class
+*)
+let is_lang_extern expr =
+	match expr.eexpr with
+		| TField ({ eexpr = TTypeExpr (TClassDecl { cl_path = path }) }, _) when path = syntax_type_path -> true
+		| _ -> false
+
+(**
+	Check if specified type is actually a generic parameter
+*)
+let is_generic_parameter (target:Type.t) =
+	match follow target with
+		| TInst ({ cl_kind = KTypeParameter _ }, _) -> true
+		| _ -> false
+
+(**
+	Check if `target` type cannot be clarified on compilation
+*)
+let is_unknown_type (target:Type.t) = is_dynamic_type target || is_generic_parameter target
+
+(**
+	@return `Type.t` instance for `Void`
+*)
+let void = ref None
+let get_void ctx : Type.t =
+	match !void with
+		| Some value -> value
+		| None ->
+			let find com_type =
+				match com_type with
+					| TAbstractDecl ({ a_path = ([], "Void") } as abstr) -> void := Some (TAbstract (abstr, []));
+					| _ -> ()
+			in
+			List.iter find ctx.types;
+			match !void with
+				| Some value -> value
+				| None -> fail dummy_pos (try assert false with Assert_failure mlpos -> mlpos)
+
+(**
+	@return `tclass` instance for `php.Boot`
+*)
+let boot = ref None
+let get_boot ctx : tclass =
+	match !boot with
+		| Some value -> value
+		| None ->
+			let find com_type =
+				match com_type with
+					| TClassDecl ({ cl_path = path } as cls) when path = boot_type_path -> boot := Some cls;
+					| _ -> ()
+			in
+			List.iter find ctx.types;
+			match !boot with
+				| Some value -> value
+				| None -> fail dummy_pos (try assert false with Assert_failure mlpos -> mlpos)
+
+(**
+	@return `expr` wrapped in parenthesis
+*)
+let parenthesis expr = {eexpr = TParenthesis expr; etype = expr.etype; epos = expr.epos}
+
+(**
+	Check if `current` binary should be surrounded with parenthesis
+*)
+let need_parenthesis_for_binop current parent =
+	if current = parent then
+		false
+	else
+		match (current, parent) with
+			| (_, OpAssign) -> false
+			| (_, OpAssignOp _) -> false
+			| (OpAdd, OpSub) -> false
+			| (OpSub, OpAdd) -> false
+			| (OpMult, OpDiv) -> false
+			| (OpDiv, OpMult) -> false
+			| (OpMult, OpAdd) -> false
+			| (OpMult, OpSub) -> false
+			| (OpDiv, OpAdd) -> false
+			| (OpDiv, OpSub) -> false
+			| _ -> true
+
+(**
+	Check if specified expression may require dereferencing if used as "temporary expression"
+*)
+let needs_dereferencing expr =
+	let rec is_create target_expr =
+		match target_expr.eexpr with
+			| TParenthesis e -> is_create e
+			| TCast (e, _) -> is_create e
+			| TNew _ -> true
+			| TArrayDecl _ -> true
+			| TObjectDecl _ -> true
+			| TConst TNull -> true
+			| _ -> false
+	in
+	match expr.eexpr with
+		| TField (target_expr, _) -> is_create target_expr
+		| TArray (target_expr, _) -> is_create target_expr
+		| _ -> false
+
+(**
+	@return (arguments_list, return_type)
+*)
+let get_function_signature (field:tclass_field) : (string * bool * Type.t) list * Type.t =
+	match follow field.cf_type with
+		| TFun (args, return_type) -> (args, return_type)
+		| _ -> fail field.cf_pos (try assert false with Assert_failure mlpos -> mlpos)
+
+(**
+	Check if `target` is 100% guaranteed to be a scalar type in PHP.
+	Inversion of `is_sure_scalar` does not guarantee `target` is not scalar.
+*)
+let is_sure_scalar (target:Type.t) =
+	match follow target with
+		| TInst ({ cl_path = ([], "String") }, _) -> true
+		| TAbstract (abstr, _) ->
+			(match abstr.a_path with
+				| ([],"Int") -> true
+				| ([],"Float") -> true
+				| ([],"Bool") -> true
+				| _ -> false
+			)
+		| _ -> false
+
+(**
+	Indicates if `expr` is guaranteed to be an access to a `var` field.
+*)
+let is_sure_var_field_access expr =
+	match (reveal_expr expr).eexpr with
+		| TField (_, FStatic (_, { cf_kind = Var _ })) -> true
+		| TField (_, FInstance (_, _, { cf_kind = Var _ })) -> true
+		(* | TField (_, FAnon { cf_kind = Var _ }) -> true *) (* Sometimes we get anon access to non-anonymous objects *)
+		| _ -> false
+
+(**
+	Check if specified unary operation modifies value in place
+*)
+let is_modifying_unop op =
+	match op with
+		| Increment
+		| Decrement -> true
+		| _ -> false
+
+(**
+	Indicates whether `expr` is a field access which should be generated as global namespace function
+*)
+let is_php_global expr =
+	match expr.eexpr with
+		| TField (_, FStatic ({ cl_extern = true; cl_meta = meta }, _)) -> Meta.has Meta.PhpGlobal meta
+		| _ -> false
+
+(**
+	Indicates whether `expr` is a field access which should be generated as class constant access
+*)
+let is_php_class_const expr =
+	match expr.eexpr with
+		| TField (_, FStatic ({ cl_extern = true }, { cf_meta = meta; cf_kind = Var _ })) ->
+			Meta.has Meta.PhpClassConst meta
+		| _ -> false
+
+(**
+	Check if specified enum constructor has arguments
+*)
+let is_enum_constructor_with_args (constructor:tenum_field) =
+	match follow constructor.ef_type with
+		| TFun _ -> true
+		| _ -> false
+
+(**
+	Check if `target` is 100% guaranteed to be or extend an extern class.
+	Inversion of `sure_extends_extern` does not guarantee `target` does not extend an extern class.
+*)
+let rec sure_extends_extern (target:Type.t) =
+	match follow target with
+		| TInst ({ cl_path = ([], "String") }, _) -> false
+		| TInst ({ cl_extern = true }, _) -> true
+		| TInst ({ cl_super = Some (tsuper, params) }, _) -> sure_extends_extern (TInst (tsuper,params))
+		| _ -> false
+
+(**
+	@return `opt` value or `default` if `opt` is None
+*)
+let get_option_value (opt:'a option) default =
+	match opt with
+		| None -> default
+		| Some value -> value
+
+(**
+	@param path Something like [ "/some/path/first_dir_to_create"; "nested_level1"; "nested_level2" ]
+	@return String representation of created path (E.g. "/some/path/first_dir_to_create/nested_level1/nested_level2")
+*)
+let create_dir_recursive (path:string list) =
+	let rec create dir nested_dirs =
+		if not (Sys.file_exists dir) then (Unix.mkdir dir 0o755);
+		match nested_dirs with
+			| [] -> ();
+			| next :: rest -> create (dir ^ "/" ^ next) rest
+	in
+	match path with
+		| [] -> "";
+		| root :: rest ->
+			create root rest;
+			(String.concat "/" path)
+
+(**
+	@return String representation of specified type path. E.g. returns "\example\Test" for (["example"], "Test")
+*)
+let get_full_type_name ?escape ?omit_first_slash (type_path:path) =
+	let name =
+		match type_path with
+			| (module_path, type_name) ->
+				let parts =
+					match omit_first_slash with
+						| Some true -> get_real_path module_path
+						| _ -> "" :: get_real_path module_path
+				in
+				(String.concat "\\" parts) ^ "\\" ^ type_name
+	in
+	match escape with
+		| Some true -> String.escaped name
+		| _ -> name
+
+(**
+	Check if `target` is or implements native PHP `Throwable` interface
+*)
+let rec is_native_exception (target:Type.t) =
+	match follow target with
+		| TInst ({ cl_path = path }, _) when path = native_exception_path -> true
+		| TInst ({ cl_super = parent ; cl_implements = interfaces ; cl_path = path }, _) ->
+			let (parent, params) =
+				match parent with
+					| Some (parent, params) -> (Some parent, params)
+					| None -> (None, [])
+			in
+			let found = ref false in
+			List.iter
+				(fun (cls, params) ->
+					if not !found then
+						found := is_native_exception (TInst (cls, params))
+				)
+				interfaces;
+			if !found then
+				true
+			else
+				(match parent with
+					| Some parent -> is_native_exception (TInst (parent, params))
+					| None -> false
+				)
+		| _ -> false
+
+(**
+	@return Short type name. E.g. returns "Test" for (["example"], "Test")
+*)
+let get_type_name (type_path:path) = match type_path with (_, type_name) -> type_name
+
+(**
+	@return E.g. returns ["example"] for (["example"], "Test")
+*)
+let get_module_path (type_path:path) = match type_path with (module_path, _) -> module_path
+
+(**
+	@return PHP visibility keyword.
+*)
+let get_visibility (meta:metadata) = if Meta.has Meta.Protected meta then "protected" else "public"
+
+(**
+	Writes arguments list to output buffer
+*)
+let rec write_args buffer arg_writer (args:'a) =
+	match args with
+		| [] -> ()
+		| [arg] -> arg_writer arg
+		| arg :: rest ->
+			arg_writer arg;
+			Buffer.add_string buffer ", ";
+			write_args buffer arg_writer rest
+
+(**
+	Check if specified field is a var with non-constant expression
+*)
+let is_var_with_nonconstant_expr (field:tclass_field) =
+	match field.cf_kind with
+		| Var _ ->
+			(match field.cf_expr with
+				| None -> false
+				| Some ({eexpr = TConst _ }) -> false
+				| Some _ -> true
+			)
+		| Method _ -> false
+
+(**
+	@return New TBlock expression which is composed of setting default values for optional arguments and function body.
+*)
+let inject_defaults (ctx:Common.context) (func:tfunc) =
+	let rec inject args body_exprs =
+		match args with
+			| [] -> body_exprs
+			| (_, None) :: rest -> inject rest body_exprs
+			| (_, Some TNull) :: rest -> inject rest body_exprs
+			| (var, Some const) :: rest ->
+				let expr = Codegen.set_default ctx var const func.tf_expr.epos in
+			 	expr :: (inject rest body_exprs)
+	in
+	let exprs =
+		match func.tf_expr.eexpr with
+			| TBlock exprs -> inject func.tf_args exprs
+			| _ -> inject func.tf_args [ func.tf_expr ]
+	in
+	{
+		eexpr = TBlock exprs;
+		etype = follow func.tf_expr.etype;
+		epos  = func.tf_expr.epos;
+	}
+
+(**
+	Check if `expr` is a constant string
+*)
+let is_constant_string expr =
+	match expr.eexpr with
+		| TConst (TString _) -> true
+		| _ -> false
+
+(**
+	Check if `expr` is a constant null
+*)
+let is_constant_null expr =
+	match expr.eexpr with
+		| TConst TNull -> true
+		| _ -> false
+
+(**
+	Check if `expr` is a constant
+*)
+let is_constant expr =
+	match expr.eexpr with
+		| TConst _ -> true
+		| _ -> false
+
+(**
+	Check if `expr` is a concatenation
+*)
+let is_concatenation expr =
+	match expr.eexpr with
+		| TBinop (OpAdd, expr1, expr2) -> (is_string expr1) || (is_string expr2)
+		| _ -> false
+
+(**
+	Check if provided expression is a binary operation
+*)
+let is_binop expr = match expr.eexpr with TBinop _ -> true | _ -> false
+
+(**
+	Check if provided expression is an assignment binary operation
+*)
+let is_binop_assign expr =
+	match expr.eexpr with
+		| TBinop (operation, _, _) ->
+			(match operation with
+				| OpAssign | OpAssignOp _ -> true
+				| _ -> false
+			)
+		| _ -> false
+
+(**
+	Check if specified expression is field access or array access
+*)
+let is_access expr =
+	match expr.eexpr with
+		| TField _ | TArray _ -> true
+		| _ -> false
+
+(**
+	Indicates if `expr` is actually a call to Haxe->PHP magic function
+	@see http://old.haxe.org/doc/advanced/magic#php-magic
+*)
+let is_magic expr =
+	match expr.eexpr with
+	| TCall ({ eexpr = TLocal { v_name = name }}, _) ->
+		(match name with
+			| "__php__" -> true
+			| "__call__" -> true
+			| "__physeq__" -> true
+			| "__var__" -> true
+			| _ -> false
+		)
+	| _ -> false
+
+(**
+	Check if `expr1` and `expr2` can be reliably checked for equality only with `Boot.equal()`
+*)
+let need_boot_equal expr1 expr2 =
+	if is_constant_null expr1 || is_constant_null expr2 then
+		false
+	else
+		(is_int expr1 && (is_float expr2 || is_unknown_type expr2.etype))
+		|| (is_float expr1 && (is_float expr2 || is_int expr2 || is_unknown_type expr2.etype))
+		|| (is_unknown_type expr1.etype && (is_int expr2 || is_float expr2))
+		|| (is_unknown_type expr1.etype && is_unknown_type expr2.etype)
+
+(**
+	Adds `return` expression to block if it does not have one already
+*)
+let ensure_return_in_block block_expr =
+	match block_expr.eexpr with
+		| TBlock [] -> fail block_expr.epos (try assert false with Assert_failure mlpos -> mlpos)
+		| TBlock exprs ->
+			let reversed = List.rev exprs in
+			let last_expr = List.hd reversed in
+			let return_expr = { last_expr with eexpr = TReturn (Some last_expr) } in
+			let reversed = return_expr::(List.tl reversed) in
+			{ block_expr with eexpr = TBlock (List.rev reversed) }
+		| _ -> fail block_expr.epos (try assert false with Assert_failure mlpos -> mlpos)
+
+(**
+	Check if specified type has rtti meta
+*)
+let has_rtti_meta ctx mtype =
+	match Codegen.build_metadata ctx mtype with
+		| None -> false
+		| Some _ -> true
+
+(**
+	Check if this var accesses and meta combination should generate a variable
+*)
+let is_real_var field =
+	if Meta.has IsVar field.cf_meta then
+		true
+	else
+		match field.cf_kind with
+			| Var { v_read = read; v_write = write } -> read = AccNormal || write = AccNormal
+			| _ -> false
+
+(**
+	Check if user-defined field has the same name as one of php magic methods, but with not compatible signature.
+*)
+let field_needs_rename field =
+	match field.cf_kind with
+		| Var _ -> false
+		| Method _ ->
+			match field.cf_name with
+				| "__construct" | "__destruct" | "__call" | "__callStatic" | "__get" | "__set" | "__isset"
+				| "__unset" | "__sleep" | "__wakeup" | "__toString" | "__invoke" | "__set_state" | "__clone"
+				| "__debugInfo" -> not (Meta.has Meta.PhpMagic field.cf_meta)
+				| _ -> false
+(**
+	Get valid `field` name.
+*)
+let field_name field =
+	if field_needs_rename field then
+		"__hx__renamed" ^ field.cf_name
+	else
+		field.cf_name
+
+(**
+	PHP DocBlock types
+*)
+type doc_type =
+	| DocVar of string * (string option) (* (type name, description) *)
+	| DocMethod of (string * bool * t) list * t * (string option) (* (arguments, return type, description) *)
+| DocClass of string option
+
+(**
+	Common interface for module_type instances
+*)
+class virtual type_wrapper (type_path:path) (meta:metadata) (needs_generation:bool) =
+	object (self)
+		(**
+			Indicates if this type should be rendered to corresponding php file
+		*)
+		method needs_generation = needs_generation
+		(**
+			Indicates if class initialization method should be executed upon class loaded
+		*)
+		method virtual needs_initialization : bool
+		(**
+			Returns hx source file name where this type was declared
+		*)
+		method virtual get_source_file : string
+		(**
+			Returns `Type.module_type` instance for this type
+		*)
+		method virtual get_module_type : module_type
+		(**
+			Returns expression of a user-defined static __init__ method
+			@see http://old.haxe.org/doc/advanced/magic#initialization-magic
+		*)
+		method get_magic_init : texpr option = None
+		(**
+			Namespace path. E.g. ["some"; "pack"] for "some.pack.MyType"
+		*)
+		method get_namespace = get_module_path type_path
+		(**
+			Short type name. E.g. `SomeType` for `pack.SomeType`
+		*)
+		method get_name = get_type_name type_path
+		(**
+			Full type path
+		*)
+		method get_type_path = type_path
+	end
+
+(**
+	TClassDecl
+*)
+class class_wrapper (cls) =
+	object (self)
+		inherit type_wrapper cls.cl_path cls.cl_meta (not cls.cl_extern)
+		(**
+			Indicates if class initialization method should be executed upon class loaded
+		*)
+		method needs_initialization =
+			(* Interfaces may need initialization only for RTTI meta data.
+				But that meta is written in `class_wrapper#write_rtti_meta` *)
+			if cls.cl_interface then
+				false
+			else
+				match cls.cl_init with
+					| Some _ -> true
+					| None ->
+						let needs = ref false in
+						PMap.iter
+							(fun _ field ->
+								(* Check static vars with non-constant expressions *)
+								if not !needs then needs := is_var_with_nonconstant_expr field;
+								(* Check static dynamic functions *)
+								if not !needs then needs := is_dynamic_method field
+							)
+							cls.cl_statics;
+						!needs
+		(**
+			Returns expression of a user-defined static __init__ method
+			@see http://old.haxe.org/doc/advanced/magic#initialization-magic
+		*)
+		method get_magic_init = cls.cl_init
+		(**
+			Returns hx source file name where this type was declared
+		*)
+		method get_source_file = cls.cl_pos.pfile
+		(**
+			Returns `Type.module_type` instance for this type
+		*)
+		method get_module_type = TClassDecl cls
+	end
+
+(**
+	TEnumDecl
+*)
+class enum_wrapper (enm) =
+	object (self)
+		inherit type_wrapper enm.e_path enm.e_meta (not enm.e_extern)
+		(**
+			Indicates if class initialization method should be executed upon class loaded
+		*)
+		method needs_initialization = false
+		(**
+			Returns hx source file name where this type was declared
+		*)
+		method get_source_file = enm.e_pos.pfile
+		(**
+			Returns `Type.module_type` instance for this type
+		*)
+		method get_module_type = TEnumDecl enm
+	end
+
+(**
+	TTypeDecl
+*)
+class typedef_wrapper (tdef) =
+	object (self)
+		inherit type_wrapper tdef.t_path tdef.t_meta false
+		(**
+			Indicates if class initialization method should be executed upon class loaded
+		*)
+		method needs_initialization = false
+		(**
+			Returns hx source file name where this type was declared
+		*)
+		method get_source_file = tdef.t_pos.pfile
+		(**
+			Returns `Type.module_type` instance for this type
+		*)
+		method get_module_type = TTypeDecl tdef
+	end
+
+(**
+	TAbstractDecl
+*)
+class abstract_wrapper (abstr) =
+	object (self)
+		inherit type_wrapper abstr.a_path abstr.a_meta false
+		(**
+			Indicates if class initialization method should be executed upon class loaded
+		*)
+		method needs_initialization = false
+		(**
+			Returns hx source file name where this type was declared
+		*)
+		method get_source_file = abstr.a_pos.pfile
+		(**
+			Returns `Type.module_type` instance for this type
+		*)
+		method get_module_type = TAbstractDecl abstr
+	end
+
+(**
+	type_wrapper for classes
+*)
+let classes = Hashtbl.create 1000
+let get_class_wrapper cls  : type_wrapper =
+	try
+		let wrapper = Hashtbl.find classes cls in
+		wrapper
+	with
+		| Not_found ->
+			let wrapper = new class_wrapper cls in
+			Hashtbl.add classes cls wrapper;
+			wrapper
+		| e -> raise e
+
+(**
+	type_wrapper for enums
+*)
+let enums = Hashtbl.create 200
+let get_enum_wrapper enm : type_wrapper=
+	try
+		let wrapper = Hashtbl.find enums enm in
+		wrapper
+	with
+		| Not_found ->
+			let wrapper = new enum_wrapper enm in
+			Hashtbl.add enums enm wrapper;
+			wrapper
+		| e -> raise e
+
+(**
+	type_wrapper for typedefs
+*)
+let typedefs = Hashtbl.create 200
+let get_typedef_wrapper typedef : type_wrapper =
+	try
+		let wrapper = Hashtbl.find typedefs typedef in
+		wrapper
+	with
+		| Not_found ->
+			let wrapper = new typedef_wrapper typedef in
+			Hashtbl.add typedefs typedef wrapper;
+			wrapper
+		| e -> raise e
+
+(**
+	type_wrapper for abstracts
+*)
+let abstracts = Hashtbl.create 200
+let get_abstract_wrapper abstr : type_wrapper =
+	try
+		let wrapper = Hashtbl.find abstracts abstr in
+		wrapper
+	with
+		| Not_found ->
+			let wrapper = new abstract_wrapper abstr in
+			Hashtbl.add abstracts abstr wrapper;
+			wrapper
+		| e -> raise e
+
+(**
+	Returns wrapper for module_type.
+	Caches wrappers so that each type will always return the same wrapper instance.
+*)
+let get_wrapper (mtype:module_type) : type_wrapper =
+	match mtype with
+		| TClassDecl cls -> get_class_wrapper cls
+		| TEnumDecl enm -> get_enum_wrapper enm
+		| TTypeDecl typedef -> get_typedef_wrapper typedef
+		| TAbstractDecl abstr -> get_abstract_wrapper abstr
+
+(**
+	Drop cached instances of type_wrapper
+*)
+let clear_wrappers () =
+	Hashtbl.clear classes;
+	Hashtbl.clear enums;
+	Hashtbl.clear typedefs;
+	Hashtbl.clear abstracts
+
+(**
+	Check if specified type name is used in specified namespace
+*)
+let namespaces_types_cache = Hashtbl.create 512
+let type_name_used_in_namespace ctx name namespace =
+	let types = Hashtbl.find_all namespaces_types_cache namespace in
+	match types with
+		| [] ->
+			List.iter
+				(fun ctx_type ->
+					let wrapper = get_wrapper ctx_type in
+					Hashtbl.add namespaces_types_cache wrapper#get_namespace wrapper#get_name
+				)
+				ctx.types;
+			let types = Hashtbl.find_all namespaces_types_cache namespace in
+			List.mem name types
+		| _ ->
+			List.mem name types
+
+(**
+	Class to simplify collecting lists of declared and used local vars.
+	Collected data is needed to generate closures correctly.
+*)
+class local_vars =
+	object (self)
+		(** Hashtbl to collect local var used in current scope *)
+		val mutable used_locals = [Hashtbl.create 100]
+		(** Hashtbl to collect local vars declared in current scope *)
+		val mutable declared_locals = [Hashtbl.create 100]
+		(**
+			Clear collected data
+		*)
+		method clear : unit =
+			used_locals <- [Hashtbl.create 100];
+			declared_locals <- [Hashtbl.create 100]
+		(**
+			This method should be called upone entering deeper scope.
+			E.g. right before processing a closure. Just before closure arguments handling.
+		*)
+		method dive : unit =
+			used_locals <- (Hashtbl.create 100) :: used_locals;
+			declared_locals <- (Hashtbl.create 100) :: declared_locals
+		(**
+			This method should be called right after leaving a scope.
+			@return List of vars names used in finished scope, but declared in higher scopes.
+					And list of vars names declared in finished scope.
+		*)
+		method pop : string list * string list =
+			match used_locals with
+				| [] -> assert false
+				| used :: rest_used ->
+					match declared_locals with
+						| [] -> assert false
+						| declared :: rest_declared ->
+							let higher_vars = diff_lists (hashtbl_keys used) (hashtbl_keys declared)
+							and declared_vars = hashtbl_keys declared in
+							used_locals <- rest_used;
+							declared_locals <- rest_declared;
+							List.iter self#used higher_vars;
+							(higher_vars, declared_vars)
+		(**
+			This method should be called right after leaving a scope.
+			@return List of vars names used in finished scope, but declared in higher scopes
+		*)
+		method pop_used : string list = match self#pop with (higher_vars, _) -> higher_vars
+		(**
+			This method should be called right after leaving a scope.
+			@return List of vars names declared in finished scope
+		*)
+		method pop_declared : string list = match self#pop with (_, declared_vars) -> declared_vars
+		(**
+			Specify local var name declared in current scope
+		*)
+		method declared (name:string) : unit =
+			match declared_locals with
+				| [] -> assert false
+				| current :: _ -> Hashtbl.replace current name name
+		(**
+			Specify local var name used in current scope
+		*)
+		method used (name:string) : unit =
+			match used_locals with
+				| [] -> assert false
+				| current :: _ -> Hashtbl.replace current name name
+	end
+
+(**
+	Base class for type builders
+*)
+class virtual type_builder ctx wrapper =
+	object (self)
+		(** This is required to make wrapper accessible by extending classes *)
+		val wrapper = wrapper
+		(** This is required to make conext accessible by extending classes *)
+		val ctx = ctx
+		(** List of types for "use" section *)
+		val use_table = Hashtbl.create 50
+		(** Output buffer *)
+		val mutable buffer = Buffer.create 1024
+		(** Cache for generated conent *)
+		val mutable contents = ""
+		(** Intendation used for each line written *)
+		val mutable indentation = ""
+		(** Expressions nesting. E.g. "if(callFn(ident))" will be represented as [ident, callFn, if] *)
+		val mutable expr_hierarchy : texpr list = []
+		(** Object to collect local vars declarations and usage as we iterate through methods' expressions *)
+		val vars = new local_vars
+		(**
+			Get PHP namespace path
+		*)
+		method get_namespace =
+			match get_php_prefix ctx with
+				| [] -> get_real_path wrapper#get_namespace
+				| prefix -> get_real_path (prefix @ wrapper#get_namespace)
+		(**
+			Get type name
+		*)
+		method get_name : string = get_real_name wrapper#get_name
+		(**
+			Get full type path
+		*)
+		method get_type_path : path =
+			match wrapper#get_type_path with
+				| (path, name) -> (get_real_path path, get_real_name name)
+		(**
+			Returns hx source file name where this type was declared
+		*)
+		method get_source_file : string = wrapper#get_source_file
+		(**
+			Writes type declaration line to output buffer.
+			E.g. "class SomeClass extends Another implements IFace"
+		*)
+		method virtual private write_declaration : unit
+		(**
+			Writes type body to output buffer.
+			E.g. for "class SomeClass { <BODY> }" writes <BODY> part.
+		*)
+		method virtual private write_body : unit
+		(**
+			Writes expressions for `__hx__init` method
+		*)
+		method virtual private write_hx_init_body : unit
+		(**
+			Writes additional initialization code, which should be called before `__hx__init()`
+		*)
+		method virtual private write_pre_hx_init : unit
+		(**
+			Writes initialization code for type instances
+		*)
+		method virtual private write_instance_initialization : unit
+		(**
+			Indicates if type should be declared as `final`
+		*)
+		method is_final = false
+		(**
+			Indicates if `field` should be declared as `final`
+		*)
+		method is_final_field (field:tclass_field) : bool = false
+		(**
+			Increase indentation by one level
+		*)
+		method indent_more =
+			indentation <- indentation ^ "\t";
+		(**
+			Decrease indentation by one level
+		*)
+		method indent_less =
+			indentation <- String.make ((String.length indentation) - 1) '\t';
+		(**
+			Set indentation level (starting from zero for no indentation)
+		*)
+		method indent level =
+			indentation <- String.make level '\t';
+		(**
+			Indicates if class has user-defined static __init__ method
+			@see http://old.haxe.org/doc/advanced/magic#initialization-magic
+		*)
+		method has_magic_init = match wrapper#get_magic_init with None -> false | Some _ -> true
+		(**
+			Returns generated file contents
+		*)
+		method get_contents =
+			if (String.length contents) = 0 then begin
+				self#write_declaration;
+				self#write_line " {"; (** opening bracket for a class *)
+				self#write_body;
+				if wrapper#needs_initialization then self#write_hx_init;
+				self#indent 0;
+				self#write_line "}"; (** closing bracket for a class *)
+				self#write_empty_lines;
+				let boot_class = self#use boot_type_path in
+				(* Boot initialization *)
+				if boot_type_path = self#get_type_path then
+					self#write_statement (boot_class ^ "::__hx__init()");
+				(*let php_class = get_full_type_name ~escape:true ~omit_first_slash:true (add_php_prefix ctx self#get_type_path)*)
+				let haxe_class = match wrapper#get_type_path with (path, name) -> String.concat "." (path @ [name]) in
+				self#write_statement (boot_class ^ "::registerClass(" ^ (self#get_name) ^ "::class, '" ^ haxe_class ^ "')");
+				self#write_rtti_meta;
+				self#write_pre_hx_init;
+				(* Current class initialization *)
+				if wrapper#needs_initialization && boot_type_path <> self#get_type_path then
+					self#write_statement (self#get_name ^ "::__hx__init()");
+				let body = Buffer.contents buffer in
+				Buffer.clear buffer;
+				self#write_header;
+				self#write "\n";
+				let header = Buffer.contents buffer in
+				contents <- header ^ body;
+			end;
+			contents
+		(**
+			Adds type to "use" section if not added yet.
+			If it's a top-level type then type name returned without adding to "use" section.
+			@return Unique alias for specified type.
+		*)
+		method use ?prefix (type_path:path) =
+			if type_path = wrapper#get_type_path then
+				self#get_name
+			else
+				let type_path = match type_path with (pack, name) -> (pack, get_real_name name) in
+				let type_path =
+					match prefix with
+						| Some false -> type_path
+						| _ -> add_php_prefix ctx type_path
+				in
+				let module_path = get_module_path type_path in
+				match type_path with
+					| ([], type_name) -> "\\" ^ type_name
+					| _ ->
+						let alias_source = ref (List.rev module_path) in
+						let get_alias_next_part () =
+							match !alias_source with
+								| [] ->  failwith ("Failed to find already used type: " ^ get_full_type_name type_path)
+								| name :: rest ->
+									alias_source := rest;
+									String.capitalize name
+						and added = ref false
+						and alias = ref (get_type_name type_path) in
+						if !alias = self#get_name then
+							alias := get_alias_next_part () ^ !alias;
+						while not !added do
+							try
+								if (get_module_path type_path) <> wrapper#get_namespace && type_name_used_in_namespace ctx !alias wrapper#get_namespace then
+									alias := get_alias_next_part () ^ !alias
+								else
+									let used_type = Hashtbl.find use_table !alias in
+									if used_type = type_path then
+										added := true
+									else
+										alias := get_alias_next_part () ^ !alias;
+							with
+								| Not_found ->
+									Hashtbl.add use_table !alias type_path;
+									added := true
+								| _ -> fail self#pos (try assert false with Assert_failure mlpos -> mlpos)
+						done;
+						!alias
+		(**
+			Extracts type path from Type.t value and execute self#use on it
+			@return Unique alias for specified type.
+		*)
+		method use_t (t_inst:Type.t) =
+			match follow t_inst with
+				| TEnum (tenum, _) -> self#use tenum.e_path
+				| TInst (tcls, _) ->
+					(match tcls.cl_kind with
+						| KTypeParameter _ -> "mixed"
+						| _ ->
+							(match tcls.cl_path with
+								| ([], "String") -> "string"
+								| _ -> self#use ~prefix:(not tcls.cl_extern) tcls.cl_path
+							)
+					)
+				| TFun _ -> self#use ~prefix:false ([], "Closure")
+				| TAnon _ -> "object"
+				| TDynamic _ -> "mixed"
+				| TLazy _ -> failwith "TLazy not implemented"
+				| TMono mono ->
+					(match !mono with
+						| None -> "mixed"
+						| Some t -> self#use_t t
+					)
+				| TType _ -> failwith "TType not implemented"
+				| TAbstract (abstr, _) ->
+					match abstr.a_path with
+						| ([],"Int") -> "int"
+						| ([],"Float") -> "float"
+						| ([],"Bool") -> "bool"
+						| ([],"Void") -> "void"
+						| ([],"Enum") -> "Enum"
+						| ([], "Class") -> "Class"
+						| _ when Meta.has Meta.CoreType abstr.a_meta -> "mixed"
+						| _ -> self#use_t abstr.a_this
+		(**
+			Indicates whether current expression nesting level is a top level of a block
+		*)
+		method private parent_expr_is_block =
+			let rec expr_is_block expr parents =
+				match expr.eexpr with
+					| TBlock _ -> true
+					| TIf (_, if_expr, Some else_expr) ->
+						if (expr_is_block if_expr []) || (expr_is_block else_expr []) then
+							true
+						else
+							(match parents with
+								| parent :: rest -> expr_is_block parent rest
+								| [] -> false
+							)
+					| TIf (_, _, None) -> true
+					| TTry _ -> true
+					| TWhile _ -> true
+					| TSwitch _ -> true
+					| _ -> false
+			in
+			match expr_hierarchy with
+				| _ :: parent :: rest -> expr_is_block parent rest
+				| _ -> false
+		(**
+			Returns parent expression.
+		*)
+		method private parent_expr =
+			match expr_hierarchy with
+				| _ :: expr :: _ -> Some expr
+				| _ -> None
+		(**
+			Indicates if parent expression is a call (bypasses casts and metas)
+		*)
+		method private parent_expr_is_call =
+			let rec expr_is_call expr parents =
+				match expr.eexpr with
+					| TCast _
+					| TMeta _ ->
+						(match parents with
+							| parent :: rest -> expr_is_call parent rest
+							| [] -> false
+						)
+					| TCall _ -> true
+					| _ -> false
+			in
+			match expr_hierarchy with
+				| _ :: parent :: rest -> expr_is_call parent rest
+				| _ -> false
+		(**
+			Position of currently generated code in source hx files
+		*)
+		method private pos =
+			match expr_hierarchy with
+				| { epos = pos } :: _ -> pos
+				| _ -> dummy_pos
+		(**
+			Add a function call to "dereference" part of expression to avoid "Cannot use temporary expression in write context"
+			erro in expressions like:
+			```
+			new MyClass().fieldName = 'value';
+			```
+		*)
+		method private dereference expr =
+			let boot_cls = get_boot ctx in
+			let deref_field = PMap.find "deref" boot_cls.cl_statics in
+			match expr.eexpr with
+				| TField (target_expr, access) ->
+					{
+						expr with eexpr = TField (
+							{
+								target_expr with eexpr = TCall (
+									{
+										target_expr with eexpr = TField (
+											{
+												target_expr with eexpr = TTypeExpr (TClassDecl boot_cls)
+											},
+											FStatic (boot_cls, deref_field)
+										)
+									},
+									[ target_expr ]
+								)
+							},
+							access
+						)
+					}
+				| TArray (target_expr, access_expr) ->
+					{
+						expr with eexpr = TArray (
+							{
+								target_expr with eexpr = TCall (
+									{
+										target_expr with eexpr = TField (
+											{
+												target_expr with eexpr = TTypeExpr (TClassDecl boot_cls)
+											},
+											FStatic (boot_cls, deref_field)
+										)
+									},
+									[ target_expr ]
+								)
+							},
+							access_expr
+						)
+					}
+				| _ -> fail self#pos (try assert false with Assert_failure mlpos -> mlpos)
+		(**
+			Writes specified string to output buffer
+		*)
+		method private write str =
+			Buffer.add_string buffer str
+		(**
+			Writes fixed amount of empty lines (E.g. between methods)
+		*)
+		method private write_empty_lines =
+			self#write "\n";
+			self#write "\n"
+		(**
+			Writes current indentation to output buffer
+		*)
+		method private write_indentation =
+			Buffer.add_string buffer indentation
+		(**
+			Writes specified line to output buffer and appends \n
+		*)
+		method private write_line line =
+			Buffer.add_string buffer (indentation ^ line ^ "\n")
+		(**
+			Writes specified statement to output buffer and appends ";\n"
+		*)
+		method private write_statement statement =
+			Buffer.add_string buffer (indentation ^ statement ^ ";\n")
+		(**
+			Build file header (<?php, namespace and file doc block)
+		*)
+		method private write_header =
+			self#indent 0;
+			self#write_line "<?php";
+			self#write_line "/**";
+			Codegen.map_source_header ctx (fun s -> self#write_line (" * " ^ s));
+			if ctx.debug then self#write_line (" * Haxe source file: " ^ self#get_source_file);
+			self#write_line " */";
+			self#write "\n";
+			let namespace = self#get_namespace in
+			if List.length namespace > 0 then
+				self#write_line ("namespace " ^ (String.concat "\\" namespace) ^ ";\n");
+			self#write_use
+		(**
+			Build "use" statements
+		*)
+		method private write_use =
+			self#indent 0;
+			let write alias type_path =
+				if (get_module_path type_path) <> wrapper#get_namespace then
+					if get_type_name type_path = alias then
+						self#write_statement ("use " ^ (get_full_type_name type_path))
+					else
+						let full_name = get_full_type_name type_path in
+						self#write_statement ("use " ^ full_name ^ " as " ^ alias)
+			in
+			Hashtbl.iter write use_table
+		(**
+			Writes array item declaration to output buffer and appends ",\n"
+		*)
+		method private write_array_item ?key value_expr =
+			(match key with
+				| None ->
+					self#write_indentation;
+					self#write_expr value_expr;
+				| Some key_str ->
+					self#write (indentation  ^ "\"" ^ (String.escaped key_str) ^ "\" => ");
+					self#write_expr value_expr
+			);
+			self#write ",\n"
+		(**
+			Generates PHP docblock to output buffer.
+		*)
+		method private write_doc doc_block =
+			match doc_block with
+				| DocVar (type_name, doc) ->
+					self#write_line "/**";
+					self#write_line (" * @var " ^ type_name);
+					(match doc with
+						| None -> ()
+						| Some txt -> self#write_doc_description txt
+					);
+					self#write_line " */"
+				| DocClass doc ->
+					(match doc with
+						| None -> ()
+						| Some txt ->
+							self#write_line "/**";
+							self#write_doc_description txt;
+							self#write_line " */"
+					)
+				| DocMethod (args, return, doc) ->
+					self#write_method_docblock args return doc
+		(**
+			Writes description section of docblocks
+		*)
+		method write_doc_description (doc:string) =
+			let lines = Str.split (Str.regexp "\n") (String.trim doc)
+			and write_line line =
+				let trimmed = String.trim line in
+				if String.length trimmed > 0 then (
+					if String.get trimmed 0 = '*' then
+						self#write_line (" " ^ trimmed)
+					else
+						self#write_line (" * " ^ trimmed)
+				)
+			in
+			List.iter write_line lines
+		(**
+			Generates docblock for a method and writes it to output buffer
+		*)
+		method write_method_docblock args return_type doc =
+			self#write_line "/**";
+			(match doc with
+				| None -> ()
+				| Some txt ->
+					self#write_doc_description txt;
+					self#write_line " * "
+			);
+			let write_arg arg =
+				match arg with
+					| (arg_name, is_optional, arg_type) ->
+						self#write_line (" * @param " ^ (self#use_t arg_type) ^ " $" ^ arg_name)
+			in
+			List.iter write_arg args;
+			if List.length args > 0 then self#write_line " * ";
+			self#write_line (" * @return " ^ (self#use_t return_type));
+			self#write_line " */"
+		(**
+			Writes rtti meta to output buffer
+		*)
+		method write_rtti_meta =
+			match Codegen.build_metadata ctx wrapper#get_module_type with
+				| None -> ()
+				| Some meta_expr ->
+					let boot_class = self#use boot_type_path in
+					self#write (boot_class ^ "::registerMeta(" ^ (self#get_name) ^ "::class, ");
+					self#write_expr meta_expr;
+					self#write ");\n"
+		(**
+			Writes expression to output buffer
+		*)
+		method private write_expr (expr:texpr) =
+			expr_hierarchy <- expr :: expr_hierarchy;
+			(match expr.eexpr with
+				| TConst const -> self#write_expr_const const
+				| TLocal var ->
+					vars#used var.v_name;
+					self#write ("$" ^ var.v_name)
+				| TArray (target, index) -> self#write_expr_array_access target index
+				| TBinop (operation, expr1, expr2) when needs_dereferencing expr1 ->
+					self#write_expr { expr with eexpr = TBinop (operation, self#dereference expr1, expr2) }
+				| TBinop (operation, expr1, expr2) -> self#write_expr_binop operation expr1 expr2
+				| TField (fexpr, access) when is_php_global expr -> self#write_expr_php_global expr
+				| TField (fexpr, access) when is_php_class_const expr -> self#write_expr_php_class_const expr
+				| TField (expr, access) -> self#write_expr_field expr access
+				| TTypeExpr mtype -> self#write_expr_type mtype
+				| TParenthesis expr ->
+					self#write "(";
+					self#write_expr expr;
+					self#write ")"
+				| TObjectDecl fields -> self#write_expr_object_declaration fields
+				| TArrayDecl exprs -> self#write_expr_array_decl exprs
+				| TCall ({ eexpr = TLocal { v_name = name }}, args) when is_magic expr -> self#write_expr_magic name args
+				| TCall ({ eexpr = TField (expr, access) }, args) when is_string expr -> self#write_expr_call_string expr access args
+				| TCall (expr, args) when is_lang_extern expr -> self#write_expr_call_lang_extern expr args
+				| TCall (target, args) when is_sure_var_field_access target -> self#write_expr_call (parenthesis target) args
+				| TCall (target, args) -> self#write_expr_call target args
+				| TNew (_, _, args) when is_string expr -> write_args buffer self#write_expr args
+				| TNew (tcls, _, args) -> self#write_expr_new tcls args
+				| TUnop (operation, flag, target_expr) when needs_dereferencing target_expr ->
+					self#write_expr { expr with eexpr = TUnop (operation, flag, self#dereference target_expr) }
+				| TUnop (operation, flag, expr) -> self#write_expr_unop operation flag expr
+				| TFunction fn -> self#write_expr_function fn
+				| TVar (var, expr) -> self#write_expr_var var expr
+				| TBlock exprs -> self#write_expr_block expr
+				| TFor (var, iterator, body) -> fail self#pos (try assert false with Assert_failure mlpos -> mlpos)
+				| TIf (condition, if_expr, else_expr) -> self#write_expr_if condition if_expr else_expr
+				| TWhile (condition, expr, do_while) -> self#write_expr_while condition expr do_while
+				| TSwitch (switch, cases, default ) -> self#write_expr_switch switch cases default
+				| TTry (try_expr, catches) -> self#write_expr_try_catch try_expr catches
+				| TReturn expr -> self#write_expr_return expr
+				| TBreak -> self#write_expr_loop_flow "break"
+				| TContinue -> self#write_expr_loop_flow "continue"
+				| TThrow expr -> self#write_expr_throw expr
+				| TCast (expr, mtype) -> self#write_expr_cast expr mtype
+				| TMeta (_, expr) -> self#write_expr expr
+				| TEnumParameter (expr, constructor, index) -> self#write_expr_enum_parameter expr constructor index
+			);
+			expr_hierarchy <- List.tl expr_hierarchy
+		(**
+			Writes type initialization method.
+		*)
+		method private write_hx_init =
+			self#write_empty_lines;
+			self#indent 1;
+			self#write_line "/**";
+			self#write_line " * @internal";
+			self#write_line " * @access private";
+			self#write_line " */";
+			self#write_line "static public function __hx__init ()";
+			self#write_line "{";
+			self#indent_more;
+			self#write_statement "static $called = false";
+			self#write_statement "if ($called) return";
+			self#write_statement "$called = true";
+			self#write "\n";
+			vars#clear;
+			(match wrapper#get_magic_init with
+				| None -> ()
+				| Some expr -> self#write_fake_block expr
+			);
+			self#write "\n";
+			vars#clear;
+			self#write_hx_init_body;
+			self#indent 1;
+			self#write_line "}"
+		(**
+			Writes `continue N` or `break N` with required N depending on nearest parent loop and amount of `switch` between loop and
+			`continue/break`
+		*)
+		method private write_expr_loop_flow word =
+			let rec count_N parent_exprs count =
+				match parent_exprs with
+					| [] -> count
+					| { eexpr = TWhile _ } :: _ -> count
+					| { eexpr = TSwitch _ } :: rest -> count_N rest (count + 1)
+					| _ :: rest -> count_N rest count
+			in
+			let count = count_N expr_hierarchy 1 in
+			if count > 1 then
+				self#write (word ^ " " ^ (string_of_int count))
+			else
+				self#write word
+		(**
+			Writes TConst to output buffer
+		*)
+		method private write_expr_const const =
+			match const with
+				| TInt value -> self#write (Int32.to_string value)
+				| TFloat str -> self#write str
+				| TString str -> self#write ("\"" ^ (escape_bin str) ^ "\"")
+				| TBool value -> self#write (if value then "true" else "false")
+				| TNull -> self#write "null"
+				| TThis -> self#write "$this"
+				| TSuper -> self#write "parent"
+		(**
+			Writes TArrayDecl to output buffer
+		*)
+		method private write_expr_array_decl exprs =
+			match exprs with
+				| [] -> self#write ("new " ^ (self#use array_type_path) ^ "()")
+				| [expr] ->
+					self#write ((self#use array_type_path) ^ "::wrap([");
+					self#write_expr expr;
+					self#write "])"
+				| _ ->
+					self#write ((self#use array_type_path) ^ "::wrap([\n");
+					self#indent_more;
+					List.iter (fun expr -> self#write_array_item expr) exprs;
+					self#indent_less;
+					self#write_indentation;
+					self#write "])"
+		(**
+			Writes TArray to output buffer
+		*)
+		method private write_expr_array_access target index =
+			(*self#write_expr target;
+			self#write "[";
+			self#write_expr index;
+			self#write "]"*)
+			let write_index left_bracket right_bracket =
+				self#write left_bracket;
+				self#write_expr index;
+				self#write right_bracket
+			in
+			match follow target.etype with
+				| TInst ({ cl_path = path }, _) when path = array_type_path ->
+					(match self#parent_expr with
+						| Some { eexpr = TBinop (OpAssign, { eexpr = TArray (t, i) }, _) } when t == target ->
+							self#write_expr target;
+							write_index "[" "]"
+						| Some { eexpr = TBinop (OpAssignOp _, { eexpr = TArray (t, i) }, _) } when t == target ->
+							self#write_expr target;
+							write_index "[" "]"
+						| Some { eexpr = TUnop (op, _, { eexpr = TArray (t, i) }) } when t == target && is_modifying_unop op ->
+							self#write_expr target;
+							write_index "[" "]"
+						| Some { eexpr = TField ({ eexpr = TArray (t, i) }, _) } ->
+							self#write_expr target;
+							write_index "[" "]"
+						| _ ->
+							self#write "(";
+							self#write_expr target;
+							self#write "->arr";
+							write_index "[" "] ?? null)"
+					)
+				| _ ->
+					self#write_expr target;
+					write_index "[" "]"
+		(**
+			Writes TVar to output buffer
+		*)
+		method private write_expr_var var expr =
+			vars#declared var.v_name;
+			self#write ("$" ^ var.v_name ^ " = ");
+			match expr with
+				| None -> self#write "null"
+				| Some expr -> self#write_expr expr
+		(**
+			Writes TFunction to output buffer
+		*)
+		method private write_expr_function ?name func =
+			match name with
+				| None -> self#write_closure_declaration func self#write_function_arg
+				| Some "__construct" -> self#write_constructor_function_declaration func self#write_function_arg
+				| Some name -> self#write_method_function_declaration name func self#write_function_arg
+		(**
+			Writes constructor declaration (except visibility and `static` keywords) to output buffer
+		*)
+		method private write_constructor_function_declaration func write_arg =
+			self#write ("function __construct (");
+			write_args buffer write_arg func.tf_args;
+			self#write ") {\n";
+			self#indent_more;
+			self#write_instance_initialization;
+			let func = inject_defaults ctx func in
+			begin match func.eexpr with
+				| TBlock [] -> ()
+				| _ -> self#write_fake_block func;
+			end;
+			self#indent_less;
+			self#write_indentation;
+			self#write "}"
+		(**
+			Writes method declaration (except visibility and `static` keywords) to output buffer
+		*)
+		method private write_method_function_declaration name func write_arg =
+			let by_ref = if is_ref func.tf_type then "&" else "" in
+			self#write ("function " ^ by_ref ^ name ^ " (");
+			write_args buffer write_arg func.tf_args;
+			self#write ") ";
+			self#write_expr (inject_defaults ctx func)
+		(**
+			Writes closure declaration to output buffer
+		*)
+		method private write_closure_declaration func write_arg =
+			vars#dive;
+			self#write "function (";
+			write_args buffer write_arg func.tf_args;
+			self#write ")";
+			(* Generate closure body to separate buffer *)
+			let original_buffer = buffer in
+			buffer <- Buffer.create 256;
+			self#write_expr (inject_defaults ctx func);
+			let body = Buffer.contents buffer in
+			buffer <- original_buffer;
+			(* Use captured local vars *)
+			let used_vars = vars#pop_used in
+			self#write " ";
+			if List.length used_vars > 0 then begin
+				self#write " use (";
+				write_args buffer (fun name -> self#write ("&$" ^ name)) used_vars;
+				self#write ") "
+			end;
+			self#write body
+		(**
+			Writes TBlock to output buffer
+		*)
+		method private write_expr_block block_expr =
+			(* Check if parent expr could not contain blocks in PHP, and this block needs to be wrapped in a closure. *)
+			let needs_closure = match self#parent_expr with
+				| None -> false
+				| Some e ->
+					match e.eexpr with
+						| TIf (_, _, _) -> false
+						| TWhile (_, _, _) -> false
+						| TTry (_, _) -> false
+						| TFor (_, _, _) -> false
+						| TFunction _ -> false
+						| TBlock _ -> false
+						| TSwitch (_, _, _) -> false
+						| _ -> true
+			in
+			if needs_closure then
+				begin
+					self#write "(";
+					self#write_expr {
+						block_expr with eexpr = TFunction {
+							tf_args = [];
+							tf_type = block_expr.etype;
+							tf_expr = ensure_return_in_block block_expr;
+						}
+					};
+					self#write ")()"
+				end
+			else
+				begin
+					let inline_block = self#parent_expr_is_block in
+					self#write_as_block ~inline:inline_block block_expr
+				end
+		(**
+			Emulates TBlock for parent expression and writes `expr` as inlined block
+		*)
+		method private write_fake_block expr =
+			self#write_indentation;
+			let fake_block = { expr with eexpr = TBlock [expr] } in
+			expr_hierarchy <- fake_block :: expr_hierarchy;
+			self#write_as_block ~inline:true expr;
+			expr_hierarchy <- List.tl expr_hierarchy
+		(**
+			Writes "{ <expressions> }" to output buffer
+		*)
+		method private write_as_block ?inline ?unset_locals expr =
+			let unset_locals = match unset_locals with Some true -> true | _ -> false
+			and exprs = match expr.eexpr with TBlock exprs -> exprs | _ -> [expr] in
+			let write_body () =
+				let write_expr expr =
+					self#write_expr expr;
+					match expr.eexpr with
+						| TBlock _ | TIf _ | TTry _ | TSwitch _ | TWhile (_, _, NormalWhile) -> self#write "\n"
+						| _ -> self#write ";\n"
+				in
+				let write_expr_with_indent expr =
+					self#write_indentation;
+					write_expr expr
+				in
+				let write_exprs () =
+					match exprs with
+						| [] -> ()
+						| first :: rest ->
+							write_expr first; (* write first expression without indentation in case of block inlining *)
+							List.iter write_expr_with_indent rest
+				in
+				if unset_locals then
+					begin
+						let original_buffer = buffer in
+						buffer <- Buffer.create 256;
+						vars#dive;
+						write_exprs();
+						let body = Buffer.contents buffer in
+						buffer <- original_buffer;
+						let locals = vars#pop_declared in
+						if List.length locals > 0 then begin
+							self#write ("unset($" ^ (String.concat ", $" locals) ^ ");\n");
+							self#write_indentation
+						end;
+						self#write body
+					end
+				else
+					write_exprs()
+			in
+			match inline with
+				| Some true -> write_body ()
+				| _ ->
+					self#write "{\n";
+					self#indent_more;
+					(match exprs with
+						| [] -> ()
+						| _ ->
+							self#write_indentation; (* indentation for the first expression in block *)
+							write_body ()
+					);
+					self#indent_less;
+					self#write_indentation;
+					self#write "}"
+		(**
+			Writes TReturn to output buffer
+		*)
+		method private write_expr_return expr =
+			match expr with
+				| None -> self#write "return";
+				| Some expr ->
+					self#write "return ";
+					self#write_expr expr
+		(**
+			Writes TThrow to output buffer
+		*)
+		method private write_expr_throw expr =
+			self#write "throw ";
+			if is_native_exception expr.etype then
+				self#write_expr expr
+			else if sure_extends_extern expr.etype || is_dynamic_type expr.etype then
+				begin
+					self#write "(is_object($__hx__throw = ";
+					self#write_expr expr;
+					self#write (") && $__hx__throw instanceof \\Throwable ? $__hx__throw : new " ^ (self#use hxexception_type_path) ^ "($__hx__throw))")
+				end
+			else
+				begin
+					self#write ("new " ^ (self#use hxexception_type_path) ^ "(");
+					self#write_expr expr;
+					self#write ")"
+				end
+		(**
+			Writes try...catch to output buffer
+		*)
+		method private write_expr_try_catch try_expr catches =
+			let catching_dynamic = ref false in
+			let haxe_exception = self#use hxexception_type_path
+			and first_catch = ref true in
+			let write_catch (var, expr) =
+				let dynamic = ref false in
+				(match follow var.v_type with
+					| TInst ({ cl_path = ([], "String") }, _) -> self#write "if (is_string($__hx__real_e)) {\n"
+					| TAbstract ({ a_path = ([], "Float") }, _) -> self#write "if (is_float($__hx__real_e)) {\n"
+					| TAbstract ({ a_path = ([], "Int") }, _) -> self#write "if (is_int($__hx__real_e)) {\n"
+					| TAbstract ({ a_path = ([], "Bool") }, _) -> self#write "if (is_bool($__hx__real_e)) {\n"
+					| TDynamic _ ->
+						dynamic := true;
+						catching_dynamic := true;
+						if not !first_catch then self#write "{\n"
+					| vtype -> self#write ("if ($__hx__real_e instanceof " ^ (self#use_t vtype) ^ ") {\n")
+				);
+				if !dynamic && !first_catch then
+					begin
+						self#write ("$" ^ var.v_name ^ " = $__hx__real_e;\n");
+						self#write_indentation;
+						self#write_as_block ~inline:true expr;
+					end
+				else
+					begin
+						self#indent_more;
+						self#write_statement ("$" ^ var.v_name ^ " = $__hx__real_e");
+						self#write_indentation;
+						self#write_as_block ~inline:true expr;
+						self#indent_less;
+						self#write_indentation;
+						self#write "}";
+					end;
+				if not !dynamic then self#write " else ";
+				first_catch := false;
+			in
+			self#write "try ";
+			self#write_as_block try_expr;
+			self#write " catch (\\Throwable $__hx__caught_e) {\n";
+			self#indent_more;
+			if ctx.debug then
+				self#write_statement ((self#use (["haxe"], "CallStack")) ^ "::saveExceptionTrace($__hx__caught_e)");
+			self#write_statement ("$__hx__real_e = ($__hx__caught_e instanceof " ^ haxe_exception ^ " ? $__hx__caught_e->e : $__hx__caught_e)");
+			self#write_indentation;
+			List.iter write_catch catches;
+			if not !catching_dynamic then
+				self#write " throw $__hx__caught_e;\n"
+			else
+				(match catches with [_] -> () | _ -> self#write "\n");
+			self#indent_less;
+			self#write_indentation;
+			self#write "}"
+		(**
+			Writes TCast to output buffer
+		*)
+		method private write_expr_cast expr (mtype:module_type option) =
+			match mtype with
+				| None -> self#write_expr expr
+				| Some mtype ->
+					self#write ((self#use boot_type_path) ^ "::typedCast(");
+					self#write_expr_type mtype;
+					self#write ", ";
+					self#write_expr expr;
+					self#write ")"
+		(**
+			Write Haxe->PHP magic function call
+			@see http://old.haxe.org/doc/advanced/magic#php-magic
+		*)
+		method private write_expr_magic name args =
+			let error = error_message self#pos ("Invalid arguments for " ^ name ^ " magic call") in
+			match args with
+				| [] -> failwith error
+				| { eexpr = TConst (TString code) } as expr :: args ->
+					(match name with
+						| "__php__" ->
+							(match expr.eexpr with
+								| TConst (TString php) ->
+									Codegen.interpolate_code ctx php args self#write self#write_expr self#pos
+								| _ -> fail self#pos (try assert false with Assert_failure mlpos -> mlpos)
+							)
+						| "__call__" ->
+							self#write (code ^ "(");
+							write_args buffer self#write_expr args;
+							self#write ")"
+						| "__physeq__" ->
+							(match args with
+								| [expr2] -> self#write_expr_binop OpEq expr expr2
+								| _ -> failwith error
+							)
+						| "__var__" ->
+							(match args with
+								| [expr2] ->
+									self#write ("$" ^ code ^ "[");
+									self#write_expr expr2;
+									self#write "]"
+								| _ -> failwith error
+							)
+						| _ -> failwith error
+					)
+				| [expr1; expr2] ->
+					(match name with
+						| "__physeq__" ->
+							(match args with
+								| [expr1; expr2] -> self#write_expr_binop OpEq expr1 expr2
+								| _ -> failwith error
+							)
+						| _ -> failwith error
+					)
+				| _ -> failwith error
+		(**
+			Writes TTypeExpr to output buffer
+		*)
+		method private write_expr_type (mtype:module_type) =
+			let ttype = type_of_module_type mtype in
+			match expr_hierarchy with
+				| _ :: { eexpr = TField _ } :: _ -> self#write (self#use_t ttype)
+				| _ ->
+					let class_name =
+						match self#use_t ttype with
+							| "int" -> "'Int'"
+							| "float" -> "'Float'"
+							| "bool" -> "'Bool'"
+							| "string" -> "'String'"
+							| "mixed" -> "'Dynamic'"
+							| "Enum" -> "'Enum'"
+							| "Class" -> "'Class'"
+							| name -> name ^ "::class"
+					in
+					self#write ((self#use boot_type_path) ^ "::getClass(" ^ class_name ^ ")")
+		(**
+			Writes binary operation to output buffer
+		*)
+		method private write_expr_binop operation expr1 expr2 =
+			let write_method method_name =
+				self#write (method_name ^ "(");
+				self#write_expr expr1;
+				self#write ", ";
+				self#write_expr expr2;
+				self#write ")"
+			in
+			let write_for_concat expr =
+				if ((is_constant expr) && not (is_constant_null expr)) || (is_concatenation expr) then
+					self#write_expr expr
+				else begin
+					self#write "(";
+					self#write_expr expr;
+					self#write "??'null')"
+				end
+			and write_binop ?writer ?right_writer str =
+				let write_left = match writer with None -> self#write_expr | Some writer -> writer in
+				let write_right = match right_writer with None -> write_left | Some writer -> writer
+				and need_parenthesis =
+					match expr_hierarchy with
+						| _ :: { eexpr = TBinop (parent, _, _) } :: _ -> need_parenthesis_for_binop operation parent
+						| _ -> false
+				in
+				if need_parenthesis then self#write "(";
+				write_left expr1;
+				self#write str;
+				write_right expr2;
+				if need_parenthesis then self#write ")"
+			and compare_strings op =
+				write_method "strcmp";
+				self#write (op ^ "0")
+			in
+			let compare op =
+				if is_string expr1 && is_string expr2 then
+						compare_strings op
+					else
+						write_binop op
+			in
+			match operation with
+				| OpAdd ->
+					if (is_string expr1) || (is_string expr2) then
+						write_binop ~writer:write_for_concat " . "
+					else if (is_unknown_type expr1.etype) && (is_unknown_type expr2.etype) then
+						write_method ((self#use boot_type_path) ^ "::addOrConcat")
+					else
+						write_binop " + "
+				| OpMult -> write_binop " * "
+				| OpDiv -> write_binop " / "
+				| OpSub -> write_binop " - "
+				| OpAssign -> write_binop " = "
+				| OpEq ->
+					if need_boot_equal expr1 expr2 then
+						write_method ((self#use boot_type_path) ^ "::equal")
+					else
+						write_binop " === "
+				| OpNotEq ->
+					if need_boot_equal expr1 expr2 then
+						begin
+							self#write "!";
+							write_method ((self#use boot_type_path) ^ "::equal")
+						end
+					else
+						write_binop " !== "
+				| OpGt -> compare " > "
+				| OpGte -> compare " >= "
+				| OpLt -> compare " < "
+				| OpLte -> compare " <= "
+				| OpAnd -> write_binop " & "
+				| OpOr -> write_binop " | "
+				| OpXor -> write_binop " ^ "
+				| OpBoolAnd -> write_binop " && "
+				| OpBoolOr -> write_binop " || "
+				| OpShl  -> write_binop " << "
+				| OpShr -> write_binop " >> "
+				| OpMod ->
+					if is_int expr1 && is_int expr2 then
+						write_binop " % "
+					else
+						write_method "fmod"
+				| OpUShr -> write_method ((self#use boot_type_path) ^ "::shiftRightUnsigned")
+				| OpAssignOp OpAdd ->
+					if (is_string expr1) then
+						begin
+							self#write_expr expr1;
+							self#write " = ";
+							write_binop ~writer:write_for_concat " . "
+						end
+					else if (is_unknown_type expr1.etype) && (is_unknown_type expr2.etype) then
+						begin
+							self#write_expr expr1;
+							self#write " = ";
+							write_method ((self#use boot_type_path) ^ "::addOrConcat")
+						end
+					else
+						write_binop " += "
+				| OpAssignOp OpMult -> write_binop " *= "
+				| OpAssignOp OpDiv -> write_binop " /= "
+				| OpAssignOp OpSub -> write_binop " -= "
+				| OpAssignOp OpAnd -> write_binop " &= "
+				| OpAssignOp OpOr -> write_binop " |= "
+				| OpAssignOp OpXor -> write_binop " ^= "
+				| OpAssignOp OpShl  -> write_binop " <<= "
+				| OpAssignOp OpShr -> write_binop " >>= "
+				| OpAssignOp OpMod ->
+					if is_int expr1 && is_int expr2 then
+						write_binop " %= "
+					else
+						write_method "fmod"
+				| OpAssignOp OpUShr ->
+					self#write_expr expr1;
+					self#write " = ";
+					write_method ((self#use boot_type_path) ^ "::shiftRightUnsigned")
+				| _ -> fail self#pos (try assert false with Assert_failure mlpos -> mlpos)
+		(**
+			Writes TUnOp to output buffer
+		*)
+		method private write_expr_unop operation flag expr =
+			let write_unop operation =
+				match operation with
+					| Increment -> self#write "++"
+					| Decrement -> self#write "--"
+					| Not -> self#write "!"
+					| Neg -> self#write "-"
+					| NegBits -> self#write "~"
+			in
+			match flag with
+				| Prefix ->
+					write_unop operation;
+					self#write_expr expr
+				| Postfix ->
+					self#write_expr expr;
+					write_unop operation
+		(**
+			Writes TField to output buffer
+		*)
+		method private write_expr_field expr access =
+			let write_access access_str field_str =
+				let access_str = ref access_str in
+				let expr_without_casts = reveal_expr expr in
+				(match expr_without_casts.eexpr with
+					| TNew _
+					| TArrayDecl _
+					| TObjectDecl _ -> self#write_expr (parenthesis expr)
+					| TConst TSuper ->
+						self#write "parent";
+						access_str := "::"
+					| _ -> self#write_expr expr
+				);
+				self#write (!access_str ^ field_str)
+			in
+			match (follow expr.etype, access) with
+				| (TInst ({ cl_path = ([], "String") }, _), FInstance (_, _, { cf_name = "length"; cf_kind = Var _ })) ->
+					self#write "strlen(";
+					self#write_expr expr;
+					self#write ")"
+				| (_, FInstance (_, _, field)) -> write_access "->" (field_name field)
+				| (_, FStatic (_, ({ cf_kind = Var _ } as field))) ->
+					(match (reveal_expr expr).eexpr with
+						| TTypeExpr _ -> write_access "::" ("$" ^ (field_name field))
+						| _ -> write_access "->" (field_name field)
+					)
+				| (_, FStatic (_, ({ cf_kind = Method MethDynamic } as field))) ->
+					(match expr_hierarchy with
+						| _ :: { eexpr = TCall ({ eexpr = TField (e, a) }, _) } :: _ when a == access ->
+							self#write "(";
+							write_access "::" ("$" ^ (field_name field));
+							self#write ")"
+						| _ ->
+							write_access "::" ("$" ^ (field_name field))
+					)
+				| (_, FStatic (_, ({ cf_kind = Method _ } as field))) -> self#write_expr_field_static expr field
+				| (_, FAnon field) ->
+					let written_as_probable_string = self#write_expr_field_if_string expr (field_name field) in
+					if not written_as_probable_string then write_access "->" (field_name field)
+				| (_, FDynamic field_name) ->
+					let written_as_probable_string = self#write_expr_field_if_string expr field_name in
+					if not written_as_probable_string then write_access "->" field_name
+				| (_, FClosure (tcls, field)) -> self#write_expr_field_closure tcls field expr
+				| (_, FEnum (_, field)) ->
+					if is_enum_constructor_with_args field then
+						if not self#parent_expr_is_call then
+							begin
+								self#write (self#use boot_type_path ^ "::closure(");
+								self#write_expr expr;
+								(match (reveal_expr expr).eexpr with
+									| TTypeExpr _ -> self#write "::class"
+									| _ -> self#write "->phpClassName"
+								);
+								self#write (", '" ^ field.ef_name ^ "')")
+							end
+						else
+							write_access "::" field.ef_name
+					else
+						begin
+							write_access "::" field.ef_name;
+							self#write "()"
+						end
+
+		(**
+			Writes field access on Dynamic expression to output buffer
+		*)
+		method private write_expr_field_if_string expr field_name =
+			(* Special case for String fields *)
+			match field_name with
+				| "length"
+				| "toUpperCase"
+				| "toLowerCase"
+				| "charAt"
+				| "indexOf"
+				| "lastIndexOf"
+				| "split"
+				| "toString"
+				| "substring"
+				| "substr"
+				| "charCodeAt" ->
+					self#write ((self#use hxdynamicstr_type_path) ^ "::wrap(");
+					self#write_expr expr;
+					self#write (")->" ^ field_name);
+					true
+				| _ ->
+					false
+		(**
+			Convert field access expressions for strings to native PHP string functions and write to output buffer
+		*)
+		method private write_expr_call_string expr access args =
+			match access with
+				| FInstance (_, _, ({ cf_kind = Method _ } as field)) ->
+					self#write ((self#use hxstring_type_path) ^ "::" ^ (field_name field) ^ "(");
+					write_args buffer self#write_expr (expr :: args);
+					self#write ")"
+				| _ -> fail self#pos (try assert false with Assert_failure mlpos -> mlpos)
+		(**
+			Writes FStatic field access for methods to output buffer
+		*)
+		method private write_expr_field_static expr field =
+			let write_expr () =
+				match expr.eexpr with
+					| TTypeExpr (TClassDecl { cl_path = ([], "String") }) -> self#write (self#use hxstring_type_path)
+					| _ -> self#write_expr expr
+			and operator =
+				match (reveal_expr expr).eexpr with
+					| TTypeExpr _ -> "::"
+					| _ -> "->"
+			in
+			match expr_hierarchy with
+				| _ :: { eexpr = TCall ({ eexpr = TField (e, FStatic (_, f)) }, _) } :: _ when e == expr && f == field ->
+					write_expr ();
+					self#write (operator ^ (field_name field))
+				| _ ->
+					let (args, return_type) = get_function_signature field  in
+					self#write "function(";
+					write_args buffer (self#write_arg true) args;
+					self#write ") { return ";
+					write_expr ();
+					self#write (operator ^ (field_name field) ^ "(");
+					write_args buffer (self#write_arg false) args;
+					self#write "); }"
+		(**
+			Writes FClosure field access to output buffer
+		*)
+		method private write_expr_field_closure tcls field expr =
+			if is_dynamic_method field then
+				match expr.eexpr with
+					| TTypeExpr mtype ->
+						let class_name = self#use_t (type_of_module_type mtype) in
+						self#write (class_name ^ "::$" ^ (field_name field) ^ "'");
+					| _ ->
+						self#write_expr expr;
+						self#write ("->" ^ (field_name field))
+			else
+				let new_closure = "new " ^ (self#use hxclosure_type_path) in
+				match expr.eexpr with
+					| TTypeExpr mtype ->
+						let class_name = self#use_t (type_of_module_type mtype) in
+						self#write (new_closure ^ "(" ^ class_name ^ "::class, '" ^ (field_name field) ^ "')");
+					| _ ->
+						self#write (new_closure ^ "(");
+						(match follow expr.etype with
+							| TInst ({ cl_path = ([], "String") }, []) ->
+								self#write ((self#use hxdynamicstr_type_path) ^ "::wrap(");
+								self#write_expr expr;
+								self#write ")"
+							| _ ->
+								self#write_expr expr
+						);
+						self#write (", '" ^ (field_name field) ^ "')")
+		(**
+			Write anonymous object declaration to output buffer
+		*)
+		method private write_expr_object_declaration fields =
+			match fields with
+				| [] ->  self#write ("new " ^ (self#use hxanon_type_path) ^ "()")
+				| _ ->
+					self#write ("new " ^ (self#use hxanon_type_path)  ^ "([\n");
+					self#indent_more;
+					let write_field (key, value) = self#write_array_item ~key:key value in
+					List.iter write_field fields;
+					self#indent_less;
+					self#write_indentation;
+					self#write "])"
+		(**
+			Writes specified type to output buffer depending on type of expression.
+		*)
+		method private write_type type_expr =
+			match type_expr.eexpr with
+				| TTypeExpr (TClassDecl tcls) ->
+					self#write (self#use_t (TInst (tcls, [])))
+				| _ ->
+					if is_string type_expr then
+						self#write_expr type_expr
+					else begin
+						self#write "(";
+						self#write_expr type_expr;
+						self#write "->phpClassName)";
+					end
+		(**
+			Write language specific expression declared in `php.PHP` extern
+		*)
+		method private write_expr_call_lang_extern expr args =
+			let name = match expr.eexpr with
+				| TField (_, FStatic (_, field)) -> field_name field
+				| _ -> fail self#pos (try assert false with Assert_failure mlpos -> mlpos)
+			in
+			match name with
+				| "int" | "float"
+				| "string" | "bool"
+				| "object" | "array" -> self#write_expr_lang_cast name args
+				| "binop" -> self#write_expr_lang_binop args
+				| "instanceof" -> self#write_expr_lang_instanceof args
+				| "foreach" -> self#write_expr_lang_foreach args
+				| "construct" -> self#write_expr_lang_construct args
+				| "getField" -> self#write_expr_lang_get_field args
+				| "setField" -> self#write_expr_lang_set_field args
+				| "getStaticField" -> self#write_expr_lang_get_static_field args
+				| "setStaticField" -> self#write_expr_lang_set_static_field args
+				| "call" -> self#write_expr_lang_call args
+				| "staticCall" -> self#write_expr_lang_static_call args
+				| "arrayDecl" -> self#write_expr_lang_array_decl args
+				| "splat" -> self#write_expr_lang_splat args
+				| "suppress" -> self#write_expr_lang_suppress args
+				| "keepVar" -> ()
+				| _ -> fail self#pos (try assert false with Assert_failure mlpos -> mlpos)
+		(**
+			Writes splat operator (for `php.Syntax.splat()`)
+		*)
+		method private write_expr_lang_splat args =
+			match args with
+				| [ args_expr ] ->
+					self#write "...";
+					self#write_expr args_expr
+				| _ -> fail self#pos (try assert false with Assert_failure mlpos -> mlpos)
+		(**
+			Writes error suppression operator (for `php.Syntax.suppress()`)
+		*)
+		method private write_expr_lang_suppress args =
+			match args with
+				| [ args_expr ] ->
+					self#write "@";
+					self#write_expr args_expr
+				| _ -> fail self#pos (try assert false with Assert_failure mlpos -> mlpos)
+		(**
+			Writes native array declaration (for `php.Syntax.arrayDecl()`)
+		*)
+		method private write_expr_lang_array_decl args =
+			self#write "[";
+			write_args buffer (fun e -> self#write_expr e) args;
+			self#write "]"
+		(**
+			Writes a call to instance method (for `php.Syntax.call()`)
+		*)
+		method private write_expr_lang_call args =
+			match args with
+				| obj_expr :: method_expr :: args ->
+					self#write_expr obj_expr;
+					self#write "->{";
+					self#write_expr method_expr;
+					self#write "}(";
+					write_args buffer (fun e -> self#write_expr e) args;
+					self#write ")"
+				| _ -> fail self#pos (try assert false with Assert_failure mlpos -> mlpos)
+		(**
+			Writes a call to a static method (for `php.Syntax.staticCall()`)
+		*)
+		method private write_expr_lang_static_call args =
+			match args with
+				| type_expr :: method_expr :: args ->
+					self#write_type type_expr;
+					self#write "::{";
+					self#write_expr method_expr;
+					self#write "}(";
+					write_args buffer (fun e -> self#write_expr e) args;
+					self#write ")"
+				| _ -> fail self#pos (try assert false with Assert_failure mlpos -> mlpos)
+		(**
+			Writes field access for reading (for `php.Syntax.getField()`)
+		*)
+		method private write_expr_lang_get_field args =
+			match args with
+				| obj_expr :: field_expr :: [] ->
+					self#write_expr obj_expr;
+					self#write "->{";
+					self#write_expr field_expr;
+					self#write "}"
+				| _ -> fail self#pos (try assert false with Assert_failure mlpos -> mlpos)
+		(**
+			Writes field access for writing (for `php.Syntax.setField()`)
+		*)
+		method private write_expr_lang_set_field args =
+			match args with
+				| obj_expr :: field_expr :: value_expr :: [] ->
+					self#write_expr obj_expr;
+					self#write "->{";
+					self#write_expr field_expr;
+					self#write "}";
+					self#write " = ";
+					self#write_expr value_expr
+				| _ -> fail self#pos (try assert false with Assert_failure mlpos -> mlpos)
+		(**
+			Writes static field access for reading (for `php.Syntax.getStaticField()`)
+		*)
+		method private write_expr_lang_get_static_field args =
+			match args with
+				| type_expr :: field_expr :: [] ->
+					self#write_type type_expr;
+					self#write "::${";
+					self#write_expr field_expr;
+					self#write "}"
+				| _ -> fail self#pos (try assert false with Assert_failure mlpos -> mlpos)
+		(**
+			Writes static field access for writing (for `php.Syntax.setField()`)
+		*)
+		method private write_expr_lang_set_static_field args =
+			match args with
+				| type_expr :: field_expr :: value_expr :: [] ->
+					self#write_expr type_expr;
+					self#write "::${";
+					self#write_expr field_expr;
+					self#write "}";
+					self#write " = ";
+					self#write_expr value_expr
+				| _ -> fail self#pos (try assert false with Assert_failure mlpos -> mlpos)
+		(**
+			Writes `new` expression with class name taken local variable (for `php.Syntax.construct()`)
+		*)
+		method private write_expr_lang_construct args =
+			let (class_expr, args) = match args with
+				| class_expr :: args -> (class_expr, args)
+				| _ -> fail self#pos (try assert false with Assert_failure mlpos -> mlpos)
+			in
+			self#write "new ";
+			self#write_expr class_expr;
+			self#write "(";
+			write_args buffer (fun e -> self#write_expr e) args;
+			self#write ")"
+		(**
+			Writes native php type conversion to output buffer (e.g. `php.Syntax.int()`)
+		*)
+		method private write_expr_lang_cast type_name args =
+			match args with
+				| expr :: [] ->
+					let add_parentheses = match self#parent_expr with Some e -> is_access e | None -> false
+					and expr = match expr.eexpr with
+						| TLocal e -> expr
+						| _ -> parenthesis expr
+					in
+					if add_parentheses then self#write "(";
+					self#write ("(" ^ type_name ^")");
+					self#write_expr expr;
+					if add_parentheses then self#write ")"
+				| _ -> fail self#pos (try assert false with Assert_failure mlpos -> mlpos)
+		(**
+			Generates binary operation to output buffer (for `php.Syntax.binop()`)
+		*)
+		method private write_expr_lang_binop args =
+			match args with
+				| val_expr1 :: operator_expr :: val_expr2 :: [] ->
+					let operator = match operator_expr.eexpr with
+						| TConst (TString operator) -> operator
+						| _ -> error_and_exit self#pos "Second argument for php.Syntax.binop() must be a constant string"
+					in
+					self#write "(";
+					self#write_expr val_expr1;
+					self#write (" " ^ operator ^ " ");
+					self#write_expr val_expr2;
+					self#write ")"
+				| _ -> fail self#pos (try assert false with Assert_failure mlpos -> mlpos)
+		(**
+			Writes `instanceof` expression to output buffer (for `php.Syntax.instanceof()`)
+		*)
+		method private write_expr_lang_instanceof args =
+			match args with
+				| val_expr :: type_expr :: [] ->
+					self#write_expr val_expr;
+					self#write " instanceof ";
+					(match type_expr.eexpr with
+						| TTypeExpr (TClassDecl tcls) ->
+							self#write (self#use_t (TInst (tcls, [])))
+						| _ ->
+							self#write_expr type_expr;
+							if not (is_string type_expr) then self#write "->phpClassName"
+					)
+				| _ -> fail self#pos (try assert false with Assert_failure mlpos -> mlpos)
+		(**
+			Writes `foreach` expression to output buffer (for `php.Syntax.foreach()`)
+		*)
+		method private write_expr_lang_foreach args =
+			match args with
+				| collection_expr :: { eexpr = TFunction fn } :: [] ->
+					let (key_name, value_name) = match fn.tf_args with
+						| ({ v_name = key_name }, _) :: ({ v_name = value_name }, _) :: [] -> (key_name, value_name)
+						| _ -> fail self#pos (try assert false with Assert_failure mlpos -> mlpos)
+					and add_parentheses =
+						match collection_expr.eexpr with
+							| TLocal _ -> false
+							| _ -> true
+					in
+					self#write "foreach (";
+					if add_parentheses then self#write "(";
+					self#write_expr collection_expr;
+					if add_parentheses then self#write ")";
+					self#write (" as $" ^ key_name ^ " => $" ^ value_name ^ ") ");
+					self#write_as_block fn.tf_expr
+				| _ ->
+					error_and_exit self#pos "PHP.foreach() only accepts anonymous function declaration for second argument."
+		(**
+			Writes TCall to output buffer
+		*)
+		method private write_expr_call target_expr args =
+			let target_expr = reveal_expr target_expr in
+			(match target_expr.eexpr with
+				| TConst TSuper -> self#write "parent::__construct"
+				| TField (expr, FClosure (_,_)) -> self#write_expr (parenthesis target_expr)
+				| _ -> self#write_expr target_expr
+			);
+			self#write "(";
+			write_args buffer self#write_expr args;
+			self#write ")";
+		(**
+			Writes a name of a function or a constant from global php namespace
+		*)
+		method private write_expr_php_global target_expr =
+			match target_expr.eexpr with
+				| TField (_, FStatic (_, field)) -> self#write (field_name field)
+				| _ -> fail self#pos (try assert false with Assert_failure mlpos -> mlpos)
+		(**
+			Writes access to PHP class constant
+		*)
+		method private write_expr_php_class_const target_expr =
+			match target_expr.eexpr with
+				| TField (_, FStatic (ecls, field)) ->
+					self#write ((self#use_t (TInst (ecls, []))) ^ "::" ^ (field_name field))
+				| _ -> fail self#pos (try assert false with Assert_failure mlpos -> mlpos)
+		(**
+			Writes TNew to output buffer
+		*)
+		method private write_expr_new inst_class args =
+			let needs_php_prefix = not inst_class.cl_extern in
+			self#write ("new " ^ (self#use ~prefix:needs_php_prefix inst_class.cl_path) ^ "(");
+			write_args buffer self#write_expr args;
+			self#write ")"
+		(**
+			Writes ternary operator expressions to output buffer
+		*)
+		method private write_expr_ternary condition if_expr (else_expr:texpr) pos =
+			let parent_is_if = match self#parent_expr with Some { eexpr = TIf _ } -> true | _ -> false in
+			if parent_is_if then self#write "(";
+			(match condition.eexpr with
+				| TParenthesis expr -> self#write_expr expr;
+				| _ -> self#write_expr else_expr
+			);
+			self#write " ? ";
+			self#write_expr if_expr;
+			self#write " : ";
+			self#write_expr else_expr;
+			if parent_is_if then self#write ")"
+		(**
+			Writes "if...else..." expression to output buffer
+		*)
+		method private write_expr_if condition if_expr (else_expr:texpr option) =
+			let is_ternary =
+				if self#parent_expr_is_block then
+					false
+				else
+					match (if_expr.eexpr, else_expr) with
+						| (TBlock _, _) | (_, Some { eexpr=TBlock _ }) -> false
+						| _ -> true
+			in
+			if is_ternary then
+				match else_expr with
+					| None -> fail self#pos (try assert false with Assert_failure mlpos -> mlpos)
+					| Some expr ->
+						self#write_expr_ternary condition if_expr expr self#pos
+			else begin
+				self#write "if ";
+				self#write_expr condition;
+				self#write " ";
+				self#write_as_block if_expr;
+				(match else_expr with
+					| None -> ()
+					| Some expr ->
+						self#write " else ";
+						match expr.eexpr with
+							| TIf _ -> self#write_expr expr
+							| _ -> self#write_as_block expr
+				)
+			end
+		(**
+			Writes TWhile ("while..." or "do...while") to output buffer
+		*)
+		method private write_expr_while condition expr do_while =
+			match do_while with
+				| NormalWhile ->
+					self#write "while ";
+					self#write_expr condition;
+					self#write " ";
+					self#write_as_block ~unset_locals:true expr
+				| DoWhile ->
+					self#write "do ";
+					self#write_as_block ~unset_locals:true expr;
+					self#write " while ";
+					self#write_expr condition
+		(**
+			Writes TSwitch to output buffer
+		*)
+		method private write_expr_switch switch cases default =
+			let write_case (conditions, expr) =
+				List.iter
+					(fun condition ->
+						self#write_indentation;
+						self#write "case ";
+						self#write_expr condition;
+						self#write ":\n";
+					)
+					conditions;
+				self#indent_more;
+				self#write_indentation;
+				self#write_as_block ~inline:true expr;
+				self#write_statement "break";
+				self#indent_less
+			in
+			self#write "switch ";
+			self#write_expr switch;
+			self#write " {\n";
+			self#indent_more;
+			List.iter write_case cases;
+			(match default with
+				| None -> ()
+				| Some expr ->
+					self#write_line "default:";
+					self#indent_more;
+					self#write_indentation;
+					self#write_as_block ~inline:true expr;
+					self#write_statement "break";
+					self#indent_less
+			);
+			self#indent_less;
+			self#write_indentation;
+			self#write "}"
+		(**
+			Write TEnumParameter expression to output buffer
+		*)
+		method private write_expr_enum_parameter expr constructor index =
+			(match expr.eexpr with
+				| TConst TNull -> self#write "(null)"
+				| _ -> self#write_expr expr
+			);
+			self#write ("->params[" ^ (string_of_int index) ^ "]")
+		(**
+			Writes argument for function declarations or calls
+		*)
+		method write_arg with_optionals (arg_name, optional, (arg_type:Type.t)) =
+			self#write ("$" ^ arg_name ^ (if with_optionals && optional then " = null" else ""))
+		(**
+			Writes argument with optional value for function declarations
+		*)
+		method write_function_arg arg =
+			match arg with
+				| ({ v_name = arg_name; v_type = arg_type }, default_value) ->
+					vars#declared arg_name;
+					if is_ref arg_type then self#write "&";
+					self#write ("$" ^ arg_name);
+					match default_value with
+						| None -> ()
+						| Some const ->
+							self#write " = ";
+							self#write_expr_const const
+	end
+
+(**
+	Builds enum contents
+*)
+class enum_builder ctx (enm:tenum) =
+	object (self)
+		inherit type_builder ctx (get_wrapper (TEnumDecl enm))
+		(**
+			Writes type declaration line to output buffer.
+			E.g. "class SomeClass extends Another implements IFace"
+		*)
+		method private write_declaration =
+			self#write_doc (DocClass enm.e_doc);
+			self#write ("class " ^ self#get_name ^ " extends " ^ (self#use hxenum_type_path))
+		(**
+			Writes type body to output buffer.
+			E.g. for "class SomeClass { <BODY> }" writes <BODY> part.
+		*)
+		method private write_body =
+			let write_empty_lines = ref false in
+			PMap.iter
+				(fun name field ->
+					if !write_empty_lines then
+						self#write_empty_lines
+					else
+						write_empty_lines := true;
+					self#write_constructor name field
+				)
+				enm.e_constrs;
+			self#write_reflection
+		(**
+			Writes constructor declaration to output buffer
+		*)
+		method private write_constructor name (field:tenum_field) =
+			let args =
+				match follow field.ef_type with
+					| TFun (args, _) -> args
+					| TEnum _ -> []
+					| _ -> fail field.ef_pos (try assert false with Assert_failure mlpos -> mlpos)
+			in
+			self#indent 1;
+			self#write_doc (DocMethod (args, TEnum (enm, []), field.ef_doc));
+			self#write_indentation;
+			self#write ("static public function " ^ name ^ " (");
+			write_args buffer (self#write_arg true) args;
+			self#write ") {\n";
+			self#indent_more;
+			self#write_indentation;
+			self#write "return ";
+			let index_str = string_of_int field.ef_index in
+			(match args with
+				| [] -> self#write ((self#use hxenum_type_path) ^ "::singleton(static::class, '" ^ name ^ "', " ^ index_str ^")")
+				| args ->
+					self#write ("new " ^ self#get_name ^ "('" ^ name ^ "', " ^ index_str ^", [");
+					write_args buffer (fun (name, _, _) -> self#write ("$" ^ name)) args;
+					self#write "])"
+			);
+			self#write ";\n";
+			self#indent_less;
+			self#write_line "}"
+		(**
+			Writes special methods for reflection
+		*)
+		method private write_reflection =
+			(* __hx__list *)
+			self#write_empty_lines;
+			self#indent 1;
+			self#write_line "/**";
+			self#write_line " * Returns array of (constructorIndex => constructorName)";
+			self#write_line " *";
+			self#write_line " * @return string[]";
+			self#write_line " */";
+			self#write_line "static public function __hx__list () {";
+			self#indent_more;
+			self#write_line "return [";
+			self#indent_more;
+			PMap.iter
+				(fun name field ->
+					self#write_line ((string_of_int field.ef_index) ^ " => '" ^ name ^ "',")
+				)
+				enm.e_constrs;
+			self#indent_less;
+			self#write_statement "]";
+			self#indent_less;
+			self#write_line "}";
+			(* __hx__paramsCount *)
+			self#write_empty_lines;
+			self#indent 1;
+			self#write_line "/**";
+			self#write_line " * Returns array of (constructorName => parametersCount)";
+			self#write_line " *";
+			self#write_line " * @return int[]";
+			self#write_line " */";
+			self#write_line "static public function __hx__paramsCount () {";
+			self#indent_more;
+			self#write_line "return [";
+			self#indent_more;
+			PMap.iter
+				(fun name field ->
+					let count = match follow field.ef_type with
+						| TFun (params, _) -> List.length params
+						| TEnum _ -> 0
+						| _ -> fail field.ef_pos (try assert false with Assert_failure mlpos -> mlpos)
+					in
+					self#write_line ("'" ^ name ^ "' => " ^ (string_of_int count) ^ ",")
+				)
+				enm.e_constrs;
+			self#indent_less;
+			self#write_statement "]";
+			self#indent_less;
+			self#write_line "}";
+		(**
+			Method `__hx__init` is not needed for enums
+		**)
+		method private write_hx_init_body = ()
+		(**
+			No need for additional initialization of enum instances
+		*)
+		method private write_instance_initialization = ()
+		(**
+			No need for additional type initialization for enums
+		*)
+		method private write_pre_hx_init = ()
+	end
+
+(**
+	Builds class contents
+*)
+class class_builder ctx (cls:tclass) =
+	object (self)
+		inherit type_builder ctx (get_wrapper (TClassDecl cls)) as super
+		(**
+			Indicates if type should be declared as `final`
+		*)
+		method is_final =
+			if not (Meta.has Meta.Final cls.cl_meta) then
+				false
+			else begin
+				let hacked = ref false in
+				List.iter
+					(fun com_type ->
+						if not !hacked then
+							match com_type with
+								| TClassDecl tcls ->
+									if self#extended_by tcls then hacked := Meta.has Meta.Hack tcls.cl_meta
+								| _ -> ()
+					)
+					ctx.types;
+				not !hacked
+			end
+		(**
+			Indicates if `field` should be declared as `final`
+		*)
+		method is_final_field (field:tclass_field) : bool =
+			Meta.has Meta.Final field.cf_meta
+		(**
+			Recursively check if current class is a parent class for a `child`
+		*)
+		method private extended_by child =
+			let result =
+				if child == cls then
+					false
+				else
+					let rec check current =
+						match current.cl_super with
+							| None -> false
+							| Some (scls, _) ->
+								if scls == cls then true else check scls
+					in
+					check child
+			in
+			result
+		(**
+			Writes type declaration line to output buffer.
+			E.g. "class SomeClass extends Another implements IFace"
+		*)
+		method private write_declaration =
+			if self#is_final then self#write "final ";
+			self#write_doc (DocClass cls.cl_doc);
+			self#write (if cls.cl_interface then "interface " else "class ");
+			self#write self#get_name;
+			(
+				match cls.cl_super with
+					| None -> ();
+					| Some (super_class, params) ->
+						let super_name = self#use_t (TInst (super_class, params)) in
+						self#write (" extends " ^ super_name)
+			);
+			if List.length cls.cl_implements > 0 then begin
+				self#write (if cls.cl_interface then " extends " else " implements ");
+				let use_interface iface =
+					match iface with
+						| (i, params) -> self#use_t (TInst (i, params))
+				in
+				let interfaces = List.map use_interface cls.cl_implements in
+				self#write (String.concat ", " interfaces);
+			end;
+		(**
+			Returns either user-defined constructor or creates empty constructor if instance initialization is required.
+		*)
+		method private get_constructor : tclass_field option =
+			match cls.cl_constructor with
+				| Some field -> Some field
+				| None ->
+					if not self#constructor_is_required then
+						None
+					else
+						Some {
+							cf_name = "new";
+							cf_type = TFun ([], get_void ctx);
+							cf_public = true;
+							cf_pos = cls.cl_pos;
+							cf_name_pos = cls.cl_pos;
+							cf_doc = None;
+							cf_meta = [];
+							cf_kind = Method MethNormal;
+							cf_params = [];
+							cf_expr = Some {
+								eexpr = TFunction {
+									tf_args = [];
+									tf_type = get_void ctx;
+									tf_expr = { eexpr = TBlock []; epos = cls.cl_pos; etype = get_void ctx; };
+								};
+								epos = cls.cl_pos;
+								etype = get_void ctx;
+							};
+							cf_expr_unoptimized = None;
+							cf_overloads = [];
+						}
+		(**
+			Writes type body to output buffer.
+			E.g. for "class SomeClass { <BODY> }" writes <BODY> part.
+		*)
+		method private write_body =
+			let at_least_one_field_written = ref false in
+			let write_if_constant _ field =
+				match field.cf_kind with
+					| Var { v_read = AccInline; v_write = AccNever } ->
+						at_least_one_field_written := true;
+						self#write_field true field
+					| _ -> ()
+			and write_if_method is_static _ field =
+				match field.cf_kind with
+					| Var _ -> ()
+					| Method MethDynamic when is_static -> ()
+					| Method _ ->
+						if !at_least_one_field_written then self#write_empty_lines;
+						at_least_one_field_written := true;
+						self#write_field is_static field
+			and write_if_var is_static _ field =
+				match field.cf_kind with
+					| Var { v_read = AccInline; v_write = AccNever } -> ()
+					| Method MethDynamic ->
+						at_least_one_field_written := true;
+						let kind = Var { v_read = AccNormal; v_write = AccNormal; } in
+						self#write_field is_static { field with cf_kind = kind }
+					| Var _ ->
+						at_least_one_field_written := true;
+						self#write_field is_static field
+					| Method _ -> ()
+			in
+			if boot_type_path = self#get_type_path then begin
+				self#write_php_prefix ();
+				at_least_one_field_written := true
+			end;
+		 	if not cls.cl_interface then begin
+		 		(* Inlined statc vars (constants) *)
+				PMap.iter (write_if_constant) cls.cl_statics;
+				if !at_least_one_field_written then self#write_empty_lines;
+				at_least_one_field_written := false;
+		 		(* Statc vars *)
+				PMap.iter (write_if_var true) cls.cl_statics;
+				if !at_least_one_field_written then self#write_empty_lines;
+				at_least_one_field_written := false;
+				(* instance vars *)
+				PMap.iter (write_if_var false) cls.cl_fields
+			end;
+			(* Statc methods *)
+			PMap.iter (write_if_method true) cls.cl_statics;
+			(* Constructor *)
+			(match self#get_constructor with
+				| Some field -> write_if_method false "new" field
+				| None -> ()
+			);
+			(* Instance methods *)
+			PMap.iter (write_if_method false) cls.cl_fields;
+			(* Generate `__toString()` if not defined by user, but has `toString()` *)
+			self#write_toString_if_required
+		method private write_toString_if_required =
+			if PMap.exists "toString" cls.cl_fields then
+				if (not cls.cl_interface) && (not (PMap.exists "__toString" cls.cl_statics)) && (not (PMap.exists "__toString" cls.cl_fields)) then
+					begin
+						self#write_empty_lines;
+						self#indent 1;
+						self#write_line "public function __toString() {";
+						self#indent_more;
+						self#write_line "return $this->toString();";
+						self#indent_less;
+						self#write_line "}"
+					end
+		(**
+			Check if this class requires constructor to be generated even if there is no user-defined one
+		*)
+		method private constructor_is_required =
+			if List.length self#get_namespace > 0 then
+				false
+			else begin
+				let required = ref false in
+				List.iter
+					(fun field ->
+						if not !required then
+							required := (String.lowercase field.cf_name = String.lowercase self#get_name)
+					)
+					(cls.cl_ordered_statics @ cls.cl_ordered_fields);
+				!required
+			end
+		(**
+			Writes `--php-prefix` value as class constant PHP_PREFIX
+		*)
+		method private write_php_prefix () =
+			let prefix = String.concat "\\" (get_php_prefix ctx) in
+			let indentation = String.length indentation in
+			self#indent 1;
+			self#write_statement ("const PHP_PREFIX = \"" ^ (String.escaped prefix) ^ "\"");
+			self#indent indentation
+		(**
+			Writes expressions for `__hx__init` method
+		*)
+		method private write_hx_init_body =
+			(* `static dynamic function` initialization *)
+			let write_dynamic_method_initialization field =
+				let field_access = "self::$" ^ (field_name field) in
+				self#write_indentation;
+				self#write (field_access ^ " = ");
+				(match field.cf_expr with
+					| Some expr -> self#write_expr expr
+					| None -> fail field.cf_pos (try assert false with Assert_failure mlpos -> mlpos)
+				);
+				self#write ";\n"
+			in
+			PMap.iter
+				(fun _ field ->
+					match field.cf_kind with
+						| Method MethDynamic -> write_dynamic_method_initialization field
+						| _ -> ()
+				)
+				cls.cl_statics;
+			(* `static var` initialization *)
+			let write_var_initialization _ field =
+				let write_assign expr =
+					self#write_indentation;
+					self#write ("self::$" ^ (field_name field) ^ " = ");
+					self#write_expr expr
+				in
+				(* Do not generate fields for RTTI meta, because this generator uses another way to store it *)
+				let is_auto_meta_var = field.cf_name = "__meta__" && (has_rtti_meta ctx wrapper#get_module_type) in
+				if (is_var_with_nonconstant_expr field) && (not is_auto_meta_var) then begin
+					(match field.cf_expr with
+						| None -> ()
+						(* There can be not-inlined blocks when compiling with `-debug` *)
+						| Some { eexpr = TBlock exprs } ->
+							let rec write_per_line exprs =
+								match exprs with
+									| [] -> ()
+									| [expr] -> write_assign expr
+									| expr :: rest ->
+										self#write_indentation;
+										self#write_expr expr;
+										self#write ";\n";
+										write_per_line rest
+							in
+							write_per_line exprs
+						| Some expr -> write_assign expr
+					);
+					self#write ";\n"
+				end
+			in
+			PMap.iter write_var_initialization cls.cl_statics
+		(**
+			Writes single field to output buffer.
+		*)
+		method private write_field is_static field =
+			match field.cf_kind with
+				| Var { v_read = AccInline; v_write = AccNever } -> self#write_const field
+				| Var _ when is_real_var field ->
+					(* Do not generate fields for RTTI meta, because this generator uses another way to store it *)
+					let is_auto_meta_var = is_static && field.cf_name = "__meta__" && (has_rtti_meta ctx wrapper#get_module_type) in
+					if not is_auto_meta_var then self#write_var field is_static;
+				| Var _ -> ()
+				| Method MethMacro -> ()
+				| Method MethDynamic when is_static -> ()
+				| Method MethDynamic -> self#write_dynamic_method field
+				| Method _ -> self#write_method field is_static
+		(**
+			Writes var-field to output buffer
+		*)
+		method private write_var field is_static =
+			self#indent 1;
+			self#write_doc (DocVar (self#use_t field.cf_type, field.cf_doc));
+			self#write_indentation;
+			if is_static then self#write "static ";
+			let visibility = get_visibility field.cf_meta in
+			self#write (visibility ^ " $" ^ (field_name field));
+			match field.cf_expr with
+				| None -> self#write ";\n"
+				| Some expr ->
+					match expr.eexpr with
+						| TConst _ ->
+							self#write " = ";
+							self#write_expr expr;
+							self#write ";\n"
+						| _ -> self#write ";\n"
+		(**
+			Writes "inline var" to output buffer as constant
+		*)
+		method private write_const field =
+			self#indent 1;
+			self#write_doc (DocVar (self#use_t field.cf_type, field.cf_doc));
+			self#write_indentation;
+			self#write ("const " ^ (field_name field) ^ " = ");
+			match field.cf_expr with
+				| None -> fail self#pos (try assert false with Assert_failure mlpos -> mlpos)
+				| Some expr ->
+					self#write_expr expr;
+					self#write ";\n"
+		(**
+			Writes method to output buffer
+		*)
+		method private write_method field is_static =
+			vars#clear;
+			self#indent 1;
+			let (args, return_type) = get_function_signature field in
+			List.iter (fun (arg_name, _, _) -> vars#declared arg_name) args;
+			self#write_doc (DocMethod (args, return_type, field.cf_doc));
+			self#write_indentation;
+			if self#is_final_field field then self#write "final ";
+			if is_static then self#write "static ";
+			self#write ((get_visibility field.cf_meta) ^ " ");
+			match field.cf_expr with
+				| None ->
+					self#write ("function " ^ (field_name field) ^ " (");
+					write_args buffer (self#write_arg true) args;
+					self#write ")";
+					self#write " ;\n"
+				| Some { eexpr = TFunction fn } ->
+					let name = if field.cf_name = "new" then "__construct" else (field_name field) in
+					self#write_expr_function ~name:name fn;
+					self#write "\n"
+				| _ -> fail field.cf_pos (try assert false with Assert_failure mlpos -> mlpos)
+		(**
+			Writes dynamic method to output buffer.
+			Only for non-static methods. Static methods are created as static vars in `__hx__init`.
+		*)
+		method private write_dynamic_method field =
+			vars#clear;
+			self#indent 1;
+			let (args, return_type) = get_function_signature field in
+			List.iter (fun (arg_name, _, _) -> vars#declared arg_name) args;
+			self#write_doc (DocMethod (args, return_type, field.cf_doc));
+			self#write_indentation;
+			self#write ((get_visibility field.cf_meta) ^ " function " ^ (field_name field));
+			(match field.cf_expr with
+				| None -> (* interface *)
+					self#write " (";
+					write_args buffer (self#write_arg true) args;
+					self#write ");\n";
+				| Some { eexpr = TFunction fn } -> (* normal class *)
+					self#write " (";
+					write_args buffer self#write_function_arg fn.tf_args;
+					self#write ")\n";
+					self#write_line "{";
+					self#indent_more;
+					self#write_indentation;
+					let field_access = "$this->" ^ (field_name field)
+					and default_value = "$this->__hx__default__" ^ (field_name field) in
+					self#write ("if (" ^ field_access ^ " !== " ^ default_value ^ ") return call_user_func_array(" ^ field_access ^ ", func_get_args());\n");
+					self#write_fake_block fn.tf_expr;
+					self#indent_less;
+					self#write_line "}";
+					(* Don't forget to create a field for default value *)
+					self#write_statement ("protected $__hx__default__" ^ (field_name field))
+				| _ -> fail field.cf_pos (try assert false with Assert_failure mlpos -> mlpos)
+			);
+		(**
+			Writes initialization code for instances of this class
+		*)
+		method private write_instance_initialization =
+			let init_dynamic_method field =
+				let field_name = field_name field in
+				let default_field = "$this->__hx__default__" ^ field_name in
+				self#write_line ("if (!" ^ default_field ^ ") {");
+				self#indent_more;
+				self#write_statement (default_field ^ " = new " ^ (self#use hxclosure_type_path) ^ "($this, '" ^ field_name ^ "')");
+				self#write_statement ("if ($this->" ^ field_name ^ " === null) $this->" ^ field_name ^ " = " ^ default_field);
+				self#indent_less;
+				self#write_line "}"
+			in
+			PMap.iter
+				(fun _ field ->
+					match field.cf_kind with
+						| Method MethDynamic -> init_dynamic_method field
+						| _ -> ()
+				)
+				cls.cl_fields
+		(**
+			Writes additional initialization code, which should be called before `__hx__init()`
+		*)
+		method private write_pre_hx_init =
+			let getters = ref []
+			and setters = ref [] in
+			let collect field =
+				match field.cf_kind with
+					| Var { v_read = read; v_write = write } ->
+						if read = AccCall then getters := field.cf_name :: !getters;
+						if write = AccCall then setters := field.cf_name :: !setters;
+					| _ -> ()
+			in
+			List.iter collect cls.cl_ordered_fields;
+			List.iter collect cls.cl_ordered_statics;
+			let rec write lst =
+				match lst with
+					| [] -> ()
+					| [item] -> self#write_line ("'" ^ item ^ "' => true");
+					| item :: rest ->
+						self#write_line ("'" ^ item ^ "' => true,");
+						write rest
+			and type_name = get_full_type_name ~escape:true ~omit_first_slash:true (add_php_prefix ctx wrapper#get_type_path) in
+			let write_register register_method lst =
+				self#write_line ((self#use boot_type_path) ^ "::" ^ register_method ^ "('" ^ type_name ^ "', [");
+				self#indent_more;
+				write lst;
+				self#indent_less;
+				self#write_statement "])"
+			in
+			if List.length !getters > 0 then write_register "registerGetters" !getters;
+			if List.length !setters > 0 then write_register "registerSetters" !setters;
+	end
+
+(**
+	Handles generation process
+*)
+class generator (com:context) =
+	object (self)
+		val mutable build_dir = ""
+		val root_dir = com.file
+		val mutable init_types = []
+		val mutable boot : (type_builder * string) option  = None
+		(**
+			Perform required actions before actual php files generation
+		*)
+		method initialize =
+			self#create_output_dirs;
+		(**
+			Generates php file for specified type
+		*)
+		method generate (builder:type_builder) =
+			let contents = builder#get_contents
+			and namespace = builder#get_namespace
+			and name = builder#get_name in
+			let filename = (create_dir_recursive (build_dir :: namespace)) ^ "/" ^ name ^ ".php" in
+			let channel = open_out filename in
+			output_string channel contents;
+			close_out channel;
+			if builder#get_type_path = boot_type_path then
+				boot <- Some (builder, filename)
+			else if builder#has_magic_init then
+				init_types <- (get_full_type_name (namespace, name)) :: init_types
+		(**
+			Perform actions which should be executed after all classes were processed
+		*)
+		method finalize : unit =
+			self#generate_magic_init;
+			self#generate_entry_point
+		(**
+			Generates calls to static __init__ methods in Boot.php
+		*)
+		method generate_magic_init : unit =
+			match init_types with
+				| [] -> ()
+				| _ ->
+					match boot with
+						| None -> fail dummy_pos (try assert false with Assert_failure mlpos -> mlpos)
+						| Some (_, filename) ->
+							let channel = open_out_gen [Open_creat; Open_text; Open_append] 0o644 filename in
+							List.iter
+								(fun class_name -> output_string channel (class_name ^ "::__hx__init();\n"))
+								init_types;
+							close_out channel
+		(**
+			Creates `index.php` which can be used as entry-point for standalone Haxe->PHP app
+		*)
+		method generate_entry_point =
+			match self#get_main_class with
+				| None -> ()
+				| Some main_class ->
+					let channel = open_out (root_dir ^ "/index.php") in
+					output_string channel "<?php\n";
+					output_string channel ("set_include_path(__DIR__.'/" ^ (String.concat "/" self#get_lib_path) ^ "');\n");
+					output_string channel "spl_autoload_register(\n";
+					output_string channel "	function($class){\n";
+					output_string channel "		$file = stream_resolve_include_path(str_replace('\\\\', '/', $class) .'.php');\n";
+					output_string channel "		if ($file) {\n";
+					output_string channel "			include_once $file;\n";
+					output_string channel "		}\n";
+					output_string channel "	}\n";
+					output_string channel ");\n";
+					(match boot with
+						| None -> fail dummy_pos (try assert false with Assert_failure mlpos -> mlpos)
+						| Some (builder, filename) ->
+							let boot_class = get_full_type_name (add_php_prefix com builder#get_type_path) in
+							output_string channel (boot_class ^ "::__hx__init();\n")
+					);
+					output_string channel (main_class ^ "::main();\n");
+					close_out channel
+		(**
+			Create necessary directories  before processing types
+		*)
+		method private create_output_dirs =
+			let build_path = (root_dir :: self#get_lib_path) in
+			build_dir <- create_dir_recursive build_path
+		(**
+			Returns path from `index.php` to directory which will contain all generated classes
+		*)
+		method private get_lib_path : string list =
+			match com.php_lib with
+				| None -> ["lib"];
+				| Some path -> (Str.split (Str.regexp "/")  path)
+		(**
+			Returns FQN for main class if defined
+		*)
+		method private get_main_class : string option =
+			match com.main_class with
+				| None -> None
+				| Some type_path -> Some (get_full_type_name (add_php_prefix com type_path))
+	end
+
+(**
+	Entry point to Genphp7
+*)
+let generate (com:context) =
+	let gen = new generator com in
+	gen#initialize;
+	let generate com_type =
+		let wrapper = get_wrapper com_type in
+		if wrapper#needs_generation then
+			match com_type with
+				| TClassDecl cls -> gen#generate (new class_builder com cls);
+				| TEnumDecl enm -> gen#generate (new enum_builder com enm);
+				| TTypeDecl typedef -> ();
+				| TAbstractDecl abstr -> ()
+	in
+	List.iter generate com.types;
+	gen#finalize;
+	Hashtbl.iter
+		(fun name data ->
+			write_resource com.file name data
+		)
+		com.resources;
+	clear_wrappers ();

+ 2 - 2
src/generators/genpy.ml

@@ -2461,7 +2461,7 @@ module Generator = struct
 	let run com =
 		Transformer.init com;
 		let ctx = mk_context com in
-		Codegen.map_source_header com (fun s -> print ctx "# %s\n" s);
+		Codegen.map_source_header com (fun s -> print ctx "# %s\n# coding: utf-8\n" s);
 		if has_feature ctx "closure_Array" || has_feature ctx "closure_String" then
 			spr ctx "from functools import partial as _hx_partial";
 		gen_imports ctx;
@@ -2478,4 +2478,4 @@ module Generator = struct
 end
 
 let generate com =
-	Generator.run com
+	Generator.run com

+ 27 - 1
src/generators/hlinterp.ml

@@ -1496,6 +1496,31 @@ let load_native ctx lib name t =
 			(function
 			| [] -> VBool true
 			| _ -> assert false)
+		| "sys_string" ->
+			let cached_sys_name = ref None in
+			(function
+			| [] ->
+				VBytes (caml_to_hl (match Sys.os_type with
+				| "Unix" ->
+					(match !cached_sys_name with
+					| Some n -> n
+					| None ->
+						let ic = Unix.open_process_in "uname" in
+						let uname = (match input_line ic with
+							| "Darwin" -> "Mac"
+							| n -> n
+						) in
+						close_in ic;
+						cached_sys_name := Some uname;
+						uname)
+				| "Win32" | "Cygwin" -> "Windows"
+				| s -> s))
+			| _ ->
+				assert false)
+		| "sys_is64" ->
+			(function
+			| [] -> VBool (Sys.word_size = 64)
+			| _ -> assert false)
 		| "hash" ->
 			(function
 			| [VBytes str] -> VInt (hash ctx (hl_to_caml str))
@@ -1912,7 +1937,8 @@ let load_native ctx lib name t =
 						| '1'..'9' | '+' | '$' | '^' | '*' | '?' | '.' | '[' | ']' ->
 							Buffer.add_char buf '\\';
 							Buffer.add_char buf c;
-						| _ -> failwith ("Unsupported escaped char '" ^ String.make 1 c ^ "'"));
+						| _ ->
+							Buffer.add_char buf c);
 						loop c false l
 					| c :: l ->
 						match c with

+ 2 - 1
src/macro/interp.ml

@@ -1963,7 +1963,8 @@ let reg_lib =
 						| '1'..'9' | '+' | '$' | '^' | '*' | '?' | '.' | '[' | ']' ->
 							Buffer.add_char buf '\\';
 							Buffer.add_char buf c;
-						| _ -> failwith ("Unsupported escaped char '" ^ String.make 1 c ^ "'"));
+						| _ ->
+							Buffer.add_char buf c);
 						loop c false l
 					| c :: l ->
 						match c with

+ 12 - 2
src/main.ml

@@ -277,7 +277,14 @@ module Initialize = struct
 				add_std "lua";
 				"lua"
 			| Php ->
-				add_std "php";
+				if Common.php7 com then
+					begin
+						com.package_rules <- PMap.add "php" (Directory "php7") com.package_rules;
+						com.package_rules <- PMap.add "php7" Forbidden com.package_rules;
+						add_std "php7"
+					end
+				else
+					add_std "php";
 				"php"
 			| Cpp ->
 				Common.define_value com Define.HxcppApiLevel "331";
@@ -346,7 +353,10 @@ let generate tctx ext xml_out interp swf_header =
 		| Lua ->
 			Genlua.generate,"lua"
 		| Php ->
-			Genphp.generate,"php"
+			if Common.php7 com then
+				Genphp7.generate,"php"
+			else
+				Genphp.generate,"php"
 		| Cpp ->
 			Gencpp.generate,"cpp"
 		| Cs ->

+ 1 - 0
src/optimization/analyzerConfig.ml

@@ -118,6 +118,7 @@ let update_config_from_meta com config meta =
 				| EConst (Ident s) when s = flag_user_var_fusion -> {config with user_var_fusion = true}
 				| EConst (Ident s) when s = "no_" ^ flag_user_var_fusion -> {config with user_var_fusion = false}
 				| EConst (Ident s) when s = flag_fusion_debug -> {config with fusion_debug = true}
+				| EConst (Ident s) when s = "as_var" -> config
 				| _ ->
 					let s = Ast.s_expr e in
 					com.warning (StringError.string_error s all_flags ("Unrecognized analyzer option: " ^ s)) (pos e);

+ 20 - 7
src/optimization/analyzerTexpr.ml

@@ -123,13 +123,13 @@ let rec can_be_used_as_value com e =
 		(* | 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
+		| TNew _ when com.platform = Php && not (Common.php7 com) -> raise Exit
 		| TFunction _ -> ()
 		| _ -> Type.iter loop e
 	in
 	try
 		begin match com.platform,e.eexpr with
-			| (Cs | Cpp | Java | Flash),TConst TNull -> raise Exit
+			| (Cs | Cpp | Java | Flash | Lua),TConst TNull -> raise Exit
 			| _ -> ()
 		end;
 		loop e;
@@ -155,9 +155,22 @@ let is_ref_type = function
 	| TAbstract({a_path=["hl";"types"],"Ref"},_) -> true
 	| _ -> false
 
-let is_asvar_type t = match follow t with
-	| TAbstract({a_path = (["haxe";"extern"],"AsVar")},_) -> true
-	| _ -> false
+let rec is_asvar_type t =
+	let check meta =
+		AnalyzerConfig.has_analyzer_option meta "as_var"
+	in
+	match t with
+	| TInst(c,_) -> check c.cl_meta
+	| TEnum(en,_) -> check en.e_meta
+	| TType(t,tl) -> check t.t_meta || (is_asvar_type (apply_params t.t_params tl t.t_type))
+	| TAbstract(a,_) -> check a.a_meta
+	| TLazy f -> is_asvar_type (!f())
+	| TMono r ->
+		(match !r with
+		| Some t -> is_asvar_type t
+		| _ -> false)
+	| _ ->
+		false
 
 let type_change_ok com t1 t2 =
 	if t1 == t2 then
@@ -669,7 +682,7 @@ module Fusion = struct
 							let el = List.map replace el in
 							let e2 = replace e2 in
 							e2,el
-						| Php | Cpp  when not (Common.defined com Define.Cppia) ->
+						| Php | Cpp  when not (Common.defined com Define.Cppia) && not (Common.php7 com) ->
 							let is_php_safe e1 =
 								let rec loop e = match e.eexpr with
 									| TCall _ -> raise Exit
@@ -781,7 +794,7 @@ module Fusion = struct
 							let e3 = replace e3 in
 							if not !found && has_state_read ir then raise Exit;
 							{e with eexpr = TBinop(OpAssign,{ea with eexpr = TArray(e1,e2)},e3)}
-						| TBinop(op,e1,e2) when (match com.platform with Cpp | Php -> true | _ -> false) ->
+						| TBinop(op,e1,e2) when (match com.platform with Cpp | Php when not (Common.php7 com) -> true | _ -> false) ->
 							let e1 = replace e1 in
 							let temp_found = !found in
 							found := false;

+ 28 - 20
src/optimization/analyzerTexprTransformer.ml

@@ -246,6 +246,7 @@ let rec func ctx bb tf t p =
 		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 =
+		(* TODO: this section shouldn't be here because it can be handled as part of the normal value processing *)
 		let rec loop bb e = match e.eexpr with
 			| TParenthesis e1 ->
 				loop bb e1
@@ -255,6 +256,7 @@ let rec func ctx bb tf t p =
 						bb,e
 					| e1 :: el ->
 						let bb = block_element bb e1 in
+						if bb == g.g_unreachable then raise Exit;
 						loop2 bb el
 					| [] ->
 						assert false
@@ -264,27 +266,33 @@ let rec func ctx bb tf t p =
 			| _ ->
 				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
-		let close = push_name v.v_name in
-		let bb = try
-			block_element_plus bb (map_values assign e) (fun e -> mk (TVar(v,Some e)) ctx.com.basic.tvoid ev.epos)
-		with Exit ->
-			let bb,e = value bb e in
-			add_texpr bb (mk (TVar(v,Some e)) ctx.com.basic.tvoid ev.epos);
+		let generate bb e =
+			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
+			let close = push_name v.v_name in
+			let bb = try
+				block_element_plus bb (map_values assign e) (fun e -> mk (TVar(v,Some e)) ctx.com.basic.tvoid ev.epos)
+			with Exit ->
+				let bb,e = value bb e in
+				add_texpr bb (mk (TVar(v,Some e)) ctx.com.basic.tvoid ev.epos);
+				bb
+			in
+			close();
 			bb
 		in
-		close();
-		bb
+		try
+			let bb,e = loop bb e in
+			generate bb e
+		with Exit ->
+			g.g_unreachable
 	and block_element_plus bb (e,efinal) f =
 		let bb = block_element bb e in
 		let bb = match efinal with
@@ -481,7 +489,7 @@ let rec func ctx bb tf t p =
 			let bb_try_next = block bb_try e1 in
 			close();
 			(* We always want to keep catch-blocks, so let's add a pseudo CFG edge if it's unreachable. *)
-			if bb_exc.bb_incoming = [] then add_cfg_edge bb_try_next bb_exc CFGMaybeThrow;
+			if bb_exc.bb_incoming = [] then add_cfg_edge (if bb_try_next == g.g_unreachable then bb_try else bb_try_next) bb_exc CFGMaybeThrow;
 			let is_reachable = ref (not (bb_try_next == g.g_unreachable)) in
 			let catches = List.map (fun (v,e) ->
 				let bb_catch = create_node (BKCatch v) e.etype e.epos in

+ 1 - 1
src/optimization/filters.ml

@@ -901,7 +901,7 @@ let add_meta_field ctx t = match t with
 			f.cf_expr <- Some e;
 			let can_deal_with_interface_metadata () = match ctx.com.platform with
 				| Flash when Common.defined ctx.com Define.As3 -> false
-				| Php -> false
+				| Php when not (Common.php7 ctx.com) -> false
 				| _ -> true
 			in
 			if c.cl_interface && not (can_deal_with_interface_metadata()) then begin

+ 1 - 1
src/optimization/optimizer.ml

@@ -1286,7 +1286,7 @@ let inline_constructors ctx e =
 				if cf.cf_name <> "length" then
 					begin match (IntMap.find v.v_id !vars).ii_kind with
 					| IKArray _ -> cancel v e.epos
-					| _ -> ()
+					| _ -> (try ignore(get_field_var v cf.cf_name) with Not_found -> ignore(add_field_var v cf.cf_name e.etype));
 					end
 			| _ -> cancel v e.epos
 			end

+ 14 - 11
src/syntax/parser.ml

@@ -1228,27 +1228,30 @@ and parse_var_assignment = parser
 		end
 	| [< >] -> None
 
+and parse_var_assignment_resume vl name pn t s =
+	try
+		let eo = parse_var_assignment s in
+		((name,pn),t,eo)
+	with Display e ->
+		let v = ((name,pn),t,Some e) in
+		let e = (EVars(List.rev (v :: vl)),punion pn (pos e)) in
+		display e
+
 and parse_var_decls_next vl = parser
 	| [< '(Comma,p1); name,t,pn = parse_var_decl_head; s >] ->
-		begin try
-			let eo = parse_var_assignment s in
-			parse_var_decls_next (((name,pn),t,eo) :: vl) s
-		with Display e ->
-			let v = ((name,pn),t,Some e) in
-			let e = (EVars(List.rev (v :: vl)),punion p1 (pos e)) in
-			display e
-		end
+		let v_decl = parse_var_assignment_resume vl name pn t s in
+		parse_var_decls_next (v_decl :: vl) s
 	| [< >] ->
 		vl
 
 and parse_var_decls p1 = parser
 	| [< name,t,pn = parse_var_decl_head; s >] ->
-		let eo = parse_var_assignment s in
-		List.rev (parse_var_decls_next [(name,pn),t,eo] s)
+		let v_decl = parse_var_assignment_resume [] name pn t s in
+		List.rev (parse_var_decls_next [v_decl] s)
 	| [< s >] -> error (Custom "Missing variable identifier") p1
 
 and parse_var_decl = parser
-	| [< name,t,pn = parse_var_decl_head; eo = parse_var_assignment >] -> ((name,pn),t,eo)
+	| [< name,t,pn = parse_var_decl_head; v_decl = parse_var_assignment_resume [] name pn t >] -> v_decl
 
 and inline_function = parser
 	| [< '(Kwd Inline,_); '(Kwd Function,p1) >] -> true, p1

+ 1 - 1
src/typing/matcher.ml

@@ -672,7 +672,7 @@ module Useless = struct
 						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)
+						loop pAcc qAcc rAcc (((pat1 :: patterns2) :: (pat2 :: patterns2) :: pM)) (q1 :: q1 :: qM) (r1 :: r1 :: rM)
 					| (PatBind(_,pat1),_) :: patterns2 ->
 						loop2 (pat1 :: patterns2)
 					| _ ->

+ 1 - 0
src/typing/typeload.ml

@@ -3517,6 +3517,7 @@ let resolve_module_file com m remap p =
 			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

+ 19 - 20
src/typing/typer.ml

@@ -970,7 +970,7 @@ let error_require r p =
 		error "This field is not available with the current compilation flags" p
 	else
 	let r = if r = "sys" then
-		"a system platform (php,neko,cpp,etc.)"
+		"a system platform (php,php7,neko,cpp,etc.)"
 	else try
 		if String.sub r 0 5 <> "flash" then raise Exit;
 		let _, v = ExtString.String.replace (String.sub r 5 (String.length r - 5)) "_" "." in
@@ -1801,10 +1801,10 @@ let unify_int ctx e k =
 	with Typeload.Generic_Exception (msg,p) ->
 		error msg p)
 
-let call_to_string ctx e =
+let call_to_string ctx ?(resume=false) e =
 	(* Ignore visibility of the toString field. *)
 	ctx.meta <- (Meta.PrivateAccess,[],e.epos) :: ctx.meta;
-	let acc = type_field ctx e "toString" e.epos MCall in
+	let acc = type_field ~resume ctx e "toString" e.epos MCall in
 	ctx.meta <- List.tl ctx.meta;
 	!build_call_ref ctx acc [] (WithType ctx.t.tstring) e.epos
 
@@ -4012,23 +4012,28 @@ and maybe_type_against_enum ctx f with_type p =
 	try
 		begin match with_type with
 		| WithType t ->
-			let rec loop t = match follow t with
+			let rec loop stack t = match follow t with
 				| TEnum (en,_) ->
 					en.e_path,en.e_names,TEnumDecl en
 				| TAbstract ({a_impl = Some c} as a,_) when has_meta Meta.Enum a.a_meta ->
-					a.a_path,List.map (fun cf -> cf.cf_name) c.cl_ordered_fields,TAbstractDecl a
+					let fields = ExtList.List.filter_map (fun cf ->
+						if Meta.has Meta.Enum cf.cf_meta then Some cf.cf_name else None
+					) c.cl_ordered_statics in
+					a.a_path,fields,TAbstractDecl a
 				| TAbstract (a,pl) when not (Meta.has Meta.CoreType a.a_meta) ->
 					begin match get_abstract_froms a pl with
-						| [t] -> loop t
+						| [t2] ->
+							if (List.exists (fast_eq t) stack) then raise Exit;
+							loop (t :: stack) t2
 						| _ -> raise Exit
 					end
 				(* We might type against an enum constructor. *)
 				| TFun(_,tr) ->
-					loop tr
+					loop stack tr
 				| _ ->
 					raise Exit
 			in
-			let path,fields,mt = loop t in
+			let path,fields,mt = loop [] t in
 			let old = ctx.m.curmod.m_types in
 			let restore () = ctx.m.curmod.m_types <- old in
 			ctx.m.curmod.m_types <- ctx.m.curmod.m_types @ [mt];
@@ -4062,23 +4067,17 @@ and type_call ctx e el (with_type:with_type) p =
 		if Common.defined ctx.com Define.NoTraces then
 			null ctx.t.tvoid p
 		else
-		let mk_to_string_meta e = EMeta((Meta.ToString,[],pos e),e),pos e in
+		let mk_to_string_meta e = EMeta((Meta.ToString,[],null_pos),e),pos e in
 		let params = (match el with [] -> [] | _ -> [("customParams",null_pos),(EArrayDecl (List.map mk_to_string_meta el) , p)]) in
 		let infos = mk_infos ctx p params in
 		if (platform ctx.com Js || platform ctx.com Python) && el = [] && has_dce ctx.com then
 			let e = type_expr ctx e Value in
 			let infos = type_expr ctx infos Value in
-			let e = try
-				begin match follow e.etype with
-					| TInst({cl_path=[],"String"},_) -> raise Not_found
-					| TMono _ -> raise Not_found
-					| TDynamic _ -> raise Not_found
-					| _ -> ()
-				end;
-				let acc = type_field ~resume:true ctx e "toString" p MCall in
-				build_call ctx acc [] (WithType ctx.t.tstring) p
-			with Not_found ->
-				e
+			let e = match follow e.etype with
+				| TAbstract({a_impl = Some c},_) when PMap.mem "toString" c.cl_statics ->
+					call_to_string ctx e
+				| _ ->
+					e
 			in
 			let v_trace = alloc_unbound_var "`trace" t_dynamic p in
 			mk (TCall (mk (TLocal v_trace) t_dynamic p,[e;infos])) ctx.t.tvoid p

+ 17 - 4
std/DateTools.hx

@@ -33,10 +33,23 @@ class DateTools {
 	#elseif (neko && !(macro || interp))
 	static var date_format = neko.Lib.load("std","date_format",2);
 	#else
+	static var DAY_SHORT_NAMES = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
+	static var DAY_NAMES = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"];
+	static var MONTH_SHORT_NAMES = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
+	static var MONTH_NAMES = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"];
+	
 	private static function __format_get( d : Date, e : String ) : String {
 		return switch( e ){
 			case "%":
 				"%";
+			case "a":
+				DAY_SHORT_NAMES[d.getDay()];
+			case "A":
+				DAY_NAMES[d.getDay()];
+			case "b","h":
+				MONTH_SHORT_NAMES[d.getMonth()];
+			case "B":
+				MONTH_NAMES[d.getMonth()];
 			case "C":
 				untyped StringTools.lpad(Std.string(Std.int(d.getFullYear()/100)),"0",2);
 			case "d":
@@ -114,16 +127,16 @@ class DateTools {
 		supported.
 
 		```haxe
-		var t = DateTools.format(Date.now(), "%Y-%m-%d_%H:%M:%S"); 
+		var t = DateTools.format(Date.now(), "%Y-%m-%d_%H:%M:%S");
 		// 2016-07-08_14:44:05
 
-		var t = DateTools.format(Date.now(), "%r"); 
+		var t = DateTools.format(Date.now(), "%r");
 		// 02:44:05 PM
 
-		var t = DateTools.format(Date.now(), "%T"); 
+		var t = DateTools.format(Date.now(), "%T");
 		// 14:44:05
 
-		var t = DateTools.format(Date.now(), "%F"); 
+		var t = DateTools.format(Date.now(), "%F");
 		// 2016-07-08
 		```
 	**/

+ 1 - 7
std/cpp/ConstPointer.hx

@@ -21,7 +21,7 @@
  */
  package cpp;
 
-@:coreType @:include("cpp/Pointer.h") @:native("cpp.Pointer")
+@:coreType @:include("cpp/Pointer.h") @:native("cpp.Pointer") @:analyzer(as_var)
 extern class ConstPointer<T>
 {
    // ptr actually returns the pointer - not strictly a 'T' - for pointers to smart pointers
@@ -56,12 +56,6 @@ extern class ConstPointer<T>
 
    public function reinterpret<Other>():Pointer<Other>;
 
-   inline public function typeCast<Other>():Pointer<Other>
-   {
-      var tmp:haxe.extern.AsVar<Pointer<Other>> = reinterpret();
-      return tmp;
-   }
-
    public function rawCast<Other>():RawPointer<Other>;
 
    public function at(inIndex:Int):Reference<T>;

+ 12 - 13
std/cpp/NativeArray.hx

@@ -30,12 +30,14 @@ extern class NativeArray {
       NativeArray.setSize(result,length);
       return result;
    }
+
    #else
+
    @:native("_hx_create_array_length")
    public static function create<T>(length:Int):Array<T>;
    #end
 
-	public static inline function blit<T>( ioDestArray:Array<T>,
+   public static inline function blit<T>( ioDestArray:Array<T>,
 		inDestElement:Int, inSourceArray:Array<T>,
 		inSourceElement:Int, inElementCount:Int ): Void  {
 	untyped ioDestArray.blit(inDestElement, inSourceArray, inSourceElement, inElementCount);
@@ -58,21 +60,18 @@ extern class NativeArray {
       return Pointer.arrayElem(inArray,inIndex);
    }
 
-	public static inline function setData<T>( inArray:Array<T>,inData:Pointer<T>,inElementCount:Int ) : Void {
-      untyped inArray.setData(inData.raw,inElementCount);
-      }
-	public static inline function setUnmanagedData<T>( inArray:Array<T>,inData:ConstPointer<T>,inElementCount:Int ) : Void {
-      untyped inArray.setUnmanagedData(inData.raw,inElementCount);
-   }
+   @:nativeStaticExtension
+	public static function setData<T>( inArray:Array<T>,inData:Pointer<T>,inElementCount:Int ) : Void { }
+
+   @:nativeStaticExtension
+	public static function setUnmanagedData<T>( inArray:Array<T>,inData:ConstPointer<T>,inElementCount:Int ) : Void { }
 
+   @:nativeStaticExtension
+	public static function zero<T>( ioDestArray:Array<T>, ?inFirst:Int, ?inElements:Int ) : Void { };
 
-	public static inline function zero<T>( ioDestArray:Array<T>, ?inFirst:Int, ?inElements:Int ) : Void {
-		untyped ioDestArray.zero(inFirst, inElements);
-	};
+   @:nativeStaticExtension
+	public static function memcmp<T>( inArrayA:Array<T>, inArrayB:Array<T>) : Int { }
 
-	public static inline function memcmp<T>( inArrayA:Array<T>, inArrayB:Array<T>) : Int {
-		return untyped inArrayA.memcmp(inArrayB);
-	}
 
    #if cppia
 	public static inline function unsafeGet<T>( inDestArray:Array<T>, inIndex:Int) : T {

+ 2 - 1
std/cpp/Pointer.hx

@@ -24,6 +24,7 @@
 import haxe.extern.AsVar;
 
 @:coreType
+@:analyzer(as_var)
 extern class Pointer<T> extends ConstPointer<T> implements ArrayAccess<T>
 {
    public var ref(get,set):Reference<T>;
@@ -71,7 +72,7 @@ extern class Pointer<T> extends ConstPointer<T> implements ArrayAccess<T>
 
    inline public function toUnmanagedVector(elementCount:Int) : haxe.ds.Vector<T>
       return cast toUnmanagedArray(elementCount);
- 
+
 
    override public function inc():Pointer<T>;
    override public function dec():Pointer<T>;

+ 1 - 1
std/cpp/_std/haxe/ds/IntMap.hx

@@ -43,7 +43,7 @@ package haxe.ds;
   inline void set(int key, const ::cpp::Pointer<V> &value) {__int_hash_set(h,key,(Dynamic)value ); }
 
   template<typename VALUE>
-  inline Void set(Dynamic &key, const VALUE &value) { set( (int)key, value ); return null(); }
+  inline void set(Dynamic &key, const VALUE &value) { set( (int)key, value ); }
 ")
 @:coreApi class IntMap<T> implements haxe.Constraints.IMap<Int,T> {
 

+ 1 - 1
std/cpp/_std/haxe/ds/StringMap.hx

@@ -43,7 +43,7 @@ package haxe.ds;
   inline void set(String key, const ::cpp::Pointer<V> &value) {__string_hash_set(h,key,(Dynamic)value ); }
 
   template<typename VALUE>
-  inline Void set(Dynamic &key, const VALUE &value) { set( (String)key, value ); return null(); }
+  inline void set(Dynamic &key, const VALUE &value) { set( (String)key, value ); }
 ")
 @:coreApi class StringMap<T> implements haxe.Constraints.IMap<String,T> {
 	@:ifFeature("haxe.ds.StringMap.*")

+ 2 - 1
std/cpp/_std/sys/io/Process.hx

@@ -104,7 +104,8 @@ class Process {
 		return NativeProcess.process_pid(p);
 	}
 
-	public function exitCode() : Int {
+	public function exitCode( block : Bool = true ) : Null<Int> {
+		if( block == false ) throw "Non blocking exitCode() not supported on this platform";
 		return NativeProcess.process_exit(p);
 	}
 

+ 2 - 0
std/cs/_std/sys/FileSystem.hx

@@ -103,11 +103,13 @@ class FileSystem {
 
 	public static function deleteFile( path : String ) : Void
 	{
+		if (!File.Exists(path)) throw "Path '" + path + "' doesn't exist";
 		File.Delete(path);
 	}
 
 	public static function deleteDirectory( path : String ) : Void
 	{
+		if (!Directory.Exists(path)) throw "Path '" + path + "' doesn't exist";
 		Directory.Delete(path);
 	}
 

+ 3 - 1
std/cs/_std/sys/io/Process.hx

@@ -109,8 +109,10 @@ class Process {
 		return native.Id;
 	}
 
-	public function exitCode() : Int
+	public function exitCode( block : Bool = true ) : Null<Int>
 	{
+		if( block == false && !native.HasExited )
+			return null;
 		native.WaitForExit();
 		return native.ExitCode;
 	}

+ 7 - 0
std/haxe/CallStack.hx

@@ -138,6 +138,13 @@ class CallStack {
 				stack.push(FilePos(null, file, Std.parseInt(line)));
 			}
 			return stack;
+		#elseif hl
+			try {
+				throw null;
+			} catch( e : Dynamic ) {
+				var st = _getExceptionStack();
+				return makeStack(st.length > 2 ? st.sub(2,st.length - 2) : st);
+			}
 		#else
 			return []; // Unsupported
 		#end

+ 1 - 1
std/haxe/Http.hx

@@ -436,7 +436,7 @@ class Http {
 				sock = new php.net.SslSocket();
 				#elseif java
 				sock = new java.net.SslSocket();
-				#elseif (!no_ssl && (hxssl || cpp || (neko && !(macro || interp))))
+				#elseif (!no_ssl && (hxssl || hl || cpp || (neko && !(macro || interp))))
 				sock = new sys.ssl.Socket();
 				#else
 				throw "Https is only supported with -lib hxssl";

+ 1 - 1
std/haxe/Int64.hx

@@ -454,7 +454,7 @@ private typedef __Int64 = ___Int64;
 
 private class ___Int64 {
 	public var high : Int32;
-	public var low : Int32; 
+	public var low : Int32;
 
 	public inline function new( high, low ) {
 		this.high = high;

+ 4 - 2
std/haxe/Log.hx

@@ -35,8 +35,8 @@ class Log {
 
 		This method can be rebound to a custom function:
 			var oldTrace = haxe.Log.trace; // store old function
-			haxe.Log.trace = function(v, ?infos) { 
-			  // handle trace 
+			haxe.Log.trace = function(v, ?infos) {
+			  // handle trace
 			}
 			...
 			haxe.Log.trace = oldTrace;
@@ -62,6 +62,8 @@ class Log {
 			}
 		#elseif js
 			untyped js.Boot.__trace(v,infos);
+		#elseif (php && php7)
+			php.Boot.trace(v, infos);
 		#elseif php
 			if (infos!=null && infos.customParams!=null) {
 				var extra:String = "";

+ 7 - 2
std/haxe/Serializer.hx

@@ -377,7 +377,7 @@ class Serializer {
 				#end
 			default:
 				if( useCache ) cache.pop();
-				if( #if flash try v.hxSerialize != null catch( e : Dynamic ) false #elseif (cs || java || python) Reflect.hasField(v, "hxSerialize") #else v.hxSerialize != null #end  ) {
+				if( #if flash try v.hxSerialize != null catch( e : Dynamic ) false #elseif (cs || java || python) Reflect.hasField(v, "hxSerialize") #elseif (php && php7) php.Global.method_exists(v, 'hxSerialize') #else v.hxSerialize != null #end  ) {
 					buf.add("C");
 					serializeString(Type.getClassName(c));
 					if( useCache ) cache.push(v);
@@ -476,8 +476,13 @@ class Serializer {
 				buf.add(0);
 			else {
 				buf.add(l);
-				for( i in 0...l )
+				for( i in 0...l ) {
+					#if (php && php7)
+					serialize(v.params[i]);
+					#elseif php
 					serialize(untyped __field__(v, __php__("params"), i));
+					#end
+				}
 			}
 			#elseif (java || cs || python || hl)
 			if( useEnumIndex ) {

+ 1 - 1
std/haxe/Timer.hx

@@ -179,7 +179,7 @@ class Timer {
 			return Sys.cpuTime();
 		#elseif sys
 			return Sys.time();
-		
+
 		#else
 			return 0;
 		#end

+ 1 - 0
std/haxe/extern/AsVar.hx

@@ -27,4 +27,5 @@ package haxe.extern;
 	argument expressions are bound to a local variable.
 **/
 @:forward
+@:analyzer(as_var)
 abstract AsVar<T>(T) from T to T {}

+ 2 - 0
std/haxe/io/BytesData.hx

@@ -27,6 +27,8 @@ package haxe.io;
 	typedef BytesData =	flash.utils.ByteArray;
 #elseif php
 	typedef BytesData = php.BytesData;
+#elseif php
+	typedef BytesData = php.NativeString;
 #elseif cpp
 	typedef BytesData = Array< cpp.UInt8 >;
 #elseif java

+ 20 - 17
std/haxe/io/Input.hx

@@ -24,6 +24,9 @@ package haxe.io;
 /**
 	An Input is an abstract reader. See other classes in the `haxe.io` package
 	for several possible implementations.
+
+	All functions which read data throw `Eof` when the end of the stream
+	is reached.
 **/
 class Input {
 
@@ -65,17 +68,17 @@ class Input {
 			throw Error.OutsideBounds;
 		try {
 			while( k > 0 ) {
-			    #if neko
-				    untyped __dollar__sset(b,pos,readByte());
-			    #elseif php
-				    b.set(pos, readByte());
-			    #elseif cpp
-				    b[pos] = untyped readByte();
-			    #else
-				    b[pos] = cast readByte();
-			    #end
-			    pos++;
-			    k--;
+				#if neko
+					untyped __dollar__sset(b,pos,readByte());
+				#elseif php
+					b.set(pos, readByte());
+				#elseif cpp
+					b[pos] = untyped readByte();
+				#else
+					b[pos] = cast readByte();
+				#end
+				pos++;
+				k--;
 			}
 		} catch (eof: haxe.io.Eof){}
 		return len-k;
@@ -284,7 +287,7 @@ class Input {
 		// php will overflow integers.  Convert them back to signed 32-bit ints.
 		var n = bigEndian ? ch4 | (ch3 << 8) | (ch2 << 16) | (ch1 << 24) : ch1 | (ch2 << 8) | (ch3 << 16) | (ch4 << 24);
 		if (n & 0x80000000 != 0)
-		    return ( n | 0x80000000);
+			return ( n | 0x80000000);
 		else return n;
 #elseif lua
 		var n = bigEndian ? ch4 | (ch3 << 8) | (ch2 << 16) | (ch1 << 24) : ch1 | (ch2 << 8) | (ch3 << 16) | (ch4 << 24);
@@ -317,11 +320,11 @@ class Input {
 
 #if (flash || js || python)
 	function getDoubleSig(bytes:Array<Int>)
-    {
-        return (((bytes[1]&0xF) << 16) | (bytes[2] << 8) | bytes[3] ) * 4294967296. +
-            (bytes[4] >> 7) * 2147483648 +
-            (((bytes[4]&0x7F) << 24) | (bytes[5] << 16) | (bytes[6] << 8) | bytes[7]);
-    }
+	{
+		return (((bytes[1]&0xF) << 16) | (bytes[2] << 8) | bytes[3] ) * 4294967296. +
+			(bytes[4] >> 7) * 2147483648 +
+			(((bytes[4]&0x7F) << 24) | (bytes[5] << 16) | (bytes[6] << 8) | bytes[7]);
+	}
 #end
 
 }

+ 3 - 5
std/haxe/macro/CompilationServer.hx

@@ -50,7 +50,7 @@ abstract ModuleCheckPolicy(Int) {
 	`--macro server.field(args)`.
 **/
 class CompilationServer {
-	#if neko
+	#if macro
 
 	/**
 		Sets the `ModuleCheckPolicy` of all files whose dot-path matches an
@@ -71,16 +71,14 @@ class CompilationServer {
 		compilation server should be restarted to ensure it takes effect.
 	**/
 	static public function setModuleCheckPolicy(pathFilters:Array<String>, policy:Array<ModuleCheckPolicy>, ?recursive = true, ?contextOptions:ContextOptions = NormalContext) {
-		pathFilters = [for (pathFilter in pathFilters) untyped pathFilter.__s];
-		@:privateAccess Compiler.load("server_add_module_check_policy", 4)(untyped pathFilters.__neko(), policy.__neko(), recursive, contextOptions);
+		@:privateAccess Compiler.load("server_add_module_check_policy", 4)(pathFilters, policy, recursive, contextOptions);
 	}
 
 	/**
 		Invalidates all files given in `filePaths`, removing them from the cache.
 	**/
 	static public function invalidateFiles(filePaths:Array<String>) {
-		filePaths = [for (filePath in filePaths) untyped filePath.__s];
-		@:privateAccess Compiler.load("server_invalidate_files", 1)(untyped filePaths.__neko());
+		@:privateAccess Compiler.load("server_invalidate_files", 1)(filePaths);
 	}
 	#end
 }

+ 1 - 1
std/haxe/macro/Compiler.hx

@@ -213,7 +213,7 @@ class Compiler {
 				continue;
 			found = true;
 			for( file in sys.FileSystem.readDirectory(path) ) {
-				if( StringTools.endsWith(file, ".hx") && file.indexOf(".") < 0 ) {
+				if( StringTools.endsWith(file, ".hx") && file.substr(0, file.length - 3).indexOf(".") < 0 ) {
 					var cl = prefix + file.substr(0, file.length - 3);
 					if( skip(cl) )
 						continue;

+ 5 - 3
std/haxe/rtti/Meta.hx

@@ -46,11 +46,11 @@ class Meta {
 	private static function isInterface(t:Dynamic):Bool {
 		#if java
 			return java.Lib.toNativeType(t).isInterface();
-		#elseif cs
+	#elseif cs
 			return cs.Lib.toNativeType(t).IsInterface;
 		#elseif (flash && as3)
 			return untyped flash.Lib.describeType(t).factory.extendsClass.length() == 0;
-		#elseif php
+		#elseif (php && !php7)
 			return untyped __php__("{0} instanceof _hx_interface", t);
 		#else
 			throw "Something went wrong";
@@ -59,7 +59,9 @@ class Meta {
 
 	private static function getMeta(t:Dynamic):MetaObject
 	{
-#if (java || cs || php || (flash && as3))
+#if (php && php7)
+		return php.Boot.getMeta(t.phpClassName);
+#elseif (java || cs || php || (flash && as3))
 		var ret = Reflect.field(t, "__meta__");
 		if (ret == null && Std.is(t,Class))
 		{

+ 8 - 8
std/haxe/xml/Parser.hx

@@ -52,27 +52,27 @@ class XmlParserException
 	 * the XML parsing error message
 	 */
 	public var message:String;
-	
+
 	/**
 	 * the line number at which the XML parsing error occured
 	 */
 	public var lineNumber:Int;
-	
+
 	/**
 	 * the character position in the reported line at which the parsing error occured
 	 */
 	public var positionAtLine:Int;
-	
+
 	/**
 	 * the character position in the XML string at which the parsing error occured
 	 */
 	public var position:Int;
-	
+
 	/**
 	 * the invalid XML string
 	 */
 	public var xml:String;
-	
+
 	public function new(message:String, xml:String, position:Int)
 	{
 		this.xml = xml;
@@ -80,7 +80,7 @@ class XmlParserException
 		this.position = position;
 		lineNumber = 1;
 		positionAtLine = 0;
-		
+
 		for( i in 0...position)
 		{
 			var c = xml.fastCodeAt(i);
@@ -92,7 +92,7 @@ class XmlParserException
 			}
 		}
 	}
-	
+
 	public function toString():String
 	{
 		return Type.getClassName(Type.getClass(this)) + ": " + message + " at line " + lineNumber + " char " + positionAtLine;
@@ -113,7 +113,7 @@ class Parser
 
 	/**
 	 * Parses the String into an XML Document. Set strict parsing to true in order to enable a strict check of XML attributes and entities.
-	 * 
+	 *
 	 * @throws haxe.xml.XmlParserException
 	 */
 	static public function parse(str:String, strict = false)

+ 2 - 1
std/hl/Api.hx

@@ -36,5 +36,6 @@ extern class Api {
 	@:hlNative("std", "get_virtual_value") static function getVirtualValue( v : Dynamic ) : Dynamic;
 	@:hlNative("std", "set_error_handler") static function setErrorHandler( v : Dynamic -> Void ) : Void;
 	@:hlNative("std", "breakpoint") static function breakPoint() : Void;
-	
+	@:hlNative("std", "sys_is64") static function is64() : Bool;
+
 }

+ 35 - 1
std/hl/Bytes.hx

@@ -113,10 +113,44 @@ package hl;
 		Please note that you need to retain the original unoffset'ed Bytes so it does not get garbage collected, unless the pointer was not GC allocated.
 	**/
 	@:hlNative("std","bytes_offset")
-	public function offset( pos : Int ) : Bytes {
+	public function offset( delta : Int ) : Bytes {
 		return null;
 	}
 
+	/**
+		Returns an offset between the two pointers. This might overflow in 64 bits if the addresses of the two pointers differs by more than 4GB
+	**/
+	@:hlNative("std","bytes_subtract")
+	public function subtract( other : Bytes ) : Int {
+		return 0;
+	}
+
+	@:hlNative("std", "bytes_address")
+	static function get_address( b : Bytes, high : Ref<Int> ) : Int {
+		return 0;
+	}
+
+	@:hlNative("std", "bytes_from_address")
+	static function from_address( low : Int, high : Int ) : Bytes {
+		return null;
+	}
+
+	/**
+		Creates an pointer at a given memory address (highly unsafe)
+	**/
+	public static inline function fromAddress( h : haxe.Int64 ) : Bytes {
+		return from_address(h.low, h.high);
+	}
+
+	/**
+		Returns the address value of the bytes. On 32 bit system the upper 32 bits will always be 0
+	**/
+	public function address() : haxe.Int64 {
+		var high = 0;
+		var low = get_address(this, high);
+		return haxe.Int64.make(high,low);
+	}
+
 	public function sub( pos : Int, size : Int ) {
 		var b = new Bytes(size);
 		b.blit(0, this, pos, size);

+ 1 - 1
std/hl/Type.hx

@@ -97,7 +97,7 @@ abstract TypeKind(Int) {
 		return null;
 	}
 
-	@:hlNative("std", "alloc_enum") public function allocEnum( index : Int, args : NativeArray<Dynamic> ) : Dynamic {
+	@:hlNative("std", "alloc_enum") public function allocEnum( index : Int, args : NativeArray<Dynamic>, nargs : Int ) : Dynamic {
 		return null;
 	}
 

+ 1 - 1
std/hl/_std/String.hx

@@ -117,7 +117,7 @@ class String {
 			if( pos < 0 ) pos = 0;
 		} else if( len < 0 )
 			len = sl + len - pos;
-		if( pos + len > sl )
+		if( ((pos + len) : UInt) > (sl:UInt) )
 			len = sl - pos;
 		if( pos < 0 || len <= 0 ) return "";
 

+ 3 - 3
std/hl/_std/Type.hx

@@ -121,16 +121,16 @@ class Type {
 			return v;
 		}
 		var a : hl.types.ArrayDyn = cast params;
-		var aobj = Std.instance(@:privateAccess a.array, hl.types.ArrayObj);
 		var narr;
-		if( aobj == null ) {
+		if( @:privateAccess !a.array.isArrayObj() ) {
 			narr = new hl.NativeArray<Dynamic>(a.length);
 			for( i in 0...a.length )
 				narr[i] = @:privateAccess a.array.getDyn(i);
 		} else {
+			var aobj : hl.types.ArrayObj<Dynamic> = cast @:privateAccess a.array;
 			narr = @:privateAccess aobj.array;
 		}
-		var v = @:privateAccess e.__type__.allocEnum(index, narr);
+		var v = @:privateAccess e.__type__.allocEnum(index, narr, a.length);
 		if( v == null ) throw "Constructor " + e.__ename__ +"." + e.__constructs__[index] + " does not takes " + narr.length + " parameters";
 		return v;
 	}

+ 8 - 4
std/hl/_std/haxe/io/Bytes.hx

@@ -36,6 +36,10 @@ class Bytes {
 		return (pos:UInt) >= (length : UInt);
 	}
 
+	inline function outRange(pos:Int,len:Int) : Bool {
+		return pos < 0 || len < 0 || ((pos+len):UInt) > (length : UInt);
+	}
+
 	public function get( pos : Int ) : Int {
 		return if( out(pos) ) 0 else b[pos];
 	}
@@ -46,17 +50,17 @@ class Bytes {
 	}
 
 	public function blit( pos : Int, src : Bytes, srcpos : Int, len : Int ) : Void {
-		if( pos < 0 || srcpos < 0 || len < 0 || pos + len > length || srcpos + len > src.length ) throw Error.OutsideBounds;
+		if( outRange(pos, len) || src.outRange(srcpos,len) ) throw Error.OutsideBounds;
 		b.blit(pos, src.b, srcpos, len);
 	}
 
 	public function fill( pos : Int, len : Int, value : Int ) : Void {
-		if( pos < 0 || len < 0 || pos + len > length ) throw Error.OutsideBounds;
+		if( outRange(pos,len) ) throw Error.OutsideBounds;
 		b.fill(pos, len, value);
 	}
 
 	public function sub( pos : Int, len : Int ) : Bytes {
-		if( pos < 0 || len < 0 || pos + len > length ) throw Error.OutsideBounds;
+		if( outRange(pos,len) ) throw Error.OutsideBounds;
 		return new Bytes(b.sub(pos, len), len);
 	}
 
@@ -116,7 +120,7 @@ class Bytes {
 	}
 
 	public function getString( pos : Int, len : Int ) : String {
-		if( pos < 0 || len < 0 || pos + len > length ) throw Error.OutsideBounds;
+		if( outRange(pos,len) ) throw Error.OutsideBounds;
 
 		var b = new hl.Bytes(len + 1);
 		b.blit(0, this.b, pos, len);

+ 2 - 2
std/hl/_std/sys/db/Sqlite.hx

@@ -22,8 +22,8 @@
 package sys.db;
 import haxe.crypto.BaseCode;
 
-private typedef SqliteConnectionHandle = hl.types.NativeAbstract<"sqlite_database">;
-private typedef SqliteResultHandle = hl.types.NativeAbstract<"sqlite_result">;
+private typedef SqliteConnectionHandle = hl.Abstract<"sqlite_database">;
+private typedef SqliteResultHandle = hl.Abstract<"sqlite_result">;
 
 @:hlNative("sqlite")
 private class SqliteLib

+ 4 - 0
std/hl/_std/sys/io/File.hx

@@ -21,7 +21,11 @@
  */
 package sys.io;
 
+#if doc_gen
+enum FileHandle { }
+#else
 typedef FileHandle = hl.Abstract<"hl_fdesc">;
+#end
 
 @:access(Sys)
 @:coreApi class File {

+ 56 - 7
std/hl/_std/sys/io/Process.hx

@@ -21,7 +21,7 @@
  */
 package sys.io;
 
-private typedef ProcessHandle = hl.types.NativeAbstract<"hl_process">;
+private typedef ProcessHandle = hl.Abstract<"hl_process">;
 
 private class Stdin extends haxe.io.Output {
 
@@ -50,7 +50,7 @@ private class Stdin extends haxe.io.Output {
 	}
 
 	@:hlNative("std","process_stdin_write") static function _stdin_write( p : ProcessHandle, bytes : hl.Bytes, pos : Int, len : Int ) : Int { return 0; }
-	@:hlNative("std","process_stdin_close") static function _stdin_close( p : ProcessHandle ) : Void { }
+	@:hlNative("std", "process_stdin_close") static function _stdin_close( p : ProcessHandle ) : Bool { return false; }
 
 }
 
@@ -89,9 +89,54 @@ private class Stdout extends haxe.io.Input {
 	var p : ProcessHandle;
 	public var stdout(default,null) : haxe.io.Input;
 	public var stderr(default,null) : haxe.io.Input;
-	public var stdin(default,null) : haxe.io.Output;
+	public var stdin(default, null) : haxe.io.Output;
+
+	static var isWin = Sys.systemName() == "Windows";
 
 	public function new( cmd : String, ?args : Array<String> ) : Void {
+		var runCmd = cmd;
+		if( isWin ) {
+			var b = new StringBuf();
+			if( args == null ) {
+				var exe = Sys.getEnv("COMSPEC");
+				if( exe == null ) exe = "cmd.exe";
+				b.add("\"");
+				b.add(exe);
+				b.add("\" /C \"");
+				b.add(cmd);
+				b.addChar('"'.code);
+			} else {
+				b.addChar('"'.code);
+				b.add(cmd);
+				b.addChar('"'.code);
+				for( a in args ) {
+					b.add(" \"");
+					var bsCount = 0;
+					for( i in 0...a.length ) {
+						switch( StringTools.fastCodeAt(a, i) ) {
+						case '"'.code:
+							for( i in 0...bsCount * 2 )
+								b.addChar('\\'.code);
+							bsCount = 0;
+							b.add("\\\"");
+						case '\\'.code:
+							bsCount++;
+						case c:
+							for( i in 0...bsCount )
+								b.addChar('\\'.code);
+							bsCount = 0;
+							b.addChar(c);
+						}
+					}
+					// Add remaining backslashes, if any.
+					for( i in 0...bsCount * 2 )
+						b.addChar('\\'.code);
+					b.addChar('"'.code);
+				}
+				args = null;
+			}
+			runCmd = b.toString();
+		}
 		@:privateAccess {
 			var aargs = null;
 			if( args != null ) {
@@ -99,7 +144,7 @@ private class Stdout extends haxe.io.Input {
 				for( i in 0...args.length )
 					aargs[i] = Sys.getPath(args[i]);
 			}
-			p = _run(Sys.getPath(cmd), aargs);
+			p = _run(Sys.getPath(runCmd), aargs);
 		}
 		if( p == null )
 			throw new Sys.SysError("Process creation failure : "+cmd);
@@ -112,8 +157,12 @@ private class Stdout extends haxe.io.Input {
 		return _pid(p);
 	}
 
-	public function exitCode() : Int {
-		return _exit(p);
+	public function exitCode( block : Bool = true ) : Null<Int> {
+		var running = false;
+		var code = _exit(p, block == false ? new hl.Ref(running) : null);
+		if( block == false )
+			return running ? null : code;
+		return code;
 	}
 
 	public function close() : Void {
@@ -125,7 +174,7 @@ private class Stdout extends haxe.io.Input {
 	}
 
 	@:hlNative("std","process_run")	static function _run( cmd : hl.Bytes, args : hl.NativeArray<hl.Bytes> ) : ProcessHandle { return null; }
-	@:hlNative("std", "process_exit") static function _exit( p : ProcessHandle ) : Int { return 0; }
+	@:hlNative("std", "process_exit") static function _exit( p : ProcessHandle, running : hl.Ref<Bool> ) : Int { return 0; }
 	@:hlNative("std", "process_pid") static function _pid( p : ProcessHandle ) : Int { return 0; }
 	@:hlNative("std","process_close") static function _close( p : ProcessHandle ) : Void { }
 	@:hlNative("std","process_kill") static function _kill( p : ProcessHandle ) : Void { }

+ 10 - 2
std/hl/_std/sys/net/Socket.hx

@@ -22,7 +22,11 @@
 package sys.net;
 import haxe.io.Error;
 
-private typedef SocketHandle = hl.Abstract<"hl_socket">;
+#if doc_gen
+@:noDoc enum SocketHandle { }
+#else
+@:noDoc typedef SocketHandle = hl.Abstract<"hl_socket">;
+#end
 
 private class SocketOutput extends haxe.io.Output {
 
@@ -110,7 +114,11 @@ class Socket {
 	}
 
 	public function new() : Void {
-		if( __s == null ) __s = socket_new(false);
+		init();
+	}
+	
+	function init() : Void {
+		__s = socket_new(false);
 		input = new SocketInput(this);
 		output = new SocketOutput(this);
 	}

+ 130 - 0
std/hl/_std/sys/ssl/Certificate.hx

@@ -0,0 +1,130 @@
+package sys.ssl;
+import sys.ssl.Lib;
+
+@:noDoc
+typedef CertificatePtr = hl.Abstract<"hl_ssl_cert">;
+
+@:coreApi
+class Certificate {
+	
+	var __h : Null<Certificate>;
+	var __x : CertificatePtr;
+
+	@:allow(sys.ssl.Socket)
+	function new( x : CertificatePtr, ?h: Null<Certificate> ){
+		__x = x;
+		__h = h;
+	}
+
+	public static function loadFile( file : String ) : Certificate {
+		return new Certificate( cert_load_file( @:privateAccess file.toUtf8() ) );
+	}
+	
+	public static function loadPath( path : String ) : Certificate {
+		return new Certificate( cert_load_path( @:privateAccess path.toUtf8() ) );
+	}
+
+	public static function fromString( str : String ) : Certificate {
+		return new Certificate( cert_add_pem(null, @:privateAccess str.toUtf8() ) );
+	}
+	
+	public static function loadDefaults() : Certificate {
+		var x = cert_load_defaults();
+		if ( x != null )
+			return new Certificate( x );
+		
+		var defPaths = null;
+		switch( Sys.systemName() ){
+			case "Linux":
+				defPaths = [
+					"/etc/ssl/certs/ca-certificates.crt", // Debian/Ubuntu/Gentoo etc.
+					"/etc/pki/tls/certs/ca-bundle.crt",   // Fedora/RHEL
+					"/etc/ssl/ca-bundle.pem",             // OpenSUSE
+					"/etc/pki/tls/cacert.pem",            // OpenELEC
+					"/etc/ssl/certs",                     // SLES10/SLES11
+					"/system/etc/security/cacerts"        // Android
+				];
+			case "BSD":
+				defPaths = [
+					"/usr/local/share/certs/ca-root-nss.crt", // FreeBSD/DragonFly
+					"/etc/ssl/cert.pem",                      // OpenBSD
+					"/etc/openssl/certs/ca-certificates.crt", // NetBSD	
+				];
+			case "Android":
+				defPaths = ["/system/etc/security/cacerts"];
+			default:
+		}
+		if( defPaths != null ){
+			for( path in defPaths ){
+				if( sys.FileSystem.exists(path) ){
+					if( sys.FileSystem.isDirectory(path) )
+						return loadPath(path);
+					else
+						return loadFile(path);
+				}
+			}
+		}
+		return null;
+	}
+
+	public var commonName(get,null) : Null<String>;
+	public var altNames(get, null) : Array<String>;
+	public var notBefore(get,null) : Date;
+	public var notAfter(get,null) : Date;
+
+	function get_commonName() : Null<String> {
+		return subject("CN");
+	}
+
+	function get_altNames() : Array<String> {
+		var a = cert_get_altnames(__x);
+		return [for( e in a ) @:privateAccess String.fromUTF8(e)];
+	}
+	
+	public function subject( field : String ) : Null<String> {
+		var s = cert_get_subject(__x, @:privateAccess field.toUtf8() );
+		return s==null ? null : new String( cast s );
+	}
+	
+	public function issuer( field : String ) : Null<String> {
+		var s = cert_get_issuer(__x, @:privateAccess field.toUtf8());
+		return s==null ? null : new String( cast s );
+	}
+
+	function get_notBefore() : Date {
+		var a = cert_get_notbefore( __x );
+		return new Date( a[0], a[1] - 1, a[2], a[3], a[4], a[5] );
+	}
+
+	function get_notAfter() : Date {
+		var a = cert_get_notafter( __x );
+		return new Date( a[0], a[1] - 1, a[2], a[3], a[4], a[5] );
+	}
+	
+	public function next() : Null<Certificate> {
+		var n = cert_get_next(__x);
+		return n == null ? null : new Certificate( n, __h==null ? this : __h );
+	}
+
+	public function add( pem : String ) : Void {
+		cert_add_pem(__x, @:privateAccess pem.toUtf8());
+	}
+
+	public function addDER( der : haxe.io.Bytes ) : Void {
+		cert_add_der(__x, @:privateAccess der.b, @:privateAccess der.length);
+	}
+
+	@:hlNative("ssl","cert_load_defaults") static function cert_load_defaults() : CertificatePtr { return null; }
+	@:hlNative("ssl","cert_load_file") static function cert_load_file( file : hl.Bytes ) : CertificatePtr { return null; }
+	@:hlNative("ssl","cert_load_path") static function cert_load_path( path : hl.Bytes ) : CertificatePtr { return null; }
+	@:hlNative("ssl","cert_get_subject") static function cert_get_subject( cert : CertificatePtr, obj : hl.Bytes ) : hl.Bytes { return null; }
+	@:hlNative("ssl","cert_get_issuer") static function cert_get_issuer( cert : CertificatePtr, obj : hl.Bytes ) : hl.Bytes { return null; }
+	@:hlNative("ssl","cert_get_altnames") static function cert_get_altnames( cert : CertificatePtr ) : hl.NativeArray<hl.Bytes> { return null; }
+	@:hlNative("ssl","cert_get_notbefore") static function cert_get_notbefore( cert : CertificatePtr ) : hl.NativeArray<Int> { return null; }
+	@:hlNative("ssl","cert_get_notafter") static function cert_get_notafter( cert : CertificatePtr ) : hl.NativeArray<Int> { return null; }
+	@:hlNative("ssl","cert_get_next") static function cert_get_next( cert : CertificatePtr ) : Null<CertificatePtr> { return null; }
+	@:hlNative("ssl","cert_add_pem") static function cert_add_pem( cert : Null<CertificatePtr>, data : hl.Bytes ) : CertificatePtr { return null; }
+	@:hlNative("ssl","cert_add_der") static function cert_add_der( cert : Null<CertificatePtr>, data : hl.Bytes, len : Int ) : CertificatePtr { return null; }
+	
+
+}

+ 27 - 0
std/hl/_std/sys/ssl/Digest.hx

@@ -0,0 +1,27 @@
+package sys.ssl;
+import sys.ssl.Lib;
+
+@:coreApi
+class Digest {
+	
+	public static function make( data : haxe.io.Bytes, alg : DigestAlgorithm ) : haxe.io.Bytes {
+		var size = 0;
+		var b = @:privateAccess dgst_make( data.b, data.length, (alg:String).toUtf8(), size );
+		return @:privateAccess new haxe.io.Bytes(b,size);
+	}
+	
+	public static function sign( data : haxe.io.Bytes, privKey : Key, alg : DigestAlgorithm ) : haxe.io.Bytes {
+		var size = 0;
+		var b = @:privateAccess dgst_sign( data.b, data.length, privKey.__k, (alg:String).toUtf8(), size );
+		return @:privateAccess new haxe.io.Bytes(b,size);
+	}
+	
+	public static function verify( data : haxe.io.Bytes, signature : haxe.io.Bytes, pubKey : Key, alg : DigestAlgorithm ) : Bool{
+		return @:privateAccess dgst_verify( data.b, data.length, signature.b, signature.length, pubKey.__k, (alg:String).toUtf8() );
+	}
+
+	@:hlNative("ssl","dgst_make") static function dgst_make( data : hl.Bytes, len : Int, alg : hl.Bytes, size : hl.Ref<Int> ) : hl.Bytes { return null; }
+	@:hlNative("ssl","dgst_sign") static function dgst_sign( data : hl.Bytes, len : Int, key : sys.ssl.Key.KeyPtr, alg : hl.Bytes, size : hl.Ref<Int> ) : hl.Bytes { return null; }
+	@:hlNative("ssl","dgst_verify") static function dgst_verify( data : hl.Bytes, dlen : Int, sign : hl.Bytes, slen : Int, key : sys.ssl.Key.KeyPtr, alg : hl.Bytes ) : Bool { return false; }
+	
+}

+ 36 - 0
std/hl/_std/sys/ssl/Key.hx

@@ -0,0 +1,36 @@
+package sys.ssl;
+import sys.ssl.Lib;
+
+@:noDoc
+typedef KeyPtr = hl.Abstract<"hl_ssl_pkey">;
+
+@:coreApi
+class Key {
+	
+	private var __k : KeyPtr;
+
+	private function new( k : KeyPtr ){
+		__k = k;
+	}
+	
+	public static function loadFile( file : String, ?isPublic : Bool, ?pass : String ) : Key {
+		var data = sys.io.File.getBytes( file );
+		var start = data.getString(0,11);
+		if( start == "-----BEGIN " )
+			return readPEM( data.toString(), isPublic==true, pass );
+		else
+			return readDER( data, isPublic==true );
+	}
+	
+	public static function readPEM( data : String, isPublic : Bool, ?pass : String ) : Key {
+		return new Key( key_from_pem( @:privateAccess data.toUtf8(), isPublic, pass == null ? null : @:privateAccess pass.toUtf8() ) );
+	}
+
+	public static function readDER( data : haxe.io.Bytes, isPublic : Bool ) : Key {
+		return new Key( key_from_der( @:privateAccess data.b, @:privateAccess data.length, isPublic ) );
+	}
+
+	@:hlNative("ssl","key_from_pem") static function key_from_pem( data : hl.Bytes, pub : Bool, pass : Null<hl.Bytes> ) : KeyPtr { return null; }
+	@:hlNative("ssl","key_from_der") static function key_from_der( data : hl.Bytes, len : Int, pub : Bool ) : KeyPtr { return null; }
+
+}

+ 10 - 0
std/hl/_std/sys/ssl/Lib.hx

@@ -0,0 +1,10 @@
+package sys.ssl;
+
+@:noDoc @:keep
+class Lib {
+	static function __init__() : Void{
+		ssl_init();
+	}
+	
+	@:hlNative("ssl","ssl_init") static function ssl_init(){};
+}

+ 251 - 0
std/hl/_std/sys/ssl/Socket.hx

@@ -0,0 +1,251 @@
+package sys.ssl;
+import sys.ssl.Lib;
+import sys.ssl.Key.KeyPtr;
+import sys.ssl.Certificate.CertificatePtr;
+import sys.net.Socket.SocketHandle;
+
+private typedef ConfigPtr = hl.Abstract<"mbedtls_ssl_config">;
+private typedef ContextPtr = hl.Abstract<"mbedtls_ssl_context">;
+
+@:keep
+private class SNICbResult {
+	public var cert : CertificatePtr;
+	public var key : KeyPtr; 
+	public function new( cert : Certificate, key : Key ){
+		this.cert = @:privateAccess cert.__x;
+		this.key = @:privateAccess key.__k;
+	}
+}
+
+private class SocketInput extends haxe.io.Input {
+	@:allow(sys.ssl.Socket) private var __s : Socket;
+
+	public function new( s : Socket ) {
+		this.__s = s;
+	}
+
+	public override function readByte() {
+		__s.handshake();
+		var r = ssl_recv_char( @:privateAccess __s.ssl );
+		if( r == -1 )
+			throw haxe.io.Error.Blocked;
+		else if( r < 0 )
+			throw new haxe.io.Eof();
+		return r;
+	}
+
+	public override function readBytes( buf : haxe.io.Bytes, pos : Int, len : Int ) : Int {
+		__s.handshake();
+		var r = ssl_recv(  @:privateAccess __s.ssl, @:privateAccess buf.b, pos, len );
+		if( r == -1 )
+			throw haxe.io.Error.Blocked;
+		else if( r < 0 )
+			throw new haxe.io.Eof();
+		return r;
+	}
+
+	public override function close() {
+		super.close();
+		if( __s != null ) __s.close();
+	}
+	
+	@:hlNative("ssl","ssl_recv") static function ssl_recv( ssl : ContextPtr, bytes : hl.Bytes, pos : Int, len : Int ) : Int { return -1; }
+	@:hlNative("ssl","ssl_recv_char") static function ssl_recv_char( ssl : ContextPtr ) : Int { return -1; }
+}
+
+private class SocketOutput extends haxe.io.Output {
+	@:allow(sys.ssl.Socket) private var __s : Socket;
+
+	public function new( s : Socket ) {
+		this.__s = s;
+	}
+
+	public override function writeByte( c : Int ) {
+		__s.handshake();
+		var r = ssl_send_char( @:privateAccess __s.ssl, c);
+		if( r == -1 )
+			throw haxe.io.Error.Blocked;
+		else if( r < 0 )
+			throw new haxe.io.Eof();
+	}
+
+	public override function writeBytes( buf : haxe.io.Bytes, pos : Int, len : Int) : Int {
+		__s.handshake();
+		var r = ssl_send( @:privateAccess __s.ssl, @:privateAccess buf.b, pos, len);
+		if( r == -1 )
+			throw haxe.io.Error.Blocked;
+		else if( r < 0 )
+			throw new haxe.io.Eof();
+		return r;
+	}
+
+	public override function close() {
+		super.close();
+		if( __s != null ) __s.close();
+	}
+
+	@:hlNative("ssl","ssl_send") static function ssl_send( ssl : ContextPtr, bytes : hl.Bytes, pos : Int, len : Int ) : Int { return -1; }
+	@:hlNative("ssl","ssl_send_char") static function ssl_send_char( ssl : ContextPtr, c : Int ) : Int { return -1; }
+
+}
+
+@:coreApi @:access(sys.net.Socket)
+class Socket extends sys.net.Socket {
+	
+	public static var DEFAULT_VERIFY_CERT : Null<Bool> = true;
+
+	public static var DEFAULT_CA : Null<Certificate>;
+	
+	private var conf : ConfigPtr;
+	private var ssl : ContextPtr;
+	
+	public var verifyCert : Null<Bool>;
+	private var caCert : Null<Certificate>;
+	private var hostname : String;
+
+	private var ownCert : Null<Certificate>;
+	private var ownKey : Null<Key>;
+	private var altSNIContexts : Null<Array<{match: String->Bool, key: Key, cert: Certificate}>>;
+	private var sniCallback : hl.Bytes -> SNICbResult;
+	private var handshakeDone : Bool;
+
+	private override function init() : Void {
+		__s = sys.net.Socket.socket_new( false );
+		input = new SocketInput( this );
+		output = new SocketOutput( this );
+		if( DEFAULT_VERIFY_CERT && DEFAULT_CA == null ){
+			try {
+				DEFAULT_CA = Certificate.loadDefaults();
+			}catch( e : Dynamic ){}
+		}
+		verifyCert = DEFAULT_VERIFY_CERT;
+		caCert = DEFAULT_CA;
+	}
+
+	public override function connect(host : sys.net.Host, port : Int) : Void {
+		conf = buildConfig( false );
+		ssl = ssl_new( conf );
+		ssl_set_socket( ssl, __s );
+		handshakeDone = false;
+		if( hostname == null )
+			hostname = host.host;
+		if( hostname != null )
+			ssl_set_hostname( ssl, @:privateAccess hostname.toUtf8() );
+		if( !sys.net.Socket.socket_connect( __s, host.ip, port ) )
+			throw new Sys.SysError("Failed to connect on "+host.toString()+":"+port);
+		handshake();
+	}
+
+	public function handshake() : Void {
+		if( !handshakeDone ){
+			var r = ssl_handshake( ssl );
+			if( r == 0 )
+				handshakeDone = true;
+			else if( r == -1 )
+				throw haxe.io.Error.Blocked;
+			else
+				throw new haxe.io.Eof();
+		}
+	}
+
+	public function setCA( cert : Certificate ) : Void {
+		caCert = cert;
+	}
+
+	public function setHostname( name : String ) : Void {
+		hostname = name;
+	}
+
+	public function setCertificate( cert : Certificate, key : Key ) : Void {
+		ownCert = cert;
+		ownKey = key;
+	}
+
+	public override function close() : Void {
+		if( ssl != null ) ssl_close( ssl );
+		if( conf != null ) conf_close( conf );
+		if( altSNIContexts != null )
+			sniCallback = null;
+		sys.net.Socket.socket_close( __s );
+		var input : SocketInput = cast input;
+		var output : SocketOutput = cast output;
+		@:privateAccess input.__s = output.__s = null;
+		input.close();
+		output.close();
+	}
+
+	public function addSNICertificate( cbServernameMatch : String->Bool, cert : Certificate, key : Key ) : Void {
+		if( altSNIContexts == null )
+			altSNIContexts = [];
+		altSNIContexts.push( {match: cbServernameMatch, cert: cert, key: key} );
+	}
+
+	public override function bind( host : sys.net.Host, port : Int ) : Void {
+		conf = buildConfig( true );
+
+		sys.net.Socket.socket_bind( __s, host.ip, port );
+	}
+
+	public override function accept() : Socket {
+		var c = sys.net.Socket.socket_accept( __s );
+		var cssl = ssl_new( conf );
+		ssl_set_socket( cssl, c );
+
+		var s = Type.createEmptyInstance( sys.ssl.Socket );
+		s.__s = c;
+		s.ssl = cssl;
+		s.input = new SocketInput(s);
+		s.output = new SocketOutput(s);
+		s.handshakeDone = false;
+
+		return s;
+	}
+
+	public function peerCertificate() : sys.ssl.Certificate {
+		var x = ssl_get_peer_certificate( ssl );
+		return x==null ? null : new sys.ssl.Certificate( x );
+	}
+
+	private function buildConfig( server : Bool ) : ConfigPtr {
+		var conf = conf_new( server );
+
+		if( ownCert != null && ownKey != null )
+			conf_set_cert( conf, @:privateAccess ownCert.__x, @:privateAccess ownKey.__k );
+
+		if ( altSNIContexts != null ) {
+			sniCallback = function(servername:hl.Bytes) : SNICbResult {
+				var servername = @:privateAccess String.fromUTF8(servername);
+				for( c in altSNIContexts ){
+					if( c.match(servername) )
+						return new SNICbResult(c.cert, c.key);
+				}
+				if( ownKey != null && ownCert != null )
+					return new SNICbResult(ownCert, ownKey);
+				return null;
+			}
+			conf_set_servername_callback( conf, sniCallback );
+		}
+
+		if ( caCert != null ) 
+			conf_set_ca( conf, caCert == null ? null : @:privateAccess caCert.__x  );
+		conf_set_verify( conf, if( verifyCert ) 1 else if( verifyCert==null ) 2 else 0 );
+		
+		return conf;
+	}
+	
+	
+	@:hlNative("ssl","ssl_new") static function ssl_new( conf : ConfigPtr ) : ContextPtr { return null; }
+	@:hlNative("ssl","ssl_close") static function ssl_close( ssl : ContextPtr ) : Void {}
+	@:hlNative("ssl","ssl_handshake") static function ssl_handshake( ssl : ContextPtr ) : Int { return -1; }
+	@:hlNative("ssl","ssl_set_socket") static function ssl_set_socket( ssl : ContextPtr, socket : SocketHandle ) : Void { }
+	@:hlNative("ssl","ssl_set_hostname") static function ssl_set_hostname( ssl : ContextPtr, name : hl.Bytes ) : Void { }
+	@:hlNative("ssl","ssl_get_peer_certificate") static function ssl_get_peer_certificate( ssl : ContextPtr ) : CertificatePtr { return null; }
+	
+	@:hlNative("ssl","conf_new") static function conf_new( server : Bool ) : ConfigPtr { return null; }
+	@:hlNative("ssl","conf_close") static function conf_close( conf : ConfigPtr ) : Void { }
+	@:hlNative("ssl","conf_set_ca") static function conf_set_ca( conf : ConfigPtr, ca : CertificatePtr ) : Void { }
+	@:hlNative("ssl","conf_set_verify") static function conf_set_verify( conf : ConfigPtr, mode : Int ) : Void { }
+	@:hlNative("ssl","conf_set_cert") static function conf_set_cert( conf : ConfigPtr, cert : CertificatePtr, pkey : KeyPtr ) : Void { }
+	@:hlNative("ssl","conf_set_servername_callback") static function conf_set_servername_callback( conf : ConfigPtr, cb : hl.Bytes -> SNICbResult ) : Void { }
+	
+}

+ 4 - 0
std/hl/types/ArrayBase.hx

@@ -107,6 +107,10 @@ class ArrayBase extends ArrayAccess {
 		return null;
 	}
 
+	function isArrayObj() {
+		return false;
+	}
+
 	public static function allocI32( bytes : BytesAccess<Int>, length : Int ) @:privateAccess {
 		var a : ArrayBytes.ArrayI32 = untyped $new(ArrayBytes.ArrayI32);
 		a.length = length;

+ 1 - 1
std/hl/types/ArrayBytes.hx

@@ -281,7 +281,7 @@ class BytesIterator<T> {
 
 	// called by compiler when accessing the array outside of its bounds, might trigger resize
 	function __expand( index : Int ) {
-		if( index < 0 ) throw "Invalid array access";
+		if( index < 0 ) throw "Invalid array index "+index;
 		var newlen = index + 1;
 		if( newlen > size ) {
 			var next = (size * 3) >> 1;

+ 5 - 1
std/hl/types/ArrayObj.hx

@@ -47,6 +47,10 @@ class ArrayObj<T> extends ArrayBase {
 		return b.toString();
 	}
 
+	override function isArrayObj() {
+		return true;
+	}
+
 	public function pop() : Null<T> {
 		if( length == 0 )
 			return null;
@@ -239,7 +243,7 @@ class ArrayObj<T> extends ArrayBase {
 
 	// called by compiler when accessing the array outside of its bounds, might trigger resize
 	function __expand( index : Int ) {
-		if( index < 0 ) throw "Invalid array access";
+		if( index < 0 ) throw "Invalid array index " + index;
 		var newlen = index + 1;
 		var size : Int = array.length;
 		if( newlen > size ) {

+ 9 - 1
std/java/_std/sys/io/Process.hx

@@ -92,8 +92,16 @@ class Process {
 		return -1;
 	}
 
-	public function exitCode() : Int
+	public function exitCode( block : Bool = true ) : Null<Int>
 	{
+		if( block == false ) {
+			try {
+				return proc.exitValue();
+			} catch( e : Dynamic ) {
+				return null;
+			}
+		}
+		
 		cast(stdout, ProcessInput).bufferContents();
 		cast(stderr, ProcessInput).bufferContents();
 		try

+ 1 - 1
std/js/html/AlignSetting.hx

@@ -20,7 +20,7 @@
  * DEALINGS IN THE SOFTWARE.
  */
 
-// This file is generated from mozilla\VTTCue.webidl line 14:0. Do not edit!
+// This file is generated from mozilla\VTTCue.webidl. Do not edit!
 
 package js.html;
 

+ 56 - 1
std/js/html/AnchorElement.hx

@@ -20,25 +20,80 @@
  * DEALINGS IN THE SOFTWARE.
  */
 
-// This file is generated from mozilla\HTMLAnchorElement.webidl line 18:0. Do not edit!
+// This file is generated from mozilla\HTMLAnchorElement.webidl. Do not edit!
 
 package js.html;
 
+/**
+	The `HTMLAnchorElement` interface represents hyperlink elements and provides special properties and methods (beyond those of the regular `HTMLElement` object interface they also have available to them by inheritance) for manipulating the layout and presentation of such elements.
+
+	Documentation [HTMLAnchorElement](https://developer.mozilla.org/en-US/docs/Web/API/HTMLAnchorElement) by [Mozilla Contributors](https://developer.mozilla.org/en-US/docs/Web/API/HTMLAnchorElement$history), licensed under [CC-BY-SA 2.5](https://creativecommons.org/licenses/by-sa/2.5/).
+
+	@see <https://developer.mozilla.org/en-US/docs/Web/API/HTMLAnchorElement>
+**/
 @:native("HTMLAnchorElement")
 extern class AnchorElement extends Element
 {
+	
+	/**
+		Is a `DOMString` that reflects the `target` HTML attribute, indicating where to display the linked resource.
+	**/
 	var target : String;
+	
+	/**
+		Is a `DOMString` indicating that the linked resource is intended to be downloaded rather than displayed in the browser. The value represent the proposed name of the file. If the name is not a valid filename of the underlying OS, browser will adapt it. The value is a URL with a scheme like `http:`, `file:`, `data:` or even `blob:` (created with `URL.createObjectURL`).
+	**/
 	var download : String;
 	var ping : String;
+	
+	/**
+		Is a `DOMString` that reflects the `rel` HTML attribute, specifying the relationship of the target object to the linked object.
+	**/
 	var rel : String;
+	
+	/**
+		Returns a `DOMTokenList` that reflects the `rel` HTML attribute, as a list of tokens.
+	**/
 	var relList(default,null) : DOMTokenList;
+	
+	/**
+		Is a `DOMString` that reflects the `hreflang` HTML attribute, indicating the language of the linked resource.
+	**/
 	var hreflang : String;
+	
+	/**
+		Is a `DOMString` that reflects the `type` HTML attribute, indicating the MIME type of the linked resource.
+	**/
 	var type : String;
+	
+	/**
+		Is a `DOMString` being a synonym for the `Node.textContent` property.
+	**/
 	var text : String;
+	
+	/**
+		Is a `DOMString` representing a comma-separated list of coordinates.
+	**/
 	var coords : String;
+	
+	/**
+		Is a `DOMString` representing the character encoding of the linked resource.
+	**/
 	var charset : String;
+	
+	/**
+		Is a `DOMString` representing the anchor name.
+	**/
 	var name : String;
+	
+	/**
+		Is a `DOMString` representing that the `rev` HTML attribute, specifying the relationship of the link object to the target object.
+	**/
 	var rev : String;
+	
+	/**
+		Is a `DOMString` representing the shape of the active area.
+	**/
 	var shape : String;
 	var href : String;
 	var origin(default,null) : String;

+ 72 - 1
std/js/html/Animation.hx

@@ -20,34 +20,105 @@
  * DEALINGS IN THE SOFTWARE.
  */
 
-// This file is generated from mozilla\Animation.webidl line 20:0. Do not edit!
+// This file is generated from mozilla\Animation.webidl. Do not edit!
 
 package js.html;
 
+/**
+	The `Animation` interface of the Web Animations API represents a single animation player and provides playback controls and a timeline for an animation node or source.
+
+	Documentation [Animation](https://developer.mozilla.org/en-US/docs/Web/API/Animation) by [Mozilla Contributors](https://developer.mozilla.org/en-US/docs/Web/API/Animation$history), licensed under [CC-BY-SA 2.5](https://creativecommons.org/licenses/by-sa/2.5/).
+
+	@see <https://developer.mozilla.org/en-US/docs/Web/API/Animation>
+**/
 @:native("Animation")
 extern class Animation extends EventTarget
 {
+	
+	/**
+		Gets and sets the `String` used to identify the animation.
+	**/
 	var id : String;
+	
+	/**
+		Gets and sets the `AnimationEffectReadOnly` associated with this animation. This will usually be a `KeyframeEffect` object.
+	**/
 	var effect(default,null) : AnimationEffectReadOnly;
+	
+	/**
+		Gets or sets the `AnimationTimeline` associated with this animation.
+	**/
 	var timeline(default,null) : AnimationTimeline;
+	
+	/**
+		Gets or sets the scheduled time when an animation's playback should begin.
+	**/
 	var startTime : Float;
+	
+	/**
+		The current time value of the animation in milliseconds, whether running or paused. If the animation lacks a `AnimationTimeline`, is inactive or hasn't been played yet, its value is `null`.
+	**/
 	var currentTime : Float;
+	
+	/**
+		Gets or sets the playback rate of the animation.
+	**/
 	var playbackRate : Float;
+	
+	/**
+		Returns an enumerated value describing the playback state of an animation.
+	**/
 	var playState(default,null) : AnimationPlayState;
+	
+	/**
+		Returns the current ready Promise for this animation.
+	**/
 	var ready(default,null) : Promise<Animation>;
+	
+	/**
+		Returns the current finished Promise for this animation.
+	**/
 	var finished(default,null) : Promise<Animation>;
+	
+	/**
+		Gets and sets the event handler for the `finish` event.
+	**/
 	var onfinish : haxe.Constraints.Function;
+	
+	/**
+		Gets and sets the event handler for the `cancel` event.
+	**/
 	var oncancel : haxe.Constraints.Function;
 	
 	/** @throws DOMError */
 	function new( ?effect : KeyframeEffectReadOnly, ?timeline : AnimationTimeline ) : Void;
+	
+	/**
+		Clears all `KeyframeEffect` caused by this animation and aborts its playback.
+	**/
 	function cancel() : Void;
 	/** @throws DOMError */
+	
+	/**
+		Seeks either end of an animation, depending on whether the animation is playing or reversing.
+	**/
 	function finish() : Void;
 	/** @throws DOMError */
+	
+	/**
+		Starts or resumes playing of an animation, or begins the animation again if it previously finished.
+	**/
 	function play() : Void;
 	/** @throws DOMError */
+	
+	/**
+		Suspends playing of an animation.
+	**/
 	function pause() : Void;
 	/** @throws DOMError */
+	
+	/**
+		Reverses playback direction, stopping at the start of the animation. If the animation is finished or unplayed, it will play from end to beginning.
+	**/
 	function reverse() : Void;
 }

+ 16 - 1
std/js/html/AnimationEffectReadOnly.hx

@@ -20,14 +20,29 @@
  * DEALINGS IN THE SOFTWARE.
  */
 
-// This file is generated from mozilla\AnimationEffectReadOnly.webidl line 50:0. Do not edit!
+// This file is generated from mozilla\AnimationEffectReadOnly.webidl. Do not edit!
 
 package js.html;
 
+/**
+	The `AnimationEffectReadOnly` interface of the Web Animations API defines current and future animation effects like `KeyframeEffect`, which can be passed to `Animation` objects for playing, and `KeyframeEffectReadOnly` (which is used by CSS Animations and Transitions).
+
+	Documentation [AnimationEffectReadOnly](https://developer.mozilla.org/en-US/docs/Web/API/AnimationEffectReadOnly) by [Mozilla Contributors](https://developer.mozilla.org/en-US/docs/Web/API/AnimationEffectReadOnly$history), licensed under [CC-BY-SA 2.5](https://creativecommons.org/licenses/by-sa/2.5/).
+
+	@see <https://developer.mozilla.org/en-US/docs/Web/API/AnimationEffectReadOnly>
+**/
 @:native("AnimationEffectReadOnly")
 extern class AnimationEffectReadOnly
 {
+	
+	/**
+		The `AnimationEffectTimingReadOnly` object associated with the animation containing all the animation's timing values.
+	**/
 	var timing(default,null) : AnimationEffectTimingReadOnly;
 	
+	
+	/**
+		Returns the calculated timing properties for this Animation Effect.
+	**/
 	function getComputedTiming() : ComputedTimingProperties;
 }

+ 8 - 1
std/js/html/AnimationEffectTiming.hx

@@ -20,10 +20,17 @@
  * DEALINGS IN THE SOFTWARE.
  */
 
-// This file is generated from mozilla\AnimationEffectTiming.webidl line 16:0. Do not edit!
+// This file is generated from mozilla\AnimationEffectTiming.webidl. Do not edit!
 
 package js.html;
 
+/**
+	The `AnimationEffectTiming` interface of the Web Animations API is comprised of timing properties. It is returned by the `timing` attribute of a `KeyframeEffect`.
+
+	Documentation [AnimationEffectTiming](https://developer.mozilla.org/en-US/docs/Web/API/AnimationEffectTiming) by [Mozilla Contributors](https://developer.mozilla.org/en-US/docs/Web/API/AnimationEffectTiming$history), licensed under [CC-BY-SA 2.5](https://creativecommons.org/licenses/by-sa/2.5/).
+
+	@see <https://developer.mozilla.org/en-US/docs/Web/API/AnimationEffectTiming>
+**/
 @:native("AnimationEffectTiming")
 extern class AnimationEffectTiming extends AnimationEffectTimingReadOnly
 {

+ 37 - 0
std/js/html/AnimationEffectTimingProperties.hx

@@ -0,0 +1,37 @@
+/*
+ * Copyright (C)2005-2016 Haxe Foundation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+// This file is generated from mozilla\AnimationEffectReadOnly.webidl. Do not edit!
+
+package js.html;
+
+typedef AnimationEffectTimingProperties =
+{
+	@:optional var delay : Float;
+	@:optional var direction : PlaybackDirection;
+	@:optional var duration : haxe.extern.EitherType<Float,String>;
+	@:optional var easing : String;
+	@:optional var endDelay : Float;
+	@:optional var fill : FillMode;
+	@:optional var iterationStart : Float;
+	@:optional var iterations : Float;
+}

+ 40 - 1
std/js/html/AnimationEffectTimingReadOnly.hx

@@ -20,20 +20,59 @@
  * DEALINGS IN THE SOFTWARE.
  */
 
-// This file is generated from mozilla\AnimationEffectTimingReadOnly.webidl line 16:0. Do not edit!
+// This file is generated from mozilla\AnimationEffectTimingReadOnly.webidl. Do not edit!
 
 package js.html;
 
+/**
+	The `AnimationEffectTimingReadOnly` interface of the Web Animations API is comprised of timing properties.
+
+	Documentation [AnimationEffectTimingReadOnly](https://developer.mozilla.org/en-US/docs/Web/API/AnimationEffectTimingReadOnly) by [Mozilla Contributors](https://developer.mozilla.org/en-US/docs/Web/API/AnimationEffectTimingReadOnly$history), licensed under [CC-BY-SA 2.5](https://creativecommons.org/licenses/by-sa/2.5/).
+
+	@see <https://developer.mozilla.org/en-US/docs/Web/API/AnimationEffectTimingReadOnly>
+**/
 @:native("AnimationEffectTimingReadOnly")
 extern class AnimationEffectTimingReadOnly
 {
+	
+	/**
+		The number of milliseconds to delay the start of the animation. Defaults to `0`.
+	**/
 	var delay(default,null) : Float;
+	
+	/**
+		The number of milliseconds to delay after the end of an animation. This is primarily of use when sequencing animations based on the end time of another animation. Defaults to `0`.
+	**/
 	var endDelay(default,null) : Float;
+	
+	/**
+		Dictates whether the animation's effects should be reflected by the element(s) state prior to playing (`backwards`), retained after the animation has completed playing (`forwards`), or `both`. Defaults to `none`.
+	**/
 	var fill(default,null) : FillMode;
+	
+	/**
+		A number representing which repetition the animation begins at and its progress through it.
+	**/
 	var iterationStart(default,null) : Float;
+	
+	/**
+		The number of times the animation should repeat. Defaults to `1`, and can also take a value of infinity to make it repeat infinitely.
+	**/
 	var iterations(default,null) : Float;
+	
+	/**
+		The number of milliseconds each iteration of the animation takes to complete. Defaults to `0`.
+	**/
 	var duration(default,null) : haxe.extern.EitherType<Float,String>;
+	
+	/**
+		Whether the animation runs forwards (`normal`), backwards (`reverse`), switches direction after each iteration (`alternate`), or runs backwards and switches direction after each iteration (`alternate-reverse`). Defaults to `normal`.
+	**/
 	var direction(default,null) : PlaybackDirection;
+	
+	/**
+		The rate of the animation's change over time. Accepts the pre-defined values `linear`, `ease`, `ease-in`, `ease-out`, and `ease-in-out`, or a custom cubic-bezier value like `cubic-bezier(0.42, 0, 0.58, 1)`. Defaults to `linear`.
+	**/
 	var easing(default,null) : String;
 	
 }

+ 20 - 1
std/js/html/AnimationEvent.hx

@@ -20,15 +20,34 @@
  * DEALINGS IN THE SOFTWARE.
  */
 
-// This file is generated from mozilla\AnimationEvent.webidl line 17:0. Do not edit!
+// This file is generated from mozilla\AnimationEvent.webidl. Do not edit!
 
 package js.html;
 
+/**
+	The `AnimationEvent` interface represents events providing information related to animations.
+
+	Documentation [AnimationEvent](https://developer.mozilla.org/en-US/docs/Web/API/AnimationEvent) by [Mozilla Contributors](https://developer.mozilla.org/en-US/docs/Web/API/AnimationEvent$history), licensed under [CC-BY-SA 2.5](https://creativecommons.org/licenses/by-sa/2.5/).
+
+	@see <https://developer.mozilla.org/en-US/docs/Web/API/AnimationEvent>
+**/
 @:native("AnimationEvent")
 extern class AnimationEvent extends Event
 {
+	
+	/**
+		Is a `DOMString` containing the value of the `animation-name` CSS property associated with the transition.
+	**/
 	var animationName(default,null) : String;
+	
+	/**
+		Is a `float` giving the amount of time the animation has been running, in seconds, when this event fired, excluding any time the animation was paused. For an `"animationstart"` event, `elapsedTime` is `0.0` unless there was a negative value for `animation-delay`, in which case the event will be fired with `elapsedTime` containing  `(-1 * `delay`)`.
+	**/
 	var elapsedTime(default,null) : Float;
+	
+	/**
+		Is a `DOMString`, starting with `'::'`, containing the name of the pseudo-element the animation runs on. If the animation doesn't run on a pseudo-element but on the element, an empty string: `''``.`
+	**/
 	var pseudoElement(default,null) : String;
 	
 	/** @throws DOMError */

+ 1 - 1
std/js/html/AnimationEventInit.hx

@@ -20,7 +20,7 @@
  * DEALINGS IN THE SOFTWARE.
  */
 
-// This file is generated from mozilla\AnimationEvent.webidl line 23:0. Do not edit!
+// This file is generated from mozilla\AnimationEvent.webidl. Do not edit!
 
 package js.html;
 

+ 1 - 1
std/js/html/AnimationPlayState.hx

@@ -20,7 +20,7 @@
  * DEALINGS IN THE SOFTWARE.
  */
 
-// This file is generated from mozilla\Animation.webidl line 15:0. Do not edit!
+// This file is generated from mozilla\Animation.webidl. Do not edit!
 
 package js.html;
 

+ 12 - 1
std/js/html/AnimationTimeline.hx

@@ -20,13 +20,24 @@
  * DEALINGS IN THE SOFTWARE.
  */
 
-// This file is generated from mozilla\AnimationTimeline.webidl line 16:0. Do not edit!
+// This file is generated from mozilla\AnimationTimeline.webidl. Do not edit!
 
 package js.html;
 
+/**
+	The `AnimationTimeline` interface of the Web Animations API represents the timeline of an animation. This interface exists to define timeline features (inherited by `DocumentTimeline` and future timeline types) and is not itself directly used by developers. Anywhere you see `AnimationTimeline`, you should use `DocumentTimeline` or any other timeline type instead.
+
+	Documentation [AnimationTimeline](https://developer.mozilla.org/en-US/docs/Web/API/AnimationTimeline) by [Mozilla Contributors](https://developer.mozilla.org/en-US/docs/Web/API/AnimationTimeline$history), licensed under [CC-BY-SA 2.5](https://creativecommons.org/licenses/by-sa/2.5/).
+
+	@see <https://developer.mozilla.org/en-US/docs/Web/API/AnimationTimeline>
+**/
 @:native("AnimationTimeline")
 extern class AnimationTimeline
 {
+	
+	/**
+		Returns the time value in milliseconds for this timeline or `null` if this timeline is inactive.
+	**/
 	var currentTime(default,null) : Float;
 	
 }

+ 1 - 1
std/js/html/AppletElement.hx

@@ -20,7 +20,7 @@
  * DEALINGS IN THE SOFTWARE.
  */
 
-// This file is generated from mozilla\HTMLAppletElement.webidl line 19:0. Do not edit!
+// This file is generated from mozilla\HTMLAppletElement.webidl. Do not edit!
 
 package js.html;
 

+ 1 - 1
std/js/html/ApplicationCache.hx

@@ -20,7 +20,7 @@
  * DEALINGS IN THE SOFTWARE.
  */
 
-// This file is generated from mozilla\OfflineResourceList.webidl line 6:0. Do not edit!
+// This file is generated from mozilla\OfflineResourceList.webidl. Do not edit!
 
 package js.html;
 

+ 40 - 1
std/js/html/AreaElement.hx

@@ -20,21 +20,60 @@
  * DEALINGS IN THE SOFTWARE.
  */
 
-// This file is generated from mozilla\HTMLAreaElement.webidl line 19:0. Do not edit!
+// This file is generated from mozilla\HTMLAreaElement.webidl. Do not edit!
 
 package js.html;
 
+/**
+	The `HTMLAreaElement` interface provides special properties and methods (beyond those of the regular object `HTMLElement` interface it also has available to it by inheritance) for manipulating the layout and presentation of area elements.
+
+	Documentation [HTMLAreaElement](https://developer.mozilla.org/en-US/docs/Web/API/HTMLAreaElement) by [Mozilla Contributors](https://developer.mozilla.org/en-US/docs/Web/API/HTMLAreaElement$history), licensed under [CC-BY-SA 2.5](https://creativecommons.org/licenses/by-sa/2.5/).
+
+	@see <https://developer.mozilla.org/en-US/docs/Web/API/HTMLAreaElement>
+**/
 @:native("HTMLAreaElement")
 extern class AreaElement extends Element
 {
+	
+	/**
+		Is a `DOMString` that reflects the `alt` HTML attribute, containing alternative text for the element.
+	**/
 	var alt : String;
+	
+	/**
+		Is a `DOMString` that reflects the `coords` HTML attribute, containing coordinates to define the hot-spot region.
+	**/
 	var coords : String;
+	
+	/**
+		Is a `DOMString` that reflects the `shape` HTML attribute, indicating the shape of the hot-spot, limited to known values.
+	**/
 	var shape : String;
+	
+	/**
+		Is a `DOMString` that reflects the `target` HTML attribute, indicating the browsing context in which to open the linked resource.
+	**/
 	var target : String;
+	
+	/**
+		Is a `DOMString` indicating that the linked resource is intended to be downloaded rather than displayed in the browser. The value represent the proposed name of the file. If the name is not a valid filename of the underlying OS, browser will adapt it.
+	**/
 	var download : String;
 	var ping : String;
+	
+	/**
+		Is a `DOMString` that reflects the `rel` HTML attribute, indicating relationships of the current document to the linked resource.
+	**/
 	var rel : String;
+	
+	/**
+		Returns a `DOMTokenList` that reflects the `rel` HTML attribute, indicating relationships of the current document to the linked resource, as a list of tokens.
+	**/
 	var relList(default,null) : DOMTokenList;
+	
+	/**
+		Is a `Boolean` flag indicating if the area is inactive (`true`) or active (`false`).
+	**/
 	var noHref : Bool;
 	var href : String;
 	var origin(default,null) : String;

+ 1 - 1
std/js/html/ArrayBuffer.hx

@@ -20,7 +20,7 @@
  * DEALINGS IN THE SOFTWARE.
  */
 
-// This file is generated from typedarray.webidl line 13:0. Do not edit!
+// This file is generated from typedarray.webidl. Do not edit!
 
 package js.html;
 

+ 8 - 1
std/js/html/ArrayBufferView.hx

@@ -20,10 +20,17 @@
  * DEALINGS IN THE SOFTWARE.
  */
 
-// This file is generated from typedarray.webidl line 24:0. Do not edit!
+// This file is generated from typedarray.webidl. Do not edit!
 
 package js.html;
 
+/**
+	`ArrayBufferView` is a helper type representing any of the following JavaScript `TypedArray` types:
+
+	Documentation [ArrayBufferView](https://developer.mozilla.org/en-US/docs/Web/API/ArrayBufferView) by [Mozilla Contributors](https://developer.mozilla.org/en-US/docs/Web/API/ArrayBufferView$history), licensed under [CC-BY-SA 2.5](https://creativecommons.org/licenses/by-sa/2.5/).
+
+	@see <https://developer.mozilla.org/en-US/docs/Web/API/ArrayBufferView>
+**/
 @:native("ArrayBufferView")
 extern class ArrayBufferView
 {

+ 8 - 1
std/js/html/Attr.hx

@@ -20,10 +20,17 @@
  * DEALINGS IN THE SOFTWARE.
  */
 
-// This file is generated from mozilla\Attr.webidl line 15:0. Do not edit!
+// This file is generated from mozilla\Attr.webidl. Do not edit!
 
 package js.html;
 
+/**
+	This type represents a DOM element's attribute as an object. In most DOM methods, you will probably directly retrieve the attribute as a string (e.g., `Element.getAttribute()`, but certain functions (e.g., `Element.getAttributeNode()`) or means of iterating give `Attr` types.
+
+	Documentation [Attr](https://developer.mozilla.org/en-US/docs/Web/API/Attr) by [Mozilla Contributors](https://developer.mozilla.org/en-US/docs/Web/API/Attr$history), licensed under [CC-BY-SA 2.5](https://creativecommons.org/licenses/by-sa/2.5/).
+
+	@see <https://developer.mozilla.org/en-US/docs/Web/API/Attr>
+**/
 @:native("Attr")
 extern class Attr extends Node
 {

+ 1 - 1
std/js/html/Audio.hx

@@ -20,7 +20,7 @@
  * DEALINGS IN THE SOFTWARE.
  */
 
-// This file is generated from namedConstructors.webidl line 2:0. Do not edit!
+// This file is generated from namedConstructors.webidl. Do not edit!
 
 package js.html;
 

+ 1 - 1
std/js/html/AudioChannel.hx

@@ -20,7 +20,7 @@
  * DEALINGS IN THE SOFTWARE.
  */
 
-// This file is generated from mozilla\AudioChannel.webidl line 77:0. Do not edit!
+// This file is generated from mozilla\AudioChannel.webidl. Do not edit!
 
 package js.html;
 

+ 1 - 1
std/js/html/AudioContextState.hx

@@ -20,7 +20,7 @@
  * DEALINGS IN THE SOFTWARE.
  */
 
-// This file is generated from mozilla\AudioContext.webidl line 18:0. Do not edit!
+// This file is generated from mozilla\AudioContext.webidl. Do not edit!
 
 package js.html;
 

+ 8 - 1
std/js/html/AudioElement.hx

@@ -20,10 +20,17 @@
  * DEALINGS IN THE SOFTWARE.
  */
 
-// This file is generated from mozilla\HTMLAudioElement.webidl line 17:0. Do not edit!
+// This file is generated from mozilla\HTMLAudioElement.webidl. Do not edit!
 
 package js.html;
 
+/**
+	The `HTMLAudioElement` interface provides access to the properties of `audio` elements, as well as methods to manipulate them. It derives from the `HTMLMediaElement` interface.
+
+	Documentation [HTMLAudioElement](https://developer.mozilla.org/en-US/docs/Web/API/HTMLAudioElement) by [Mozilla Contributors](https://developer.mozilla.org/en-US/docs/Web/API/HTMLAudioElement$history), licensed under [CC-BY-SA 2.5](https://creativecommons.org/licenses/by-sa/2.5/).
+
+	@see <https://developer.mozilla.org/en-US/docs/Web/API/HTMLAudioElement>
+**/
 @:native("HTMLAudioElement")
 extern class AudioElement extends MediaElement
 {

+ 1 - 1
std/js/html/AudioStreamTrack.hx

@@ -20,7 +20,7 @@
  * DEALINGS IN THE SOFTWARE.
  */
 
-// This file is generated from mozilla\AudioStreamTrack.webidl line 17:0. Do not edit!
+// This file is generated from mozilla\AudioStreamTrack.webidl. Do not edit!
 
 package js.html;
 

+ 1 - 1
std/js/html/AudioTrack.hx

@@ -20,7 +20,7 @@
  * DEALINGS IN THE SOFTWARE.
  */
 
-// This file is generated from mozilla\AudioTrack.webidl line 13:0. Do not edit!
+// This file is generated from mozilla\AudioTrack.webidl. Do not edit!
 
 package js.html;
 

+ 1 - 1
std/js/html/AudioTrackList.hx

@@ -20,7 +20,7 @@
  * DEALINGS IN THE SOFTWARE.
  */
 
-// This file is generated from mozilla\AudioTrackList.webidl line 13:0. Do not edit!
+// This file is generated from mozilla\AudioTrackList.webidl. Do not edit!
 
 package js.html;
 

+ 12 - 1
std/js/html/BRElement.hx

@@ -20,13 +20,24 @@
  * DEALINGS IN THE SOFTWARE.
  */
 
-// This file is generated from mozilla\HTMLBRElement.webidl line 19:0. Do not edit!
+// This file is generated from mozilla\HTMLBRElement.webidl. Do not edit!
 
 package js.html;
 
+/**
+	The `HTMLBRElement` interface represents a HTML line break element (`br`). It inherits from `HTMLElement`.
+
+	Documentation [HTMLBRElement](https://developer.mozilla.org/en-US/docs/Web/API/HTMLBRElement) by [Mozilla Contributors](https://developer.mozilla.org/en-US/docs/Web/API/HTMLBRElement$history), licensed under [CC-BY-SA 2.5](https://creativecommons.org/licenses/by-sa/2.5/).
+
+	@see <https://developer.mozilla.org/en-US/docs/Web/API/HTMLBRElement>
+**/
 @:native("HTMLBRElement")
 extern class BRElement extends Element
 {
+	
+	/**
+		Is a `DOMString` indicating the flow of text around floating objects.
+	**/
 	var clear : String;
 	
 }

+ 1 - 1
std/js/html/BarProp.hx

@@ -20,7 +20,7 @@
  * DEALINGS IN THE SOFTWARE.
  */
 
-// This file is generated from mozilla\BarProp.webidl line 9:0. Do not edit!
+// This file is generated from mozilla\BarProp.webidl. Do not edit!
 
 package js.html;
 

+ 16 - 1
std/js/html/BaseElement.hx

@@ -20,14 +20,29 @@
  * DEALINGS IN THE SOFTWARE.
  */
 
-// This file is generated from mozilla\HTMLBaseElement.webidl line 18:0. Do not edit!
+// This file is generated from mozilla\HTMLBaseElement.webidl. Do not edit!
 
 package js.html;
 
+/**
+	The `HTMLBaseElement` interface contains the base URI for a document. This object inherits all of the properties and methods as described in the `HTMLElement` interface.
+
+	Documentation [HTMLBaseElement](https://developer.mozilla.org/en-US/docs/Web/API/HTMLBaseElement) by [Mozilla Contributors](https://developer.mozilla.org/en-US/docs/Web/API/HTMLBaseElement$history), licensed under [CC-BY-SA 2.5](https://creativecommons.org/licenses/by-sa/2.5/).
+
+	@see <https://developer.mozilla.org/en-US/docs/Web/API/HTMLBaseElement>
+**/
 @:native("HTMLBaseElement")
 extern class BaseElement extends Element
 {
+	
+	/**
+		Is a `DOMString` that reflects the `href` HTML attribute, containing a base URL for relative URLs in the document.
+	**/
 	var href : String;
+	
+	/**
+		Is a `DOMString` that reflects the `target` HTML attribute, containing a default target browsing context or frame for elements that do not have a target reference specified.
+	**/
 	var target : String;
 	
 }

+ 40 - 1
std/js/html/BatteryManager.hx

@@ -20,20 +20,59 @@
  * DEALINGS IN THE SOFTWARE.
  */
 
-// This file is generated from mozilla\BatteryManager.webidl line 15:0. Do not edit!
+// This file is generated from mozilla\BatteryManager.webidl. Do not edit!
 
 package js.html;
 
+/**
+	The `BatteryManager` interface provides ways to get information about the system's battery charge level.
+
+	Documentation [BatteryManager](https://developer.mozilla.org/en-US/docs/Web/API/BatteryManager) by [Mozilla Contributors](https://developer.mozilla.org/en-US/docs/Web/API/BatteryManager$history), licensed under [CC-BY-SA 2.5](https://creativecommons.org/licenses/by-sa/2.5/).
+
+	@see <https://developer.mozilla.org/en-US/docs/Web/API/BatteryManager>
+**/
 @:native("BatteryManager")
 extern class BatteryManager extends EventTarget
 {
+	
+	/**
+		A Boolean value indicating whether or not the battery is currently being charged.
+	**/
 	var charging(default,null) : Bool;
+	
+	/**
+		A number representing the remaining time in seconds until the battery is fully charged, or 0 if the battery is already fully charged.
+	**/
 	var chargingTime(default,null) : Float;
+	
+	/**
+		A number representing the remaining time in seconds until the battery is completely discharged and the system will suspend.
+	**/
 	var dischargingTime(default,null) : Float;
+	
+	/**
+		A number representing the system's battery charge level scaled to a value between 0.0 and 1.0.
+	**/
 	var level(default,null) : Float;
+	
+	/**
+		A handler for the `chargingchange` event; This event is sent when the battery charging state is updated.
+	**/
 	var onchargingchange : haxe.Constraints.Function;
+	
+	/**
+		A handler for the `chargingtimechange` event; This event is sent when the battery charging time is updated
+	**/
 	var onchargingtimechange : haxe.Constraints.Function;
+	
+	/**
+		A handler for the `dischargingtimechange` event; This event is sent when the battery discharging time is updated.
+	**/
 	var ondischargingtimechange : haxe.Constraints.Function;
+	
+	/**
+		A handler for the `levelchange` event; This event is sent when the battery level is updated.
+	**/
 	var onlevelchange : haxe.Constraints.Function;
 	
 }

Some files were not shown because too many files changed in this diff