Selaa lähdekoodia

4.0.0-preview.1 (#6575)

* Actually disable the tests

* [java/cs] Make sure that cast new NativeArray uses the right type parameters

Closes #5751

* Fix test

* [cs] Only generate casts on NativeArray if the type parameters are not the same

Fixes fast_cast define

* Fix #6375 (#6376)

* also check tempvar'd this field access when typing getter/setter (closes #6375)

* add test

* [java/cs] Make sure to collect the right type parameters on anonymous functions

Closes #5793

* [neko] fix Sys.programPath() in nekotools booted exe (close #5708)

* don't remove enumIndex optimizations because EnumFlags is super important

* [TravisCI] fix boolean logic for neko installation

* [TravisCI] Fix neko installation on OSX

* Treat object array sets as having gc references

* [appveyor] merge php tests with the others

* [php7] fix haxe.io.Input.readAll() with disabled analyzer optimizations (closes #6387)

* changelog

* [php7] fix accessing static methods of String class stored into a variable (#4667)

* [php7] replace `Syntax.binop(.=)` with more analyzer-friendly `= Syntax.binop(.)`

* fixed stacks not being correctly reset in case of not unify detection (close #6235)

* add native mysqli error message when throwing an exception (#6390)

* use one-based error format, add -D old-error-format to keep zero-based behavior (#6391)

* [lua] old string metatable index is a function, not a table. fixes #6377

* [lua] previous string __index can be a table or a function

* fix position tests (see #6391)

* fix import.hx diagnostics condition (closes #6382)

* deal with EDisplay in assignment patterns (closes #6381)

* small toplevel collector cleanup (is going to help with #6341)

* filter duplicate toplevel identifiers (closes #6341)

* [eval] catch Failure on all Process calls (closes #6333)

* [eval] implement missing Date.fromString formats (closes #6323)

* print usage to stdout instead of stderr (closes #6363)

* [make] try to fix build directory dependency problem

* [lexer] count braces in code strings (closes #6369)

* [dce] keep everything if a class extends an extern (closes #6168)

* [display] delay metadata check behind field typing (closes #6162)

* fix test

* [matcher] fix assignment pattern display differently (closes #6395)

* fix json range column positions (back to zero-based after nicolas change)

gotta look into eval position stuff and check there too

* [java] Initial support for treating Null<BasicType> as their actual Java counterparts

* [java] java.lang.Long is also a boxed type

* [java] Properly rewrite basic type parameters as their boxed counterparts

Closes #784
Related #3728

* [java/cs] Add test

Closes #2688

* [cs] Automatically set nativegen to structs, and enforce that they cannot be subclassed, nor contain a constructor with no arguments

Closes #5399

* [java/cs] Make sure to reset old values when resizing a map

Closes #5862

* [java/cs] Avoid boxing with Runtime.compare expressions on dynamic field access

Closes #4047

* [java/cs] Avoid boxing when comparing Null<> basic types

Closes #4048

* [cs] Add C# optional arguments on Haxe-defined functions that have optional arguments

Closes #4147

* [java/cs] Prepare the code for the abstract null type PR

* [java/cs] Cast field field accesses to type parameters to their implementation instead of going through reflection

Closes #3790

* [cpp] No FastIterator for cppia

* [TravisCI] fix "Please check that the PPA name or format is correct."

* add test (closes #6215)

* [hl] disable failing test

* [java] disable failing test

* [parser] don't make reification call positions span the entire expression (closes #6396)

* Revert "[cs] Add C# optional arguments on Haxe-defined functions that have optional arguments"

This reverts commit 261e73ce6e15ad7dd4bca6a1c66902a90bc6710f.

* don't use global status var for newly created anon types (closes #6400)

* [lua] fix lazy function type handling for anon method binding (#6368)

* Make _build source directory on demand

* [Makefile] always generate version.ml

such that the version info is always up-to-date

* ignore haxelib run.n changes

* [macro] add haxe.macro.Expr.TypeDefinition.doc field and support printing it with haxe.macro.Printer (#6401)

* Change Null<T> to an abstract (#6380)

* use abstract instead of typedef for Null<T>

* make Null<T> a @:coreType (broken on C#/Java)

* fix infinite recursion

* fix flash

* fix overloads

* Do not dce fields which override extern fields (discussion in 23f94bf617b4806a196170426443499b35ddee06) (#6168)

* [eval] fix inconsistent error formatting (#6410)

see vshaxe/vshaxe#138

* [cs/java] add an expr filter that optimizes Type.createEmptyInstace when the type is known

this helps avoid redundant casting overhead on c# when type parameters are involved (e.g. `(Type.createInstance(C) : C<Int>)` becomes `new C<int>(EMPTY)`

* Changed is_extern_field to is_physical_field (#6404)

* changed is_extern_field to is_physical_field

* fix is_extern_field->is_physical_field change in genlua

* [js] reserve flattened paths for variable renaming (closes #6409)

* [inliner] only unify_min if we go from no-type to some type (closes #6406)

* [display] don't show Module.platform.hx in import completion (closes #6408)

* [java] disable failing test

* fix comments in php7.Syntax

* [macro] flush pass in typing-timer

Also make `cast_of_unify` go through typing-timer. Closes #6402

* [parser] fix binop position spanning (closes #6399)

* [display] respect `@:noCompletion` for toplevel (closes #6407)

* Use Meta.Pos instead of Meta.Custom ":pos" (#6419)

That way, it shows up in --help-metas.

* [parser] fix more positions (closes #6416) (closes #6417)

* [display] don't show non-static abstract fields via using (closes #6421)

* [typer] disallow accessing non-static abstract field statically (closes #5940)

* [display] do not generate property accessor calls if we are displaying the property (closes #6422)

* TIdent (#6424)

* add TIdent

* remove Meta.Unbound and break everything

* first replacement batch

* fix neko/hl

* fix java/cs

* fix cpp/php and maybe lua

* fix cppia and maybe fix lua (again)

* remove unused NoPatternMatching define

* support specifying platforms for defines (so they are mentioned in --help-defines)

* [dce] don't add @:directlyUsed to a class if it's used from within its methods (fixes #6425)

* add some platform tags to define info

* [gencommon] remove unused fix_tparam_access argument

* [lua] introduce @:luaDotMethod for using dot-style invocation on appropriate extern instances

* [cpp] Split CppGlobal to allow untyped identifiers as externs. Type the __trace function more strongly

* [cpp] Do not use GC memory for const enums

* support argument names in function type (closes #4799) (#6428)

* [lua] fix inconsistent function assignment (#6426)

* Revert "support argument names in function type (closes #4799) (#6428)"

This reverts commit b225e9f4b7f76459bab483b035a85702d882e718.

* [php7] implemented Lib.mail()

* [php7] php.Lib.rethrow()

* [php7] Global.require(), Global.include()

* [php7] implemented php.Lib.loadLib()

* [php7] implemented php.Lib.getClasses() (closes #6384)

* [lua] generate prototypes with plain field access, rather than through _hx_a(...)

* [lua] drop unused param

* [lua] ocp-indent

* [cpp] Use stack allocations to get class vtables

* [cpp] No need to mark class statics if there are none

* [cpp] Fix hasMarkFunc condition

* [lua] fix multireturn return type

* [TravisCI] move osx_image to matrix

* [eval] catch a Break (closes #6443)

* [eval] support extending special classes (closes #6446)

* [display] check field display on extern function fields (closes #6442)

* [typer] don't lose return expressions even if they error (closes #6445)

* fix test

* make -D old-error-format deal with display completion being sent in bytes instead of chars

* [js] apply var escaping rules to function arguments as well (fixes #6449)

* [TravisCI] use brew bundle to install brew formulae

It avoids getting "xxx already installed" error.

* [lua] use _hx_string_wrapper helper function instead of modifying string prototype

* [lua] wrap string operations so that metatable changes are not necessary

* (HL) Fixed non-blocking SSL Socket connect (#6452)

* added getThis() in jquery event, allow to easily get real this in a callback

* [lua] faster std Math min/max (#6455)

ternary expressions seem to be faster when not part of an inline

* Add jit option to cppia host

* Add alias 'Single' for cpp.Float32.  Closes https://github.com/HaxeFoundation/hxcpp/issues/555

* Check for null object in Reflect.callMethod.  Closes https://github.com/HaxeFoundation/hxcpp/issues/593

* [js] consider @:native type names when collecting reserved names for local vars (see #6448)

* [js] support unqualified static field access for @:native("") classes (see #6448)

* [js] consider statics of @:native("") classes when renaming local vars (closes #6448)

* Disable extra cppia test until I can debug

* oops, add #if

* [hl] fixed Type.getClass with interface/virtual input

* [js] Add compiler switch to generate enums as objects instead of arrays (#6350)

* add -D js_enums_as_objects switch

* add $hxEnums, change __enum__ to String

* whitespace

* resolve genjs.ml conflict

* add -D js-enums-as-objects to tests

* [lua] fix/simplify Rex extern

* [lua] one more Rex tweak

* fix haxe.web.Dispatch

* [cpp] Remove some scalar casts when not requires, and add some to reduce warnings.  Closes https://github.com/HaxeFoundation/hxcpp/issues/485

* [jQuery] regenerate extern with added Event.getThis()

https://github.com/andyli/jQueryExternForHaxe/commit/4a6e3d5a4b5473660ee630965651d5ac75262b74

* [TravisCI] install a patched ptmap for Mac builds

* [TravisCI] use the hererocks in python3 in mac builds

* use abstract field resolution instead of "implements Dynamic" for haxe.xml.Fast

this actually makes more lightweight due to less fields/allocations

* move haxe.web.Dispatch to hx3compat

* move magic types support code into their own module

* [java/cs] Fix IntMap implementation when setting 0 keys

Reviewed the whole Map implementations, added a few more comments
Closes #6457

* Disable toString test on C++

* [java] Add `no_map_cache` checks on WeakMap, and reimplement its iterators as classes

* Do not deploy binaries if we're running a PR

This only happened on PRs that were made by HF members, but still
we certainly don't want to submit them as nightlies

* [swf] rewrite safe-casts to if in non-retval mode

This is stupid. But it fixes #6482.

* [inliner] object declarations are _not_ constants

closes #6480

* disable failing test (see #6482)

* [eval] fix FileSystem path patching (closes #6481)

* [js] output file:line for traces (closes #6467)

* update submodule

* Add copy() to DynamicAccess (#5863)

* [xml] mention haxe.rtti.XmlParser (closes #6451)

* [display] allow dots in paths (closes #6435)

* [display] don't show private types in toplevel display (closes #6434)

* remove unused MSub module kind

* [matcher] print deprecation messages for enum (fields) (closes #6485)

* [parser] patch EField positions in reification (closes #6423)

* fix gadt pattern matching bug, closes #6413

* add interface tests

* [display] show constructor signatures on `new` (closes #6275)

* more local #if java for TestInterface

* make `in` a Binop and remove `EIn` (closes #6224) (#6471)

* make `in` a Binop and remove `EIn` (closes #6224)

* use make_binop

* fix enumIndex in tests

* change OpIn precedence to be lessthan OpAssign(Op)

* [js] fix tracing (closes #6492)

* minor

* Update JsonParser.hx (#6493)

haxe.format.JsonParser.parse("{\"\"\"A\":\"B\"}") -- any strings (empty string for sample), that are not separated by commas.

* add test for #6493

* [js] some doc on Symbol, add ofObject utility method and the standard `iterator` static field.

* supposedly fix #6491

* temp fix for #6467

* [js] setup proper keyword list based on selected ES version

* use rev_file_hash instead of rev_hash_s for eval position reporting so that it reports original file instead of unique_full_path

this also fixes positions multibyte utf8 files because original files are stored in lexer cache

* haxe now uses 1-based columns, so no need to +1 that in the eval debugger

* Split the Json parse test from the lambda arrow test until it is fixed.  Add shorcut for cpp on linux

* Add some additional cppia tests

* [make] don't add -native to ocamldep in bytecode mode

* casually walking into Mordor

* more comments

* more comment

* [lua] fix various coroutine-related signatures

* [lua] if dynamic is not table, return empty array for Reflect.fields

* [lua] Table helper methods

* [lua] do not generate function wrapping for explicit nulls (#6484)

* Make cppia use new ast by default.

* Update the docs for @:expose (#6515)

It works for Lua as well, and for both classes and fields.

* Fix duplicated platform in @:phpGlobal docs (#6518)

* [lua] fix Compiler.includeFile() with position = Inline

`haxe.macro.Compiler.includeFile("test.lua", "inline");` made `__js__` appear in the Lua output, leading to a rather funny error:

>attempt to call global '__js__' (a nil value)

* more comments for handle_efield

* remove weird loop from handle_efields. tests still pass, let's see what travis has to say.

* revert 36ea42db148a2d469e6b7a40688e6ef3f8b45f10. this actually does something for untyped...

* more comments for handle_efield, also move the [] case to the bottom of the match for easier reading

* more comments

* add comment about the weird loop now when I get it :)

* more comments, also move the inner `loop` function closer to its usage

* add a couple more comments to handle_efield, remove unused argument from check_module (as it uses sname)

* add common decl_flag type for parsing common type flags instead of returning tuples of class+enum flag and then mapping it awkwardly

* Update @:selfCall docs to include Lua (#6523)

* Add a VSCode settings.json (#6524)

It's common practice to commit .vscode settings (eventually, it might be good to have a tasks.json and a launch.json too).

The first two are necessary to work with the vscode-ocaml extension. The files.exclude pattern prevents always ending up in _build files instead of the actual source files by accident (for instance via Ctrl+P, which used to show both).
Note that F1 -> Clear Editor History might be necessary for Ctrl+P not to show _build files anymore since it shows recently opened files regardless of the files.exclude setting.

[skip ci]

* Remove spaces around "in" in printBinop (#6527)

* [lua] properly escape reserved keywords when used as prototype declarations (#6528)

* [lua] convert all abort directives to error, in order to emit normalized line number/column info (#6519)

* [lua] change "failwith" to "error" for consistent error reporting

* [lua] speed up some array methods

* Revert "[lua] speed up some array methods"

This reverts commit f11ed3c5101ac34a993b634b704f5b0f1aaade89.

* fix OpIn precedence: it's actually the lowest one. closes #6531

* Date.fromTime expects milliseconds (#6530)

* [matcher] support `@:forward` (closes #6532)

* Update MainLoop.hx (doc) (#6537)

Typo on call event's doc

* fixed generic getFloat to use FPHelper

* [eval] push block scope even for single-element blocks (see #6543)

* [eval] add constructor for haxe.io.Bytes (see #6543)

* [eval] print position for field index errors (#6543)

* [eval] support setting Bytes.length (see #6543)

* [matcher] don't leak `_` extractor variable (closes #6542)

* [js] create directory on setCustomJSGenerator (closes #6539)

* more binary support for js : use dataview for FPHelper, added dataview based BytesBuffer

* [lua] spacing nit

* [lua] do a better job marking haxe modules and classes as local

* [lua] speed up array methods (take 2)

* [TravisCI] ptmap 2.0.2 is compatible with ocaml 4.05

* [lua] fix unshift for empty array

* [lua] fix jit detection in tests

* [lua] de-inline a lengthy filesystem method

* [lua] fix unshift again

* [lua] fix array.shift for lua 5.3

* [lua] revert back to old array.unshift behavior

* [lua] use lua alias instead of luajit on hererocks installation

* [doc] fix Sys.getChar documentation (closes #6139)

* [lua] small std tweaks

* [js] initially grow BytesBuffer

* [inliner] deal with Dynamic returns (closes #6555)

* [matcher] don't let extractors access bindings (closes #6550)

* [matcher] fix extractor pattern offsets (closes #6548)

* [php7] do not throw on Reflect.getProperty(o, field) if field does not exist (fixes #6659, fixes 6554)

* unit test for #6559

* [cpp] Respect no_debug define

* [matcher] don't forget switch type when optimizing single-case matches (closes #6561)

* [tests] dodge C# issue (see #6561)

* unit test for #6502 (closes #6502)

* fixed some specific cases with empty buffer

* changed underlying TLazy implementation + make it a bit more abstract (allow to know if it's already processed or not)

* bugfix

* - error when accessing type currently being typed in macro
- don't assume not-method fields are not lazy typed
- flush core context (enter in PTypeExpr mode) when accessing a TLazy or calling field.expr()

close #683
close #5456

* I should have wait indeed

* fixed warning

* [TravisCI] install hererocks with --user

* force classes to be build after returning from a build call (same as we do when we load a type) close #6505

* partially revert fix for #6505

* better fix for #6505, do not flush if we are in module build (causes assert in typeload)

* don't consider hidden timers for timer alignment

* [eval] fix debug setVariable

* [inliner] don't mess up non-terminal try/catch (closes #6562)

* CHANGES.txt

* merge waneck-changes from nightly-travis branch
Simon Krajewski 8 vuotta sitten
vanhempi
commit
ee42cde4ef
100 muutettua tiedostoa jossa 21995 lisäystä ja 16225 poistoa
  1. 3 2
      .gitattributes
  2. 15 0
      .gitignore
  3. 1 0
      .gitmodules
  4. 9 1
      .merlin
  5. 156 79
      .travis.yml
  6. 12 0
      .vscode/settings.json
  7. 165 198
      Makefile
  8. 32 5
      Makefile.win
  9. 8 8
      README.md
  10. 22 27
      appveyor.yml
  11. 42 0
      extra/CHANGES.txt
  12. 2 0
      extra/ImportAll.hx
  13. 4 0
      extra/all.hxml
  14. 16 0
      extra/build-haxesetup.xml
  15. 1 1
      extra/haxelib_src
  16. BIN
      extra/mac-installer/installer-structure.pkg
  17. 13 0
      extra/mac-installer/scripts/haxe-postinstall.sh
  18. 8 0
      extra/mac-installer/scripts/haxe-preinstall.sh
  19. 18 0
      extra/mac-installer/scripts/install.sh
  20. 19 0
      extra/mac-installer/scripts/neko-postinstall.sh
  21. 3 0
      extra/mac-installer/scripts/neko-preinstall.sh
  22. 1 1
      libs
  23. 10 5
      src/compiler/globals.ml
  24. 396 0
      src/compiler/json.ml
  25. 46 79
      src/compiler/main.ml
  26. 9 1
      src/compiler/path.ml
  27. 173 76
      src/compiler/server.ml
  28. 0 0
      src/compiler/sourcemaps.ml
  29. 161 94
      src/context/common.ml
  30. 15 11
      src/context/meta.ml
  31. 24 15
      src/display/display.ml
  32. 24 12
      src/display/displayOutput.ml
  33. 279 0
      src/filters/capturedVars.ml
  34. 159 0
      src/filters/defaultArguments.ml
  35. 69 0
      src/filters/localUsage.ml
  36. 187 0
      src/filters/tryCatchWrapper.ml
  37. 22 6
      src/generators/codegen.ml
  38. 33 29
      src/generators/genas3.ml
  39. 158 9021
      src/generators/gencommon.ml
  40. 43 0
      src/generators/gencommon/abstractImplementationFix.ml
  41. 51 0
      src/generators/gencommon/arrayDeclSynf.ml
  42. 1240 0
      src/generators/gencommon/castDetect.ml
  43. 57 0
      src/generators/gencommon/classInstance.ml
  44. 1131 0
      src/generators/gencommon/closuresToClass.ml
  45. 132 0
      src/generators/gencommon/dynamicFieldAccess.ml
  46. 181 0
      src/generators/gencommon/dynamicOperators.ml
  47. 301 0
      src/generators/gencommon/enumToClass.ml
  48. 399 0
      src/generators/gencommon/enumToClass2.ml
  49. 638 0
      src/generators/gencommon/expressionUnwrap.ml
  50. 86 0
      src/generators/gencommon/filterClosures.ml
  51. 266 0
      src/generators/gencommon/fixOverrides.ml
  52. 280 0
      src/generators/gencommon/hardNullableSynf.ml
  53. 235 0
      src/generators/gencommon/initFunction.ml
  54. 79 0
      src/generators/gencommon/intDivisionSynf.ml
  55. 43 0
      src/generators/gencommon/interfaceProps.ml
  56. 84 0
      src/generators/gencommon/interfaceVarsDeleteModf.ml
  57. 98 0
      src/generators/gencommon/normalize.ml
  58. 37 0
      src/generators/gencommon/objectDeclMap.ml
  59. 437 0
      src/generators/gencommon/overloadingConstructor.ml
  60. 729 0
      src/generators/gencommon/realTypeParams.ml
  61. 1673 0
      src/generators/gencommon/reflectionCFs.ml
  62. 101 0
      src/generators/gencommon/renameTypeParameters.ml
  63. 101 0
      src/generators/gencommon/setHXGen.ml
  64. 162 0
      src/generators/gencommon/switchToIf.ml
  65. 104 0
      src/generators/gencommon/tArrayTransform.ml
  66. 60 0
      src/generators/gencommon/unnecessaryCastsRemoval.ml
  67. 212 0
      src/generators/gencommon/unreachableCodeEliminationSynf.ml
  68. 242 145
      src/generators/gencpp.ml
  69. 2430 3717
      src/generators/gencs.ml
  70. 240 102
      src/generators/genhl.ml
  71. 390 194
      src/generators/genjava.ml
  72. 123 95
      src/generators/genjs.ml
  73. 1229 1188
      src/generators/genlua.ml
  74. 8 4
      src/generators/genneko.ml
  75. 67 36
      src/generators/genphp.ml
  76. 193 124
      src/generators/genphp7.ml
  77. 37 80
      src/generators/genpy.ml
  78. 15 542
      src/generators/genswf.ml
  79. 46 29
      src/generators/genswf9.ml
  80. 8 7
      src/generators/genxml.ml
  81. 36 38
      src/generators/hl2c.ml
  82. 21 17
      src/generators/hlcode.ml
  83. 187 109
      src/generators/hlinterp.ml
  84. 35 23
      src/generators/hlopt.ml
  85. 0 104
      src/json.ml
  86. 193 0
      src/macro/eval/evalArray.ml
  87. 369 0
      src/macro/eval/evalContext.ml
  88. 99 0
      src/macro/eval/evalDebug.ml
  89. 389 0
      src/macro/eval/evalDebugCLI.ml
  90. 185 0
      src/macro/eval/evalDebugMisc.ml
  91. 571 0
      src/macro/eval/evalDebugSocket.ml
  92. 116 0
      src/macro/eval/evalDecode.ml
  93. 1502 0
      src/macro/eval/evalEmitter.ml
  94. 204 0
      src/macro/eval/evalEncode.ml
  95. 149 0
      src/macro/eval/evalExceptions.ml
  96. 69 0
      src/macro/eval/evalField.ml
  97. 138 0
      src/macro/eval/evalHash.ml
  98. 858 0
      src/macro/eval/evalJit.ml
  99. 144 0
      src/macro/eval/evalJitContext.ml
  100. 427 0
      src/macro/eval/evalMain.ml

+ 3 - 2
.gitattributes

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

+ 15 - 0
.gitignore

@@ -9,6 +9,7 @@
 *.exe
 .*.swp
 /out
+/installer
 
 /extra/hxclasses
 /extra/*.swf
@@ -68,6 +69,7 @@ build.bat
 /.vscode
 tests/unit/compile.php.hxml
 /extra/*.xml
+!/extra/build-haxesetup.xml
 tests/optimization/testopt.js
 tests/misc/pythonImport/native_python/__pycache__
 tests/unit/unit.py
@@ -77,6 +79,7 @@ tests/sys/bin/
 /tests/sys/dump/
 tests/optimization/dump/
 tests/misc/projects/*/*.n
+tests/misc/*/*/*.lua
 tests/unit/bin/
 tests/*.n
 tests/misc/projects/Issue3756/cpp/
@@ -94,3 +97,15 @@ tests/misc/projects/Issue4070/cpp/
 
 /tests/sys/temp
 *.dll
+_build/
+Makefile.dependencies
+Makefile.modules
+
+/tests/unit/compiler_loops/All.n
+
+/tests/unit/compiler_loops/log.txt
+tests/benchs/mandelbrot/bin/
+tests/server/test/cases/
+tests/server/test.js
+
+tests/unit/pypy3-*

+ 1 - 0
.gitmodules

@@ -4,3 +4,4 @@
 [submodule "extra/haxelib_src"]
 	path = extra/haxelib_src
 	url = https://github.com/HaxeFoundation/haxelib.git
+	ignore = dirty # run.n will be recompiled during make

+ 9 - 1
.merlin

@@ -1,4 +1,12 @@
 S src/**
-B src/**
+B _build/src/**
 S libs/**
 B libs/**
+B +threads
+PKG rope
+PKG ptmap
+PKG sedlex
+PKG extlib
+PKG camlzip
+PKG xml-light
+FLG -safe-string

+ 156 - 79
.travis.yml

@@ -1,12 +1,35 @@
+cache:
+  timeout: 1000
+  directories:
+    - $HOME/.opam
+    - $HOME/neko
+    - $HOME/apt-cache
+    - $HOME/hxcache
+    - $HOME/lua_env
+    - $HOME/.luarocks
+
+before_cache:
+  - if [ $TRAVIS_OS_NAME = 'linux' ]; then
+      sudo apt-get autoclean;
+      sudo rm -f $HOME/apt-cache/lock || true;
+    fi
+  # somehow these files are recreated everytime
+  - rm -f $HOME/lua_env/lib/liblua51.a
+  - rm -f $HOME/lua_env/lib/liblua52.a
+  - rm -f $HOME/lua_env/lib/liblua53.a
+  - rm -f $HOME/lua_env/lib/libluajit-5.1.a
+  #- if [ $TRAVIS_OS_NAME = 'osx' ]; then brew cleanup; fi
+
 env:
   global:
     # make variables
-    - OCAMLC=ocamlc.opt
-    - OCAMLOPT=ocamlopt.opt
     - ADD_REVISION=1
+    # nightly builds submit
+    - secure: "UoGjYvQqt66GWmeLC4Pih1iue5AufVgW8XQOd2Bx839NN/2mQQ9bD1HuONJe+taWBJ+PHInkAjYROYYaiCQUA7B1SXs3oQD7Og6arVcR7kY7XOdAQ2t8ZkxJHTnuYGWW/2gNFBESv+3H17bkXG4rzaSn2LV5PJLOxSjw0ziBUMY="
+    - secure: "ugpxt+zeYiAiMYKLK96f5TLSxbQAtmDWiumdwaLHl88fIUeefxJJPIF1Xm0AHeYEJE7sD8dLE1dMbRSzOpXFfTmJoQZv19Wjv+2N5B+DaabKjGj1nZG7q3blGa3nUYzWVfFNFiIpM9c5fvW8yiUFzacZE5itEY8+lZQeGsNh+WQ="
     # SauceLabs
-    - secure: SjyKefmjUEXi0IKHGGpcbLAajU0mLHONg8aA8LoY7Q9nAkSN6Aql+fzS38Boq7w1jWn+2FOpr+4jy0l6wVd/bftsF+huFfYpFJmdh8BlKmE0K71zZAral0H1c7YxkuQpPiJCIFGXqtkvev7SWTy0z31u7kuuQeEyW27boXe5cDA=
-    - secure: sUvWUjCyPuWht4seNa4f2VG9DkvXkhZyLZfjJO9TUAHB2JndS16E2j/qrvKEjycyH6w8tU/B9vnjDRvvGrYXxEXcBEwsJVfkorFnRl9uwGCGIYrzjMhssEl3fMYZK7P304f+gAp5ULrDBX2gIaKeSa8lUNRtz2PsZOieE4kMdhk=
+    # - secure: SjyKefmjUEXi0IKHGGpcbLAajU0mLHONg8aA8LoY7Q9nAkSN6Aql+fzS38Boq7w1jWn+2FOpr+4jy0l6wVd/bftsF+huFfYpFJmdh8BlKmE0K71zZAral0H1c7YxkuQpPiJCIFGXqtkvev7SWTy0z31u7kuuQeEyW27boXe5cDA=
+    # - secure: sUvWUjCyPuWht4seNa4f2VG9DkvXkhZyLZfjJO9TUAHB2JndS16E2j/qrvKEjycyH6w8tU/B9vnjDRvvGrYXxEXcBEwsJVfkorFnRl9uwGCGIYrzjMhssEl3fMYZK7P304f+gAp5ULrDBX2gIaKeSa8lUNRtz2PsZOieE4kMdhk=
     # Bintray
     # - secure: "ETbwZaeRq8wIVZVyUk1IsNctYVuQa/U2biRkF9pQkz3MEXpaneynclVzNjm8rnm8JqfKcjUDUvQJBP1KYrJYq3tAJFhl31YUnS0FsF3sgLIcnHkhbRA24xJdIlCwHP6QUPoiyPbkec43NRwrF0071KOMD51vgUToXRtAe3o/15g="
     # - secure: "Fcrrge2f4jFYDOopig2rwkQvgJw6Ra8UK6OwTVk08wecytzVaOJK1TcB22PSvZ+h0ZLJs34T+pXHFjlNuSWm4+CwGSvnltRD1/svjS8zOqK7RzuUdzHz87yruz9PFqV63HTas6qtmgLqp8n/Q6AhtDLF39BTZPyDzEbi9qkwRuI="
@@ -18,70 +41,138 @@ env:
 
 sudo: required
 dist: trusty
-osx_image: xcode6.4
 addons: &addons
   ssh_known_hosts:
     - haxe.org
     - api.haxe.org
 
 install_linux: &install_linux
-  # Install neko and haxe dependencies
+  - if [ ! -d "$HOME/neko" ]; then
+      export CACHE_AVAILABLE=0;
+    elif [ ! -d "$HOME/neko/.git" ]; then
+      export CACHE_AVAILABLE=0;
+    else
+      export CACHE_AVAILABLE=1;
+    fi
+  # Install dependencies
+  - export APT_CACHE_DIR=~/apt-cache && mkdir -pv $APT_CACHE_DIR
+  - sudo apt-get install --reinstall ca-certificates # workaround for "Cannot add PPA: 'ppa:haxe/ocaml'. Please check that the PPA name or format is correct."
   - sudo add-apt-repository ppa:haxe/ocaml -y
-  - sudo apt-get update
-  - sudo apt-get install -y
+  - sudo apt-get update -y
+  - sudo apt-get -o dir::cache::archives="$APT_CACHE_DIR" install -y
       ocaml
       ocaml-native-compilers
+      ocaml-findlib
       camlp4
-      pkg-config
-      libgc-dev
-      libssl-dev
       libpcre3-dev
       zlib1g-dev
-      apache2-dev
-      libmysqlclient-dev
-      libsqlite3-dev
       libgtk2.0-dev
-  # Install neko
-  - travis_retry git clone https://github.com/HaxeFoundation/neko.git ~/neko
-  - pushd ~/neko
-  - cmake -DSTATIC_DEPS=MbedTLS
-  - make
-  - sudo make install
+      ninja-build
+      awscli
+      $JOB_DEPENDENCIES
+  - wget https://raw.github.com/ocaml/opam/master/shell/opam_installer.sh -O - | sh -s /usr/local/bin system
+  - export OPAMYES=1
+  - opam install sedlex xml-light extlib rope ptmap
+  # check if we need to install neko
+  - export REF_CHANGED=1;
+  - if [ ! -d "$HOME/neko" ]; then
+      mkdir $HOME/neko;
+      pushd $HOME/neko;
+      git clone https://github.com/HaxeFoundation/neko.git .;
+      git submodule update --init --recursive;
+    else
+      pushd $HOME/neko;
+      if [ ! -d "$HOME/neko/.git" ]; then
+        git clone https://github.com/HaxeFoundation/neko.git .;
+        git submodule update --init --recursive;
+      else
+        git fetch --all;
+        export REF_CUR=`git rev-list master | head -n 1`;
+        export REF_REMOTE=`git rev-list origin/master | head -n 1`;
+        export REF_CHANGED=`(test $REF_CUR != $REF_REMOTE && echo 1) || echo 0`;
+        if [ $REF_CHANGED = 1 ]; then
+          git reset --hard origin/master;
+          git submodule update --init --recursive;
+        fi;
+      fi;
+    fi;
+  - env
+  - test $REF_CHANGED = 0 || cmake . -DSTATIC_DEPS=all -G Ninja || (git clean -dfx && export CACHE_AVAILABLE=0 && cmake . -DSTATIC_DEPS=all -G Ninja)
+  # download static dependencies before actual build, with 3 chances to deal with network issues
+  - test $REF_CHANGED = 0 || ninja download_static_deps || ninja download_static_deps || ninja download_static_deps
+  - test $REF_CHANGED = 0 || (ninja -j 4)
+  - sudo cmake -P cmake_install.cmake
   - popd
-  # Setup database
-  - 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
+  - opam config exec -- make -s STATICLINK=1 libs
+  - opam config exec -- make -s -j STATICLINK=1 haxe
+  - opam config exec -- make -s haxelib
   - make package_bin -s
   - ls -l out
+  - ldd -v ./haxe
+  - ldd -v ./haxelib
   - export PATH="$PATH:$TRAVIS_BUILD_DIR"
   - export HAXE_STD_PATH="$TRAVIS_BUILD_DIR/std"
 
 install_osx: &install_osx
-  # Install haxe dependencies
+  - if [ ! -d "$HOME/neko" ]; then
+      export CACHE_AVAILABLE=0;
+    elif [ ! -d "$HOME/neko/.git" ]; then
+      export CACHE_AVAILABLE=0;
+    else
+      export CACHE_AVAILABLE=1;
+    fi
+  # Install dependencies
+  - travis_retry brew update --merge
   - brew uninstall --force brew-cask # https://github.com/caskroom/homebrew-cask/pull/15381
-  - travis_retry brew update
-  - travis_retry brew install ocaml camlp4;
-  # Install neko
-  - travis_retry brew install neko --HEAD;
-  # Setup database
-  - travis_retry brew install mysql
-  - mysql.server start
-  - mysql -u root -e "create user if not exists travis@localhost identified by '';"
-  - mysql -u root -e "create database haxe_test;"
-  - mysql -u root -e "grant all on haxe_test.* to travis@localhost;"
+  - travis_retry brew tap Homebrew/bundle
+  - travis_retry brew bundle --file=tests/Brewfile
+  - export OPAMYES=1
+  - opam init
+  - eval `opam config env`
+  - opam install camlp4 sedlex ocamlfind xml-light extlib rope ptmap
+  # check if we need to install neko
+  - export REF_CHANGED=1;
+  - if [ ! -d "$HOME/neko" ]; then
+      mkdir $HOME/neko;
+      pushd $HOME/neko;
+      git clone https://github.com/HaxeFoundation/neko.git .;
+      git submodule update --init --recursive;
+    else
+      pushd $HOME/neko;
+      if [ ! -d "$HOME/neko/.git" ]; then
+        git clone https://github.com/HaxeFoundation/neko.git .;
+        git submodule update --init --recursive;
+      else
+        git fetch --all;
+        export REF_CUR=`git rev-list master | head -n 1`;
+        export REF_REMOTE=`git rev-list origin/master | head -n 1`;
+        export REF_CHANGED=`(test $REF_CUR != $REF_REMOTE && echo 1) || echo 0`;
+        if [ $REF_CHANGED = 1 ]; then
+          git reset --hard origin/master;
+          git submodule update --init --recursive;
+        fi;
+      fi;
+    fi
+  - env
+  - test $REF_CHANGED = 0 || cmake . -DSTATIC_DEPS=all -G Ninja || (git clean -dfx && export CACHE_AVAILABLE=0 && cmake . -DSTATIC_DEPS=all -G Ninja)
+  # download static dependencies before actual build, with 3 chances to deal with network issues
+  - test $REF_CHANGED = 0 || ninja download_static_deps || ninja download_static_deps || ninja download_static_deps
+  - test $REF_CHANGED = 0 || (ninja -j 4)
+  - sudo cmake -P cmake_install.cmake
+  - popd
   # Build haxe
-  - make package_src -s
-  - make -s
+  - make -s STATICLINK=1 "LIB_PARAMS=/usr/local/opt/zlib/lib/libz.a /usr/local/lib/libpcre.a" libs
+  - make -s -j STATICLINK=1 "LIB_PARAMS=/usr/local/opt/zlib/lib/libz.a /usr/local/lib/libpcre.a" haxe
+  - make -s haxelib
   - make package_bin -s
   - ls -l out
+  - otool -L ./haxe
+  - otool -L ./haxelib
   - export PATH="$PATH:$TRAVIS_BUILD_DIR"
   - export HAXE_STD_PATH="$TRAVIS_BUILD_DIR/std"
 
@@ -92,75 +183,61 @@ matrix:
     #########
     - os: linux
       env:
-        - TEST=macro,neko,js,php,flash9,as3,java,cs,python,hl,lua
-        - DEPLOY=1
-        - SAUCE=1
+        - TEST=macro,neko,js,php,php7,flash9,as3,java,cs,python,hl,lua
+        - DEPLOY_API_DOCS=1
+        - DEPLOY_NIGHTLIES=1
+        # - SAUCE=1
         # haxeci_decrypt (Deploy source package to ppa:haxe/snapshots.)
         - secure: "Mw3p6bDZuqVQ6u7GrwLQfje5hhIOA4+mdqqLXYHP79UKdhgqb91Dn6IbG9vQ1VXVe64W4YZbQAMBMMRX5kEPDl6JvTVGSBhg00Mi69oO5qrCMcBI6f9FntG72YaVvLf+PA7co+vKrnJzaP2M9pe4SH9Ztbhy0YNxULp7NQ8FLsM="
         # deploy_key_decrypt (Deploy doc to api.haxe.org.)
         - secure: "A75uYqU0Xz6plIgSewEs0QQWe472dCMb9kf3j7Hx0DS7dApXgx8++189sw9Sv0wam5KPtbcIM292MucjGCb5zocVj9xCUVgajhEA0QpTuDMBjk/cg3ClWCGjfybaCl2E5LLdUs7Zy4b4oNWtVikOWLWJ4sC1kaarR9p6kv8yYZg="
-      addons:
-        <<: *addons
-        sauce_connect: true
+      # addons:
+      #   <<: *addons
+      #  sauce_connect: true
       before_install:
-        - sudo dpkg --add-architecture i386
-        - sudo apt-get update -y || true
-        - travis_retry sudo apt-get install -y
-            libcurl3:i386
-            libglib2.0-0:i386
-            libx11-6:i386
-            libxext6:i386
-            libxt6:i386
-            libxcursor1:i386
-            libnss3:i386
-            libgtk2.0-0:i386
         - "export DISPLAY=:99.0"
         - "sh -e /etc/init.d/xvfb start"
         - "export AUDIODEV=null"
       install: *install_linux
-
     - os: linux
       env:
         - TEST=cpp
+        - HXCPP_COMPILE_THREADS=4
+        - HXCPP_COMPILE_CACHE=~/hxcache
       before_install:
-        - sudo apt-get update -y || true
-        - travis_retry sudo apt-get install -y
-            gcc-multilib
-            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
+        - export JOB_DEPENDENCIES="gcc-multilib g++-multilib"
       install: *install_linux
 
     #######
     # osx #
     #######
     - os: osx
+      osx_image: xcode6.4 # to compile binaries that support older versions of Mac
       env:
-        - TEST=macro,neko,js,php,flash9,as3,java,cs,python,hl,lua
-        - DEPLOY=1
+        - TEST=macro,neko,js,php,python,hl,lua
+        - DEPLOY_NIGHTLIES=1
       install: *install_osx
 
     - os: osx
+      osx_image: xcode8.3 # to compile C++ faster
       env:
         - TEST=cpp
+        - HXCPP_COMPILE_CACHE=~/hxcache
+        - HXCPP_COMPILE_THREADS=4
       install: *install_osx
 
 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
-  - popd
+  - export CAN_BUILD=`(test $CACHE_AVAILABLE = 1 || test $TRAVIS_OS_NAME = 'linux') && echo 1`
+  - if [ ! $CAN_BUILD ]; then
+      echo "No cache available, but initial cache created, please try restarting this job";
+    fi
+  - test $CAN_BUILD && pushd tests
+  - test $CAN_BUILD && mkdir ~/haxelib && haxelib setup ~/haxelib
+  - test $CAN_BUILD && haxe -version
+  - test $CAN_BUILD && haxe RunCi.hxml
+  - test $CAN_BUILD && neko RunCi.n
+  - test $CAN_BUILD && popd
 
 branches:
   except:

+ 12 - 0
.vscode/settings.json

@@ -0,0 +1,12 @@
+{
+	"files.associations": {
+		"*.mly": "ocaml",
+		"*.ml": "ocaml"
+	},
+	"[ocaml]": {
+		"editor.tabSize": 4
+	},
+	"files.exclude": {
+		"**/_build": true
+	}
+}

+ 165 - 198
Makefile

@@ -8,72 +8,60 @@
 #  - use 'make -f Makefile.win' to build for Windows
 #  - use 'make MSVC=1 -f Makefile.win' to build for Windows with OCaml/MSVC
 #
-.SUFFIXES : .ml .mli .cmo .cmi .cmx .mll .mly
+.SUFFIXES : .ml .mli .cmo .cmi .cmx .mly
 
-INSTALL_DIR=$(DESTDIR)/usr
+INSTALL_DIR=$(DESTDIR)/usr/local
 INSTALL_BIN_DIR=$(INSTALL_DIR)/bin
 INSTALL_LIB_DIR=$(INSTALL_DIR)/lib/haxe
-INSTALL_STD_DIR=$(INSTALL_LIB_DIR)/std
+INSTALL_STD_DIR=$(INSTALL_DIR)/share/haxe/std
 PACKAGE_OUT_DIR=out
+INSTALLER_TMP_DIR=installer
 PACKAGE_SRC_EXTENSION=.tar.gz
 
+MAKEFILENAME?=Makefile
 PLATFORM?=unix
 
 OUTPUT=haxe
 EXTENSION=
-OCAMLOPT?=ocamlopt
-OCAMLC?=ocamlc
 LFLAGS=
 STATICLINK?=0
 
-CFLAGS= -bin-annot
-ALL_CFLAGS= $(CFLAGS) -g -w -3 -I libs/extlib -I libs/extc -I libs/neko -I libs/javalib -I libs/ziplib -I libs/swflib -I libs/xml-light -I libs/ttflib -I libs/ilib -I libs/objsize -I libs/pcre \
-	-I src -I src/context -I src/generators -I src/macro -I src/optimization -I src/syntax -I src/typing -I src/display
+# Configuration
 
-LIBS=unix str libs/extlib/extLib libs/xml-light/xml-light libs/swflib/swflib \
-	libs/extc/extc libs/neko/neko libs/javalib/java libs/ziplib/zip \
-	libs/ttflib/ttf libs/ilib/il libs/objsize/objsize libs/pcre/pcre
+HAXE_DIRECTORIES=compiler context generators generators/gencommon macro filters macro/eval optimization syntax typing display
+EXTLIB_LIBS=extlib-leftovers extc neko javalib swflib ttflib ilib objsize pcre ziplib
+FINDLIB_LIBS=unix str threads sedlex xml-light extlib rope ptmap dynlink
 
+# Includes, packages and compiler
 
-ifneq ($(STATICLINK),0)
-LIB_PARAMS= -cclib '-Wl,-Bstatic -lpcre -lz -Wl,-Bdynamic '
-
-else
-LIB_PARAMS?= -cclib -lpcre -cclib -lz
-
-endif
-
-NATIVE_LIBS=-cclib libs/extc/extc_stubs.o -cclib libs/extc/process_stubs.o -cclib libs/objsize/c_objsize.o -cclib libs/pcre/pcre_stubs.o -ccopt -L/usr/local/lib $(LIB_PARAMS)
+HAXE_INCLUDES=$(HAXE_DIRECTORIES:%=-I _build/src/%)
+EXTLIB_INCLUDES=$(EXTLIB_LIBS:%=-I libs/%)
+ALL_INCLUDES=$(EXTLIB_INCLUDES) $(HAXE_INCLUDES)
+FINDLIB_PACKAGES=$(FINDLIB_LIBS:%=-package %)
+CFLAGS=
+ALL_CFLAGS=-bin-annot -safe-string -thread -g -w -3 $(CFLAGS) $(ALL_INCLUDES) $(FINDLIB_PACKAGES)
 
 ifeq ($(BYTECODE),1)
 	TARGET_FLAG = bytecode
-	COMPILER = $(OCAMLC)
+	COMPILER = ocamlfind ocamlc
 	LIB_EXT = cma
 	MODULE_EXT = cmo
 	NATIVE_LIB_FLAG = -custom
 else
 	TARGET_FLAG = native
-	COMPILER = $(OCAMLOPT)
+	COMPILER = ocamlfind ocamlopt
 	LIB_EXT = cmxa
 	MODULE_EXT = cmx
+	OCAMLDEP_FLAGS = -native
 endif
 
 CC_CMD = $(COMPILER) $(ALL_CFLAGS) -c $<
 
-CC_PARSER_CMD = $(COMPILER) -pp camlp4o $(ALL_CFLAGS) -c src/syntax/parser.ml
-
-RELDIR=../../..
+# Meta information
 
-MODULES=json version globals path context/meta syntax/ast display/displayTypes typing/type typing/error \
-	syntax/lexer context/common generators/genxml \
-	syntax/parser typing/abstract typing/typecore display/display optimization/optimizerTexpr \
-	optimization/optimizer typing/overloads typing/typeload sourcemaps generators/codegen generators/gencommon generators/genas3 \
-	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 \
-	optimization/analyzerTexprTransformer optimization/analyzer generators/genhl \
-	optimization/filters macro/hlmacro macro/macroContext typing/typer typing/matcher display/displayOutput server main
+BUILD_DIRECTORIES := $(HAXE_DIRECTORIES:%=_build/src/%)
+HAXE_SRC := $(wildcard $(HAXE_DIRECTORIES:%=src/%/*.ml))
+BUILD_SRC := $(HAXE_SRC:%=_build/%)
 
 ADD_REVISION?=0
 
@@ -96,23 +84,85 @@ else
 	export HAXE_STD_PATH=$(CURDIR)/std
 endif
 
+# Native libraries
+
+ifneq ($(STATICLINK),0)
+	LIB_PARAMS= -cclib '-Wl,-Bstatic -lpcre -lz -Wl,-Bdynamic '
+else
+	LIB_PARAMS?= -cclib -lpcre -cclib -lz
+endif
+
+NATIVE_LIBS=-thread -cclib libs/extc/extc_stubs.o -cclib libs/extc/process_stubs.o -cclib libs/objsize/c_objsize.o -cclib libs/pcre/pcre_stubs.o -ccopt -L/usr/local/lib $(LIB_PARAMS)
+
+# Modules
+
+-include Makefile.modules
+
+# Rules
+
 all: libs haxe tools
 
 libs:
-	make -C libs/extlib OCAMLOPT=$(OCAMLOPT) OCAMLC=$(OCAMLC) $(TARGET_FLAG)
-	make -C libs/extc OCAMLOPT=$(OCAMLOPT) OCAMLC=$(OCAMLC) $(TARGET_FLAG)
-	make -C libs/neko OCAMLOPT=$(OCAMLOPT) OCAMLC=$(OCAMLC) $(TARGET_FLAG)
-	make -C libs/javalib OCAMLOPT=$(OCAMLOPT) OCAMLC=$(OCAMLC) $(TARGET_FLAG)
-	make -C libs/ilib OCAMLOPT=$(OCAMLOPT) OCAMLC=$(OCAMLC) $(TARGET_FLAG)
-	make -C libs/ziplib OCAMLOPT=$(OCAMLOPT) OCAMLC=$(OCAMLC) $(TARGET_FLAG)
-	make -C libs/swflib OCAMLOPT=$(OCAMLOPT) OCAMLC=$(OCAMLC) $(TARGET_FLAG)
-	make -C libs/xml-light OCAMLOPT=$(OCAMLOPT) OCAMLC=$(OCAMLC) $(TARGET_FLAG)
-	make -C libs/ttflib OCAMLOPT=$(OCAMLOPT) OCAMLC=$(OCAMLC) $(TARGET_FLAG)
-	make -C libs/objsize OCAMLOPT=$(OCAMLOPT) OCAMLC=$(OCAMLC) $(TARGET_FLAG)
-	make -C libs/pcre OCAMLOPT=$(OCAMLOPT) OCAMLC=$(OCAMLC) $(TARGET_FLAG)
-
-haxe: $(MODULES:%=src/%.$(MODULE_EXT))
-	$(COMPILER) -o $(OUTPUT) $(NATIVE_LIBS) $(NATIVE_LIB_FLAG) $(LFLAGS) $(LIBS:=.$(LIB_EXT)) $(MODULES:%=src/%.$(MODULE_EXT))
+	$(foreach lib,$(EXTLIB_LIBS),$(MAKE) -C libs/$(lib) $(TARGET_FLAG) &&) true
+
+_build/%:%
+	mkdir -p $(dir $@)
+	cp $< $@
+
+build_dirs:
+	@mkdir -p $(BUILD_DIRECTORIES)
+
+_build/src/syntax/parser.ml:src/syntax/parser.mly
+	camlp4o -impl $< -o $@
+
+_build/src/compiler/version.ml: FORCE
+ifneq ($(ADD_REVISION),0)
+	$(MAKE) -f Makefile.version_extra -s --no-print-directory ADD_REVISION=$(ADD_REVISION) BRANCH=$(BRANCH) COMMIT_SHA=$(COMMIT_SHA) COMMIT_DATE=$(COMMIT_DATE) > _build/src/compiler/version.ml
+else
+	echo let version_extra = None > _build/src/compiler/version.ml
+endif
+
+build_src: | $(BUILD_SRC) _build/src/syntax/parser.ml _build/src/compiler/version.ml
+
+haxe: build_src
+	$(MAKE) -f $(MAKEFILENAME) build_pass_1
+	$(MAKE) -f $(MAKEFILENAME) build_pass_2
+	$(MAKE) -f $(MAKEFILENAME) build_pass_3
+	$(MAKE) -f $(MAKEFILENAME) build_pass_4
+
+build_pass_1:
+	printf MODULES= > Makefile.modules
+	ls -1 $(HAXE_DIRECTORIES:%=_build/src/%/*.ml) | tr '\n' ' ' >> Makefile.modules
+
+build_pass_2:
+	printf MODULES= > Makefile.modules
+	ocamlfind ocamldep -sort -slash $(HAXE_INCLUDES) $(MODULES) | sed -e "s/\.ml//g" >> Makefile.modules
+
+build_pass_3:
+	ocamlfind ocamldep -slash $(OCAMLDEP_FLAGS) $(HAXE_INCLUDES) $(MODULES:%=%.ml) > Makefile.dependencies
+
+build_pass_4: $(MODULES:%=%.$(MODULE_EXT))
+	$(COMPILER) -safe-string -linkpkg -o $(OUTPUT) $(NATIVE_LIBS) $(NATIVE_LIB_FLAG) $(LFLAGS) $(FINDLIB_PACKAGES) $(EXTLIB_INCLUDES) $(EXTLIB_LIBS:=.$(LIB_EXT)) $(MODULES:%=%.$(MODULE_EXT))
+
+plugin:
+ifeq ($(BYTECODE),1)
+	$(CC_CMD) $(PLUGIN).ml
+else
+	$(COMPILER) $(ALL_CFLAGS) -shared -o $(PLUGIN).cmxs $(PLUGIN).ml
+endif
+
+# Only use if you have only changed gencpp.ml
+quickcpp: build_src build_pass_4 copy_haxetoolkit
+
+CPP_OS := $(shell uname)
+ifeq ($(CPP_OS),Linux)
+copy_haxetoolkit:
+	sudo cp haxe /usr/bin/haxe
+else
+copy_haxetoolkit: /cygdrive/c/HaxeToolkit/haxe/haxe.exe
+/cygdrive/c/HaxeToolkit/haxe/haxe.exe:haxe.exe
+	cp $< $@
+endif
 
 haxelib:
 	(cd $(CURDIR)/extra/haxelib_src && $(CURDIR)/$(OUTPUT) client.hxml && nekotools boot run.n)
@@ -123,7 +173,8 @@ tools: haxelib
 install: uninstall
 	mkdir -p $(INSTALL_BIN_DIR)
 	mkdir -p $(INSTALL_LIB_DIR)/lib
-	cp -rf std $(INSTALL_STD_DIR)
+	mkdir -p $(INSTALL_STD_DIR)
+	cp -rf std/* $(INSTALL_STD_DIR)
 	cp -rf extra $(INSTALL_LIB_DIR)
 	cp haxe $(INSTALL_LIB_DIR)
 	ln -s $(INSTALL_LIB_DIR)/haxe $(INSTALL_BIN_DIR)/haxe
@@ -147,137 +198,9 @@ uninstall:
 		rm -rf $(INSTALL_LIB_DIR); \
 	fi
 
+# Dependencies
 
-# Modules
-
-# context
-
-src/context/common.$(MODULE_EXT): src/globals.$(MODULE_EXT) src/typing/type.$(MODULE_EXT) src/syntax/ast.$(MODULE_EXT) src/display/displayTypes.$(MODULE_EXT) libs/ilib/il.$(LIB_EXT)
-
-src/context/meta.$(MODULE_EXT): src/globals.$(MODULE_EXT)
-
-# display
-
-src/display/display.$(MODULE_EXT): src/globals.$(MODULE_EXT) src/context/meta.$(MODULE_EXT) src/path.$(MODULE_EXT) src/syntax/ast.$(MODULE_EXT) src/context/common.$(MODULE_EXT) src/typing/type.$(MODULE_EXT) src/typing/typecore.$(MODULE_EXT) src/syntax/parser.$(MODULE_EXT) src/display/displayTypes.$(MODULE_EXT)
-
-src/display/displayTypes.$(MODULE_EXT) : src/globals.$(MODULE_EXT) src/syntax/ast.$(MODULE_EXT)
-
-src/display/displayOutput.$(MODULE_EXT): src/globals.$(MODULE_EXT) src/typing/type.$(MODULE_EXT) src/typing/typer.$(MODULE_EXT) src/context/common.$(MODULE_EXT) src/display/display.$(MODULE_EXT)
-
-# sourcemaps
-
-src/sourcemaps.$(MODULE_EXT): src/globals.$(MODULE_EXT) src/context/common.$(MODULE_EXT) src/syntax/lexer.$(MODULE_EXT) src/syntax/ast.$(MODULE_EXT)
-
-# generators
-
-src/generators/codegen.$(MODULE_EXT): src/globals.$(MODULE_EXT) src/typing/type.$(MODULE_EXT) src/generators/genxml.$(MODULE_EXT) src/context/common.$(MODULE_EXT) src/syntax/ast.$(MODULE_EXT)
-
-src/generators/genas3.$(MODULE_EXT): src/typing/abstract.$(MODULE_EXT) src/globals.$(MODULE_EXT) src/context/meta.$(MODULE_EXT) src/typing/type.$(MODULE_EXT) src/context/common.$(MODULE_EXT) src/generators/codegen.$(MODULE_EXT) src/syntax/ast.$(MODULE_EXT)
-
-src/generators/gencommon.$(MODULE_EXT): src/typing/abstract.$(MODULE_EXT) src/context/meta.$(MODULE_EXT) src/globals.$(MODULE_EXT) src/typing/error.$(MODULE_EXT) src/typing/overloads.$(MODULE_EXT) src/path.$(MODULE_EXT) src/typing/type.$(MODULE_EXT) src/context/common.$(MODULE_EXT) src/generators/codegen.$(MODULE_EXT) src/syntax/ast.$(MODULE_EXT) libs/ilib/il.$(LIB_EXT)
-
-src/generators/gencpp.$(MODULE_EXT): src/typing/abstract.$(MODULE_EXT) src/globals.$(MODULE_EXT) src/context/meta.$(MODULE_EXT) src/path.$(MODULE_EXT) src/typing/type.$(MODULE_EXT) src/syntax/lexer.$(MODULE_EXT) src/context/common.$(MODULE_EXT) src/generators/codegen.$(MODULE_EXT) src/syntax/ast.$(MODULE_EXT)
-
-src/generators/gencs.$(MODULE_EXT): src/typing/abstract.$(MODULE_EXT) src/globals.$(MODULE_EXT) src/context/meta.$(MODULE_EXT) src/path.$(MODULE_EXT) src/typing/type.$(MODULE_EXT) src/syntax/lexer.$(MODULE_EXT) src/generators/gencommon.$(MODULE_EXT) src/context/common.$(MODULE_EXT) src/generators/codegen.$(MODULE_EXT) src/syntax/ast.$(MODULE_EXT) libs/ilib/il.$(LIB_EXT)
-
-src/generators/genjava.$(MODULE_EXT): src/typing/abstract.$(MODULE_EXT) src/globals.$(MODULE_EXT) src/context/meta.$(MODULE_EXT) src/path.$(MODULE_EXT) src/typing/type.$(MODULE_EXT) src/generators/gencommon.$(MODULE_EXT) src/context/common.$(MODULE_EXT) src/generators/codegen.$(MODULE_EXT) src/syntax/ast.$(MODULE_EXT)
-
-src/generators/genjs.$(MODULE_EXT): src/typing/abstract.$(MODULE_EXT) src/globals.$(MODULE_EXT) src/context/meta.$(MODULE_EXT) src/path.$(MODULE_EXT) src/typing/type.$(MODULE_EXT) src/syntax/lexer.$(MODULE_EXT) src/context/common.$(MODULE_EXT) src/generators/codegen.$(MODULE_EXT) src/syntax/ast.$(MODULE_EXT)
-
-src/generators/genneko.$(MODULE_EXT): src/globals.$(MODULE_EXT) src/typing/type.$(MODULE_EXT) src/syntax/lexer.$(MODULE_EXT) src/context/common.$(MODULE_EXT) src/generators/codegen.$(MODULE_EXT) src/syntax/ast.$(MODULE_EXT)
-
-src/generators/genlua.$(MODULE_EXT): src/typing/abstract.$(MODULE_EXT) src/globals.$(MODULE_EXT) src/context/meta.$(MODULE_EXT) src/typing/type.$(MODULE_EXT) src/syntax/lexer.$(MODULE_EXT) src/context/common.$(MODULE_EXT) src/generators/codegen.$(MODULE_EXT) src/syntax/ast.$(MODULE_EXT)
-
-src/generators/genphp.$(MODULE_EXT): src/typing/abstract.$(MODULE_EXT) src/globals.$(MODULE_EXT) src/context/meta.$(MODULE_EXT) src/typing/type.$(MODULE_EXT) src/syntax/lexer.$(MODULE_EXT) src/context/common.$(MODULE_EXT) src/generators/codegen.$(MODULE_EXT) src/syntax/ast.$(MODULE_EXT)
-
-src/generators/genpy.$(MODULE_EXT): src/typing/abstract.$(MODULE_EXT) src/globals.$(MODULE_EXT) src/context/meta.$(MODULE_EXT) src/typing/type.$(MODULE_EXT) src/syntax/lexer.$(MODULE_EXT) src/context/common.$(MODULE_EXT) src/generators/codegen.$(MODULE_EXT) src/syntax/ast.$(MODULE_EXT)
-
-src/generators/genswf.$(MODULE_EXT): src/globals.$(MODULE_EXT) src/typing/type.$(MODULE_EXT) src/generators/genswf9.$(MODULE_EXT) src/context/common.$(MODULE_EXT) src/syntax/ast.$(MODULE_EXT)
-
-src/generators/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/sourcemaps.$(MODULE_EXT)
-
-src/generators/hl2c.$(MODULE_EXT): src/generators/hlcode.$(MODULE_EXT) src/context/common.$(MODULE_EXT)
-
-src/generators/hlopt.$(MODULE_EXT): src/generators/hlcode.$(MODULE_EXT)
-
-src/generators/genhl.$(MODULE_EXT): src/globals.$(MODULE_EXT) src/context/meta.$(MODULE_EXT) src/typing/type.$(MODULE_EXT) src/syntax/lexer.$(MODULE_EXT) src/context/common.$(MODULE_EXT) src/generators/codegen.$(MODULE_EXT) src/syntax/ast.$(MODULE_EXT) src/generators/hlcode.$(MODULE_EXT) src/generators/hlinterp.$(MODULE_EXT) src/generators/hl2c.$(MODULE_EXT) src/generators/hlopt.$(MODULE_EXT)
-
-src/generators/genswf9.$(MODULE_EXT): src/typing/abstract.$(MODULE_EXT) src/globals.$(MODULE_EXT) src/context/meta.$(MODULE_EXT) src/path.$(MODULE_EXT) src/typing/type.$(MODULE_EXT) src/syntax/lexer.$(MODULE_EXT) src/context/common.$(MODULE_EXT) src/generators/codegen.$(MODULE_EXT) src/syntax/ast.$(MODULE_EXT)
-
-src/generators/genxml.$(MODULE_EXT): src/globals.$(MODULE_EXT) src/context/meta.$(MODULE_EXT) src/typing/type.$(MODULE_EXT) src/syntax/lexer.$(MODULE_EXT) src/context/common.$(MODULE_EXT) src/syntax/ast.$(MODULE_EXT)
-
-# macro
-
-src/macro/interp.$(MODULE_EXT): src/typing/abstract.$(MODULE_EXT) src/context/meta.$(MODULE_EXT) src/globals.$(MODULE_EXT) src/typing/error.$(MODULE_EXT) src/globals.$(MODULE_EXT) src/path.$(MODULE_EXT) src/typing/type.$(MODULE_EXT) src/syntax/lexer.$(MODULE_EXT) src/generators/genneko.$(MODULE_EXT) src/context/common.$(MODULE_EXT) src/generators/codegen.$(MODULE_EXT) src/syntax/ast.$(MODULE_EXT) src/generators/genswf.$(MODULE_EXT) src/generators/genjava.$(MODULE_EXT) src/generators/gencs.$(MODULE_EXT) src/syntax/parser.$(MODULE_EXT) libs/ilib/il.$(LIB_EXT) src/macro/macroApi.$(MODULE_EXT) libs/pcre/pcre.$(LIB_EXT)
-
-src/macro/macroContext.$(MODULE_EXT): src/typing/abstract.$(MODULE_EXT) src/context/meta.$(MODULE_EXT) src/globals.$(MODULE_EXT) src/typing/error.$(MODULE_EXT) src/optimization/optimizerTexpr.$(MODULE_EXT) src/typing/overloads.$(MODULE_EXT) src/path.$(MODULE_EXT) src/typing/typeload.$(MODULE_EXT) src/typing/typecore.$(MODULE_EXT) src/typing/type.$(MODULE_EXT) src/syntax/parser.$(MODULE_EXT) src/optimization/optimizer.$(MODULE_EXT) src/syntax/lexer.$(MODULE_EXT) src/macro/interp.$(MODULE_EXT) src/context/common.$(MODULE_EXT) src/generators/codegen.$(MODULE_EXT) src/syntax/ast.$(MODULE_EXT) src/optimization/filters.$(MODULE_EXT) src/generators/genjs.$(MODULE_EXT) src/display/display.$(MODULE_EXT) src/macro/hlmacro.$(MODULE_EXT) src/macro/macroApi.$(MODULE_EXT)
-
-src/macro/hlmacro.$(MODULE_EXT): src/generators/hlinterp.$(MODULE_EXT) src/generators/genhl.$(MODULE_EXT) src/macro/macroApi.$(MODULE_EXT)
-
-src/macro/macroApi.$(MODULE_EXT): src/generators/genjs.$(MODULE_EXT) src/generators/gencs.$(MODULE_EXT) src/generators/genjava.$(MODULE_EXT) src/syntax/parser.$(MODULE_EXT)
-
-# optimization
-
-src/optimization/analyzer.$(MODULE_EXT): src/context/meta.$(MODULE_EXT) src/globals.$(MODULE_EXT) src/optimization/optimizer.$(MODULE_EXT) src/syntax/ast.$(MODULE_EXT) src/typing/type.$(MODULE_EXT) src/context/common.$(MODULE_EXT) src/optimization/analyzerConfig.$(MODULE_EXT) src/optimization/analyzerTypes.$(MODULE_EXT) src/optimization/analyzerTexpr.$(MODULE_EXT) src/optimization/analyzerTexprTransformer.$(MODULE_EXT) src/generators/codegen.$(MODULE_EXT)
-
-src/optimization/analyzerConfig.$(MODULE_EXT): src/context/meta.$(MODULE_EXT) src/globals.$(MODULE_EXT) src/syntax/ast.$(MODULE_EXT) src/typing/type.$(MODULE_EXT) src/context/common.$(MODULE_EXT)
-
-src/optimization/analyzerTexpr.$(MODULE_EXT): src/typing/abstract.$(MODULE_EXT) src/context/meta.$(MODULE_EXT) src/globals.$(MODULE_EXT) src/typing/error.$(MODULE_EXT) src/syntax/ast.$(MODULE_EXT) src/typing/type.$(MODULE_EXT) src/context/common.$(MODULE_EXT) src/generators/codegen.$(MODULE_EXT) src/optimization/analyzerConfig.$(MODULE_EXT) src/optimization/optimizerTexpr.$(MODULE_EXT) src/generators/genphp7.$(MODULE_EXT)
-
-src/optimization/analyzerTexprTransformer.$(MODULE_EXT): src/context/meta.$(MODULE_EXT) src/globals.$(MODULE_EXT) src/typing/error.$(MODULE_EXT) src/syntax/ast.$(MODULE_EXT) src/typing/type.$(MODULE_EXT) src/context/common.$(MODULE_EXT) src/generators/codegen.$(MODULE_EXT) src/optimization/analyzerConfig.$(MODULE_EXT) src/optimization/analyzerTypes.$(MODULE_EXT) src/optimization/analyzerTexpr.$(MODULE_EXT)
-
-src/optimization/analyzerTypes.$(MODULE_EXT): src/globals.$(MODULE_EXT) src/syntax/ast.$(MODULE_EXT) src/typing/type.$(MODULE_EXT) src/context/common.$(MODULE_EXT) src/optimization/analyzerConfig.$(MODULE_EXT)
-
-src/optimization/dce.$(MODULE_EXT): src/typing/abstract.$(MODULE_EXT) src/context/meta.$(MODULE_EXT) src/globals.$(MODULE_EXT) src/typing/typecore.$(MODULE_EXT) src/typing/error.$(MODULE_EXT) src/path.$(MODULE_EXT) src/syntax/ast.$(MODULE_EXT) src/context/common.$(MODULE_EXT) src/typing/type.$(MODULE_EXT)
-
-src/optimization/filters.$(MODULE_EXT): src/context/meta.$(MODULE_EXT) src/globals.$(MODULE_EXT) src/typing/error.$(MODULE_EXT) src/syntax/ast.$(MODULE_EXT) src/optimization/analyzer.$(MODULE_EXT) src/context/common.$(MODULE_EXT) src/typing/type.$(MODULE_EXT) src/optimization/dce.$(MODULE_EXT) src/generators/codegen.$(MODULE_EXT) src/typing/typecore.$(MODULE_EXT)
-
-src/optimization/optimizer.$(MODULE_EXT): src/context/meta.$(MODULE_EXT) src/globals.$(MODULE_EXT) src/optimization/optimizerTexpr.$(MODULE_EXT) src/display/display.$(MODULE_EXT) src/typing/typecore.$(MODULE_EXT) src/typing/type.$(MODULE_EXT) src/syntax/parser.$(MODULE_EXT) src/context/common.$(MODULE_EXT) src/syntax/ast.$(MODULE_EXT)
-
-src/optimization/optimizerTexpr.$(MODULE_EXT): src/globals.$(MODULE_EXT) src/context/meta.$(MODULE_EXT) src/context/common.$(MODULE_EXT) src/typing/type.$(MODULE_EXT) src/syntax/ast.$(MODULE_EXT)
-
-# syntax
-
-src/syntax/ast.$(MODULE_EXT): src/context/meta.$(MODULE_EXT) src/globals.$(MODULE_EXT)
-
-src/syntax/lexer.$(MODULE_EXT): src/globals.$(MODULE_EXT) src/syntax/lexer.ml src/syntax/ast.$(MODULE_EXT)
-
-src/syntax/parser.$(MODULE_EXT): src/globals.$(MODULE_EXT) src/context/meta.$(MODULE_EXT) src/path.$(MODULE_EXT) src/syntax/parser.ml src/syntax/lexer.$(MODULE_EXT) src/context/common.$(MODULE_EXT) src/syntax/ast.$(MODULE_EXT)
-	$(CC_PARSER_CMD)
-
-# typing
-
-src/typing/abstract.$(MODULE_EXT): src/context/meta.$(MODULE_EXT) src/typing/type.$(MODULE_EXT) src/typing/error.$(MODULE_EXT)
-
-src/typing/error.$(MODULE_EXT): src/globals.$(MODULE_EXT) src/typing/type.$(MODULE_EXT)
-
-src/typing/matcher.$(MODULE_EXT): src/typing/abstract.$(MODULE_EXT) src/context/meta.$(MODULE_EXT) src/globals.$(MODULE_EXT) src/typing/error.$(MODULE_EXT) src/optimization/optimizer.$(MODULE_EXT) src/generators/codegen.$(MODULE_EXT) src/typing/typecore.$(MODULE_EXT) src/typing/type.$(MODULE_EXT) src/typing/typer.$(MODULE_EXT) src/context/common.$(MODULE_EXT) src/syntax/ast.$(MODULE_EXT)
-
-src/typing/overloads.$(MODULE_EXT): src/typing/abstract.$(MODULE_EXT) src/context/meta.$(MODULE_EXT) src/typing/type.$(MODULE_EXT)
-
-src/typing/type.$(MODULE_EXT): src/globals.$(MODULE_EXT) src/context/meta.$(MODULE_EXT) src/syntax/ast.$(MODULE_EXT) src/json.$(MODULE_EXT)
-
-src/typing/typecore.$(MODULE_EXT): src/typing/abstract.$(MODULE_EXT) src/context/meta.$(MODULE_EXT) src/globals.$(MODULE_EXT) src/path.$(MODULE_EXT) src/typing/type.$(MODULE_EXT) src/context/common.$(MODULE_EXT) src/syntax/ast.$(MODULE_EXT)
-
-src/typing/typeload.$(MODULE_EXT): src/globals.$(MODULE_EXT) src/context/meta.$(MODULE_EXT) src/globals.$(MODULE_EXT) src/optimization/optimizerTexpr.$(MODULE_EXT) src/typing/overloads.$(MODULE_EXT) src/path.$(MODULE_EXT) src/typing/typecore.$(MODULE_EXT) src/typing/type.$(MODULE_EXT) src/syntax/parser.$(MODULE_EXT) src/optimization/optimizer.$(MODULE_EXT) src/syntax/lexer.$(MODULE_EXT) src/context/common.$(MODULE_EXT) src/syntax/ast.$(MODULE_EXT) src/json.$(MODULE_EXT) src/display/display.$(MODULE_EXT)
-
-src/typing/typer.$(MODULE_EXT): src/typing/abstract.$(MODULE_EXT) src/context/meta.$(MODULE_EXT) src/globals.$(MODULE_EXT) src/typing/error.$(MODULE_EXT) src/optimization/optimizerTexpr.$(MODULE_EXT) src/typing/overloads.$(MODULE_EXT) src/path.$(MODULE_EXT) src/typing/typeload.$(MODULE_EXT) src/typing/typecore.$(MODULE_EXT) src/typing/type.$(MODULE_EXT) src/syntax/parser.$(MODULE_EXT) src/optimization/optimizer.$(MODULE_EXT) src/syntax/lexer.$(MODULE_EXT) src/macro/interp.$(MODULE_EXT) src/context/common.$(MODULE_EXT) src/generators/codegen.$(MODULE_EXT) src/syntax/ast.$(MODULE_EXT) src/optimization/filters.$(MODULE_EXT) src/generators/genjs.$(MODULE_EXT) src/display/display.$(MODULE_EXT) src/macro/macroContext.$(MODULE_EXT)
-
-# main
-
-src/main.$(MODULE_EXT): src/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)
-
-src/path.$(MODULE_EXT): src/globals.$(MODULE_EXT)
-
-src/server.$(MODULE_EXT): src/context/meta.$(MODULE_EXT) src/globals.$(MODULE_EXT) src/path.$(MODULE_EXT) src/typing/typer.$(MODULE_EXT) src/typing/typeload.$(MODULE_EXT) src/typing/typecore.$(MODULE_EXT) src/typing/type.$(MODULE_EXT) src/syntax/parser.$(MODULE_EXT) src/typing/typecore.$(MODULE_EXT) src/typing/type.$(MODULE_EXT) src/context/common.$(MODULE_EXT) src/context/common.$(MODULE_EXT) src/syntax/parser.$(MODULE_EXT) src/syntax/ast.$(MODULE_EXT) src/display/displayOutput.$(MODULE_EXT)
-
-src/version.$(MODULE_EXT):
-	$(MAKE) -f Makefile.version_extra -s --no-print-directory ADD_REVISION=$(ADD_REVISION) BRANCH=$(BRANCH) COMMIT_SHA=$(COMMIT_SHA) COMMIT_DATE=$(COMMIT_DATE) > src/version.ml
-	$(COMPILER) $(ALL_CFLAGS) -c src/version.ml
+-include Makefile.dependencies
 
 # Package
 
@@ -312,32 +235,77 @@ install_dox:
 
 package_doc:
 	mkdir -p $(PACKAGE_OUT_DIR)
-	cd $$(haxelib path dox | head -n 1) && haxe run.hxml && haxe gen.hxml
-	haxelib run dox -theme haxe_api -D website "http://haxe.org/" --title "Haxe API" -o $(PACKAGE_OUT_DIR)/$(PACKAGE_FILE_NAME)_doc.zip -D version "$$(haxe -version 2>&1)" -i $$(haxelib path dox | head -n 1)bin/xml -ex microsoft -ex javax -ex cs.internal -D source-path https://github.com/HaxeFoundation/haxe/blob/$(BRANCH)/std/
+	$(eval OUTFILE := $(shell pwd)/$(PACKAGE_OUT_DIR)/$(PACKAGE_FILE_NAME)_doc.zip)
+	$(eval VERSION := $(shell haxe -version 2>&1))
+	cd $$(haxelib path dox | head -n 1) && \
+		haxe run.hxml && \
+		haxe gen.hxml && \
+		haxe -lib hxtemplo -lib hxparse -lib hxargs -lib markdown \
+		-cp src -dce no --run dox.Dox -theme haxe_api -D website "http://haxe.org/" \
+		--title "Haxe API" -o $(OUTFILE) \
+		-D version "$(VERSION)" -i bin/xml -ex microsoft -ex javax -ex cs.internal \
+		-D source-path https://github.com/HaxeFoundation/haxe/blob/$(BRANCH)/std/
 
 deploy_doc:
 	scp $(PACKAGE_OUT_DIR)/$(PACKAGE_FILE_NAME)_doc.zip [email protected]:/data/haxeapi/www/v/dev/api-latest.zip
 	ssh [email protected] "cd /data/haxeapi/www/v/dev && find . ! -name 'api-latest.zip' -maxdepth 1 -mindepth 1 -exec rm -rf {} + && unzip -q -o api-latest.zip"
 
+# Installer
+
+package_installer_mac:
+	$(eval DOCFILE := $(shell pwd)/$(PACKAGE_OUT_DIR)/$(PACKAGE_FILE_NAME)_doc.zip)
+	$(eval OUTFILE := $(shell pwd)/$(PACKAGE_OUT_DIR)/$(PACKAGE_FILE_NAME)_installer.tar.gz)
+	$(eval PACKFILE := $(shell pwd)/$(PACKAGE_OUT_DIR)/$(PACKAGE_FILE_NAME)_bin.tar.gz)
+	$(eval VERSION := $(shell haxe -version 2>&1))
+	$(eval NEKOVER := $(shell neko -version 2>&1))
+	bash -c "rm -rf $(INSTALLER_TMP_DIR)/{resources,pkg,tgz,haxe.tar.gz}"
+	mkdir $(INSTALLER_TMP_DIR)/resources
+	# neko - unpack to change the dir name
+	cd $(INSTALLER_TMP_DIR)/resources && tar -zxvf ../neko-osx64.tar.gz
+	mv $(INSTALLER_TMP_DIR)/resources/neko* $(INSTALLER_TMP_DIR)/resources/neko
+	cd $(INSTALLER_TMP_DIR)/resources && tar -zcvf neko.tar.gz neko
+	# haxe - unpack to change the dir name
+	cd $(INSTALLER_TMP_DIR)/resources && tar -zxvf $(PACKFILE)
+	mv $(INSTALLER_TMP_DIR)/resources/haxe* $(INSTALLER_TMP_DIR)/resources/haxe
+	cd $(INSTALLER_TMP_DIR)/resources && tar -zcvf haxe.tar.gz haxe
+	# scripts
+	cp -rf extra/mac-installer/* $(INSTALLER_TMP_DIR)/resources
+	cd $(INSTALLER_TMP_DIR)/resources && tar -zcvf scripts.tar.gz scripts
+	# installer structure
+	mkdir -p $(INSTALLER_TMP_DIR)/pkg
+	cd $(INSTALLER_TMP_DIR)/pkg && xar -xf ../resources/installer-structure.pkg .
+	mkdir $(INSTALLER_TMP_DIR)/tgz; mv $(INSTALLER_TMP_DIR)/resources/*.tar.gz $(INSTALLER_TMP_DIR)/tgz
+	cd $(INSTALLER_TMP_DIR)/tgz; find . | cpio -o --format odc | gzip -c > ../pkg/files.pkg/Payload
+	cd $(INSTALLER_TMP_DIR)/pkg/files.pkg && bash -c "INSTKB=$$(du -sk ../../tgz | awk '{print $$1;}'); \
+	du -sk ../../tgz; \
+	echo $$INSTKB ; \
+	INSTKBH=`expr $$INSTKB - 4`; \
+	echo $$INSTKBH ;\
+	sed -i '' 's/%%INSTKB%%/$$INSTKBH/g' PackageInfo ;\
+	sed -i '' 's/%%VERSION%%/$(VERSION)/g' PackageInfo ;\
+	sed -i '' 's/%%VERSTRING%%/$(VERSION)/g' PackageInfo ;\
+	sed -i '' 's/%%VERLONG%%/$(VERSION)/g' PackageInfo ;\
+	sed -i '' 's/%%NEKOVER%%/$(NEKOVER)/g' PackageInfo ;\
+	cd .. ;\
+	sed -i '' 's/%%VERSION%%/$(VERSION)/g' Distribution ;\
+	sed -i '' 's/%%VERSTRING%%/$(VERSION)/g' Distribution ;\
+	sed -i '' 's/%%VERLONG%%/$(VERSION)/g' Distribution ;\
+	sed -i '' 's/%%NEKOVER%%/$(NEKOVER)/g' Distribution ;\
+	sed -i '' 's/%%INSTKB%%/$$INSTKBH/g' Distribution"
+	# repackage
+	cd $(INSTALLER_TMP_DIR)/pkg; xar --compression none -cf ../$(PACKAGE_FILE_NAME).pkg *
+	# tar
+	cd $(INSTALLER_TMP_DIR); tar -zcvf $(OUTFILE) $(PACKAGE_FILE_NAME).pkg
+
 # Clean
 
 clean: clean_libs clean_haxe clean_tools clean_package
 
 clean_libs:
-	make -C libs/extlib clean
-	make -C libs/extc clean
-	make -C libs/neko clean
-	make -C libs/ziplib clean
-	make -C libs/javalib clean
-	make -C libs/ilib clean
-	make -C libs/swflib clean
-	make -C libs/xml-light clean
-	make -C libs/ttflib clean
-	make -C libs/objsize clean
-	make -C libs/pcre clean
+	$(foreach lib,$(EXTLIB_LIBS),$(MAKE) -C libs/$(lib) clean &&) true
 
 clean_haxe:
-	rm -f -r  $(MODULES:%=src/%.obj) $(MODULES:%=src/%.o) $(MODULES:%=src/%.cmx) $(MODULES:%=src/%.cmi) $(MODULES:%=src/%.cmo) $(MODULES:%=src/%.cmt) src/syntax/lexer.ml src/version.ml $(OUTPUT)
+	rm -f -r _build $(OUTPUT)
 
 clean_tools:
 	rm -f $(OUTPUT) haxelib
@@ -345,6 +313,8 @@ clean_tools:
 clean_package:
 	rm -rf $(PACKAGE_OUT_DIR)
 
+FORCE:
+
 # SUFFIXES
 
 .ml.cmx:
@@ -353,7 +323,4 @@ clean_package:
 .ml.cmo:
 	$(CC_CMD)
 
-.mll.ml:
-	ocamllex $<
-
-.PHONY: haxe libs version.cmx version.cmo haxelib
+.PHONY: haxe libs haxelib

+ 32 - 5
Makefile.win

@@ -1,11 +1,10 @@
 PLATFORM=win
+MAKEFILENAME=Makefile.win
 include Makefile
 OUTPUT=haxe.exe
 EXTENSION=.exe
 PACKAGE_SRC_EXTENSION=.zip
 
-OCAMLOPT=ocamlopt.opt
-
 kill:
 	-@taskkill /F /IM haxe.exe
 
@@ -28,12 +27,12 @@ FILTER=sed 's/File "\([^"]\+\)", line \([0-9]\+\), \(.*\)/\1(\2): \3/' tmp.cmi
 endif
 
 ifeq (${FD_OUTPUT}, 1)
-FILTER=sed '/File/{ N; s/File "\([^"]\+\)", line \([0-9]\+\), characters \([0-9-]\+\):[\r\n]*\(.*\)/\1:\2: characters \3 : \4/ }' tmp.cmi
+FILTER=sed -e '/File/{ N; s/File "\([^"]\+\)", line \([0-9]\+\), characters \([0-9-]\+\):[\r\n]*\(.*\)/\1:\2: characters \3 : \4/ }' -e 's/_build\/src\//src\//' tmp.cmi
 endif
 
 ifdef FILTER
-CC_CMD=($(OCAMLOPT) $(ALL_CFLAGS) -c $< 2>tmp.cmi && $(FILTER)) || ($(FILTER) && exit 1)
-CC_PARSER_CMD=($(OCAMLOPT) -pp camlp4o $(ALL_CFLAGS) -c src/syntax/parser.ml 2>tmp.cmi && $(FILTER)) || ($(FILTER) && exit 1)
+CC_CMD=($(COMPILER) $(ALL_CFLAGS) -c $< 2>tmp.cmi && $(FILTER)) || ($(FILTER) && exit 1)
+CC_PARSER_CMD=($(COMPILER) -pp camlp4o $(ALL_CFLAGS) -c src/syntax/parser.ml 2>tmp.cmi && $(FILTER)) || ($(FILTER) && exit 1)
 endif
 
 PACKAGE_FILES=$(OUTPUT) haxelib$(EXTENSION) std "$$(cygpath -w "$$(which zlib1.dll)")" "$$(cygpath -w "$$(which libpcre-1.dll)")"
@@ -61,3 +60,31 @@ package_choco:
 	cd out/choco && choco pack
 	mv out/choco/haxe.*.nupkg out
 	rm -rf out/choco
+
+package_installer_win:
+	$(eval OUTFILE := $(PACKAGE_OUT_DIR)/$(PACKAGE_FILE_NAME)_installer.zip)
+	$(eval VERSION := $(shell haxe -version 2>&1 | cut -d ' ' -f1))
+	rm -rf $(INSTALLER_TMP_DIR)/resources
+	# neko
+	mkdir $(INSTALLER_TMP_DIR)/resources
+	cd $(INSTALLER_TMP_DIR) && 7z x -y neko-win.zip
+	rm $(INSTALLER_TMP_DIR)/neko-win.zip
+	mv $(INSTALLER_TMP_DIR)/neko* $(INSTALLER_TMP_DIR)/resources/neko
+	# haxe
+	7z x -y $(PACKAGE_OUT_DIR)/$(PACKAGE_FILE_NAME)_bin.zip -o$(INSTALLER_TMP_DIR)/resources
+	mv $(INSTALLER_TMP_DIR)/resources/haxe* $(INSTALLER_TMP_DIR)/resources/haxe
+	# haxesetup.exe
+	haxelib path hxcpp || haxelib install hxcpp
+	cd extra; haxelib run hxcpp build-haxesetup.xml
+	cp extra/haxesetup.exe $(INSTALLER_TMP_DIR)/resources/haxe
+	# extra
+	cp extra/*.nsi $(INSTALLER_TMP_DIR)
+	cp extra/*.nsh $(INSTALLER_TMP_DIR)
+	cp -rf extra/images $(INSTALLER_TMP_DIR)
+	# nsis
+	sed -i "s/%%VERSION%%/$(VERSION)/g" $(INSTALLER_TMP_DIR)/installer.nsi
+	sed -i "s/%%VERSTRING%%/$(VERSION)/g" $(INSTALLER_TMP_DIR)/installer.nsi
+	sed -i "s/%%VERLONG%%/$(VERSION)/g" $(INSTALLER_TMP_DIR)/installer.nsi
+	cd $(INSTALLER_TMP_DIR) && makensis installer.nsi
+	7z a -r -tzip $(OUTFILE) $(INSTALLER_TMP_DIR)/*.exe
+	dir $(PACKAGE_OUT_DIR)

+ 8 - 8
README.md

@@ -1,5 +1,5 @@
 
-# [<img src="http://haxe.org/img/haxe-logo-horizontal.svg" alt="Haxe logo" width="140">](https://haxe.org) - [The Cross-Platform Toolkit](https://haxe.org)
+# [<img src="https://haxe.org/img/haxe-logo-horizontal.svg" alt="Haxe logo" width="140">](https://haxe.org) - [The Cross-Platform Toolkit](https://haxe.org)
 [![TravisCI Build Status](https://travis-ci.org/HaxeFoundation/haxe.svg?branch=development)](https://travis-ci.org/HaxeFoundation/haxe)
 [![AppVeyor Build Status](https://ci.appveyor.com/api/projects/status/github/HaxeFoundation/haxe?branch=development&svg=true)](https://ci.appveyor.com/project/HaxeFoundation/haxe)
 [![SauceLabs Test Status](https://saucelabs.com/buildstatus/haxe)](https://saucelabs.com/u/haxe)
@@ -17,15 +17,15 @@ Haxe allows you to compile for the following targets:
  * C++
  * C#
  * Flash
+ * [HashLink](http://hashlink.haxe.org/)
  * Java
  * JavaScript
- * NekoVM
- * PHP
- * Python
  * Lua
- * [HashLink](http://hashlink.haxe.org/)
+ * [NekoVM](http://nekovm.org/)
+ * PHP
+ * Python 3
 
-You can try Haxe directly from your browser at [try.haxe.org](http://try.haxe.org)!
+You can try Haxe directly from your browser at [try.haxe.org](https://try.haxe.org)!
 
 For more information about Haxe, head to the [offical Haxe website](https://haxe.org).
 
@@ -76,10 +76,10 @@ For information on on using Haxe, consult the [Haxe documentation](https://haxe.
 
 You can get help and talk with fellow Haxers from around the world via:
 
- * [Haxe on Stack Overflow](http://stackoverflow.com/questions/tagged/haxe)
+ * [Haxe on Stack Overflow](https://stackoverflow.com/questions/tagged/haxe)
  * the [official Haxe Google Group](https://groups.google.com/forum/#!forum/haxelang)
  * [#Haxe on Twitter](https://twitter.com/hashtag/haxe?src=hash)
- * the [Haxe IRC chatroom](http://unic0rn.github.io/tiramisu/haxe), #haxe on chat.freenode.net
+ * the [Haxe IRC chatroom](https://unic0rn.github.io/tiramisu/haxe/), #haxe on chat.freenode.net
 
 :+1: Get notified of the latest Haxe news, follow us on [Twitter](https://twitter.com/haxelang), [Facebook](https://www.facebook.com/haxe.org) or [Google+](https://plus.google.com/+HaxeOrg)
 

+ 22 - 27
appveyor.yml

@@ -3,57 +3,57 @@ version: "{build}"
 environment:
     global:
         HAXELIB_ROOT: C:/projects/haxelib
-        CYG_ROOT: C:/cygwin
+        CYG_ROOT: C:/cygwin64
         ADD_REVISION: 1
-        OCAMLOPT: ocamlopt.opt
         MYSQL_PATH: C:\Program Files\MySQL\MySQL Server 5.7
         MYSQL_USER: root
         MYSQL_PASSWORD: Password12!
+        HXBUILDS_AWS_ACCESS_KEY_ID:
+          secure: fggQXlr5xGGl0znUi0UkqPWd6LviHnk0TR6YxJmuV3U=
+        HXBUILDS_AWS_SECRET_ACCESS_KEY:
+          secure: ewwkKcjnSKl/Vtrz1SXmI6XKk1ENmJDyzm5YaR2wi03foRhTke29TvymB21rDTSl
     matrix:
-        - TEST: "neko,python,cs,java,macro"
-        - TEST: "php7,php"
+        - TEST: "neko,python,cs,java,php7,php,macro"
+          DEPLOY_NIGHTLIES: 1
         - TEST: "cpp"
 
-services:
-    - mysql
-
 skip_tags: true
 
 cache:
-    - opam32.tar.xz -> appveyor.yml
+    - opam64.tar.xz -> appveyor.yml
 
 install:
     - 'git submodule update --init --recursive'
     - '%CYG_ROOT%/bin/bash -lc "echo initialize"'
+    - choco install curl
     # Install ocaml
-    - curl -fsS -o cygwin-setup.exe --retry 3 https://cygwin.com/setup-x86.exe
-    - 'cygwin-setup.exe -g -q -R "%CYG_ROOT%" -P make -P git -P mingw64-i686-zlib -P rsync -P patch -P diffutils -P curl -P unzip -P m4 -P perl -P mingw64-i686-gcc-core -P mingw64-i686-pcre'
-    - if not exist "opam32.tar.xz" (
-        curl -fsS -o opam32.tar.xz --retry 3 https://dl.dropboxusercontent.com/s/eo4igttab8ipyle/opam32.tar.xz
+    - curl -fsSL -o cygwin-setup.exe --retry 3 https://cygwin.com/setup-x86_64.exe
+    - 'cygwin-setup.exe -g -q -R "%CYG_ROOT%" -P make -P git -P mingw64-x86_64-zlib -P rsync -P patch -P diffutils -P curl -P unzip -P m4 -P perl -P mingw64-x86_64-gcc-core -P mingw64-x86_64-pcre'
+    - if not exist "opam64.tar.xz" (
+        curl -fsSL -o opam64.tar.xz --retry 3 https://github.com/fdopen/opam-repository-mingw/releases/download/0.0.0.1/opam64.tar.xz
       )
-    - 7z x "opam32.tar.xz" -so | 7z x -aoa -si -ttar
-    - '%CYG_ROOT%/bin/bash -lc "cd \"$OLDPWD\" && bash opam32/install.sh"'
-    - '%CYG_ROOT%/bin/bash -lc "opam init mingw \"https://github.com/fdopen/opam-repository-mingw.git\" --comp 4.02.3+mingw32c --switch 4.02.3+mingw32c --auto-setup --yes"'
-    - '%CYG_ROOT%/bin/bash -lc "opam install camlp4 --yes"'
+    - 7z x "opam64.tar.xz" -so | 7z x -aoa -si -ttar
+    - '%CYG_ROOT%/bin/bash -lc "cd \"$OLDPWD\" && bash opam64/install.sh"'
+    - '%CYG_ROOT%/bin/bash -lc "opam init mingw \"https://github.com/fdopen/opam-repository-mingw.git\" --comp 4.02.3+mingw64c --switch 4.02.3+mingw64c --auto-setup --yes"'
+    - '%CYG_ROOT%/bin/bash -lc "opam install camlp4 sedlex ocamlfind xml-light extlib rope ptmap --yes"'
     # Install neko
     - choco install neko --prerelease --ignore-dependencies -s 'https://ci.appveyor.com/nuget/neko' -y
     - choco install chocolatey-core.extension php --ignore-dependencies -y
-    - echo extension=php_mysqli.dll >> C:\tools\php71\php.ini
-    - echo extension=php_sqlite3.dll >> C:\tools\php71\php.ini
+    - choco install nsis.portable
+    - choco install wget
     - echo extension=php_openssl.dll >> C:\tools\php71\php.ini
-    - echo extension=php_pdo_mysql.dll >> C:\tools\php71\php.ini
-    - echo extension=php_pdo_sqlite.dll >> C:\tools\php71\php.ini
     - RefreshEnv
     - neko -version
     # setup python
     - cmd: mklink C:\Python34-x64\python3.exe C:\Python34-x64\python.exe
     - set PATH=%PATH%;C:\Python34-x64
     # expose the dll files
-    - set "PATH=%PATH%;%CYG_ROOT%/usr/i686-w64-mingw32/sys-root/mingw/bin"
+    - set "PATH=%CYG_ROOT%/usr/x86_64-w64-mingw32/sys-root/mingw/bin;%PATH%"
+    - choco install awscli
 
 build_script:
     - 'cd %APPVEYOR_BUILD_FOLDER%'
-    - '%CYG_ROOT%/bin/bash -lc "cd \"$OLDPWD\" && make -s -f Makefile.win"'
+    - '%CYG_ROOT%/bin/bash -lc "cd \"$OLDPWD\" && make -s -f Makefile.win libs && make -j -s -f Makefile.win haxe && make -s -f Makefile.win haxelib"'
     - 'set PATH=%PATH%;%APPVEYOR_BUILD_FOLDER%'
     - 'set HAXEPATH=%APPVEYOR_BUILD_FOLDER%'
     - '%CYG_ROOT%/bin/bash -lc "cd \"$OLDPWD\" && make -s -f Makefile.win package_bin"'
@@ -63,11 +63,6 @@ build_script:
     - cd %APPVEYOR_BUILD_FOLDER%/tests/
     - mkdir "%HAXELIB_ROOT%"
     - haxelib setup "%HAXELIB_ROOT%"
-    # setup mysql for testing
-    - set PATH=%MYSQL_PATH%\bin;%PATH%
-    - mysql --user=%MYSQL_USER% --password=%MYSQL_PASSWORD% -e "create user 'travis'@'localhost';"
-    - mysql --user=%MYSQL_USER% --password=%MYSQL_PASSWORD% -e "create database haxe_test;"
-    - mysql --user=%MYSQL_USER% --password=%MYSQL_PASSWORD% -e "grant all on haxe_test.* to 'travis'@'localhost';"
 
 test_script:
     - cd %APPVEYOR_BUILD_FOLDER%/tests/

+ 42 - 0
extra/CHANGES.txt

@@ -1,3 +1,45 @@
+2017-09-12: 4.0.0-preview.1
+
+	New features:
+
+	all : reworked macro interpreter
+	all : added support for arrow functions
+
+	General improvements and optimizations:
+
+	js : improved generation of `break` inside `switch` inside loops (#4964)
+	cs : improved generation of enum classes (#6119)
+	sys : the `database` parameter of `Mysql.connect` is now optional
+	js : updated jQuery extern (js.jquery.*) for jQuery 1.12.4 / 3.2.1 support.
+	Makefile : default Unix installation location $(INSTALL_DIR) changed from /usr to /usr/local.
+	Makefile : default Unix std location $(INSTALL_STD_DIR) changed from $(INSTALL_LIB_DIR)/std to $(INSTALL_DIR)/share/haxe/std.
+
+	Removals:
+
+	all : removed --eval command line argument
+	sys : SPOD (sys.db.Object, sys.db.Manager and friends) was moved into a separate library `record-macros` (https://github.com/HaxeFoundation/record-macros)
+	js : js.JQuery and js.SWFObject were moved into hx3compat library (https://github.com/HaxeFoundation/hx3compat),
+	     it's recommended to use more modern js.jquery.JQuery and js.swfobject.SWFObject classes.
+	all : moved haxe.web.Dispatch into hx3compat library (https://github.com/HaxeFoundation/hx3compat).
+
+	Bugfixes:
+
+	php7: fix Reflect.field() for strings (#6276)
+	php7: fix `@:enum abstract` generation  without `-dce full` (#6175)
+	php7: fix using enum constructor with arguments as a call argument (#6177)
+	php7: fix `null` property access (#6281)
+	php7: fix setting values in a map stored in another map (#6257)
+	php7: fix haxe.io.Input.readAll() with disabled analyzer optimizations (#6387)
+	php/php7: fixed accessing enum constructors on enum type stored to a variable (#6159)
+	php/php7: fix "cannot implement previously implemented interface" (#6208)
+	php: fix invoking functions stored in dynamic static vars (#6158)
+	php: fix field access on `new MyClass()` expressions wrapped in a `cast` (#6294)
+
+	Standard Library:
+
+	all : added `EReg.escape` (#5098)
+	all : `BalancedTree implements `haxe.Constraints.IMap` (#6231)
+
 2017-03-20: 3.4.2
 
 	Bugfixes:

+ 2 - 0
extra/ImportAll.hx

@@ -53,6 +53,8 @@ class ImportAll {
 			if( !Context.defined("hl") ) return;
 		case "lua":
 			if( !Context.defined("lua") ) return;
+		case "eval":
+			if( !Context.defined("eval") ) return;
 		case "tools":
 			return;
 		case "build-tool":

+ 4 - 0
extra/all.hxml

@@ -54,6 +54,10 @@
 -hl all_hl
 -xml hl.xml
 
+--next
+--interp
+-xml interp.xml
+
 --next
 
 -xml cross.xml

+ 16 - 0
extra/build-haxesetup.xml

@@ -0,0 +1,16 @@
+<xml>
+
+<include name="${HXCPP}/build-tool/BuildCommon.xml"/>
+<set name="static_link" value="1" />
+<set name="no_console" value="1" />
+
+<files id="haxesetup">
+  <file name="setup.cpp" />
+</files>
+
+<target id="default" output="haxesetup" tool="linker" toolid="exe">
+  <lib name="advapi32.lib" />
+  <files id="haxesetup" />
+</target>
+
+</xml>

+ 1 - 1
extra/haxelib_src

@@ -1 +1 @@
-Subproject commit eeac8f4e77b23b120f27d27502f43589db26d143
+Subproject commit b0b6057d24cac4fc4464b259f269e6d80bd9bf9d

BIN
extra/mac-installer/installer-structure.pkg


+ 13 - 0
extra/mac-installer/scripts/haxe-postinstall.sh

@@ -0,0 +1,13 @@
+#!/bin/sh
+rm -f /usr/bin/haxe
+rm -f /usr/local/bin/haxe
+rm -f /usr/bin/haxedoc
+rm -f /usr/local/bin/haxedoc
+rm -f /usr/bin/haxelib
+rm -f /usr/local/bin/haxelib
+rm -f ~/.haxelib
+rm -f $HOME/.haxelib 
+ln -s /usr/local/lib/haxe/haxe /usr/local/bin/haxe
+cp /usr/local/lib/haxe/haxelib /usr/local/bin/haxelib
+mkdir -p /usr/local/lib/haxe/lib
+chmod 777 /usr/local/lib/haxe/lib

+ 8 - 0
extra/mac-installer/scripts/haxe-preinstall.sh

@@ -0,0 +1,8 @@
+#!/bin/sh
+
+#ensure no conflicting version is left there
+rm -rf /usr/lib/haxe/std
+rm -rf /usr/lib/haxe/doc
+rm -f /usr/lib/haxe/haxe
+rm -rf /usr/local/lib/haxe/std
+rm -rf /usr/local/lib/haxe/doc

+ 18 - 0
extra/mac-installer/scripts/install.sh

@@ -0,0 +1,18 @@
+#!/bin/sh
+cd $(dirname $0)
+# haxe
+chmod +x *
+./haxe-preinstall.sh
+rm -f /usr/lib/haxe
+rm -f /usr/local/lib/haxe
+mkdir -p /usr/local/lib/haxe
+mkdir -p /usr/local/bin
+cp -Rf ../haxe/* /usr/local/lib/haxe
+./haxe-postinstall.sh
+./neko-preinstall.sh
+rm -f /usr/local/lib/neko
+mkdir -p /usr/local/lib/neko
+cp -Rf ../neko/* /usr/local/lib/neko
+./neko-postinstall.sh
+cd ../
+rm -Rf /tmp/haxe

+ 19 - 0
extra/mac-installer/scripts/neko-postinstall.sh

@@ -0,0 +1,19 @@
+#!/bin/sh
+rm -f /usr/bin/neko
+rm -f /usr/bin/nekoc
+rm -f /usr/bin/nekoml
+rm -f /usr/bin/nekotools
+rm -f /usr/lib/libneko.dylib
+rm -f /usr/local/bin/neko
+rm -f /usr/local/bin/nekoc
+rm -f /usr/local/bin/nekoml
+rm -f /usr/local/bin/nekotools
+rm -f /usr/local/lib/libneko*.dylib
+
+ln -s /usr/local/lib/neko/neko /usr/local/bin/neko
+ln -s /usr/local/lib/neko/nekoc /usr/local/bin/nekoc
+ln -s /usr/local/lib/neko/nekoml /usr/local/bin/nekoml
+ln -s /usr/local/lib/neko/nekotools /usr/local/bin/nekotools
+ln -s /usr/local/lib/neko/libneko.dylib /usr/local/lib/libneko.dylib
+ln -s /usr/local/lib/neko/libneko.2.dylib /usr/local/lib/libneko.2.dylib
+ln -s /usr/local/lib/neko/libneko.2.1.0.dylib /usr/local/lib/libneko.2.1.0.dylib

+ 3 - 0
extra/mac-installer/scripts/neko-preinstall.sh

@@ -0,0 +1,3 @@
+#!/bin/sh
+rm -rf /usr/lib/neko
+rm -rf /usr/local/lib/neko

+ 1 - 1
libs

@@ -1 +1 @@
-Subproject commit 5f7956d8a2f0a0d9b99339b793fb9a0a07288a20
+Subproject commit ba1679cb1ed1e74ccd5068755642dc1353780ded

+ 10 - 5
src/globals.ml → src/compiler/globals.ml

@@ -4,7 +4,7 @@ type pos = {
 	pmax : int;
 }
 
-module IntMap = Map.Make(struct type t = int let compare a b = a - b end)
+module IntMap = Ptmap
 module StringMap = Map.Make(struct type t = string let compare = String.compare end)
 
 type platform =
@@ -19,8 +19,9 @@ type platform =
 	| Java
 	| Python
 	| Hl
+	| Eval
 
-let version = 3402
+let version = 4000
 let version_major = version / 1000
 let version_minor = (version mod 1000) / 100
 let version_revision = (version mod 100)
@@ -29,9 +30,6 @@ let macro_platform = ref Neko
 
 let is_windows = Sys.os_type = "Win32" || Sys.os_type = "Cygwin"
 
-let s_version =
-	Printf.sprintf "%d.%d.%d%s" version_major version_minor version_revision (match Version.version_extra with None -> "" | Some v -> " " ^ v)
-
 let platforms = [
 	Js;
 	Lua;
@@ -43,6 +41,7 @@ let platforms = [
 	Java;
 	Python;
 	Hl;
+	Eval;
 ]
 
 let platform_name = function
@@ -57,6 +56,12 @@ let platform_name = function
 	| Java -> "java"
 	| Python -> "python"
 	| Hl -> "hl"
+	| Eval -> "eval"
+
+let platform_list_help = function
+	| [] -> ""
+	| [p] -> " (" ^ platform_name p ^ " only)"
+	| pl -> " (for " ^ String.concat "," (List.map platform_name pl) ^ ")"
 
 let null_pos = { pfile = "?"; pmin = -1; pmax = -1 }
 

+ 396 - 0
src/compiler/json.ml

@@ -0,0 +1,396 @@
+(*
+	The Haxe Compiler
+	Copyright (C) 2005-2017  Haxe Foundation
+
+	This program is free software; you can redistribute it and/or
+	modify it under the terms of the GNU General Public License
+	as published by the Free Software Foundation; either version 2
+	of the License, or (at your option) any later version.
+
+	This program is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+
+	You should have received a copy of the GNU General Public License
+	along with this program; if not, write to the Free Software
+	Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ *)
+
+type t =
+	| JString of string
+	| JFloat of float
+	| JInt of int
+	| JObject of (string * t) list
+	| JArray of t list
+	| JBool of bool
+	| JNull
+
+let write_iter f_el f_sep l =
+	let rec rest = function
+		| [] -> ()
+		| v :: l ->
+			f_sep();
+			f_el v;
+			rest l
+	in
+	match l with
+	| [] -> ()
+	| v :: l ->
+		f_el v;
+		rest l
+
+let write_sep w =
+	w ","
+
+let rec write_json w v =
+	match v with
+	| JNull -> write_null w
+	| JBool b -> write_bool w b
+	| JString s -> write_string w s
+	| JFloat f -> write_float w f
+	| JInt i -> write_int w i
+	| JObject o -> write_object w o
+	| JArray a -> write_array w a
+
+and write_null w =
+	w "null"
+
+and write_bool w b =
+	w (if b then "true" else "false")
+
+and write_string w s =
+	w "\"";
+	let b = Buffer.create (String.length s) in
+	for i = 0 to String.length s - 1 do
+		match String.unsafe_get s i with
+		| '"' -> Buffer.add_string b "\\\""
+		| '\t' -> Buffer.add_string b "\\t"
+		| '\r' -> Buffer.add_string b "\\r"
+		| '\b' -> Buffer.add_string b "\\b"
+		| '\n' -> Buffer.add_string b "\\n"
+		| '\012' -> Buffer.add_string b "\\f"
+		| '\\' -> Buffer.add_string b "\\\\"
+		| '\x00'..'\x1F' | '\x7F' as c -> Buffer.add_string b (Printf.sprintf "\\u%04X" (int_of_char c))
+		| c -> Buffer.add_char b c
+	done;
+	w (Buffer.contents b);
+	w "\""
+
+and write_int w i =
+	w (string_of_int i)
+
+and write_float w f =
+	match classify_float f with
+	| FP_nan | FP_infinite -> failwith "NaN and infinity floats are unsupported in JSON"
+	| _ ->
+		let s = Printf.sprintf "%.16g" f in
+		let s = if float_of_string s = f then s else Printf.sprintf "%.17g" f in
+		w s
+
+and write_array w a =
+	w "[";
+	write_iter (write_json w) (fun() -> write_sep w) a;
+	w "]"
+
+and write_object w o =
+	let write_el (k, v) =
+		write_string w k;
+		w ":";
+		write_json w v
+	in
+	w "{";
+	write_iter write_el (fun() -> write_sep w) o;
+	w "}"
+
+module Reader = struct
+	(*
+		The following code is basically stripped down yojson (https://github.com/mjambon/yojson),
+		adapted to our data structures and using sedlex instad of ocamllex.
+
+		TODO: we could probably re-use utf-8 stuff from our extlib, but I don't know enough about it.
+	*)
+	open Sedlexing
+	open Sedlexing.Utf8
+
+	exception Json_error of string
+	exception Int_overflow
+
+	let dec c =
+		Char.code c - 48
+
+	let hex c =
+		match (char_of_int c) with
+		| '0'..'9' -> c - int_of_char '0'
+		| 'a'..'f' -> c - int_of_char 'a' + 10
+		| 'A'..'F' -> c - int_of_char 'A' + 10
+		| _ -> assert false
+
+	let min10 = min_int / 10 - (if min_int mod 10 = 0 then 0 else 1)
+	let max10 = max_int / 10 + (if max_int mod 10 = 0 then 0 else 1)
+
+	let json_error s = raise (Json_error s)
+
+	let extract_positive_int lexbuf =
+		let s = Sedlexing.Utf8.lexeme lexbuf in
+		let n = ref 0 in
+		for i = 0 to (lexeme_length lexbuf) - 1 do
+			if !n >= max10 then
+				raise Int_overflow
+			else
+				n := 10 * !n + dec s.[i]
+		done;
+		if !n < 0 then
+			raise Int_overflow
+		else
+			!n
+
+	let make_positive_int lexbuf =
+		try JInt (extract_positive_int lexbuf)
+		with Int_overflow -> JFloat (float_of_string (lexeme lexbuf))
+
+	let extract_negative_int lexbuf =
+		let s = Sedlexing.Utf8.lexeme lexbuf in
+		let n = ref 0 in
+		for i = 1 to (lexeme_length lexbuf) - 1 do
+			if !n <= min10 then
+				raise Int_overflow
+			else
+				n := 10 * !n - dec s.[i]
+		done;
+		if !n > 0 then
+			raise Int_overflow
+		else
+			!n
+
+	let make_negative_int lexbuf =
+		try JInt (extract_negative_int lexbuf)
+		with Int_overflow -> JFloat (float_of_string (lexeme lexbuf))
+
+	let utf8_of_code buf x =
+		let add = Buffer.add_char in
+
+		(* Straight <= doesn't work with signed 31-bit ints *)
+		let maxbits n x = x lsr n = 0 in
+
+		if maxbits 7 x then
+			(* 7 *)
+			add buf (Char.chr x)
+		else if maxbits 11 x then (
+			(* 5 + 6 *)
+			add buf (Char.chr (0b11000000 lor ((x lsr 6) land 0b00011111)));
+			add buf (Char.chr (0b10000000 lor (x         land 0b00111111)))
+		)
+		else if maxbits 16 x then (
+			(* 4 + 6 + 6 *)
+			add buf (Char.chr (0b11100000 lor ((x lsr 12) land 0b00001111)));
+			add buf (Char.chr (0b10000000 lor ((x lsr  6) land 0b00111111)));
+			add buf (Char.chr (0b10000000 lor (x          land 0b00111111)))
+		)
+		else if maxbits 21 x then (
+			(* 3 + 6 + 6 + 6 *)
+			add buf (Char.chr (0b11110000 lor ((x lsr 18) land 0b00000111)));
+			add buf (Char.chr (0b10000000 lor ((x lsr 12) land 0b00111111)));
+			add buf (Char.chr (0b10000000 lor ((x lsr  6) land 0b00111111)));
+			add buf (Char.chr (0b10000000 lor (x          land 0b00111111)));
+		)
+		else if maxbits 26 x then (
+			(* 2 + 6 + 6 + 6 + 6 *)
+			add buf (Char.chr (0b11111000 lor ((x lsr 24) land 0b00000011)));
+			add buf (Char.chr (0b10000000 lor ((x lsr 18) land 0b00111111)));
+			add buf (Char.chr (0b10000000 lor ((x lsr 12) land 0b00111111)));
+			add buf (Char.chr (0b10000000 lor ((x lsr  6) land 0b00111111)));
+			add buf (Char.chr (0b10000000 lor (x          land 0b00111111)));
+		)
+		else (
+			assert (maxbits 31 x);
+			(* 1 + 6 + 6 + 6 + 6 + 6 *)
+			add buf (Char.chr (0b11111100 lor ((x lsr 30) land 0b00000001)));
+			add buf (Char.chr (0b10000000 lor ((x lsr 24) land 0b00111111)));
+			add buf (Char.chr (0b10000000 lor ((x lsr 18) land 0b00111111)));
+			add buf (Char.chr (0b10000000 lor ((x lsr 12) land 0b00111111)));
+			add buf (Char.chr (0b10000000 lor ((x lsr  6) land 0b00111111)));
+			add buf (Char.chr (0b10000000 lor (x          land 0b00111111)));
+		)
+
+	let code_of_surrogate_pair i j =
+		let high10 = i - 0xD800 in
+		let low10 = j - 0xDC00 in
+		0x10000 + ((high10 lsl 10) lor low10)
+
+	let utf8_of_surrogate_pair buf i j =
+		utf8_of_code buf (code_of_surrogate_pair i j)
+
+	let space = [%sedlex.regexp? Plus (Chars " \t\r\n")]
+	let digit = [%sedlex.regexp? '0' .. '9']
+	let nonzero = [%sedlex.regexp? '1' .. '9']
+	let digits = [%sedlex.regexp? Plus digit]
+	let frac = [%sedlex.regexp? '.', digits]
+	let e = [%sedlex.regexp? (Chars "eE"),(Opt (Chars "+-"))]
+	let exp = [%sedlex.regexp? e, digits]
+	let positive_int = [%sedlex.regexp? digit | (nonzero, digits)]
+	let float = [%sedlex.regexp? (Opt '-'), positive_int, (frac | exp | (frac, exp))]
+	let hex = [%sedlex.regexp? '0'..'9' | 'a'..'f' | 'A'..'F' ]
+
+	let rec read_json lexbuf =
+		match%sedlex lexbuf with
+		| "true" ->
+			JBool true
+		| "false" ->
+			JBool false
+		| "null" ->
+			JNull
+		| '"' ->
+			JString (finish_string (Buffer.create 0) lexbuf)
+		| positive_int ->
+			make_positive_int lexbuf
+		| '-', positive_int ->
+			make_negative_int lexbuf
+		| float ->
+			JFloat (float_of_string (lexeme lexbuf))
+		| '{' ->
+			let acc = ref [] in
+			begin try
+				skip_space lexbuf;
+				read_object_end lexbuf;
+				let field_name = read_string lexbuf in
+				skip_space lexbuf;
+				read_colon lexbuf;
+				skip_space lexbuf;
+				acc := (field_name, read_json lexbuf) :: !acc;
+				while true do
+					skip_space lexbuf;
+					read_object_sep lexbuf;
+					skip_space lexbuf;
+					let field_name = read_string lexbuf in
+					skip_space lexbuf;
+					read_colon lexbuf;
+					skip_space lexbuf;
+					acc := (field_name, read_json lexbuf) :: !acc;
+				done;
+				assert false
+			with Exit ->
+				JObject (List.rev !acc)
+			end
+		| '[' ->
+			let acc = ref [] in
+			begin try
+				skip_space lexbuf;
+				read_array_end lexbuf;
+				acc := read_json lexbuf :: !acc;
+				while true do
+					skip_space lexbuf;
+					read_array_sep lexbuf;
+					skip_space lexbuf;
+					acc := read_json lexbuf :: !acc;
+				done;
+				assert false
+			with Exit ->
+				JArray (List.rev !acc)
+			end
+		| space ->
+			read_json lexbuf
+		| eof ->
+			json_error "Unexpected end of input"
+		| _ ->
+			json_error "Invalid token"
+
+	and finish_string buf lexbuf =
+		match%sedlex lexbuf with
+		| '"' -> Buffer.contents buf
+		| '\\' ->
+			finish_escaped_char buf lexbuf;
+			finish_string buf lexbuf
+		| Plus (Compl ('"' | '\\')) ->
+			Buffer.add_string buf (Sedlexing.Utf8.lexeme lexbuf);
+			finish_string buf lexbuf
+		| eof -> json_error "Unexpected end of input"
+		| _ -> assert false
+
+	and finish_escaped_char buf lexbuf =
+		match%sedlex lexbuf with
+		| '"' | '\\' | '/' ->
+			Buffer.add_char buf (char_of_int (Sedlexing.lexeme_char lexbuf 0))
+		| 'b' ->
+			Buffer.add_char buf '\b'
+		| 'f' ->
+			Buffer.add_char buf '\012'
+		| 'n' ->
+			Buffer.add_char buf '\n'
+		| 'r' ->
+			Buffer.add_char buf '\r'
+		| 't' ->
+			Buffer.add_char buf '\t'
+		| 'u', hex, hex, hex, hex ->
+			let a,b,c,d =
+				match Sedlexing.lexeme lexbuf with
+				| [|_; a; b; c; d|] -> a, b, c, d
+				| _ -> assert false
+			in
+			let x =
+				(hex a lsl 12) lor (hex b lsl 8) lor (hex c lsl 4) lor hex d
+			in
+			if x >= 0xD800 && x <= 0xDBFF then
+				finish_surrogate_pair buf x lexbuf
+			else
+				utf8_of_code buf x
+		| _ ->
+			json_error "Invalid escape sequence"
+
+	and finish_surrogate_pair buf x lexbuf =
+		match%sedlex lexbuf with
+		| "\\u", hex, hex, hex, hex ->
+			let a,b,c,d =
+				match Sedlexing.lexeme lexbuf with
+				| [|_;_ ; a; b; c; d|] -> a, b, c, d
+				| _ -> assert false
+			in
+			let y =
+				(hex a lsl 12) lor (hex b lsl 8) lor (hex c lsl 4) lor hex d
+			in
+			if y >= 0xDC00 && y <= 0xDFFF then
+				utf8_of_surrogate_pair buf x y
+			else
+				json_error "Invalid low surrogate for code point beyond U+FFFF"
+		| _ ->
+			json_error "Missing escape sequence representing low surrogate for code point beyond U+FFFF"
+
+	and skip_space lexbuf =
+		match%sedlex lexbuf with
+		| space | "" -> ()
+		| _ -> assert false
+
+	and read_string lexbuf =
+		match%sedlex lexbuf with
+		| '"' -> finish_string (Buffer.create 0) lexbuf
+		| _ -> json_error "Expected string"
+
+	and read_array_end lexbuf =
+		match%sedlex lexbuf with
+		| ']' -> raise Exit
+		| "" -> ()
+		| _ -> assert false
+
+	and read_array_sep lexbuf =
+		match%sedlex lexbuf with
+		| ',' -> ()
+		| ']' -> raise Exit
+		| _ -> json_error "Expected ',' or ']'"
+
+	and read_object_end lexbuf =
+		match%sedlex lexbuf with
+		| '}' -> raise Exit
+		| "" -> ()
+		| _ -> assert false
+
+	and read_object_sep lexbuf =
+		match%sedlex lexbuf with
+		| ',' -> ()
+		| '}' -> raise Exit
+		| _ -> json_error "Expected ',' or '}'"
+
+	and read_colon lexbuf =
+		match%sedlex lexbuf with
+		| ':' -> ()
+		| _ -> json_error "Expected ':'"
+end

+ 46 - 79
src/main.ml → src/compiler/main.ml

@@ -43,7 +43,6 @@
 *)
 
 open Printf
-open Genswf
 open Common
 open Common.DisplayMode
 open Type
@@ -170,55 +169,21 @@ let run_command ctx cmd =
 		if not Globals.is_windows then s else String.concat "\n" (Str.split (Str.regexp "\r\n") s)
 	in
 	let pout, pin, perr = Unix.open_process_full cmd (Unix.environment()) in
-	let iout = Unix.descr_of_in_channel pout in
-	let ierr = Unix.descr_of_in_channel perr in
-	let berr = Buffer.create 0 in
 	let bout = Buffer.create 0 in
-	let tmp = String.create 1024 in
-	let result = ref None in
-	(*
-		we need to read available content on process out/err if we want to prevent
-		the process from blocking when the pipe is full
-	*)
-	let is_process_running() =
-		let pid, r = Unix.waitpid [Unix.WNOHANG] (-1) in
-		if pid = 0 then
-			true
-		else begin
-			result := Some r;
-			false;
-		end
-	in
-	let rec loop ins =
-		let (ch,_,_), timeout = (try Unix.select ins [] [] 0.02, true with _ -> ([],[],[]),false) in
-		match ch with
-		| [] ->
-			(* make sure we read all *)
-			if timeout && is_process_running() then
-				loop ins
-			else begin
-				Buffer.add_string berr (IO.read_all (IO.input_channel perr));
-				Buffer.add_string bout (IO.read_all (IO.input_channel pout));
-			end
-		| s :: _ ->
-			let n = Unix.read s tmp 0 (String.length tmp) in
-			if s == iout && n > 0 then
-				ctx.com.print (String.sub tmp 0 n)
-			else
-				Buffer.add_substring (if s == iout then bout else berr) tmp 0 n;
-			loop (if n = 0 then List.filter ((!=) s) ins else ins)
+	let berr = Buffer.create 0 in
+	let read_content channel buf =
+		Buffer.add_string buf (IO.read_all (IO.input_channel channel));
 	in
-	(try loop [iout;ierr] with Unix.Unix_error _ -> ());
+	let tout = Thread.create (fun() -> read_content pout bout) () in
+	read_content perr berr;
+	Thread.join tout;
+	let result = (match Unix.close_process_full (pout,pin,perr) with Unix.WEXITED c | Unix.WSIGNALED c | Unix.WSTOPPED c -> c) in
 	let serr = binary_string (Buffer.contents berr) in
 	let sout = binary_string (Buffer.contents bout) in
 	if serr <> "" then ctx.messages <- (if serr.[String.length serr - 1] = '\n' then String.sub serr 0 (String.length serr - 1) else serr) :: ctx.messages;
-	if sout <> "" then ctx.com.print sout;
-	let r = (match (try Unix.close_process_full (pout,pin,perr) with Unix.Unix_error (Unix.ECHILD,_,_) -> (match !result with None -> assert false | Some r -> r)) with
-		| Unix.WEXITED e -> e
-		| Unix.WSIGNALED s | Unix.WSTOPPED s -> if s = 0 then -1 else s
-	) in
+	if sout <> "" then ctx.com.print (sout ^ "\n");
 	t();
-	r
+	result
 
 module Initialize = struct
 	let set_platform com pf file =
@@ -287,7 +252,7 @@ module Initialize = struct
 					add_std "php";
 				"php"
 			| Cpp ->
-				Common.define_value com Define.HxcppApiLevel "331";
+				Common.define_value com Define.HxcppApiLevel "332";
 				add_std "cpp";
 				if Common.defined com Define.Cppia then
 					classes := (Path.parse_path "cpp.cppia.HostClasses" ) :: !classes;
@@ -298,7 +263,7 @@ module Initialize = struct
 					com.net_libs <- [];
 					old_flush()
 				);
-				Gencs.before_generate com;
+				Dotnet.before_generate com;
 				add_std "cs"; "cs"
 			| Java ->
 				let old_flush = ctx.flush in
@@ -307,7 +272,7 @@ module Initialize = struct
 					com.java_libs <- [];
 					old_flush()
 				);
-				Genjava.before_generate com;
+				Java.before_generate com;
 				add_std "java"; "java"
 			| Python ->
 				add_std "python";
@@ -315,6 +280,9 @@ module Initialize = struct
 			| Hl ->
 				add_std "hl";
 				"hl"
+			| Eval ->
+				add_std "eval";
+				"eval"
 end
 
 let generate tctx ext xml_out interp swf_header =
@@ -331,13 +299,13 @@ let generate tctx ext xml_out interp swf_header =
 			| Some(_,ctx) -> print_endline "generate"; Codegen.Dump.dump_dependencies ~target_override:(Some "macro") ctx.Typecore.com
 	end;
 	begin match com.platform with
-		| Neko | Hl when interp -> ()
+		| Neko | Hl | Eval when interp -> ()
 		| Cpp when Common.defined com Define.Cppia -> ()
 		| Cpp | Cs | Java | Php -> Common.mkdir_from_path (com.file ^ "/.")
 		| _ -> Common.mkdir_from_path com.file
 	end;
 	if interp then
-		MacroContext.interpret tctx
+		Std.finally (Common.timer ["interp"]) MacroContext.interpret tctx
 	else if com.platform = Cross then
 		()
 	else begin
@@ -367,6 +335,8 @@ let generate tctx ext xml_out interp swf_header =
 			Genpy.generate,"python"
 		| Hl ->
 			Genhl.generate,"hl"
+		| Eval ->
+			(fun _ -> MacroContext.interpret tctx),"eval"
 		| Cross ->
 			assert false
 		in
@@ -393,11 +363,12 @@ let get_std_class_paths () =
 	with Not_found ->
 		if Sys.os_type = "Unix" then
 			[
-				"/usr/lib/haxe/std/";
-				"/usr/share/haxe/std/";
+				"/usr/local/share/haxe/std/";
 				"/usr/local/lib/haxe/std/";
-				"/usr/lib/haxe/extraLibs/";
 				"/usr/local/lib/haxe/extraLibs/";
+				"/usr/share/haxe/std/";
+				"/usr/lib/haxe/std/";
+				"/usr/lib/haxe/extraLibs/";
 				""
 			]
 		else
@@ -462,7 +433,7 @@ let rec process_params create pl =
 and init ctx =
 	let usage = Printf.sprintf
 		"Haxe Compiler %s - (C)2005-2017 Haxe Foundation\n Usage : haxe%s -main <class> [-swf|-js|-neko|-php|-cpp|-cppia|-as3|-cs|-java|-python|-hl|-lua] <output> [options]\n Options :"
-		Globals.s_version (if Sys.os_type = "Win32" then ".exe" else "")
+		s_version (if Sys.os_type = "Win32" then ".exe" else "")
 	in
 	let com = ctx.com in
 	let classes = ref [([],"Std")] in
@@ -479,8 +450,7 @@ try
 	let pre_compilation = ref [] in
 	let interp = ref false in
 	let swf_version = ref false in
-	let evals = ref [] in
-	Common.define_value com Define.HaxeVer (float_repres (float_of_int Globals.version /. 1000.));
+	Common.define_value com Define.HaxeVer (Printf.sprintf "%.3f" (float_of_int Globals.version /. 1000.));
 	Common.raw_define com "haxe3";
 	Common.define_value com Define.Dce "std";
 	com.warning <- (fun msg p -> message ctx ("Warning : " ^ msg) p);
@@ -596,14 +566,14 @@ try
 		),"<header> : define SWF header (width:height:fps:color)");
 		("-swf-lib",Arg.String (fun file ->
 			process_libs(); (* linked swf order matters, and lib might reference swf as well *)
-			Genswf.add_swf_lib com file false
+			SwfLoader.add_swf_lib com file false
 		),"<file> : add the SWF library to the compiled SWF");
 		("-swf-lib-extern",Arg.String (fun file ->
-			Genswf.add_swf_lib com file true
+			SwfLoader.add_swf_lib com file true
 		),"<file> : use the SWF library for type checking");
 		("-java-lib",Arg.String (fun file ->
 			let std = file = "lib/hxjava-std.jar" in
-			arg_delays := (fun () -> Genjava.add_java_lib com file std) :: !arg_delays;
+			arg_delays := (fun () -> Java.add_java_lib com file std) :: !arg_delays;
 		),"<file> : add an external JAR or class directory library");
 		("-net-lib",Arg.String (fun file ->
 			let file, is_std = match ExtString.String.nsplit file "@" with
@@ -613,10 +583,10 @@ try
 					file,true
 				| _ -> raise Exit
 			in
-			arg_delays := (fun () -> Gencs.add_net_lib com file is_std) :: !arg_delays;
+			arg_delays := (fun () -> Dotnet.add_net_lib com file is_std) :: !arg_delays;
 		),"<file>[@std] : add an external .NET DLL file");
 		("-net-std",Arg.String (fun file ->
-			Gencs.add_net_std com file
+			Dotnet.add_net_std com file
 		),"<file> : add a root std .NET DLL search path");
 		("-c-arg",Arg.String (fun arg ->
 			com.c_args <- arg :: com.c_args
@@ -710,10 +680,6 @@ try
 			force_typing := true;
 			config_macros := e :: !config_macros
 		)," : call the given macro before typing anything else");
-		("--eval", Arg.String (fun s ->
-			force_typing := true;
-			evals := s :: !evals;
-		), " : evaluates argument as Haxe module code");
 		("--wait", Arg.String (fun hp ->
 			let accept = match hp with
 				| "stdio" ->
@@ -732,22 +698,12 @@ try
 			assert false
 		),"<dir> : set current working directory");
 		("-version",Arg.Unit (fun() ->
-			message ctx Globals.s_version null_pos;
+			message ctx s_version null_pos;
 			did_something := true;
 		),": print version and exit");
 		("--help-defines", Arg.Unit (fun() ->
-			let m = ref 0 in
-			let rec loop i =
-				let d = Obj.magic i in
-				if d <> Define.Last then begin
-					let t, doc = Define.infos d in
-					if String.length t > !m then m := String.length t;
-					((String.concat "-" (ExtString.String.nsplit t "_")),doc) :: (loop (i + 1))
-				end else
-					[]
-			in
-			let all = List.sort (fun (s1,_) (s2,_) -> String.compare s1 s2) (loop 0) in
-			let all = List.map (fun (n,doc) -> Printf.sprintf " %-*s: %s" !m n (limit_string doc (!m + 3))) all in
+			let all,max_length = Define.get_documentation_list() in
+			let all = List.map (fun (n,doc) -> Printf.sprintf " %-*s: %s" max_length n (limit_string doc (max_length + 3))) all in
 			List.iter (fun msg -> ctx.com.print (msg ^ "\n")) all;
 			did_something := true
 		),": print help for all compiler specific defines");
@@ -792,6 +748,17 @@ try
 		com.warning <- if com.display.dms_error_policy = EPCollect then (fun s p -> add_diagnostics_message com s p DisplayTypes.DiagnosticsSeverity.Warning) else message ctx;
 		com.error <- error ctx;
 	end;
+	Lexer.old_format := Common.defined com Define.OldErrorFormat;
+	if !Lexer.old_format && !Parser.resume_display <> null_pos then begin
+		let p = !Parser.resume_display in
+		(* convert byte position to utf8 position *)
+		try
+			let content = Std.input_file ~bin:true (Path.get_real_path p.pfile) in
+			let pos = UTF8.length (String.sub content 0 p.pmin) in
+			Parser.resume_display := { p with pmin = pos; pmax = pos }
+		with _ ->
+			() (* ignore *)
+	end;
 	DisplayOutput.process_display_file com classes;
 	let ext = Initialize.initialize_target ctx com classes in
 	(* if we are at the last compilation step, allow all packages accesses - in case of macros or opening another project file *)
@@ -808,7 +775,7 @@ try
 			("--help-metas", Arg.Unit (fun () -> ()),": print help for all compiler metadatas");
 			("<dot-path>", Arg.Unit (fun () -> ()),": compile the module specified by dot-path");
 		] in
-		if !cmds = [] && not !did_something then Arg.usage help_spec usage;
+		if !cmds = [] && not !did_something then print_endline (Arg.usage_string help_spec usage);
 	end else begin
 		ctx.setup();
 		Common.log com ("Classpath : " ^ (String.concat ";" com.class_path));
@@ -817,7 +784,6 @@ try
 		Typecore.type_expr_ref := (fun ctx e with_type -> Typer.type_expr ctx e with_type);
 		let tctx = Typer.create com in
 		List.iter (MacroContext.call_init_macro tctx) (List.rev !config_macros);
-		List.iter (Typer.eval tctx) !evals;
 		List.iter (fun cpath -> ignore(tctx.Typecore.g.Typecore.do_load_module tctx cpath null_pos)) (List.rev !classes);
 		Typer.finalize tctx;
 		t();
@@ -937,6 +903,7 @@ with
 		raise (DisplayOutput.Completion s)
 	| Interp.Sys_exit i | Hlinterp.Sys_exit i ->
 		ctx.flush();
+		if !measure_times then report_times prerr_endline;
 		exit i
 	| e when (try Sys.getenv "OCAMLRUNPARAM" <> "b" || CompilationServer.runs() with _ -> true) && not (is_debug_run()) ->
 		error ctx (Printexc.to_string e) null_pos

+ 9 - 1
src/path.ml → src/compiler/path.ml

@@ -25,7 +25,7 @@ let parse_path f =
 	let invalid_char x =
 		for i = 1 to String.length x - 1 do
 			match x.[i] with
-			| 'A'..'Z' | 'a'..'z' | '0'..'9' | '_' -> ()
+			| 'A'..'Z' | 'a'..'z' | '0'..'9' | '_' | '.' -> ()
 			| c -> error ("invalid character: " ^ (String.make 1 c))
 		done
 	in
@@ -120,6 +120,14 @@ let rec remove_trailing_slash p =
 		| '\\' | '/' -> remove_trailing_slash (String.sub p 0 (l - 1))
 		| _ -> p
 
+let flat_path (p,s) =
+	(* Replace _ with _$ in paths to prevent name collisions. *)
+	let escape str = String.concat "_$" (ExtString.String.nsplit str "_") in
+
+	match p with
+	| [] -> escape s
+	| _ -> String.concat "_" (List.map escape p) ^ "_" ^ (escape s)
+
 open Globals
 
 let find_directories target recursive paths =

+ 173 - 76
src/server.ml → src/compiler/server.ml

@@ -5,6 +5,7 @@ open Common
 open Common.DisplayMode
 open Type
 open DisplayOutput
+open Json
 
 exception Dirty of module_def
 
@@ -24,58 +25,115 @@ type context = {
 	mutable has_error : bool;
 }
 
+type server_message =
+	| AddedDirectory of string
+	| FoundDirectories of (string * float ref) list
+	| ChangedDirectories of (string * float) list
+	| ModulePathChanged of (module_def * float * string)
+	| NotCached of module_def
+	| Parsed of (string * string)
+	| RemovedDirectory of string
+	| Reusing of module_def
+	| SkippingDep of (module_def * module_def)
+
+let s_version =
+	Printf.sprintf "%d.%d.%d%s" version_major version_minor version_revision (match Version.version_extra with None -> "" | Some v -> " " ^ v)
+
+type timer_node = {
+	name : string;
+	path : string;
+	parent : timer_node;
+	info : string;
+	mutable time : float;
+	mutable num_calls : int;
+	mutable children : timer_node list;
+}
+
 let report_times print =
-	let tot = ref 0. in
-	Hashtbl.iter (fun _ t -> tot := !tot +. t.total) Common.htimers;
-	if !tot > 0. then begin
-		let buckets = Hashtbl.create 0 in
-		let add id time calls =
-			try
-				let time',calls' = Hashtbl.find buckets id in
-				Hashtbl.replace buckets id (time' +. time,calls' + calls)
-			with Not_found ->
-				Hashtbl.add buckets id (time,calls)
-		in
-		Hashtbl.iter (fun _ t ->
-			let rec loop acc ids = match ids with
-				| id :: ids ->
-					add (List.rev (id :: acc)) t.total t.calls;
-					loop (id :: acc) ids
-				| [] ->
-					()
-			in
-			loop [] t.id
-		) Common.htimers;
-		let max_name = ref 0 in
-		let max_calls = ref 0 in
-		let timers = Hashtbl.fold (fun id t acc ->
-			let name,indent = match List.rev id with
-				| [] -> assert false
-				| name :: l -> name,(String.make (List.length l * 2) ' ')
-			in
-			let name,info = try
-				let i = String.rindex name '.' in
-				String.sub name (i + 1) (String.length name - i - 1),String.sub name 0 i
-			with Not_found ->
-				name,""
-			in
-			let name = indent ^ name in
-			if String.length name > !max_name then max_name := String.length name;
-			if snd t > !max_calls then max_calls := snd t;
-			(id,name,info,t) :: acc
-		) buckets [] in
-		let max_calls = String.length (string_of_int !max_calls) in
-		print (Printf.sprintf "%-*s | %7s |   %% | %*s | info" !max_name "name" "time(s)" max_calls "#");
-		let sep = String.make (!max_name + max_calls + 21) '-' in
-		print sep;
-		let timers = List.sort (fun (id1,_,_,_) (id2,_,_,_) -> compare id1 id2) timers in
-		let print_timer id name info (time,calls) =
-			print (Printf.sprintf "%-*s | %7.3f | %3.0f | %*i | %s" !max_name name time (time *. 100. /. !tot) max_calls calls info)
+	let nodes = Hashtbl.create 0 in
+	let rec root = {
+		name = "";
+		path = "";
+		parent = root;
+		info = "";
+		time = 0.;
+		num_calls = 0;
+		children = [];
+	} in
+	Hashtbl.iter (fun _ timer ->
+		let rec loop parent sl = match sl with
+			| [] -> assert false
+			| s :: sl ->
+				let path = (match parent.path with "" -> "" | _ -> parent.path ^ ".") ^ s in
+				let node = try
+					let node = Hashtbl.find nodes path in
+					node.num_calls <- node.num_calls + timer.calls;
+					node.time <- node.time +. timer.total;
+					node
+				with Not_found ->
+					let name,info = try
+						let i = String.rindex s '.' in
+						String.sub s (i + 1) (String.length s - i - 1),String.sub s 0 i
+					with Not_found ->
+						s,""
+					in
+					let node = {
+						name = name;
+						path = path;
+						parent = parent;
+						info = info;
+						time = timer.total;
+						num_calls = timer.calls;
+						children = [];
+					} in
+					Hashtbl.add nodes path node;
+					node
+				in
+				begin match sl with
+					| [] -> ()
+					| _ ->
+						let child = loop node sl in
+						if not (List.memq child node.children) then
+							node.children <- child :: node.children;
+				end;
+				node
 		in
-		List.iter (fun (id,name,info,t) -> print_timer id name info t) timers;
-		print sep;
-		print_timer ["total"] "total" "" (!tot,0)
-	end
+		let node = loop root timer.id in
+		if not (List.memq node root.children) then
+			root.children <- node :: root.children
+	) Common.htimers;
+	let max_name = ref 0 in
+	let max_calls = ref 0 in
+	let rec loop depth node =
+		let l = (String.length node.name) + 2 * depth in
+		List.iter (fun child ->
+			if depth = 0 then begin
+				node.num_calls <- node.num_calls + child.num_calls;
+				node.time <- node.time +. child.time;
+			end;
+			loop (depth + 1) child;
+		) node.children;
+		node.children <- List.sort (fun node1 node2 -> compare node2.time node1.time) node.children;
+		if node.num_calls > !max_calls then max_calls := node.num_calls;
+		if node.time > 0.0009 && l > !max_name then max_name := l;
+	in
+	loop 0 root;
+	let max_calls = String.length (string_of_int !max_calls) in
+	print (Printf.sprintf "%-*s | %7s |   %% |  p%% | %*s | info" !max_name "name" "time(s)" max_calls "#");
+	let sep = String.make (!max_name + max_calls + 27) '-' in
+	print sep;
+	let print_time name node =
+		if node.time > 0.0009 then
+			print (Printf.sprintf "%-*s | %7.3f | %3.0f | %3.0f | %*i | %s" !max_name name node.time (node.time *. 100. /. root.time) (node.time *. 100. /. node.parent.time) max_calls node.num_calls node.info)
+	in
+	let rec loop depth node =
+		let name = (String.make (depth * 2) ' ') ^ node.name in
+		print_time name node;
+		List.iter (loop (depth + 1)) node.children
+	in
+	List.iter (loop 0) root.children;
+	print sep;
+	print_time "total" root
 
 let default_flush ctx =
 	List.iter prerr_endline (List.rev ctx.messages);
@@ -127,11 +185,12 @@ let ssend sock str =
 			let s = Unix.send sock str pos len [] in
 			loop (pos + s) (len - s)
 	in
-	loop 0 (String.length str)
+	loop 0 (Bytes.length str)
 
 let rec wait_loop process_params verbose accept =
 	Sys.catch_break false;
 	let has_parse_error = ref false in
+	let test_server_messages = DynArray.create () in
 	let cs = CompilationServer.create () in
 	let sign_string com =
 		let sign = get_signature com in
@@ -145,6 +204,37 @@ let rec wait_loop process_params verbose accept =
 		in
 		Printf.sprintf "%2s,%3s: " sign_id (short_platform_name com.platform)
 	in
+	let process_server_message com tabs =
+		if Common.raw_defined com "compilation-server-test" then (fun message ->
+			let module_path m = JString (s_type_path m.m_path) in
+			let kind,data = match message with
+				| AddedDirectory dir -> "addedDirectory",JString dir
+				| FoundDirectories dirs -> "foundDirectories",JInt (List.length dirs)
+				| ChangedDirectories dirs -> "changedDirectories",JArray (List.map (fun (s,_) -> JString s) dirs)
+				| ModulePathChanged(m,time,file) -> "modulePathChanged",module_path m
+				| NotCached m -> "notCached",module_path m
+				| Parsed(ffile,_) -> "parsed",JString ffile
+				| RemovedDirectory dir -> "removedDirectory",JString dir
+				| Reusing m -> "reusing",module_path m
+				| SkippingDep(m,m') -> "skipping",JObject ["skipped",module_path m;"dependency",module_path m']
+			in
+			let js = JObject [("kind",JString kind);("data",data)] in
+			DynArray.add test_server_messages js;
+		) else (fun message -> match message with
+			| AddedDirectory dir -> print_endline (Printf.sprintf "%sadded directory %s" (sign_string com) dir)
+			| FoundDirectories dirs -> print_endline (Printf.sprintf "%sfound %i directories" (sign_string com) (List.length dirs));
+			| ChangedDirectories dirs ->
+				print_endline (Printf.sprintf "%schanged directories: [%s]" (sign_string com) (String.concat ", " (List.map (fun (s,_) -> "\"" ^ s ^ "\"") dirs)))
+			| ModulePathChanged(m,time,file) ->
+				print_endline (Printf.sprintf "%smodule path might have changed: %s\n\twas: %2.0f %s\n\tnow: %2.0f %s"
+					(sign_string com) (s_type_path m.m_path) m.m_extra.m_time m.m_extra.m_file time file);
+			| NotCached m -> print_endline (Printf.sprintf "%s%s not cached (%s)" (sign_string com) (s_type_path m.m_path) (if m.m_extra.m_time = -1. then "macro-in-macro" else "modified"));
+			| Parsed(ffile,info) -> print_endline (Printf.sprintf "%sparsed %s (%s)" (sign_string com) ffile info)
+			| RemovedDirectory dir -> print_endline (Printf.sprintf "%sremoved directory %s" (sign_string com) dir);
+			| Reusing m -> print_endline (Printf.sprintf "%s%sreusing %s" (sign_string com) tabs (s_type_path m.m_path));
+			| SkippingDep(m,m') -> print_endline (Printf.sprintf "%sskipping %s%s" (sign_string com) (s_type_path m.m_path) (if m == m' then "" else Printf.sprintf "(%s)" (s_type_path m'.m_path)));
+		)
+	in
 	MacroContext.macro_enable_cache := true;
 	let current_stdin = ref None in
 	Typeload.parse_hook := (fun com2 file p ->
@@ -177,7 +267,7 @@ let rec wait_loop process_params verbose accept =
 						CompilationServer.cache_file cs fkey (ftime,data);
 						"cached",false
 				end in
-				if verbose && is_unusual then print_endline (Printf.sprintf "%sparsed %s (%s)" (sign_string com2) ffile info);
+				if verbose && is_unusual then process_server_message com2 "" (Parsed(ffile,info));
 				data
 	);
 	let check_module_shadowing com paths m =
@@ -186,8 +276,7 @@ let rec wait_loop process_params verbose accept =
 			if Sys.file_exists file then begin
 				let time = file_time file in
 				if time > m.m_extra.m_time then begin
-					if verbose then print_endline (Printf.sprintf "%smodule path might have changed: %s\n\twas: %2.0f %s\n\tnow: %2.0f %s"
-						(sign_string com) (s_type_path m.m_path) m.m_extra.m_time m.m_extra.m_file time file);
+					if verbose then process_server_message com "" (ModulePathChanged(m,time,file));
 					raise Not_found
 				end
 			end
@@ -210,7 +299,7 @@ let rec wait_loop process_params verbose accept =
 			let dirs = try
 				(* Next, get all directories from the cache and filter the ones that haven't changed. *)
 				let all_dirs = CompilationServer.find_directories cs sign in
-				List.fold_left (fun acc (dir,time) ->
+				let dirs = List.fold_left (fun acc (dir,time) ->
 					try
 						let time' = stat dir in
 						if !time < time' then begin
@@ -219,7 +308,7 @@ let rec wait_loop process_params verbose accept =
 							List.iter (fun dir ->
 								if not (CompilationServer.has_directory cs sign dir) then begin
 									let time = stat dir in
-									if verbose then print_endline (Printf.sprintf "%sadded directory %s" (sign_string com) dir);
+									if verbose then process_server_message com "" (AddedDirectory dir);
 									CompilationServer.add_directory cs sign (dir,ref time)
 								end;
 							) sub_dirs;
@@ -228,9 +317,11 @@ let rec wait_loop process_params verbose accept =
 							acc
 					with Unix.Unix_error _ ->
 						CompilationServer.remove_directory cs sign dir;
-						if verbose then print_endline (Printf.sprintf "%sremoved directory %s" (sign_string com) dir);
+						if verbose then process_server_message com "" (RemovedDirectory dir);
 						acc
-				) [] all_dirs
+				) [] all_dirs in
+				if verbose then process_server_message com "" (ChangedDirectories dirs);
+				dirs
 			with Not_found ->
 				(* There were no directories in the cache, so this must be a new context. Let's add
 				   an empty list to make sure no crazy recursion happens. *)
@@ -247,7 +338,7 @@ let rec wait_loop process_params verbose accept =
 					in
 					List.iter add_dir com.class_path;
 					List.iter add_dir (Path.find_directories (platform_name com.platform) true com.class_path);
-					if verbose then print_endline (Printf.sprintf "%sfound %i directories" (sign_string com) (List.length !dirs));
+					if verbose then process_server_message com "" (FoundDirectories !dirs);
 					CompilationServer.add_directories cs sign !dirs
 				) :: !delays;
 				(* Returning [] should be fine here because it's a new context, so we won't do any
@@ -286,7 +377,7 @@ let rec wait_loop process_params verbose accept =
 			let check_module_path () =
 				let directories = get_changed_directories ctx in
 				match m.m_extra.m_kind with
-				| MFake | MSub | MImport -> () (* don't get classpath *)
+				| MFake | MImport -> () (* don't get classpath *)
 				| MExtern ->
 					(* if we have a file then this will override our extern type *)
 					let has_file = (try check_module_shadowing com2 directories m; true with Not_found -> false) in
@@ -320,7 +411,7 @@ let rec wait_loop process_params verbose accept =
 					if has_policy CheckFileContentModification && not (content_changed m m.m_extra.m_file) then begin
 						if verbose then print_endline (Printf.sprintf "%s%s changed time not but content, reusing" (sign_string com2) m.m_extra.m_file)
 					end else begin
-						if verbose then print_endline (Printf.sprintf "%s%s not cached (%s)" (sign_string com2) (s_type_path m.m_path) (if m.m_extra.m_time = -1. then "macro-in-macro" else "modified"));
+						if verbose then process_server_message com2 "" (NotCached m);
 						if m.m_extra.m_kind = MFake then Hashtbl.remove Typecore.fake_modules m.m_extra.m_file;
 						raise Not_found;
 					end
@@ -362,7 +453,7 @@ let rec wait_loop process_params verbose accept =
 					(* this was just a dependency to check : do not add to the context *)
 					PMap.iter (Hashtbl.replace com2.resources) m.m_extra.m_binded_res;
 				| _ ->
-					(*if verbose then print_endline (Printf.sprintf "%s%sreusing %s" (sign_string com2) tabs (s_type_path m.m_path));*)
+					if verbose then process_server_message com2 tabs (Reusing m);
 					m.m_extra.m_added <- !compilation_step;
 					List.iter (fun t ->
 						match t with
@@ -380,7 +471,7 @@ let rec wait_loop process_params verbose accept =
 							a.a_meta <- List.filter (fun (m,_,_) -> m <> Meta.ValueUsed) a.a_meta
 						| _ -> ()
 					) m.m_types;
-					if m.m_extra.m_kind <> MSub then Typeload.add_module ctx m p;
+					Typeload.add_module ctx m p;
 					PMap.iter (Hashtbl.replace com2.resources) m.m_extra.m_binded_res;
 					if ctx.Typecore.in_macro || com2.display.dms_full_typing then
 						PMap.iter (fun _ m2 -> add_modules (tabs ^ "  ") m0 m2) m.m_extra.m_deps;
@@ -394,7 +485,7 @@ let rec wait_loop process_params verbose accept =
 			begin match check m with
 			| None -> ()
 			| Some m' ->
-				if verbose then print_endline (Printf.sprintf "%sskipping %s%s" (sign_string com2) (s_type_path m.m_path) (if m == m' then "" else Printf.sprintf "(%s)" (s_type_path m'.m_path)));
+				if verbose then process_server_message com2 "" (SkippingDep(m,m'));
 				tcheck();
 				raise Not_found;
 			end;
@@ -478,6 +569,7 @@ let rec wait_loop process_params verbose accept =
 			let data = parse_hxml_data hxml in
 			if verbose then print_endline ("Processing Arguments [" ^ String.concat "," data ^ "]");
 			(try
+				DynArray.clear test_server_messages;
 				Hashtbl.clear changed_directories;
 				Common.display_default := DMNone;
 				Parser.resume_display := null_pos;
@@ -501,8 +593,13 @@ let rec wait_loop process_params verbose accept =
 				if verbose then print_endline ("Completion Response =\n" ^ str);
 				write str
 			| Arg.Bad msg ->
-				prerr_endline ("Error: " ^ msg);
+				print_endline ("Error: " ^ msg);
 			);
+			if DynArray.length test_server_messages > 0 then begin
+				let b = Buffer.create 0 in
+				write_json (Buffer.add_string b) (JArray (DynArray.to_list test_server_messages));
+				write (Buffer.contents b)
+			end;
 			let fl = !delays in
 			delays := [];
 			List.iter (fun f -> f()) fl;
@@ -543,12 +640,12 @@ and init_wait_stdio() =
 	let berr = Buffer.create 0 in
 	let read = fun () ->
 		let len = IO.read_i32 chin in
-		IO.really_nread chin len
+		IO.really_nread_string chin len
 	in
 	let write = Buffer.add_string berr in
 	let close = fun() ->
 		IO.write_i32 cherr (Buffer.length berr);
-		IO.nwrite cherr (Buffer.contents berr);
+		IO.nwrite_string cherr (Buffer.contents berr);
 		IO.flush cherr
 	in
 	fun() ->
@@ -562,7 +659,7 @@ and init_wait_socket verbose host port =
 	if verbose then print_endline ("Waiting on " ^ host ^ ":" ^ string_of_int port);
 	Unix.listen sock 10;
 	let bufsize = 1024 in
-	let tmp = String.create bufsize in
+	let tmp = Bytes.create bufsize in
 	let accept() = (
 		let sin, _ = Unix.accept sock in
 		Unix.set_nonblock sin;
@@ -575,8 +672,8 @@ and init_wait_socket verbose host port =
 					failwith "Incomplete request"
 				else begin
 					if verbose then Printf.printf "Reading %d bytes\n" r;
-					Buffer.add_substring b tmp 0 r;
-					if tmp.[r-1] = '\000' then
+					Buffer.add_subbytes b tmp 0 r;
+					if Bytes.get tmp (r-1) = '\000' then
 						Buffer.sub b 0 (Buffer.length b - 1)
 					else
 						read_loop 0
@@ -591,7 +688,7 @@ and init_wait_socket verbose host port =
 				end
 		in
 		let read = fun() -> (let s = read_loop 0 in Unix.clear_nonblock sin; s) in
-		let write = ssend sin in
+		let write s = ssend sin (Bytes.unsafe_of_string s) in
 		let close() = Unix.close sin in
 		read, write, close
 	) in
@@ -601,7 +698,7 @@ and do_connect host port args =
 	let sock = Unix.socket Unix.PF_INET Unix.SOCK_STREAM 0 in
 	(try Unix.connect sock (Unix.ADDR_INET (Unix.inet_addr_of_string host,port)) with _ -> failwith ("Couldn't connect on " ^ host ^ ":" ^ string_of_int port));
 	let args = ("--cwd " ^ Unix.getcwd()) :: args in
-	ssend sock (String.concat "" (List.map (fun a -> a ^ "\n") args) ^ "\000");
+	ssend sock (Bytes.of_string (String.concat "" (List.map (fun a -> a ^ "\n") args) ^ "\000"));
 	let has_error = ref false in
 	let rec print line =
 		match (if line = "" then '\x00' else line.[0]) with
@@ -620,12 +717,12 @@ and do_connect host port args =
 		let lines = (match List.rev lines with "" :: l -> List.rev l | _ -> lines) in
 		List.iter print lines;
 	in
-	let tmp = String.create 1024 in
+	let tmp = Bytes.create 1024 in
 	let rec loop() =
 		let b = Unix.recv sock tmp 0 1024 [] in
-		Buffer.add_substring buf tmp 0 b;
+		Buffer.add_subbytes buf tmp 0 b;
 		if b > 0 then begin
-			if String.get tmp (b - 1) = '\n' then begin
+			if Bytes.get tmp (b - 1) = '\n' then begin
 				process();
 				Buffer.reset buf;
 			end;
@@ -634,4 +731,4 @@ and do_connect host port args =
 	in
 	loop();
 	process();
-	if !has_error then exit 1
+	if !has_error then exit 1

+ 0 - 0
src/sourcemaps.ml → src/compiler/sourcemaps.ml


+ 161 - 94
src/context/common.ml

@@ -38,6 +38,14 @@ type basic_types = {
 	mutable tarray : t -> t;
 }
 
+let const_type basic const default =
+	match const with
+	| TString _ -> basic.tstring
+	| TInt _ -> basic.tint
+	| TFloat _ -> basic.tfloat
+	| TBool _ -> basic.tbool
+	| _ -> default
+
 type stats = {
 	s_files_parsed : int ref;
 	s_classes_built : int ref;
@@ -452,6 +460,7 @@ module Define = struct
 		| CoreApi
 		| CoreApiSerialize
 		| Cppia
+		| NoCppiaAst
 		| Dce
 		| DceDebug
 		| Debug
@@ -465,6 +474,9 @@ module Define = struct
 		| DumpIgnoreVarIds
 		| DynamicInterfaceClosures
 		| EraseGenerics
+		| EvalDebugger
+		| EvalStack
+		| EvalTimes
 		| FastCast
 		| Fdb
 		| FileExtension
@@ -487,6 +499,7 @@ module Define = struct
 		| JsEs
 		| JsUnflatten
 		| JsSourceMap
+		| JsEnumsAsObjects
 		| SourceMap
 		| KeepOldOutput
 		| LoopUnrollMaxCost
@@ -507,11 +520,12 @@ module Define = struct
 		| NoDebug
 		| NoInline
 		| NoOpt
-		| NoPatternMatching
 		| NoRoot
 		| NoSwfCompress
 		| NoTraces
 		| Objc
+		| OldConstructorInline
+		| OldErrorFormat
 		| PhpPrefix
 		| RealPosition
 		| ReplaceFiles
@@ -537,101 +551,133 @@ module Define = struct
 		| NoMacroCache
 		| Last (* must be last *)
 
+	type define_parameter =
+		| HasParam of string
+		| Platform of platform
+		| Platforms of platform list
+
 	let infos = function
-		| AbsolutePath -> ("absolute_path","Print absolute file path in trace output")
-		| AdvancedTelemetry -> ("advanced-telemetry","Allow the SWF to be measured with Monocle tool")
-		| AnnotateSource -> ("annotate_source","Add additional comments to generated source code")
-		(* | Analyzer -> ("analyzer","Use static analyzer for optimization (experimental)") *)
-		| As3 -> ("as3","Defined when outputing flash9 as3 source code")
-		| CheckXmlProxy -> ("check_xml_proxy","Check the used fields of the xml proxy")
-		| CoreApi -> ("core_api","Defined in the core api context")
-		| CoreApiSerialize -> ("core_api_serialize","Mark some generated core api classes with the Serializable attribute on C#")
-		| Cppia -> ("cppia", "Generate cpp instruction assembly")
-		| Dce -> ("dce","<mode:std|full|no> Set the dead code elimination mode (default std)")
-		| DceDebug -> ("dce_debug","Show DCE log")
-		| Debug -> ("debug","Activated when compiling with -debug")
-		| Display -> ("display","Activated during completion")
-		| DisplayStdin -> ("display_stdin","Read the contents of a file specified in --display from standard input")
-		| DllExport -> ("dll_export", "GenCPP experimental linking")
-		| DllImport -> ("dll_import", "GenCPP experimental linking")
-		| DocGen -> ("doc_gen","Do not perform any removal/change in order to correctly generate documentation")
-		| Dump -> ("dump","<mode:pretty|record|legacy> Dump typed AST in dump subdirectory using specified mode or non-prettified default")
-		| DumpDependencies -> ("dump_dependencies","Dump the classes dependencies in a dump subdirectory")
-		| DumpIgnoreVarIds -> ("dump_ignore_var_ids","Remove variable IDs from non-pretty dumps (helps with diff)")
-		| DynamicInterfaceClosures -> ("dynamic_interface_closures","Use slow path for interface closures to save space")
-		| EraseGenerics -> ("erase_generics","Erase generic classes on C#")
-		| FastCast -> ("fast_cast","Enables an experimental casts cleanup on C# and Java")
-		| Fdb -> ("fdb","Enable full flash debug infos for FDB interactive debugging")
-		| FileExtension -> ("file_extension","Output filename extension for cpp source code")
-		| FlashStrict -> ("flash_strict","More strict typing for flash target")
-		| FlashUseStage -> ("flash_use_stage","Keep the SWF library initial stage")
+		| AbsolutePath -> "absolute_path",("Print absolute file path in trace output",[])
+		| AdvancedTelemetry -> "advanced-telemetry",("Allow the SWF to be measured with Monocle tool",[Platform Flash])
+		| AnnotateSource -> "annotate_source",("Add additional comments to generated source code",[Platform Cpp])
+		(* | Analyzer -> "analyzer",("Use static analyzer for optimization (experimental)") *)
+		| As3 -> "as3",("Defined when outputing flash9 as3 source code",[])
+		| CheckXmlProxy -> "check_xml_proxy",("Check the used fields of the xml proxy",[])
+		| CoreApi -> "core_api",("Defined in the core api context",[])
+		| CoreApiSerialize -> "core_api_serialize",("Mark some generated core api classes with the Serializable attribute on C#",[Platform Cs])
+		| Cppia -> "cppia",("Generate cpp instruction assembly",[])
+		| NoCppiaAst -> "nocppiaast",("Use legacy cppia generation",[])
+		| Dce -> "dce",("<mode:std|full|no> Set the dead code elimination mode (default std)",[])
+		| DceDebug -> "dce_debug",("Show DCE log",[])
+		| Debug -> "debug",("Activated when compiling with -debug",[])
+		| Display -> "display",("Activated during completion",[])
+		| DisplayStdin -> "display_stdin",("Read the contents of a file specified in --display from standard input",[])
+		| DllExport -> "dll_export",("GenCPP experimental linking",[Platform Cpp])
+		| DllImport -> "dll_import",("Handle Haxe-generated .NET dll imports",[Platform Cs])
+		| DocGen -> "doc_gen",("Do not perform any removal/change in order to correctly generate documentation",[])
+		| Dump -> "dump",("<mode:pretty|record|legacy> Dump typed AST in dump subdirectory using specified mode or non-prettified default",[])
+		| DumpDependencies -> "dump_dependencies",("Dump the classes dependencies in a dump subdirectory",[])
+		| DumpIgnoreVarIds -> "dump_ignore_var_ids",("Remove variable IDs from non-pretty dumps (helps with diff)",[])
+		| DynamicInterfaceClosures -> "dynamic_interface_closures",("Use slow path for interface closures to save space",[Platform Cpp])
+		| EraseGenerics -> "erase_generics",("Erase generic classes on C#",[Platform Cs])
+		| EvalDebugger -> "eval_debugger",("Support debugger in macro/interp mode. Allows host:port value to open a socket. Implies eval_stack.",[])
+		| EvalStack -> "eval_stack",("Record stack information in macro/interp mode",[])
+		| EvalTimes -> "eval_times",("Record per-method execution times in macro/interp mode. Implies eval_stack.",[])
+		| FastCast -> "fast_cast",("Enables an experimental casts cleanup on C# and Java",[Platforms [Cs;Java]])
+		| Fdb -> "fdb",("Enable full flash debug infos for FDB interactive debugging",[Platform Flash])
+		| FileExtension -> "file_extension",("Output filename extension for cpp source code",[Platform Cpp])
+		| FlashStrict -> "flash_strict",("More strict typing for flash target",[Platform Flash])
+		| FlashUseStage -> "flash_use_stage",("Keep the SWF library initial stage",[Platform Flash])
 		(* force_lib_check is only here as a debug facility - compiler checking allows errors to be found more easily *)
-		| ForceLibCheck -> ("force_lib_check","Force the compiler to check -net-lib and -java-lib added classes (internal)")
-		| ForceNativeProperty -> ("force_native_property","Tag all properties with :nativeProperty metadata for 3.1 compatibility")
-		| FormatWarning -> ("format_warning","Print a warning for each formated string, for 2.x compatibility")
-		| GencommonDebug -> ("gencommon_debug","GenCommon internal")
-		| HaxeBoot -> ("haxe_boot","Given the name 'haxe' to the flash boot class instead of a generated name")
-		| HaxeVer -> ("haxe_ver","The current Haxe version value")
-		| HxcppApiLevel -> ("hxcpp_api_level","Provided to allow compatibility between hxcpp versions")
-		| HxcppGcGenerational -> ("HXCPP_GC_GENERATIONAL","Experimental Garbage Collector")
-		| HxcppDebugger -> ("HXCPP_DEBUGGER","Include additional information for HXCPP_DEBUGGER")
-		| IncludePrefix -> ("include_prefix","prepend path to generated include files")
-		| Interp -> ("interp","The code is compiled to be run with --interp")
-		| JavaVer -> ("java_ver", "<version:5-7> Sets the Java version to be targeted")
-		| JqueryVer -> ("jquery_ver", "The jQuery version supported by js.jquery.*. The version is encoded as an interger. e.g. 1.11.3 is encoded as 11103")
-		| JsClassic -> ("js_classic","Don't use a function wrapper and strict mode in JS output")
-		| JsEs -> ("js_es","Generate JS compilant with given ES standard version (default 5)")
-		| JsUnflatten -> ("js_unflatten","Generate nested objects for packages and types")
-		| JsSourceMap -> ("js_source_map","Generate JavaScript source map even in non-debug mode")
-		| SourceMap -> ("source_map","Generate source map for compiled files (Currently supported for php7 only)")
-		| KeepOldOutput -> ("keep_old_output","Keep old source files in the output directory (for C#/Java)")
-		| LoopUnrollMaxCost -> ("loop_unroll_max_cost","Maximum cost (number of expressions * iterations) before loop unrolling is canceled (default 250)")
-		| LuaJit -> ("lua_jit","Enable the jit compiler for lua (version 5.2 only")
-		| LuaVer -> ("lua_ver","The lua version to target")
-		| Macro -> ("macro","Defined when code is compiled in the macro context")
-		| MacroDebug -> ("macro_debug","Show warnings for potential macro problems (e.g. macro-in-macro calls)")
-		| MacroTimes -> ("macro_times","Display per-macro timing when used with --times")
-		| NetVer -> ("net_ver", "<version:20-45> Sets the .NET version to be targeted")
-		| NetTarget -> ("net_target", "<name> Sets the .NET target. Defaults to \"net\". xbox, micro (Micro Framework), compact (Compact Framework) are some valid values")
-		| NekoSource -> ("neko_source","Output neko source instead of bytecode")
-		| NekoV1 -> ("neko_v1","Keep Neko 1.x compatibility")
-		| NetworkSandbox -> ("network-sandbox","Use local network sandbox instead of local file access one")
-		| NoCompilation -> ("no-compilation","Disable final compilation for Cs, Cpp and Java")
-		| NoCOpt -> ("no_copt","Disable completion optimization (for debug purposes)")
-		| NoDebug -> ("no_debug","Remove all debug macros from cpp output")
-		| NoDeprecationWarnings -> ("no-deprecation-warnings","Do not warn if fields annotated with @:deprecated are used")
-		| NoFlashOverride -> ("no-flash-override", "Change overrides on some basic classes into HX suffixed methods, flash only")
-		| NoOpt -> ("no_opt","Disable optimizations")
-		| NoPatternMatching -> ("no_pattern_matching","Disable pattern matching")
-		| NoInline -> ("no_inline","Disable inlining")
-		| NoRoot -> ("no_root","Generate top-level types into haxe.root namespace")
-		| NoMacroCache -> ("no_macro_cache","Disable macro context caching")
-		| NoSwfCompress -> ("no_swf_compress","Disable SWF output compression")
-		| NoTraces -> ("no_traces","Disable all trace calls")
-		| Objc -> ("objc","Sets the hxcpp output to objective-c++ classes. Must be defined for interop")
-		| PhpPrefix -> ("php_prefix","Compiled with --php-prefix")
-		| RealPosition -> ("real_position","Disables Haxe source mapping when targetting C#, removes position comments in Java and Php7 output")
-		| ReplaceFiles -> ("replace_files","GenCommon internal")
-		| Scriptable -> ("scriptable","GenCPP internal")
-		| ShallowExpose -> ("shallow-expose","Expose types to surrounding scope of Haxe generated closure without writing to window object")
-		| SourceHeader -> ("source-header","Print value as comment on top of generated files, use '' value to disable")
-		| SourceMapContent -> ("source-map-content","Include the hx sources as part of the JS source map")
-		| Swc -> ("swc","Output a SWC instead of a SWF")
-		| SwfCompressLevel -> ("swf_compress_level","<level:1-9> Set the amount of compression for the SWF output")
-		| SwfDebugPassword -> ("swf_debug_password", "Set a password for debugging")
-		| SwfDirectBlit -> ("swf_direct_blit", "Use hardware acceleration to blit graphics")
-		| SwfGpu -> ("swf_gpu", "Use GPU compositing features when drawing graphics")
-		| SwfMetadata -> ("swf_metadata", "<file> Include contents of <file> as metadata in the swf")
-		| SwfPreloaderFrame -> ("swf_preloader_frame", "Insert empty first frame in swf")
-		| SwfProtected -> ("swf_protected","Compile Haxe private as protected in the SWF instead of public")
-		| SwfScriptTimeout -> ("swf_script_timeout", "Maximum ActionScript processing time before script stuck dialog box displays (in seconds)")
-		| SwfUseDoAbc -> ("swf_use_doabc", "Use DoAbc swf-tag instead of DoAbcDefine")
-		| Sys -> ("sys","Defined for all system platforms")
-		| Unsafe -> ("unsafe","Allow unsafe code when targeting C#")
-		| UseNekoc -> ("use_nekoc","Use nekoc compiler instead of internal one")
-		| UseRttiDoc -> ("use_rtti_doc","Allows access to documentation during compilation")
-		| Vcproj -> ("vcproj","GenCPP internal")
+		| ForceLibCheck -> "force_lib_check",("Force the compiler to check -net-lib and -java-lib added classes (internal)",[Platforms [Cs;Java]])
+		| ForceNativeProperty -> "force_native_property",("Tag all properties with :nativeProperty metadata for 3.1 compatibility",[Platform Cpp])
+		| FormatWarning -> "format_warning",("Print a warning for each formated string, for 2.x compatibility",[])
+		| GencommonDebug -> "gencommon_debug",("GenCommon internal",[Platforms [Cs;Java]])
+		| HaxeBoot -> "haxe_boot",("Given the name 'haxe' to the flash boot class instead of a generated name",[Platform Flash])
+		| HaxeVer -> "haxe_ver",("The current Haxe version value",[])
+		| HxcppApiLevel -> "hxcpp_api_level",("Provided to allow compatibility between hxcpp versions",[Platform Cpp])
+		| HxcppGcGenerational -> "HXCPP_GC_GENERATIONAL",("Experimental Garbage Collector",[Platform Cpp])
+		| HxcppDebugger -> "HXCPP_DEBUGGER",("Include additional information for HXCPP_DEBUGGER",[Platform Cpp])
+		| IncludePrefix -> "include_prefix",("prepend path to generated include files",[Platform Cpp])
+		| Interp -> "interp",("The code is compiled to be run with --interp",[])
+		| JavaVer -> "java_ver",("<version:5-7> Sets the Java version to be targeted",[Platform Java])
+		| JqueryVer -> "jquery_ver",("The jQuery version supported by js.jquery.*. The version is encoded as an interger. e.g. 1.11.3 is encoded as 11103",[Platform Js])
+		| JsClassic -> "js_classic",("Don't use a function wrapper and strict mode in JS output",[Platform Js])
+		| JsEs -> "js_es",("Generate JS compilant with given ES standard version (default 5)",[Platform Js; HasParam "version number"])
+		| JsEnumsAsObjects -> "js_enums_as_objects",("Generate enum representation as object instead of as array",[Platform Js])
+		| JsUnflatten -> "js_unflatten",("Generate nested objects for packages and types",[Platform Js])
+		| JsSourceMap -> "js_source_map",("Generate JavaScript source map even in non-debug mode",[Platform Js])
+		| SourceMap -> "source_map",("Generate source map for compiled files (Currently supported for php7 only)",[Platform Php])
+		| KeepOldOutput -> "keep_old_output",("Keep old source files in the output directory (for C#/Java)",[Platforms [Cs;Java]])
+		| LoopUnrollMaxCost -> "loop_unroll_max_cost",("Maximum cost (number of expressions * iterations) before loop unrolling is canceled (default 250)",[])
+		| LuaJit -> "lua_jit",("Enable the jit compiler for lua (version 5.2 only)",[Platform Lua])
+		| LuaVer -> "lua_ver",("The lua version to target",[Platform Lua])
+		| Macro -> "macro",("Defined when code is compiled in the macro context",[])
+		| MacroDebug -> "macro_debug",("Show warnings for potential macro problems (e.g. macro-in-macro calls)",[])
+		| MacroTimes -> "macro_times",("Display per-macro timing when used with --times",[])
+		| NetVer -> "net_ver",("<version:20-45> Sets the .NET version to be targeted",[Platform Cs])
+		| NetTarget -> "net_target",("<name> Sets the .NET target. Defaults to \"net\". xbox, micro (Micro Framework), compact (Compact Framework) are some valid values",[Platform Cs])
+		| NekoSource -> "neko_source",("Output neko source instead of bytecode",[Platform Neko])
+		| NekoV1 -> "neko_v1",("Keep Neko 1.x compatibility",[Platform Neko])
+		| NetworkSandbox -> "network-sandbox",("Use local network sandbox instead of local file access one",[Platform Flash])
+		| NoCompilation -> "no-compilation",("Disable final compilation",[Platforms [Cs;Java;Cpp;Hl]])
+		| NoCOpt -> "no_copt",("Disable completion optimization (for debug purposes)",[])
+		| NoDebug -> "no_debug",("Remove all debug macros from cpp output",[])
+		| NoDeprecationWarnings -> "no-deprecation-warnings",("Do not warn if fields annotated with @:deprecated are used",[])
+		| NoFlashOverride -> "no-flash-override",("Change overrides on some basic classes into HX suffixed methods, flash only",[Platform Flash])
+		| NoOpt -> "no_opt",("Disable optimizations",[])
+		| NoInline -> "no_inline",("Disable inlining",[])
+		| NoRoot -> "no_root",("Generate top-level types into haxe.root namespace",[Platform Cs])
+		| NoMacroCache -> "no_macro_cache",("Disable macro context caching",[])
+		| NoSwfCompress -> "no_swf_compress",("Disable SWF output compression",[Platform Flash])
+		| NoTraces -> "no_traces",("Disable all trace calls",[])
+		| Objc -> "objc",("Sets the hxcpp output to objective-c++ classes. Must be defined for interop",[Platform Cpp])
+		| OldConstructorInline -> "old-constructor-inline",("Use old constructor inlining logic (from haxe 3.4.2) instead of the reworked version.",[])
+		| OldErrorFormat -> "old-error-format",("Use Haxe 3.x zero-based column error messages instead of new one-based format.",[])
+		| PhpPrefix -> "php_prefix",("Compiled with --php-prefix",[Platform Php])
+		| RealPosition -> "real_position",("Disables Haxe source mapping when targetting C#, removes position comments in Java and Php7 output",[Platforms [Cs;Java;Php]])
+		| ReplaceFiles -> "replace_files",("GenCommon internal",[Platforms [Java;Cs]])
+		| Scriptable -> "scriptable",("GenCPP internal",[Platform Cpp])
+		| ShallowExpose -> "shallow-expose",("Expose types to surrounding scope of Haxe generated closure without writing to window object",[Platform Js])
+		| SourceHeader -> "source-header",("Print value as comment on top of generated files, use '' value to disable",[])
+		| SourceMapContent -> "source-map-content",("Include the hx sources as part of the JS source map",[Platform Js])
+		| Swc -> "swc",("Output a SWC instead of a SWF",[Platform Flash])
+		| SwfCompressLevel -> "swf_compress_level",("<level:1-9> Set the amount of compression for the SWF output",[Platform Flash])
+		| SwfDebugPassword -> "swf_debug_password",("Set a password for debugging",[Platform Flash])
+		| SwfDirectBlit -> "swf_direct_blit",("Use hardware acceleration to blit graphics",[Platform Flash])
+		| SwfGpu -> "swf_gpu",("Use GPU compositing features when drawing graphics",[Platform Flash])
+		| SwfMetadata -> "swf_metadata",("<file> Include contents of <file> as metadata in the swf",[Platform Flash])
+		| SwfPreloaderFrame -> "swf_preloader_frame",("Insert empty first frame in swf",[Platform Flash])
+		| SwfProtected -> "swf_protected",("Compile Haxe private as protected in the SWF instead of public",[Platform Flash])
+		| SwfScriptTimeout -> "swf_script_timeout",("Maximum ActionScript processing time before script stuck dialog box displays (in seconds)",[Platform Flash])
+		| SwfUseDoAbc -> "swf_use_doabc",("Use DoAbc swf-tag instead of DoAbcDefine",[Platform Flash])
+		| Sys -> "sys",("Defined for all system platforms",[])
+		| Unsafe -> "unsafe",("Allow unsafe code when targeting C#",[Platform Cs])
+		| UseNekoc -> "use_nekoc",("Use nekoc compiler instead of internal one",[Platform Neko])
+		| UseRttiDoc -> "use_rtti_doc",("Allows access to documentation during compilation",[])
+		| Vcproj -> "vcproj",("GenCPP internal",[Platform Cpp])
 		| Last -> assert false
+
+	let get_documentation_list() =
+		let m = ref 0 in
+		let rec loop i =
+			let d = Obj.magic i in
+			if d <> Last then begin
+				let t, (doc,flags) = infos d in
+				let pfs = ref [] in
+				List.iter (function
+				| HasParam s -> () (* TODO *)
+				| Platform p -> pfs := p :: !pfs;
+				| Platforms pl -> pfs := pl @ !pfs;
+				) flags;
+				let pfs = platform_list_help (List.rev !pfs) in
+				if String.length t > !m then m := String.length t;
+				((String.concat "-" (ExtString.String.nsplit t "_")),doc ^ pfs) :: (loop (i + 1))
+			end else
+				[]
+		in
+		let all = List.sort (fun (s1,_) (s2,_) -> String.compare s1 s2) (loop 0) in
+		all,!m
 end
 
 let short_platform_name = function
@@ -646,6 +692,7 @@ let short_platform_name = function
 	| Java -> "jav"
 	| Python -> "py"
 	| Hl -> "hl"
+	| Eval -> "evl"
 
 let stats =
 	{
@@ -754,6 +801,12 @@ let get_config com =
 			pf_pad_nulls = true;
 			pf_can_skip_non_nullable_argument = false;
 		}
+	| Eval ->
+		{
+			default_config with
+			pf_static = false;
+			pf_pad_nulls = true;
+		}
 
 let memory_marker = [|Unix.time()|]
 
@@ -1034,7 +1087,14 @@ let rec mkdir_recursive base dir_list =
 				   | "/" -> "/" ^ dir
 				   | _ -> base ^ "/" ^ dir
 		in
-		if not ( (path = "") || ( ((String.length path) = 2) && ((String.sub path 1 1) = ":") ) ) then
+		let path_len = String.length path in
+		let path =
+			if path_len > 0 && (path.[path_len - 1] = '/' || path.[path_len - 1] == '\\') then
+				String.sub path 0 (path_len - 1)
+			else
+				path
+		in
+		if not ( (path = "") || ( (path_len = 2) && ((String.sub path 1 1) = ":") ) ) then
 			if not (Sys.file_exists path) then
 				Unix.mkdir path 0o755;
 		mkdir_recursive (if (path = "") then "/" else path) remaining
@@ -1133,6 +1193,13 @@ let float_repres f =
 			Printf.sprintf "%.18g" f
 		in valid_float_lexeme float_val
 
+let hash f =
+	let h = ref 0 in
+	for i = 0 to String.length f - 1 do
+		h := !h * 223 + int_of_char (String.unsafe_get f i);
+	done;
+	if Sys.word_size = 64 then Int32.to_int (Int32.shift_right (Int32.shift_left (Int32.of_int !h) 1) 1) else !h
+
 let add_diagnostics_message com s p sev =
 	let di = com.shared.shared_display_information in
 	di.diagnostics_messages <- (s,p,sev) :: di.diagnostics_messages

+ 15 - 11
src/context/meta.ml

@@ -52,6 +52,7 @@ type strict_meta =
 	| Fixed
 	| FlatEnum
 	| Font
+	| ForLoopVariable
 	| Forward
 	| ForwardStatics
 	| From
@@ -75,6 +76,7 @@ type strict_meta =
 	| ImplicitCast
 	| Include
 	| InitPackage
+	| InlineConstructorArgument of int * int
 	| InlineConstructorVariable
 	| Internal
 	| IsVar
@@ -85,7 +87,9 @@ type strict_meta =
 	| KeepInit
 	| KeepSub
 	| LibType
+	| LoopLabel
 	| LuaRequire
+	| LuaDotMethod 
 	| Meta
 	| Macro
 	| MaybeUsed
@@ -120,6 +124,7 @@ type strict_meta =
 	| PhpClassConst
 	| PhpMagic
 	| PhpNoConstructor
+	| Pos
 	| PrivateAccess
 	| Property
 	| Protected
@@ -158,7 +163,6 @@ type strict_meta =
 	| TemplatedCall
 	| ValueUsed
 	| Volatile
-	| Unbound
 	| UnifyMinDynamic
 	| Unreflective
 	| Unsafe
@@ -237,7 +241,7 @@ let get_info = function
 	| EnumConstructorParam -> ":enumConstructorParam",("Used internally to annotate GADT type parameters",[UsedOn TClass; UsedInternally])
 	| Event -> ":event",("Automatically added by -net-lib on events. Has no effect on types compiled by Haxe",[Platform Cs; UsedOn TClassField])
 	| Exhaustive -> ":exhaustive",("",[UsedInternally])
-	| Expose -> ":expose",("Makes the class available on the window object",[HasParam "?Name=Class path";UsedOn TClass;Platform Js])
+	| Expose -> ":expose",("Includes the class or field in Haxe exports",[HasParam "?Name=Class path";UsedOnEither [TClass;TClassField];Platforms [Js;Lua]])
 	| Extern -> ":extern",("Marks the field as extern so it is not generated",[UsedOn TClassField])
 	| FakeEnum -> ":fakeEnum",("Treat enum as collection of values of the specified type",[HasParam "Type name";UsedOn TEnum])
 	| File -> ":file",("Includes a given binary file into the target Swf and associates it with the class (must extend flash.utils.ByteArray)",[HasParam "File path";UsedOn TClass;Platform Flash])
@@ -246,6 +250,7 @@ let get_info = function
 	| Fixed -> ":fixed",("Delcares an anonymous object to have fixed fields",[ (*UsedOn TObjectDecl(_)*)])
 	| FlatEnum -> ":flatEnum",("Internally used to mark an enum as being flat, i.e. having no function constructors",[UsedOn TEnum; UsedInternally])
 	| Font -> ":font",("Embeds the given TrueType font into the class (must extend flash.text.Font)",[HasParam "TTF path";HasParam "Range String";UsedOn TClass])
+	| ForLoopVariable -> ":forLoopVariable",("Internally used to mark for-loop variables",[UsedInternally])
 	| Forward -> ":forward",("Forwards field access to underlying type",[HasParam "List of field names";UsedOn TAbstract])
 	| ForwardStatics -> ":forwardStatics",("Forwards static field access to underlying type",[HasParam "List of field names";UsedOn TAbstract])
 	| From -> ":from",("Specifies that the field of the abstract is a cast operation from the type identified in the function",[UsedOn TAbstractField])
@@ -269,6 +274,7 @@ let get_info = function
 	| ImplicitCast -> ":implicitCast",("Generated automatically on the AST when an implicit abstract cast happens",[UsedInternally; UsedOn TExpr])
 	| Include -> ":include",("",[Platform Cpp])
 	| InitPackage -> ":initPackage",("Some weird thing for Genjs we want to remove someday",[UsedInternally; Platform Js])
+	| InlineConstructorArgument _ -> ":inlineConstructorArgument",("Internally used to mark expressions that were passed as arguments of an inlined constructor",[UsedInternally])
 	| InlineConstructorVariable -> ":inlineConstructorVariable",("Internally used to mark variables that come from inlined constructors",[UsedInternally])
 	| Internal -> ":internal",("Generates the annotated field/class with 'internal' access",[Platforms [Java;Cs]; UsedOnEither[TClass;TEnum;TClassField]])
 	| IsVar -> ":isVar",("Forces a physical field to be generated for properties that otherwise would not require one",[UsedOn TClassField])
@@ -276,10 +282,12 @@ let get_info = function
 	| JavaNative -> ":javaNative",("Automatically added by -java-lib on classes generated from JAR/class files",[Platform Java; UsedOnEither[TClass;TEnum]; UsedInternally])
 	| JsRequire -> ":jsRequire",("Generate javascript module require expression for given extern",[Platform Js; UsedOn TClass])
 	| LuaRequire -> ":luaRequire",("Generate lua module require expression for given extern",[Platform Lua; UsedOn TClass])
+	| LuaDotMethod -> ":luaDotMethod",("Indicates that the given extern type instance should have dot-style invocation for methods instead of colon.",[Platform Lua; UsedOnEither[TClass;TClassField]])
 	| Keep -> ":keep",("Causes a field or type to be kept by DCE",[])
 	| KeepInit -> ":keepInit",("Causes a class to be kept by DCE even if all its field are removed",[UsedOn TClass])
 	| KeepSub -> ":keepSub",("Extends @:keep metadata to all implementing and extending classes",[UsedOn TClass])
 	| LibType -> ":libType",("Used by -net-lib and -java-lib to mark a class that shouldn't be checked (overrides, interfaces, etc) by the type loader",[UsedInternally; UsedOn TClass; Platforms [Java;Cs]])
+	| LoopLabel -> ":loopLabel",("Mark loop and break expressions with a label to support breaking from within switch",[UsedInternally])
 	| Meta -> ":meta",("Internally used to mark a class field as being the metadata field",[])
 	| Macro -> ":macro",("(deprecated)",[])
 	| MaybeUsed -> ":maybeUsed",("Internally used by DCE to mark fields that might be kept",[UsedInternally])
@@ -310,10 +318,11 @@ 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",("(php7) Puts the static fields of a class in the global PHP namespace",[Platforms [Php;Php];UsedOn TClass])
+	| PhpGlobal -> ":phpGlobal",("(php7) Puts the static fields of a class in the global PHP namespace",[Platform 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])
 	| PhpNoConstructor -> ":phpNoConstructor",("(php7) Special meta for extern classes which does not have native constructor in PHP, but need a constructor in Haxe extern",[Platform Php;UsedOn TClass])
+	| Pos -> ":pos",("Sets the position of a reified expression",[HasParam "Position";UsedOn TExpr])
 	| 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])
@@ -331,7 +340,7 @@ let get_info = function
 	| Runtime -> ":runtime",("?",[])
 	| RuntimeValue -> ":runtimeValue",("Marks an abstract as being a runtime value",[UsedOn TAbstract])
 	| Scalar -> ":scalar",("Used by hxcpp to mark a custom coreType abstract",[UsedOn TAbstract; Platform Cpp])
-	| SelfCall -> ":selfCall",("Translates method calls into calling object directly",[UsedOn TClassField; Platform Js])
+	| SelfCall -> ":selfCall",("Translates method calls into calling object directly",[UsedOn TClassField; Platforms [Js;Lua]])
 	| Setter -> ":setter",("Generates a native setter function on the given field",[HasParam "Class field name";UsedOn TClassField;Platform Flash])
 	| StackOnly -> ":stackOnly",("Instances of this type can only appear on the stack",[Platform Cpp])
 	| StoredTypedExpr -> ":storedTypedExpr",("Used internally to reference a typed expression returned from a macro",[UsedInternally])
@@ -352,7 +361,6 @@ let get_info = function
 	| Transient -> ":transient",("Adds the 'transient' flag to the class field",[Platform Java; UsedOn TClassField])
 	| ValueUsed -> ":valueUsed",("Internally used by DCE to mark an abstract value as used",[UsedInternally])
 	| Volatile -> ":volatile",("",[Platforms [Java;Cs]])
-	| Unbound -> ":unbound", ("Compiler internal to denote unbounded global variable",[UsedInternally])
 	| UnifyMinDynamic -> ":unifyMinDynamic",("Allows a collection of types to unify to Dynamic",[UsedOn TClassField])
 	| Unreflective -> ":unreflective",("",[Platform Cpp])
 	| Unsafe -> ":unsafe",("Declares a class, or a method with the C#'s 'unsafe' flag",[Platform Cs; UsedOnEither [TClass;TClassField]])
@@ -404,11 +412,7 @@ let get_documentation d =
 			| [] -> ""
 			| l -> "(" ^ String.concat "," l ^ ")"
 		) in
-		let pfs = (match List.rev !pfs with
-			| [] -> ""
-			| [p] -> " (" ^ platform_name p ^ " only)"
-			| pl -> " (for " ^ String.concat "," (List.map platform_name pl) ^ ")"
-		) in
+		let pfs = platform_list_help (List.rev !pfs) in
 		let str = "@" ^ t in
 		Some (str,params ^ doc ^ pfs)
 	end else
@@ -427,4 +431,4 @@ let get_documentation_list () =
 			[]
 	in
 	let all = List.sort (fun (s1,_) (s2,_) -> String.compare s1 s2) (loop 0) in
-	all,!m
+	all,!m

+ 24 - 15
src/display/display.ml

@@ -239,7 +239,7 @@ module DocumentSymbols = struct
 			| EFunction(Some s,f) ->
 				add s Function p;
 				func parent f
-			| EIn((EConst(Ident s),p),e2) ->
+			| EBinop(OpIn,(EConst(Ident s),p),e2) ->
 				add s Variable p;
 				expr parent e2;
 			| _ ->
@@ -450,9 +450,9 @@ module Diagnostics = struct
 				had_effect := true;
 			| TLocal v when not (Meta.has Meta.UserVariable v.v_meta) ->
 				()
-			| TConst _ | TLocal _ | TTypeExpr _ | TFunction _ when not in_value ->
+			| TConst _ | TLocal _ | TTypeExpr _ | TFunction _ | TIdent _ when not in_value ->
 				no_effect e.epos;
-			| TConst _ | TLocal _ | TTypeExpr _ | TEnumParameter _ | TVar _ ->
+			| TConst _ | TLocal _ | TTypeExpr _ | TEnumParameter _ | TEnumIndex _ | TVar _ | TIdent _ ->
 				()
 			| TFunction tf ->
 				loop false tf.tf_expr
@@ -786,19 +786,20 @@ module ToplevelCollector = struct
 
 	let run ctx only_types =
 		let acc = DynArray.create () in
+		let add x = DynArray.add acc x in
 
 		if not only_types then begin
 			(* locals *)
 			PMap.iter (fun _ v ->
 				if not (is_gen_local v) then
-					DynArray.add acc (ITLocal v)
+					add (ITLocal v)
 			) ctx.locals;
 
 			(* member vars *)
 			if ctx.curfun <> FunStatic then begin
 				let rec loop c =
 					List.iter (fun cf ->
-						DynArray.add acc (ITMember(ctx.curclass,cf))
+						if not (Meta.has Meta.NoCompletion cf.cf_meta) then add (ITMember(ctx.curclass,cf))
 					) c.cl_ordered_fields;
 					match c.cl_super with
 						| None ->
@@ -812,7 +813,7 @@ module ToplevelCollector = struct
 
 			(* statics *)
 			List.iter (fun cf ->
-				DynArray.add acc (ITStatic(ctx.curclass,cf))
+				if not (Meta.has Meta.NoCompletion cf.cf_meta) then add (ITStatic(ctx.curclass,cf))
 			) ctx.curclass.cl_ordered_statics;
 
 			(* enum constructors *)
@@ -820,7 +821,7 @@ module ToplevelCollector = struct
 				match t with
 				| TAbstractDecl ({a_impl = Some c} as a) when Meta.has Meta.Enum a.a_meta ->
 					List.iter (fun cf ->
-						if (Meta.has Meta.Enum cf.cf_meta) then DynArray.add acc (ITEnumAbstract(a,cf));
+						if (Meta.has Meta.Enum cf.cf_meta) && not (Meta.has Meta.NoCompletion cf.cf_meta) then add (ITEnumAbstract(a,cf));
 					) c.cl_ordered_statics
 				| TClassDecl _ | TAbstractDecl _ ->
 					()
@@ -831,7 +832,7 @@ module ToplevelCollector = struct
 					end
 				| TEnumDecl e ->
 					PMap.iter (fun _ ef ->
-						DynArray.add acc (ITEnum(e,ef))
+						add (ITEnum(e,ef))
 					) e.e_constrs;
 			in
 			List.iter enum_ctors ctx.m.curmod.m_types;
@@ -846,15 +847,15 @@ module ToplevelCollector = struct
 						| TAbstractDecl {a_impl = Some c} -> (PMap.find s c.cl_statics).cf_type
 						| _ -> raise Not_found
 					in
-					DynArray.add acc (ITGlobal(mt,s,t))
+					add (ITGlobal(mt,s,t))
 				with Not_found ->
 					()
 			) ctx.m.module_globals;
 
 			(* literals *)
-			DynArray.add acc (ITLiteral "null");
-			DynArray.add acc (ITLiteral "true");
-			DynArray.add acc (ITLiteral "false");
+			add (ITLiteral "null");
+			add (ITLiteral "true");
+			add (ITLiteral "false");
 		end;
 
 		let module_types = ref [] in
@@ -903,15 +904,23 @@ module ToplevelCollector = struct
 				if not (List.mem pack !packages) then packages := pack :: !packages
 		in
 
-		explore_class_paths ctx class_paths false add_package (fun _ -> ()) add_type;
+		let maybe_add_type mt = if not (t_infos mt).mt_private then add_type mt in
+
+		explore_class_paths ctx class_paths false add_package (fun _ -> ()) maybe_add_type;
 
 		List.iter (fun pack ->
-			DynArray.add acc (ITPackage pack)
+			add (ITPackage pack)
 		) !packages;
 
 		List.iter (fun mt ->
-			DynArray.add acc (ITType mt)
+			add (ITType mt)
 		) !module_types;
+
+		(* type params *)
+		List.iter (fun (_,t) ->
+			add (ITType (module_type_of_type t))
+		) ctx.type_params;
+
 		DynArray.to_list acc
 
 	let handle_unresolved_identifier ctx i p only_types =

+ 24 - 12
src/display/displayOutput.ml

@@ -60,22 +60,30 @@ let print_toplevel il =
 	Buffer.add_string b "<il>\n";
 	let s_type t = htmlescape (s_type (print_context()) t) in
 	let s_doc d = maybe_print_doc d in
+	let identifiers = Hashtbl.create 0 in
+	let check_ident s =
+		if Hashtbl.mem identifiers s then false
+		else begin
+			Hashtbl.add identifiers s true;
+			true
+		end
+	in
 	List.iter (fun id -> match id with
 		| IdentifierType.ITLocal v ->
-			Buffer.add_string b (Printf.sprintf "<i k=\"local\" t=\"%s\">%s</i>\n" (s_type v.v_type) v.v_name);
+			if check_ident v.v_name then Buffer.add_string b (Printf.sprintf "<i k=\"local\" t=\"%s\">%s</i>\n" (s_type v.v_type) v.v_name);
 		| IdentifierType.ITMember(c,cf) ->
-			Buffer.add_string b (Printf.sprintf "<i k=\"member\" t=\"%s\"%s>%s</i>\n" (s_type cf.cf_type) (s_doc cf.cf_doc) cf.cf_name);
+			if check_ident cf.cf_name then Buffer.add_string b (Printf.sprintf "<i k=\"member\" t=\"%s\"%s>%s</i>\n" (s_type cf.cf_type) (s_doc cf.cf_doc) cf.cf_name);
 		| IdentifierType.ITStatic(c,cf) ->
-			Buffer.add_string b (Printf.sprintf "<i k=\"static\" t=\"%s\"%s>%s</i>\n" (s_type cf.cf_type) (s_doc cf.cf_doc) cf.cf_name);
+			if check_ident cf.cf_name then Buffer.add_string b (Printf.sprintf "<i k=\"static\" t=\"%s\"%s>%s</i>\n" (s_type cf.cf_type) (s_doc cf.cf_doc) cf.cf_name);
 		| IdentifierType.ITEnum(en,ef) ->
-			Buffer.add_string b (Printf.sprintf "<i k=\"enum\" t=\"%s\"%s>%s</i>\n" (s_type ef.ef_type) (s_doc ef.ef_doc) ef.ef_name);
+			if check_ident ef.ef_name then Buffer.add_string b (Printf.sprintf "<i k=\"enum\" t=\"%s\"%s>%s</i>\n" (s_type ef.ef_type) (s_doc ef.ef_doc) ef.ef_name);
 		| IdentifierType.ITEnumAbstract(a,cf) ->
-			Buffer.add_string b (Printf.sprintf "<i k=\"enumabstract\" t=\"%s\"%s>%s</i>\n" (s_type cf.cf_type) (s_doc cf.cf_doc) cf.cf_name);
+			if check_ident cf.cf_name then Buffer.add_string b (Printf.sprintf "<i k=\"enumabstract\" t=\"%s\"%s>%s</i>\n" (s_type cf.cf_type) (s_doc cf.cf_doc) cf.cf_name);
 		| IdentifierType.ITGlobal(mt,s,t) ->
-			Buffer.add_string b (Printf.sprintf "<i k=\"global\" p=\"%s\" t=\"%s\">%s</i>\n" (s_type_path (t_infos mt).mt_path) (s_type t) s);
+			if check_ident s then Buffer.add_string b (Printf.sprintf "<i k=\"global\" p=\"%s\" t=\"%s\">%s</i>\n" (s_type_path (t_infos mt).mt_path) (s_type t) s);
 		| IdentifierType.ITType(mt) ->
 			let infos = t_infos mt in
-			Buffer.add_string b (Printf.sprintf "<i k=\"type\" p=\"%s\"%s>%s</i>\n" (s_type_path infos.mt_path) (s_doc infos.mt_doc) (snd infos.mt_path));
+			if check_ident (snd infos.mt_path) then Buffer.add_string b (Printf.sprintf "<i k=\"type\" p=\"%s\"%s>%s</i>\n" (s_type_path infos.mt_path) (s_doc infos.mt_doc) (snd infos.mt_path));
 		| IdentifierType.ITPackage s ->
 			Buffer.add_string b (Printf.sprintf "<i k=\"package\">%s</i>\n" s)
 		| IdentifierType.ITLiteral s ->
@@ -164,7 +172,7 @@ let display_memory com =
 		let modules = Hashtbl.fold (fun (path,key) m acc ->
 			let mdeps = Hashtbl.create 0 in
 			scan_module_deps m mdeps;
-			let deps = ref [] in
+			let deps = ref [Obj.repr null_module] in
 			let out = ref all_modules in
 			Hashtbl.iter (fun _ md ->
 				out := PMap.remove md.m_id !out;
@@ -174,6 +182,7 @@ let display_memory com =
 					match t with
 					| TClassDecl c ->
 						deps := Obj.repr c :: !deps;
+						c.cl_descendants <- []; (* prevent false positive *)
 						List.iter (fun f -> deps := Obj.repr f :: !deps) c.cl_ordered_statics;
 						List.iter (fun f -> deps := Obj.repr f :: !deps) c.cl_ordered_fields;
 					| TEnumDecl e ->
@@ -276,7 +285,10 @@ module TypePathHandler = struct
 					end;
 				end else if file_extension f = "hx" then begin
 					let c = Filename.chop_extension f in
-					if String.length c < 2 || String.sub c (String.length c - 2) 2 <> "__" then classes := c :: !classes;
+					try
+						ignore(String.index c '.')
+					with Not_found ->
+						if String.length c < 2 || String.sub c (String.length c - 2) 2 <> "__" then classes := c :: !classes;
 				end;
 			) r;
 		) com.class_path;
@@ -399,7 +411,7 @@ let pos_to_json_range p =
 		JNull
 	else
 		let l1, p1, l2, p2 = Lexer.get_pos_coords p in
-		let to_json l c = JObject [("line", JInt (l - 1)); ("character", JInt c)] in
+		let to_json l c = JObject [("line", JInt (l - 1)); ("character", JInt (c - 1))] in
 		JObject [
 			("start", to_json l1 p1);
 			("end", to_json l2 p2);
@@ -709,7 +721,7 @@ let process_display_file com classes =
 				if clen < String.length spath && String.sub spath 0 clen = c then begin
 					let path = String.sub spath clen (String.length spath - clen) in
 					(try
-						let path = Path.parse_type_path path in
+						let path = Path.parse_path path in
 						(match loop l with
 						| Some x as r when String.length (s_type_path x) < String.length (s_type_path path) -> r
 						| _ -> Some path)
@@ -735,7 +747,7 @@ let process_display_file com classes =
 			| None ->
 				if not (Sys.file_exists real) then failwith "Display file does not exist";
 				(match List.rev (ExtString.String.nsplit real Path.path_sep) with
-				| file :: _ when file.[0] >= 'a' && file.[1] <= 'z' -> failwith ("Display file '" ^ file ^ "' should not start with a lowercase letter")
+				| file :: _ when file.[0] >= 'a' && file.[0] <= 'z' -> failwith ("Display file '" ^ file ^ "' should not start with a lowercase letter")
 				| _ -> ());
 				failwith "Display file was not found in class path"
 			);

+ 279 - 0
src/filters/capturedVars.ml

@@ -0,0 +1,279 @@
+(*
+	The Haxe Compiler
+	Copyright (C) 2005-2017  Haxe Foundation
+
+	This program is free software; you can redistribute it and/or
+	modify it under the terms of the GNU General Public License
+	as published by the Free Software Foundation; either version 2
+	of the License, or (at your option) any later version.
+
+	This program is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+
+	You should have received a copy of the GNU General Public License
+	along with this program; if not, write to the Free Software
+	Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ *)
+open Globals
+open Type
+open Common
+open LocalUsage
+
+(* BLOCK VARIABLES CAPTURE *)
+(*
+	For some platforms, it will simply mark the variables which are used in closures
+	using the v_capture flag so it can be processed in a more optimized
+
+	For Flash/JS platforms, it will ensure that variables used in loop sub-functions
+	have an unique scope. It transforms the following expression :
+
+	for( x in array )
+		funs.push(function() return x++);
+
+	Into the following :
+
+	for( _x in array ) {
+		var x = [_x];
+		funs.push(function(x) { function() return x[0]++; }(x));
+	}
+*)
+let captured_vars com e =
+	let t = com.basic in
+
+	let impl = match com.platform with
+	(* optimized version for C#/Java - use native arrays *)
+	| Cs | Java ->
+		let cnativearray =
+			match (List.find (fun md -> match md with
+					| TClassDecl ({ cl_path = ["cs"|"java"],"NativeArray" }) -> true
+					| _ -> false
+				) com.types)
+			with TClassDecl cl -> cl | _ -> assert false
+		in
+
+		object
+			method captured_type t = TInst (cnativearray,[t])
+
+			method mk_ref v ve p =
+				match ve with
+				| None ->
+					let eone = mk (TConst (TInt (Int32.of_int 1))) t.tint p in
+					let t = match v.v_type with TInst (_, [t]) -> t | _ -> assert false in
+					mk (TNew (cnativearray,[t],[eone])) v.v_type p
+				| Some e ->
+					{ (Optimizer.mk_untyped_call "__array__" p [e]) with etype = v.v_type }
+
+			method mk_ref_access e v =
+				mk (TArray ({ e with etype = v.v_type }, mk (TConst (TInt 0l)) t.tint e.epos)) e.etype e.epos
+
+			method mk_init av v pos =
+				let elocal = mk (TLocal v) v.v_type pos in
+				let earray = { (Optimizer.mk_untyped_call "__array__" pos [elocal]) with etype = av.v_type } in
+				mk (TVar (av,Some earray)) t.tvoid pos
+		end
+	(* default implementation - use haxe array *)
+	| _ ->
+		object
+			method captured_type = t.tarray
+			method mk_ref v ve p =
+				mk (TArrayDecl (match ve with None -> [] | Some e -> [e])) v.v_type p
+			method mk_ref_access e v =
+				mk (TArray ({ e with etype = v.v_type }, mk (TConst (TInt 0l)) t.tint e.epos)) e.etype e.epos
+			method mk_init av v pos =
+				mk (TVar (av,Some (mk (TArrayDecl [mk (TLocal v) v.v_type pos]) av.v_type pos))) t.tvoid pos
+		end
+	in
+
+	let mk_var v used =
+		let v2 = alloc_var v.v_name (PMap.find v.v_id used) v.v_pos in
+		v2.v_meta <- v.v_meta;
+		v2
+	in
+
+	let rec wrap used e =
+		match e.eexpr with
+		| TVar (v,ve) ->
+			let v,ve =
+				if PMap.mem v.v_id used then
+					v, Some (impl#mk_ref v (Option.map (wrap used) ve) e.epos)
+				else
+					v, (match ve with None -> None | Some e -> Some (wrap used e))
+			 in
+			{ e with eexpr = TVar (v,ve) }
+		| TLocal v when PMap.mem v.v_id used ->
+			impl#mk_ref_access e v
+		| TFor (v,it,expr) when PMap.mem v.v_id used ->
+			let vtmp = mk_var v used in
+			let it = wrap used it in
+			let expr = wrap used expr in
+			mk (TFor (vtmp,it,Type.concat (impl#mk_init v vtmp e.epos) expr)) e.etype e.epos
+		| TTry (expr,catchs) ->
+			let catchs = List.map (fun (v,e) ->
+				let e = wrap used e in
+				try
+					let vtmp = mk_var v used in
+					vtmp, Type.concat (impl#mk_init v vtmp e.epos) e
+				with Not_found ->
+					v, e
+			) catchs in
+			mk (TTry (wrap used expr,catchs)) e.etype e.epos
+		| TFunction f ->
+			(*
+				list variables that are marked as used, but also used in that
+				function and which are not declared inside it !
+			*)
+			let fused = ref PMap.empty in
+			let tmp_used = ref used in
+			let rec browse = function
+				| Block f | Loop f | Function f -> f browse
+				| Use v | Assign v ->
+					if PMap.mem v.v_id !tmp_used then fused := PMap.add v.v_id v !fused;
+				| Declare v ->
+					tmp_used := PMap.remove v.v_id !tmp_used
+			in
+			local_usage browse e;
+			let vars = PMap.fold (fun v acc -> v :: acc) !fused [] in
+
+			(* in case the variable has been marked as used in a parallel scope... *)
+			let fexpr = ref (wrap used f.tf_expr) in
+			let fargs = List.map (fun (v,o) ->
+				if PMap.mem v.v_id used then
+					let vtmp = mk_var v used in
+					fexpr := Type.concat (impl#mk_init v vtmp e.epos) !fexpr;
+					vtmp, o
+				else
+					v, o
+			) f.tf_args in
+			let e = { e with eexpr = TFunction { f with tf_args = fargs; tf_expr = !fexpr } } in
+			(*
+				Create a new function scope to make sure that the captured loop variable
+				will not be overwritten in next loop iteration
+			*)
+			if com.config.pf_capture_policy = CPLoopVars then
+				(* We don't want to duplicate any variable declarations, so let's make copies (issue #3902). *)
+				let new_vars = List.map (fun v -> v.v_id,alloc_var v.v_name v.v_type v.v_pos) vars in
+				let rec loop e = match e.eexpr with
+					| TLocal v ->
+						begin try
+							let v' = List.assoc v.v_id new_vars in
+							v'.v_capture <- true;
+							{e with eexpr = TLocal v'}
+						with Not_found ->
+							e
+						end
+					| _ ->
+						Type.map_expr loop e
+				in
+				let e = loop e in
+				mk (TCall (
+					Codegen.mk_parent (mk (TFunction {
+						tf_args = List.map (fun (_,v) -> v, None) new_vars;
+						tf_type = e.etype;
+						tf_expr = mk_block (mk (TReturn (Some e)) e.etype e.epos);
+					}) (TFun (List.map (fun (_,v) -> v.v_name,false,v.v_type) new_vars,e.etype)) e.epos),
+					List.map (fun v -> mk (TLocal v) v.v_type e.epos) vars)
+				) e.etype e.epos
+			else
+				e
+		| _ ->
+			map_expr (wrap used) e
+
+	and do_wrap used e =
+		if PMap.is_empty used then
+			e
+		else
+			let used = PMap.map (fun v ->
+				let vt = v.v_type in
+				v.v_type <- impl#captured_type vt;
+				v.v_capture <- true;
+				vt
+			) used in
+			wrap used e
+
+	and out_loop e =
+		match e.eexpr with
+		| TFor _ | TWhile _ ->
+			(*
+				collect variables that are declared in loop but used in subfunctions
+			*)
+			let vars = ref PMap.empty in
+			let used = ref PMap.empty in
+			let depth = ref 0 in
+			let rec collect_vars in_loop = function
+				| Block f ->
+					let old = !vars in
+					f (collect_vars in_loop);
+					vars := old;
+				| Loop f ->
+					let old = !vars in
+					f (collect_vars true);
+					vars := old;
+				| Function f ->
+					incr depth;
+					f (collect_vars false);
+					decr depth;
+				| Declare v ->
+					if in_loop then vars := PMap.add v.v_id !depth !vars;
+				| Use v | Assign v ->
+					try
+						let d = PMap.find v.v_id !vars in
+						if d <> !depth then used := PMap.add v.v_id v !used;
+					with Not_found ->
+						()
+			in
+			local_usage (collect_vars false) e;
+			do_wrap !used e
+		| _ ->
+			map_expr out_loop e
+	and all_vars e =
+		let vars = ref PMap.empty in
+		let used = ref PMap.empty in
+		let assigned = ref PMap.empty in
+		let depth = ref 0 in
+		let rec collect_vars = function
+		| Block f ->
+			let old = !vars in
+			f collect_vars;
+			vars := old;
+		| Loop f ->
+			let old = !vars in
+			f collect_vars;
+			vars := old;
+		| Function f ->
+			incr depth;
+			f collect_vars;
+			decr depth;
+		| Declare v ->
+			vars := PMap.add v.v_id !depth !vars;
+		| Use v ->
+			(try
+				let d = PMap.find v.v_id !vars in
+				if d <> !depth then used := PMap.add v.v_id v !used;
+			with Not_found -> ())
+		| Assign v ->
+			(try
+				let d = PMap.find v.v_id !vars in
+				(* different depth - needs wrap *)
+				if d <> !depth then begin
+					used := PMap.add v.v_id v !used;
+					assigned := PMap.add v.v_id v !assigned;
+				end
+				(* same depth but assigned after being used on a different depth - needs wrap *)
+				else if PMap.mem v.v_id !used then
+					assigned := PMap.add v.v_id v !assigned;
+			with Not_found -> ())
+		in
+		local_usage collect_vars e;
+
+		(* mark all capture variables - also used in rename_local_vars at later stage *)
+		PMap.iter (fun _ v -> v.v_capture <- true) !used;
+
+		!assigned
+	in
+	let captured = all_vars e in
+	match com.config.pf_capture_policy with
+	| CPNone -> e
+	| CPWrapRef -> do_wrap captured e
+	| CPLoopVars -> out_loop e

+ 159 - 0
src/filters/defaultArguments.ml

@@ -0,0 +1,159 @@
+(*
+	The Haxe Compiler
+	Copyright (C) 2005-2017  Haxe Foundation
+
+	This program is free software; you can redistribute it and/or
+	modify it under the terms of the GNU General Public License
+	as published by the Free Software Foundation; either version 2
+	of the License, or (at your option) any later version.
+
+	This program is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+
+	You should have received a copy of the GNU General Public License
+	along with this program; if not, write to the Free Software
+	Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+*)
+open Common
+open Type
+open Codegen
+open Codegen.ExprBuilder
+
+(*
+	This Module Filter will go through all defined functions in all modules and change them
+	so they set all default arguments to be of a Nullable type, and adds the unroll from nullable to
+	the not-nullable type in the beginning of the function.
+*)
+
+let gen_check basic t nullable_var const pos =
+	let needs_cast t1 t2 =
+		let is_null t = match t with TAbstract ({a_path = ([],"Null")}, _) -> true | _ -> false in
+		(is_null t1) <> (is_null t2)
+	in
+
+	let const_t = const_type basic const t in
+	let const = mk (TConst const) const_t pos in
+	let const = if needs_cast t const_t then mk_cast const t pos else const in
+
+	let arg = make_local nullable_var pos in
+	let arg = if needs_cast t nullable_var.v_type then mk_cast arg t pos else arg in
+
+	let check = binop Ast.OpEq (make_local nullable_var pos) (null nullable_var.v_type pos) basic.tbool pos in
+	mk (TIf (check, const, Some arg)) t pos
+
+let add_opt com block pos (var,opt) =
+	match opt with
+	| None | Some TNull ->
+		(var,opt)
+	| Some (TString str) ->
+		block := Codegen.set_default com var (TString str) pos :: !block;
+		(var, opt)
+	| Some const ->
+		let basic = com.basic in
+		let nullable_var = alloc_var var.v_name (basic.tnull var.v_type) pos in
+		(* var v = (temp_var == null) ? const : cast temp_var; *)
+		let evar = mk (TVar(var, Some(gen_check basic var.v_type nullable_var const pos))) basic.tvoid pos in
+		block := evar :: !block;
+		(nullable_var, opt)
+
+let rec change_func com cl cf =
+	List.iter (change_func com cl) cf.cf_overloads;
+
+	match cf.cf_kind, follow cf.cf_type with
+	| Var _, _ | Method MethDynamic, _ ->
+		()
+	| _, TFun(args, ret) ->
+		let is_ctor = cf.cf_name = "new" in
+		let basic = com.basic in
+
+		let found = ref false in
+
+		let args = ref (List.map (fun (n,opt,t) ->
+			(n,opt, if opt then (found := true; basic.tnull t) else t)
+		) args) in
+
+		(match !found, cf.cf_expr with
+		| true, Some ({ eexpr = TFunction tf } as texpr) ->
+			let block = ref [] in
+			let tf_args = List.map (add_opt com block tf.tf_expr.epos) tf.tf_args in
+			let arg_assoc = List.map2 (fun (v,o) (v2,_) -> v,(v2,o) ) tf.tf_args tf_args in
+			let rec extract_super e = match e.eexpr with
+				| TBlock (({ eexpr = TCall ({ eexpr = TConst TSuper }, _) } as e2) :: tl) ->
+					e2, tl
+				| TBlock (hd :: tl) ->
+					let e2, tl2 = extract_super hd in
+					e2, tl2 @ tl
+				| _ ->
+					raise Not_found
+			in
+			let block =
+				try
+					if not is_ctor then raise Not_found;
+
+					(* issue #2570 *)
+					(* check if the class really needs the super as the first statement -
+					just to make sure we don't inadvertently break any existing code *)
+					let rec check cl =
+						if not (Meta.has Meta.HxGen cl.cl_meta) then
+							()
+						else match cl.cl_super with
+							| None ->
+								raise Not_found
+							| Some (cl, _) ->
+								check cl
+					in
+					check cl;
+
+					let super, tl = extract_super tf.tf_expr in
+					(match super.eexpr with
+					| TCall ({ eexpr = TConst TSuper } as e1, args) ->
+						(* any super argument will be replaced by an inlined version of the check *)
+						let found = ref false in
+						let rec replace_args e =
+							match e.eexpr with
+							| TLocal v ->
+								(try
+									let v2,o = List.assq v arg_assoc in
+									let o = match o with
+									| None -> raise Not_found
+									| Some o -> o
+									in
+									found := true;
+									gen_check com.basic v.v_type v2 o e.epos
+								with Not_found -> e)
+							| _ ->
+								Type.map_expr replace_args e
+						in
+						let args = List.map replace_args args in
+						{ tf.tf_expr with eexpr = TBlock ((if !found then { super with eexpr = TCall (e1, args) } else super) :: !block @ tl) }
+					| _ -> assert false)
+				with Not_found ->
+					Type.concat { tf.tf_expr with eexpr = TBlock !block; etype = basic.tvoid } tf.tf_expr
+			in
+
+			args := List.map (fun (v,s) -> (v.v_name, (s <> None), v.v_type)) tf_args;
+
+			let cf_type = TFun (!args, ret) in
+			cf.cf_expr <- Some { texpr with
+				eexpr = TFunction { tf with
+					tf_args = tf_args;
+					tf_expr = block
+				};
+				etype = cf_type
+			};
+			cf.cf_type <- cf_type
+
+		| _ -> ());
+		(if !found then cf.cf_type <- TFun(!args, ret))
+	| _, _ -> assert false
+
+let run com md =
+	match md with
+	| TClassDecl cl ->
+		let apply = change_func com cl in
+		List.iter apply cl.cl_ordered_fields;
+		List.iter apply cl.cl_ordered_statics;
+		Option.may apply cl.cl_constructor;
+	| _ -> ()

+ 69 - 0
src/filters/localUsage.ml

@@ -0,0 +1,69 @@
+(*
+	The Haxe Compiler
+	Copyright (C) 2005-2017  Haxe Foundation
+
+	This program is free software; you can redistribute it and/or
+	modify it under the terms of the GNU General Public License
+	as published by the Free Software Foundation; either version 2
+	of the License, or (at your option) any later version.
+
+	This program is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+
+	You should have received a copy of the GNU General Public License
+	along with this program; if not, write to the Free Software
+	Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ *)
+open Ast
+open Type
+
+type usage =
+	| Block of ((usage -> unit) -> unit)
+	| Loop of ((usage -> unit) -> unit)
+	| Function of ((usage -> unit) -> unit)
+	| Declare of tvar
+	| Use of tvar
+	| Assign of tvar
+
+let rec local_usage f e =
+	match e.eexpr with
+	| TBinop ((OpAssign | OpAssignOp _), { eexpr = TLocal v }, e2) ->
+		local_usage f e2;
+		f (Assign v)
+	| TUnop ((Increment | Decrement), _, { eexpr = TLocal v }) ->
+		f (Assign v)
+	| TLocal v ->
+		f (Use v)
+	| TVar (v,eo) ->
+		(match eo with None -> () | Some e -> local_usage f e);
+		f (Declare v);
+	| TFunction tf ->
+		let cc f =
+			List.iter (fun (v,_) -> f (Declare v)) tf.tf_args;
+			local_usage f tf.tf_expr;
+		in
+		f (Function cc)
+	| TBlock l ->
+		f (Block (fun f -> List.iter (local_usage f) l))
+	| TFor (v,it,e) ->
+		local_usage f it;
+		f (Loop (fun f ->
+			f (Declare v);
+			local_usage f e;
+		))
+	| TWhile _ ->
+		f (Loop (fun f ->
+			iter (local_usage f) e
+		))
+	| TTry (e,catchs) ->
+		local_usage f e;
+		List.iter (fun (v,e) ->
+			f (Block (fun f ->
+				f (Declare v);
+				local_usage f e;
+			))
+		) catchs;
+	| _ ->
+		iter (local_usage f) e

+ 187 - 0
src/filters/tryCatchWrapper.ml

@@ -0,0 +1,187 @@
+(*
+	The Haxe Compiler
+	Copyright (C) 2005-2017  Haxe Foundation
+
+	This program is free software; you can redistribute it and/or
+	modify it under the terms of the GNU General Public License
+	as published by the Free Software Foundation; either version 2
+	of the License, or (at your option) any later version.
+
+	This program is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+
+	You should have received a copy of the GNU General Public License
+	along with this program; if not, write to the Free Software
+	Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+*)
+open Globals
+open Common
+open Ast
+open Type
+open Codegen
+open Codegen.ExprBuilder
+
+(* ******************************************* *)
+(* Try / Catch + throw native types handling *)
+(* ******************************************* *)
+(*
+	Some languages/vm's do not support throwing any kind of value. For them, only
+	special kinds of objects can be thrown. Because of this, we must wrap some throw
+	statements with an expression, and also we must unwrap it on the catch() phase, and
+	maybe manually test with Std.is()
+*)
+
+(*
+	should_wrap : does the type should be wrapped? This of course works on the reverse way, so it tells us if the type should be unwrapped as well
+	wrap_throw : the wrapper for throw (throw expr->returning wrapped expression)
+	unwrap_expr : the other way around : given the catch var (maybe will need casting to wrapper_type) , return the unwrap expr
+	rethrow_expr : how to rethrow ane exception in the platform
+	catchall_type : the class used for catchall (e:Dynamic)
+	wrapper_type : the wrapper type, so we can test if exception is of type 'wrapper'
+	catch_map : maps the catch expression to include some intialization code (e.g. setting up Stack.exceptionStack)
+	gen_typecheck : generate Std.is (or similar) check expression for given expression and type
+*)
+let init com (should_wrap:t->bool) (wrap_throw:texpr->texpr) (unwrap_expr:texpr->texpr) (rethrow_expr:texpr->texpr) (catchall_type:t) (wrapper_type:t) (catch_map:tvar->texpr->texpr) (gen_typecheck:texpr->t->pos->texpr) =
+	let rec run e =
+		match e.eexpr with
+		| TThrow texpr when should_wrap texpr.etype ->
+			wrap_throw (run texpr)
+		| TTry (ttry, catches) ->
+			let nowrap_catches, must_wrap_catches, catchall = List.fold_left (fun (nowrap_catches, must_wrap_catches, catchall) (v, catch) ->
+				(* first we'll see if the type is Dynamic (catchall) *)
+				match follow v.v_type with
+				| TDynamic _ ->
+					assert (Option.is_none catchall);
+					(nowrap_catches, must_wrap_catches, Some(v, run catch))
+				(* see if we should unwrap it *)
+				| _ when should_wrap (follow v.v_type) ->
+					(nowrap_catches, (v,run catch) :: must_wrap_catches, catchall)
+				| _ ->
+					((v,catch_map v (run catch)) :: nowrap_catches, must_wrap_catches, catchall)
+			) ([], [], None) catches in
+
+			(* temp (?) fix for https://github.com/HaxeFoundation/haxe/issues/4134 *)
+			let must_wrap_catches = List.rev must_wrap_catches in
+
+			(*
+				1st catch all nowrap "the easy way"
+				2nd see if there are any must_wrap or catchall. If there is,
+					do a catchall first with a temp var.
+					then get catchall var (as dynamic) (or create one), and declare it = catchall exception
+					then test if it is of type wrapper_type. If it is, unwrap it
+					then start doing Std.is() tests for each catch type
+					if there is a catchall in the end, end with it. If there isn't, rethrow
+			*)
+			let dyn_catch = match catchall, must_wrap_catches with
+			| Some (v,c), _
+			| _, (v, c) :: _ ->
+				let pos = c.epos in
+
+				let temp_var = alloc_var "catchallException" catchall_type pos in
+				let temp_local = make_local temp_var pos in
+				let catchall_var = alloc_var "realException" t_dynamic pos in
+				let catchall_local = make_local catchall_var pos in
+
+				(* if it is of type wrapper_type, unwrap it *)
+				let catchall_expr = mk (TIf (gen_typecheck temp_local wrapper_type pos, unwrap_expr temp_local, Some temp_local)) t_dynamic pos in
+				let catchall_decl = mk (TVar (catchall_var, Some catchall_expr)) com.basic.tvoid pos in
+
+				let rec loop must_wrap_catches =
+					match must_wrap_catches with
+					| (vcatch,catch) :: tl ->
+						mk (TIf (gen_typecheck catchall_local vcatch.v_type catch.epos,
+							     mk (TBlock [(mk (TVar (vcatch, Some(mk_cast (* TODO: this should be a fast non-dynamic cast *) catchall_local vcatch.v_type pos))) com.basic.tvoid catch.epos); catch]) catch.etype catch.epos,
+							     Some (loop tl))
+						) catch.etype catch.epos
+					| [] ->
+						match catchall with
+						| Some (v,s) ->
+							Type.concat (mk (TVar (v, Some catchall_local)) com.basic.tvoid pos) s
+						| None ->
+							mk_block (rethrow_expr temp_local)
+				in
+				[(temp_var, catch_map temp_var { e with eexpr = TBlock [catchall_decl; loop must_wrap_catches] })]
+			| _ ->
+				[]
+			in
+			{ e with eexpr = TTry(run ttry, (List.rev nowrap_catches) @ dyn_catch) }
+		| _ ->
+			Type.map_expr run e
+	in
+	run
+
+let find_class com path =
+	let mt = List.find (fun mt -> match mt with TClassDecl c -> c.cl_path = path | _ -> false) com.types in
+	match mt with TClassDecl c -> c | _ -> assert false
+
+let configure_cs com =
+	let base_exception = find_class com (["cs";"system"], "Exception") in
+	let base_exception_t = TInst(base_exception, []) in
+	let hx_exception = find_class com (["cs";"internal";"_Exceptions"], "HaxeException") in
+	let hx_exception_t = TInst (hx_exception, []) in
+	let exc_cl = find_class com (["cs";"internal"],"Exceptions") in
+	let rec is_exception t =
+		match follow t with
+		| TInst (cl,_) -> is_parent base_exception cl
+		| _ -> false
+	in
+	let e_rethrow = mk (TIdent "__rethrow__") t_dynamic null_pos in
+	let should_wrap t = not (is_exception t) in
+	let wrap_throw expr =
+		match expr.eexpr with
+		| TIdent "__rethrow__" ->
+			make_throw expr expr.epos
+		| _ ->
+			let e_hxexception = make_static_this hx_exception expr.epos in
+			let e_wrap = fcall e_hxexception "wrap" [expr] base_exception_t expr.epos in
+			make_throw e_wrap expr.epos
+	in
+	let unwrap_expr local_to_unwrap = Codegen.field (mk_cast local_to_unwrap hx_exception_t local_to_unwrap.epos) "obj" t_dynamic local_to_unwrap.epos in
+	let rethrow_expr rethrow = make_throw e_rethrow rethrow.epos in
+	let catch_map v e =
+		let e_exc = make_static_this exc_cl e.epos in
+		let e_field = Codegen.field e_exc "exception" base_exception_t e.epos in
+		let e_setstack = binop OpAssign e_field (make_local v e.epos) v.v_type e.epos in
+		Type.concat e_setstack e
+	in
+	let std_cl = find_class com ([],"Std") in
+	let gen_typecheck e t pos =
+		let std = make_static_this std_cl pos in
+		let e_type = make_typeexpr (module_type_of_type t) pos in
+		fcall std "is" [e; e_type] com.basic.tbool pos
+	in
+	init com should_wrap wrap_throw unwrap_expr rethrow_expr base_exception_t hx_exception_t catch_map gen_typecheck
+
+let configure_java com =
+	let base_exception = find_class com (["java"; "lang"], "Throwable") in
+	let base_exception_t = TInst (base_exception, []) in
+	let hx_exception = find_class com (["java";"internal";"_Exceptions"], "HaxeException") in
+	let hx_exception_t = TInst (hx_exception, []) in
+	let exc_cl = find_class com (["java";"internal"],"Exceptions") in
+	let rec is_exception t =
+		match follow t with
+		| TInst (cl,_) -> is_parent base_exception cl
+		| _ -> false
+	in
+	let should_wrap t = not (is_exception t) in
+	let wrap_throw expr =
+		let e_hxexception = make_static_this hx_exception expr.epos in
+		let e_wrap = fcall e_hxexception "wrap" [expr] base_exception_t expr.epos in
+		make_throw e_wrap expr.epos
+	in
+	let unwrap_expr local_to_unwrap = Codegen.field (mk_cast local_to_unwrap hx_exception_t local_to_unwrap.epos) "obj" t_dynamic local_to_unwrap.epos in
+	let rethrow_expr exc = { exc with eexpr = TThrow exc } in
+	let catch_map v e =
+		let exc = make_static_this exc_cl e.epos in
+		let e_setstack = fcall exc "setException" [make_local v e.epos] com.basic.tvoid e.epos in
+		Type.concat e_setstack e;
+	in
+	let std_cl = find_class com ([],"Std") in
+	let gen_typecheck e t pos =
+		let std = make_static_this std_cl pos in
+		let e_type = make_typeexpr (module_type_of_type t) pos in
+		fcall std "is" [e; e_type] com.basic.tbool pos
+	in
+	init com should_wrap wrap_throw unwrap_expr rethrow_expr base_exception_t hx_exception_t catch_map gen_typecheck

+ 22 - 6
src/generators/codegen.ml

@@ -32,10 +32,23 @@ module ExprBuilder = struct
 		let ta = TAnon { a_fields = c.cl_statics; a_status = ref (Statics c) } in
 		mk (TTypeExpr (TClassDecl c)) ta p
 
+	let make_typeexpr mt pos =
+		let t =
+			match mt with
+			| TClassDecl c -> TAnon { a_fields = c.cl_statics; a_status = ref (Statics c) }
+			| TEnumDecl e -> TAnon { a_fields = PMap.empty; a_status = ref (EnumStatics e) }
+			| TAbstractDecl a -> TAnon { a_fields = PMap.empty; a_status = ref (AbstractStatics a) }
+			| _ -> assert false
+		in
+		mk (TTypeExpr mt) t pos
+
 	let make_static_field c cf p =
 		let e_this = make_static_this c p in
 		mk (TField(e_this,FStatic(c,cf))) cf.cf_type p
 
+	let make_throw e p =
+		mk (TThrow e) t_dynamic p
+
 	let make_int com i p =
 		mk (TConst (TInt (Int32.of_int i))) com.basic.tint p
 
@@ -73,6 +86,9 @@ let fcall e name el ret p =
 let mk_parent e =
 	mk (TParenthesis e) e.etype e.epos
 
+let mk_return e =
+	mk (TReturn (Some e)) t_dynamic e.epos
+
 let binop op a b t p =
 	mk (TBinop (op,a,b)) t p
 
@@ -228,7 +244,7 @@ let update_cache_dependencies t =
 			| Some t -> check_t m t
 			| _ -> ())
 		| TLazy f ->
-			check_t m (!f())
+			check_t m (lazy_type f)
 		| TDynamic t ->
 			if t == t_dynamic then
 				()
@@ -483,7 +499,7 @@ let rec is_volatile t =
 		| Some t -> is_volatile t
 		| _ -> false)
 	| TLazy f ->
-		is_volatile (!f())
+		is_volatile (lazy_type f)
 	| TType (t,tl) ->
 		(match t.t_path with
 		| _ -> is_volatile (apply_params t.t_params tl t.t_type))
@@ -499,7 +515,7 @@ let set_default ctx a c p =
 let bytes_serialize data =
 	let b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" in
 	let tbl = Array.init (String.length b64) (fun i -> String.get b64 i) in
-	Base64.str_encode ~tbl data
+	Bytes.unsafe_to_string (Base64.str_encode ~tbl data)
 
 (*
 	Tells if the constructor might be called without any issue whatever its parameters
@@ -510,12 +526,12 @@ let rec constructor_side_effects e =
 		true
 	| TField (_,FEnum _) ->
 		false
-	| TUnop _ | TArray _ | TField _ | TEnumParameter _ | TCall _ | TNew _ | TFor _ | TWhile _ | TSwitch _ | TReturn _ | TThrow _ ->
+	| TUnop _ | TArray _ | TField _ | TEnumParameter _ | TEnumIndex _ | TCall _ | TNew _ | TFor _ | TWhile _ | TSwitch _ | TReturn _ | TThrow _ ->
 		true
 	| TBinop _ | TTry _ | TIf _ | TBlock _ | TVar _
 	| TFunction _ | TArrayDecl _ | TObjectDecl _
 	| TParenthesis _ | TTypeExpr _ | TLocal _ | TMeta _
-	| TConst _ | TContinue | TBreak | TCast _ ->
+	| TConst _ | TContinue | TBreak | TCast _ | TIdent _ ->
 		try
 			Type.iter (fun e -> if constructor_side_effects e then raise Exit) e;
 			false;
@@ -839,7 +855,7 @@ let interpolate_code com code tl f_string f_expr p =
 				let expr = Array.get exprs (int_of_string n) in
 				f_expr expr;
 			with
-			| Failure "int_of_string" ->
+			| Failure _ ->
 				f_string ("{" ^ n ^ "}");
 			| Invalid_argument _ ->
 				err ("Out-of-bounds special parameter: " ^ n)

+ 33 - 29
src/generators/genas3.ml

@@ -247,6 +247,13 @@ let rec type_str ctx t p =
 	match t with
 	| TEnum _ | TInst _ when List.memq t ctx.local_types ->
 		"*"
+	| TAbstract ({a_path = [],"Null"},[t]) ->
+		(match follow t with
+		| TAbstract ({ a_path = [],"UInt" },_)
+		| TAbstract ({ a_path = [],"Int" },_)
+		| TAbstract ({ a_path = [],"Float" },_)
+		| TAbstract ({ a_path = [],"Bool" },_) -> "*"
+		| _ -> type_str ctx t p)
 	| TAbstract (a,pl) when not (Meta.has Meta.CoreType a.a_meta) ->
 		type_str ctx (Abstract.get_underlying_type a pl) p
 	| TAbstract (a,_) ->
@@ -291,19 +298,9 @@ let rec type_str ctx t p =
 	| TType (t,args) ->
 		(match t.t_path with
 		| [], "UInt" -> "uint"
-		| [] , "Null" ->
-			(match args with
-			| [t] ->
-				(match follow t with
-				| TAbstract ({ a_path = [],"UInt" },_)
-				| TAbstract ({ a_path = [],"Int" },_)
-				| TAbstract ({ a_path = [],"Float" },_)
-				| TAbstract ({ a_path = [],"Bool" },_) -> "*"
-				| _ -> type_str ctx t p)
-			| _ -> assert false);
 		| _ -> type_str ctx (apply_params t.t_params args t.t_type) p)
 	| TLazy f ->
-		type_str ctx ((!f)()) p
+		type_str ctx (lazy_type f) p
 
 let rec iter_switch_break in_switch e =
 	match e.eexpr with
@@ -338,7 +335,7 @@ let generate_resources infos =
 		let dir = (infos.com.file :: ["__res"]) in
 		create_dir [] dir;
 		let add_resource name data =
-			let name = Base64.str_encode name in
+			let name = Bytes.unsafe_to_string (Base64.str_encode name) in
 			let ch = open_out_bin (String.concat "/" (dir @ [name])) in
 			output_string ch data;
 			close_out ch
@@ -353,7 +350,7 @@ let generate_resources infos =
 		Hashtbl.iter (fun name _ ->
 			let varname = ("v" ^ (string_of_int !k)) in
 			k := !k + 1;
-			print ctx "\t\t[Embed(source = \"__res/%s\", mimeType = \"application/octet-stream\")]\n" (Base64.str_encode name);
+			print ctx "\t\t[Embed(source = \"__res/%s\", mimeType = \"application/octet-stream\")]\n" (Bytes.unsafe_to_string (Base64.str_encode name));
 			print ctx "\t\tpublic static var %s:Class;\n" varname;
 			inits := ("list[\"" ^ Ast.s_escape name ^ "\"] = " ^ varname ^ ";") :: !inits;
 		) infos.com.resources;
@@ -433,32 +430,32 @@ let rec gen_call ctx e el r =
 		spr ctx "(";
 		concat ctx "," (gen_value ctx) el;
 		spr ctx ")";
-	| TLocal { v_name = "__is__" } , [e1;e2] ->
+	| TIdent "__is__" , [e1;e2] ->
 		gen_value ctx e1;
 		spr ctx " is ";
 		gen_value ctx e2;
-	| TLocal { v_name = "__in__" } , [e1;e2] ->
+	| TIdent "__in__" , [e1;e2] ->
 		spr ctx "(";
 		gen_value ctx e1;
 		spr ctx " in ";
 		gen_value ctx e2;
 		spr ctx ")"
-	| TLocal { v_name = "__as__" }, [e1;e2] ->
+	| TIdent "__as__", [e1;e2] ->
 		gen_value ctx e1;
 		spr ctx " as ";
 		gen_value ctx e2;
-	| TLocal { v_name = "__int__" }, [e] ->
+	| TIdent "__int__", [e] ->
 		spr ctx "int(";
 		gen_value ctx e;
 		spr ctx ")";
-	| TLocal { v_name = "__float__" }, [e] ->
+	| TIdent "__float__", [e] ->
 		spr ctx "Number(";
 		gen_value ctx e;
 		spr ctx ")";
-	| TLocal { v_name = "__typeof__" }, [e] ->
+	| TIdent "__typeof__", [e] ->
 		spr ctx "typeof ";
 		gen_value ctx e;
-	| TLocal { v_name = "__keys__" }, [e] ->
+	| TIdent "__keys__", [e] ->
 		let ret = (match ctx.in_value with None -> assert false | Some r -> r) in
 		print ctx "%s = new Array()" ret.v_name;
 		newline ctx;
@@ -466,7 +463,7 @@ let rec gen_call ctx e el r =
 		print ctx "for(var %s : String in " tmp;
 		gen_value ctx e;
 		print ctx ") %s.push(%s)" ret.v_name tmp;
-	| TLocal { v_name = "__hkeys__" }, [e] ->
+	| TIdent "__hkeys__", [e] ->
 		let ret = (match ctx.in_value with None -> assert false | Some r -> r) in
 		print ctx "%s = new Array()" ret.v_name;
 		newline ctx;
@@ -474,7 +471,7 @@ let rec gen_call ctx e el r =
 		print ctx "for(var %s : String in " tmp;
 		gen_value ctx e;
 		print ctx ") %s.push(%s.substr(1))" ret.v_name tmp;
-	| TLocal { v_name = "__foreach__" }, [e] ->
+	| TIdent "__foreach__", [e] ->
 		let ret = (match ctx.in_value with None -> assert false | Some r -> r) in
 		print ctx "%s = new Array()" ret.v_name;
 		newline ctx;
@@ -482,22 +479,22 @@ let rec gen_call ctx e el r =
 		print ctx "for each(var %s : * in " tmp;
 		gen_value ctx e;
 		print ctx ") %s.push(%s)" ret.v_name tmp;
-	| TLocal { v_name = "__new__" }, e :: args ->
+	| TIdent "__new__", e :: args ->
 		spr ctx "new ";
 		gen_value ctx e;
 		spr ctx "(";
 		concat ctx "," (gen_value ctx) args;
 		spr ctx ")";
-	| TLocal { v_name = "__delete__" }, [e;f] ->
+	| TIdent "__delete__", [e;f] ->
 		spr ctx "delete(";
 		gen_value ctx e;
 		spr ctx "[";
 		gen_value ctx f;
 		spr ctx "]";
 		spr ctx ")";
-	| TLocal { v_name = "__unprotect__" }, [e] ->
+	| TIdent "__unprotect__", [e] ->
 		gen_value ctx e
-	| TLocal { v_name = "__vector__" }, [e] ->
+	| TIdent "__vector__", [e] ->
 		spr ctx (type_str ctx r e.epos);
 		spr ctx "(";
 		gen_value ctx e;
@@ -595,7 +592,7 @@ and gen_expr ctx e =
 		gen_constant ctx e.epos c
 	| TLocal v ->
 		spr ctx (s_ident v.v_name)
-	| TArray ({ eexpr = TLocal { v_name = "__global__" } },{ eexpr = TConst (TString s) }) ->
+	| TArray ({ eexpr = TIdent "__global__" },{ eexpr = TConst (TString s) }) ->
 		let path = Ast.parse_path s in
 		spr ctx (s_path ctx false path e.epos)
 	| TArray (e1,e2) ->
@@ -633,6 +630,9 @@ and gen_expr ctx e =
 		gen_expr ctx e1;
 		spr ctx ")";
 		gen_field_access ctx e1.etype (field_name s)
+	| TEnumIndex e ->
+		gen_value ctx e;
+		print ctx ".index";
 	| TEnumParameter (e,_,i) ->
 		gen_value ctx e;
 		print ctx ".params[%i]" i;
@@ -811,6 +811,8 @@ and gen_expr ctx e =
 		end
 	| TCast (e1,Some t) ->
 		gen_expr ctx (Codegen.default_cast ctx.inf.com e1 t e.etype e.epos)
+	| TIdent s ->
+		spr ctx s
 
 and gen_block_element ctx e = match e.eexpr with
 	| TObjectDecl fl ->
@@ -872,7 +874,7 @@ and gen_value ctx e =
 		)
 	in
 	match e.eexpr with
-	| TCall ({ eexpr = TLocal { v_name = "__keys__" } },_) | TCall ({ eexpr = TLocal { v_name = "__hkeys__" } },_) ->
+	| TCall ({ eexpr = TIdent "__keys__" },_) | TCall ({ eexpr = TIdent "__hkeys__" },_) ->
 		let v = value true in
 		gen_expr ctx e;
 		v()
@@ -882,6 +884,7 @@ and gen_value ctx e =
 	| TBinop _
 	| TField _
 	| TEnumParameter _
+	| TEnumIndex _
 	| TTypeExpr _
 	| TParenthesis _
 	| TObjectDecl _
@@ -889,7 +892,8 @@ and gen_value ctx e =
 	| TCall _
 	| TNew _
 	| TUnop _
-	| TFunction _ ->
+	| TFunction _
+	| TIdent _ ->
 		gen_expr ctx e
 	| TMeta (_,e1) ->
 		gen_value ctx e1

+ 158 - 9021
src/generators/gencommon.ml

@@ -15,7 +15,7 @@
 	You should have received a copy of the GNU General Public License
 	along with this program; if not, write to the Free Software
 	Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- *)
+*)
 
 (*
 	Gen Common API
@@ -63,81 +63,77 @@ open ExtString
 open Codegen
 open Overloads
 
-let alloc_var n t = alloc_var n t null_pos
-
-let debug_type_ctor = function
-	| TMono _ -> "TMono"
-	| TEnum _ -> "TEnum"
-	| TInst _ -> "TInst"
-	| TType _ -> "TType"
-	| TFun _ -> "TFun"
-	| TAnon _ -> "TAnon"
-	| TDynamic _ -> "TDynamic"
-	| TLazy _ -> "TLazy"
-	| TAbstract _ -> "TAbstract"
-
-let debug_type = (s_type (print_context()))
-
-let debug_expr = s_expr debug_type
+(* ******************************************* *)
+(* common helpers *)
+(* ******************************************* *)
 
 let rec like_float t =
 	match follow t with
-		| TAbstract({ a_path = ([], "Float") },[])
-		| TAbstract({ a_path = ([], "Int") },[]) -> true
-		| TAbstract({ a_path = (["cs"], "Pointer") },_) -> false
-		| TAbstract(a, _) -> List.exists (fun t -> like_float t) a.a_from || List.exists (fun t -> like_float t) a.a_to
-		| _ -> false
+	| TAbstract ({ a_path = ([], "Float") }, [])
+	| TAbstract ({ a_path = ([], "Int") }, []) ->
+		true
+	| TAbstract ({ a_path = (["cs"], "Pointer") }, _) ->
+		false
+	| TAbstract (a, _) ->
+		List.exists like_float a.a_from || List.exists like_float a.a_to
+	| _ ->
+		false
 
 let rec like_int t =
 	match follow t with
-		| TAbstract({ a_path = ([], "Int") },[]) -> true
-		| TAbstract({ a_path = (["cs"], "Pointer") },_) -> false
-		| TAbstract(a, _) -> List.exists (fun t -> like_int t) a.a_from || List.exists (fun t -> like_int t) a.a_to
-		| _ -> false
+	| TAbstract ({ a_path = ([], "Int") }, []) ->
+		true
+	| TAbstract ({ a_path = (["cs"], "Pointer") }, _) ->
+		false
+	| TAbstract (a, _) ->
+		List.exists like_int a.a_from || List.exists like_int a.a_to
+	| _ ->
+		false
 
 let rec like_i64 t =
 	match follow t with
-		| TAbstract({ a_path = (["cs"], "Int64") },[])
-		| TAbstract({ a_path = (["cs"], "UInt64") },[])
-		| TAbstract({ a_path = (["java"], "Int64") },[])
-		| TAbstract({ a_path = (["haxe"], "Int64") },[]) -> true
-		| TAbstract(a, _) -> List.exists (fun t -> like_i64 t) a.a_from || List.exists (fun t -> like_i64 t) a.a_to
-		| _ -> false
+	| TAbstract ({ a_path = (["cs"], ("Int64" | "UInt64")) }, [])
+	| TAbstract ({ a_path = (["java"], "Int64") }, [])
+	| TAbstract ({ a_path = (["haxe"], "Int64") }, []) ->
+		true
+	| TAbstract (a, _) ->
+		List.exists like_i64 a.a_from || List.exists like_i64 a.a_to
+	| _ ->
+		false
 
 let follow_once t =
 	match t with
 	| TMono r ->
 		(match !r with
 		| Some t -> t
-		| _ -> t_dynamic (* avoid infinite loop / should be the same in this context *))
+		| _ -> t_dynamic) (* avoid infinite loop / should be the same in this context *)
 	| TLazy f ->
-		!f()
+		lazy_type f
 	| TType (t,tl) ->
 		apply_params t.t_params tl t.t_type
-	| _ -> t
+	| TAbstract({a_path = [],"Null"},[t]) ->
+		t
+	| _ ->
+		t
 
-let t_empty = TAnon({ a_fields = PMap.empty; a_status = ref (Closed) })
+let t_empty = TAnon({ a_fields = PMap.empty; a_status = ref Closed })
 
-let tmp_count = ref 0
+let alloc_var n t = Type.alloc_var n t null_pos
 
-let reset_temps () = tmp_count := 0
+let mk_local = ExprBuilder.make_local
 
 (* the undefined is a special var that works like null, but can have special meaning *)
-let v_undefined = alloc_var "__undefined__" t_dynamic
-
-let undefined pos = ExprBuilder.make_local v_undefined pos
+let undefined =
+	(fun pos -> mk (TIdent "__undefined__") t_dynamic pos)
 
 let path_of_md_def md_def =
 	match md_def.m_types with
 		| [TClassDecl c] -> c.cl_path
 		| _ -> md_def.m_path
 
+let debug_type t = (s_type (print_context())) t
+let debug_expr = s_expr debug_type
 
-(* ******************************************* *)
-(* common helpers *)
-(* ******************************************* *)
-
-let assertions = false (* when assertions == true, many assertions will be made to guarantee the quality of the data input *)
 let debug_mode = ref false
 let trace s = if !debug_mode then print_endline s else ()
 let timer name = if !debug_mode then Common.timer name else fun () -> ()
@@ -147,39 +143,13 @@ let is_string t =
 	| TInst({ cl_path = ([], "String") }, []) -> true
 	| _ -> false
 
-(* helper function for creating Anon types of class / enum modules *)
-
-let anon_of_classtype cl =
-	TAnon {
-		a_fields = cl.cl_statics;
-		a_status = ref (Statics cl)
-	}
-
-let anon_of_enum e =
-	TAnon {
-		a_fields = PMap.empty;
-		a_status = ref (EnumStatics e)
-	}
-
-let anon_of_abstract a =
-	TAnon {
-		a_fields = PMap.empty;
-		a_status = ref (AbstractStatics a)
-	}
-
-let anon_of_mt mt = match mt with
-	| TClassDecl cl -> anon_of_classtype cl
-	| TEnumDecl e -> anon_of_enum e
-	| TAbstractDecl a -> anon_of_abstract a
-	| _ -> assert false
-
 let anon_class t =
 	match follow t with
 	| TAnon anon ->
 		(match !(anon.a_status) with
-		| Statics cl -> Some(TClassDecl cl)
-		| EnumStatics e -> Some(TEnumDecl e)
-		| AbstractStatics a -> Some(TAbstractDecl a)
+		| Statics cl -> Some (TClassDecl cl)
+		| EnumStatics e -> Some (TEnumDecl e)
+		| AbstractStatics a -> Some (TAbstractDecl a)
 		| _ -> None)
 	| _ -> None
 
@@ -194,24 +164,17 @@ let anon_class t =
 			| Statics cl -> TClassDecl cl
 			| AbstractStatics a -> TAbstractDecl a
 			| _ -> assert false)
-	| TLazy f -> t_to_md (!f())
+	| TLazy f -> t_to_md (lazy_type f)
 	| TMono r -> (match !r with | Some t -> t_to_md t | None -> assert false)
 	| _ -> assert false
 
-let get_cl mt = match mt with | TClassDecl cl -> cl | _ -> failwith (Printf.sprintf "Unexpected module type (class expected) for %s: %s" (s_type_path (t_path mt)) (s_module_type_kind mt))
-
-let get_abstract mt = match mt with | TAbstractDecl a -> a | _ -> failwith (Printf.sprintf "Unexpected module type (abstract expected) for %s: %s" (s_type_path (t_path mt)) (s_module_type_kind mt))
-
-let get_tdef mt = match mt with | TTypeDecl t -> t | _ -> assert false
-
-let mk_mt_access mt pos = { eexpr = TTypeExpr(mt); etype = anon_of_mt mt; epos = pos }
 
-let mk_local = ExprBuilder.make_local
+let get_cl mt = match mt with TClassDecl cl -> cl | _ -> failwith (Printf.sprintf "Unexpected module type (class expected) for %s: %s" (s_type_path (t_path mt)) (s_module_type_kind mt))
+let get_abstract mt = match mt with TAbstractDecl a -> a | _ -> failwith (Printf.sprintf "Unexpected module type (abstract expected) for %s: %s" (s_type_path (t_path mt)) (s_module_type_kind mt))
 
-(* this function is used by CastDetection module *)
 let get_fun t =
 	match follow t with
-	| TFun(r1,r2) -> (r1,r2)
+	| TFun (args, ret) -> args, ret
 	| t -> (trace (debug_type t)); assert false
 
 let mk_cast t e = Type.mk_cast e t e.epos
@@ -223,9 +186,12 @@ let mk_castfast t e = { e with eexpr = TCast(e, Some (TClassDecl null_class)); e
 
 let mk_static_field_access_infer cl field pos params =
 	try
-		let cf = (PMap.find field cl.cl_statics) in
-		{ eexpr = TField(ExprBuilder.make_static_this cl pos, FStatic(cl, cf)); etype = (if params = [] then cf.cf_type else apply_params cf.cf_params params cf.cf_type); epos = pos }
-	with | Not_found -> failwith ("Cannot find field " ^ field ^ " in type " ^ (s_type_path cl.cl_path))
+		let e_type = ExprBuilder.make_static_this cl pos in
+		let cf = PMap.find field cl.cl_statics in
+		let t = if params = [] then cf.cf_type else apply_params cf.cf_params params cf.cf_type in
+		mk (TField(e_type, FStatic(cl, cf))) t pos
+	with Not_found ->
+		failwith ("Cannot find field " ^ field ^ " in class " ^ (s_type_path cl.cl_path))
 
 let mk_static_field_access cl field fieldt pos =
 	{ (mk_static_field_access_infer cl field pos []) with etype = fieldt }
@@ -295,14 +261,13 @@ let indent = ref []
 
 (* the rule dispatcher is the primary way to deal with distributed "plugins" *)
 (* we will define rules that will form a distributed / extensible match system *)
-class ['tp, 'ret] rule_dispatcher name ignore_not_found =
+class ['tp, 'ret] rule_dispatcher name =
 	object(self)
 	val tbl = Hashtbl.create 16
 	val mutable keys = []
 	val names = Hashtbl.create 16
-	val mutable temp = 0
 
-	method add ?(name : string option) (* name helps debugging *) ?(priority : priority = PZero) (rule : 'tp->'ret option) =
+	method add (name : string) (* name helps debugging *) (priority : priority) (rule : 'tp->'ret option) =
 		let p = match priority with
 			| PFirst -> infinity
 			| PLast -> neg_infinity
@@ -317,10 +282,6 @@ class ['tp, 'ret] rule_dispatcher name ignore_not_found =
 			keys <- List.sort (fun x y -> - (compare x y)) keys;
 			q
 		end else Hashtbl.find tbl p in
-		let name = match name with
-			| None -> temp <- temp + 1; "$_" ^ (string_of_int temp)
-			| Some s -> s
-		in
 		(if Hashtbl.mem names name then raise (DuplicateName(name)));
 		Hashtbl.add names name q;
 
@@ -329,37 +290,10 @@ class ['tp, 'ret] rule_dispatcher name ignore_not_found =
 	method describe =
 		Hashtbl.iter (fun s _ -> (trace s)) names;
 
-	method remove (name : string) =
-		if Hashtbl.mem names name then begin
-			let q = Hashtbl.find names name in
-			let q_temp = Stack.create () in
-			Stack.iter (function
-				| (n, _) when n = name -> ()
-				| _ as r -> Stack.push r q_temp
-			) q;
-
-			Stack.clear q;
-			Stack.iter (fun r -> Stack.push r q) q_temp;
-
-			Hashtbl.remove names name;
-			true
-		end else false
-
 	method run_f tp = get (self#run tp)
 
-	method did_run tp = is_some (self#run tp)
-
-	method get_list =
-		let ret = ref [] in
-		List.iter (fun key ->
-			let q = Hashtbl.find tbl key in
-			Stack.iter (fun (_, rule) -> ret := rule :: !ret) q
-		) keys;
-
-		List.rev !ret
-
 	method run_from (priority:float) (tp:'tp) : 'ret option =
-		let ok = ref ignore_not_found in
+		let ok = ref false in
 		let ret = ref None in
 		indent := "\t" :: !indent;
 
@@ -391,32 +325,50 @@ class ['tp, 'ret] rule_dispatcher name ignore_not_found =
 end;;
 
 (* this is a special case where tp = tret and you stack their output as the next's input *)
-class ['tp] rule_map_dispatcher name =
-	object(self)
-	inherit ['tp, 'tp] rule_dispatcher name true as super
+class ['tp] rule_map_dispatcher name = object(self)
+	val tbl = Hashtbl.create 16
+	val mutable keys = []
+	val names = Hashtbl.create 16
 
-	method run_f tp = get (self#run tp)
+	method add (name : string) (* name helps debugging *) (priority : priority) (rule : 'tp->'tp) =
+		let p = match priority with
+			| PFirst -> infinity
+			| PLast -> neg_infinity
+			| PZero -> 0.0
+			| PCustom i -> i
+		in
+		let q = if not (Hashtbl.mem tbl p) then begin
+			let q = Stack.create() in
+			Hashtbl.add tbl p q;
+			keys <- p :: keys;
+			keys <- List.sort (fun x y -> - (compare x y)) keys;
+			q
+		end else Hashtbl.find tbl p in
+		if Hashtbl.mem names name then raise (DuplicateName name);
+		Hashtbl.add names name q;
 
-	method run_from (priority:float) (tp:'tp) : 'ret option =
-		let cur = ref tp in
-		(try begin
-			List.iter (fun key ->
+		Stack.push (name, rule) q
 
-				if key < priority then begin
-					let q = Hashtbl.find tbl key in
-					Stack.iter (fun (n, rule) ->
-						trace ("running rule " ^ n);
-						let t = if !debug_mode then Common.timer [("rule map dispatcher rule: " ^ n)] else fun () -> () in
-						let r = rule(!cur) in
-						t();
-						if is_some r then begin cur := get r end
-					) q
-				end
-			) keys
+	method describe =
+		Hashtbl.iter (fun s _ -> (trace s)) names;
 
-		end with Exit -> ());
-		Some (!cur)
+	method run (tp:'tp) : 'tp =
+		self#run_from infinity tp
 
+	method run_from (priority:float) (tp:'tp) : 'tp =
+		let cur = ref tp in
+		List.iter (fun key ->
+			if key < priority then begin
+				let q = Hashtbl.find tbl key in
+				Stack.iter (fun (n, rule) ->
+					trace ("running rule " ^ n);
+					let t = if !debug_mode then Common.timer [("rule map dispatcher rule: " ^ n)] else fun () -> () in
+					cur := rule !cur;
+					t();
+				) q
+			end
+		) keys;
+		!cur
 end;;
 
 
@@ -430,13 +382,6 @@ type generator_ctx =
 
 	gtools : gen_tools;
 
-	(*
-		configurable function that receives a desired name and makes it "internal", doing the best
-		to ensure that it will not be called from outside.
-		To avoid name clashes between internal names, user must specify two strings: a "namespace" and the name itself
-	 *)
-	gmk_internal_name : string->string->string;
-
 	(*
 		module filters run before module filters and they should generate valid haxe syntax as a result.
 		Module filters shouldn't go through the expressions as it adds an unnecessary burden to the GC,
@@ -507,10 +452,6 @@ type generator_ctx =
 	mutable gcurrent_classfield : tclass_field option;
 
 	(* events *)
-	(* is executed once every new classfield *)
-	mutable gon_classfield_start : (unit -> unit) list;
-	(* is executed once every new module type *)
-	mutable gon_new_module_type : (unit -> unit) list;
 	(* after module filters ended *)
 	mutable gafter_mod_filters_ended : (unit -> unit) list;
 	(* after expression filters ended *)
@@ -546,7 +487,7 @@ type generator_ctx =
 	mutable gallow_tp_dynamic_conversion : bool;
 
 	(* internal apis *)
-	(* param_func_call : used by TypeParams and CastDetection *)
+	(* param_func_call : used by RealTypeParams and CastDetection *)
 	mutable gparam_func_call : texpr->texpr->tparams->texpr list->texpr;
 	(* does it already have a type parameter cast handler? This is used by CastDetect to know if it should handle type parameter casts *)
 	mutable ghas_tparam_cast_handler : bool;
@@ -587,14 +528,20 @@ and gen_tools =
 	mutable r_create_empty : tclass->tparams->pos->texpr;
 }
 
-let get_type types path =
-	List.find (fun md -> match md with
-		| TClassDecl cl when cl.cl_path = path -> true
-		| TEnumDecl e when e.e_path = path -> true
-		| TTypeDecl t when t.t_path = path -> true
-		| TAbstractDecl a when a.a_path = path -> true
-		| _ -> false
-	) types
+(**
+	Function that receives a desired name and makes it "internal", doing the best to ensure that it will not be called from outside.
+	To avoid name clashes between internal names, user must specify two strings: a "namespace" and the name itself
+*)
+let mk_internal_name ns name = Printf.sprintf "__%s_%s" ns name
+
+let mk_temp, reset_temps =
+	let tmp_count = ref 0 in
+	(fun name t ->
+		incr tmp_count;
+		let name = mk_internal_name "temp" (name ^ (string_of_int !tmp_count)) in
+		alloc_var name t
+	),
+	(fun () -> tmp_count := 0)
 
 let new_ctx con =
 	let types = Hashtbl.create (List.length con.types) in
@@ -606,7 +553,11 @@ let new_ctx con =
 			| TAbstractDecl a -> Hashtbl.add types a.a_path mt
 	) con.types;
 
-	let cl_dyn = match get_type con.types ([], "Dynamic") with
+	let get_type path =
+		List.find (fun md -> (t_path md) = path) con.types
+	in
+
+	let cl_dyn = match get_type  ([], "Dynamic") with
 		| TClassDecl c -> c
 		| TAbstractDecl a ->
 				mk_class a.a_module ([], "Dynamic") a.a_pos null_pos
@@ -616,8 +567,8 @@ let new_ctx con =
 	let rec gen = {
 		gcon = con;
 		gclasses = {
-			cl_reflect = get_cl (get_type con.types ([], "Reflect"));
-			cl_type = get_cl (get_type con.types ([], "Type"));
+			cl_reflect = get_cl (get_type ([], "Reflect"));
+			cl_type = get_cl (get_type ([], "Type"));
 			cl_dyn = cl_dyn;
 
 			nativearray = (fun _ -> assert false);
@@ -643,11 +594,10 @@ let new_ctx con =
 
 			r_create_empty = (fun _ _ pos -> gen.gcon.error "r_create_empty implementation is not provided" pos; assert false);
 		};
-		gmk_internal_name = (fun ns s -> sprintf "__%s_%s" ns s);
 		gexpr_filters = new rule_map_dispatcher "gexpr_filters";
 		gmodule_filters = new rule_map_dispatcher "gmodule_filters";
 		gsyntax_filters = new rule_map_dispatcher "gsyntax_filters";
-		gfollow = new rule_dispatcher "gfollow" false;
+		gfollow = new rule_dispatcher "gfollow";
 		gtypes = types;
 		gtypes_list = con.types;
 		gmodules = con.modules;
@@ -656,7 +606,7 @@ let new_ctx con =
 		ghandle_cast = (fun to_t from_t e -> mk_cast to_t e);
 		gon_unsafe_cast = (fun t t2 pos -> (gen.gcon.warning ("Type " ^ (debug_type t2) ^ " is being cast to the unrelated type " ^ (s_type (print_context()) t)) pos));
 		gneeds_box = (fun t -> false);
-		gspecial_needs_cast = (fun to_t from_t -> true);
+		gspecial_needs_cast = (fun to_t from_t -> false);
 		gsupported_conversions = Hashtbl.create 0;
 
 		gadd_type = (fun md should_filter ->
@@ -675,8 +625,6 @@ let new_ctx con =
 		gcurrent_class = None;
 		gcurrent_classfield = None;
 
-		gon_classfield_start = [];
-		gon_new_module_type = [];
 		gafter_mod_filters_ended = [];
 		gafter_expr_filters_ended = [];
 		gafter_filters_ended = [];
@@ -707,14 +655,15 @@ let init_ctx gen =
 			| Some t -> follow_f t
 			| _ -> Some t)
 		| TLazy f ->
-			follow_f (!f())
+			follow_f (lazy_type f)
 		| TType (t,tl) ->
 			follow_f (apply_params t.t_params tl t.t_type)
+		| TAbstract({a_path = [],"Null"},[t]) ->
+			follow_f t
 		| _ -> Some t
 	in
-	gen.gfollow#add ~name:"final" ~priority:PLast follow
+	gen.gfollow#add "final" PLast follow
 
-(* run_follow (gen:generator_ctx) (t:t) *)
 let run_follow gen = gen.gfollow#run_f
 
 let reorder_modules gen =
@@ -739,13 +688,10 @@ let run_filters_from gen t filters =
 		gen.gcurrent_path <- c.cl_path;
 		gen.gcurrent_class <- Some(c);
 
-		List.iter (fun fn -> fn()) gen.gon_new_module_type;
-
 		gen.gcurrent_classfield <- None;
 		let rec process_field f =
 			reset_temps();
 			gen.gcurrent_classfield <- Some(f);
-			List.iter (fun fn -> fn()) gen.gon_classfield_start;
 
 			trace f.cf_name;
 			(match f.cf_expr with
@@ -775,12 +721,12 @@ let run_filters gen =
 	(* first of all, we have to make sure that the filters won't trigger a major Gc collection *)
 	let t = Common.timer ["gencommon_filters"] in
 	(if Common.defined gen.gcon Define.GencommonDebug then debug_mode := true else debug_mode := false);
-	let run_filters filter =
+	let run_filters (filter : texpr rule_map_dispatcher) =
 		let rec loop acc mds =
 			match mds with
 				| [] -> acc
 				| md :: tl ->
-					let filters = [ filter#run_f ] in
+					let filters = [ filter#run ] in
 					let added_types = ref [] in
 					gen.gadd_to_module <- (fun md_type priority ->
 						gen.gtypes_list <- md_type :: gen.gtypes_list;
@@ -790,7 +736,7 @@ let run_filters gen =
 					run_filters_from gen md filters;
 
 					let added_types = List.map (fun (t,p) ->
-						run_filters_from gen t [ fun e -> get (filter#run_from p e) ];
+						run_filters_from gen t [ fun e -> filter#run_from p e ];
 						if Hashtbl.mem gen.gtypes (t_path t) then begin
 							let rec loop i =
 								let p = t_path t in
@@ -814,7 +760,7 @@ let run_filters gen =
 		List.rev (loop [] gen.gtypes_list)
 	in
 
-	let run_mod_filter filter =
+	let run_mod_filter (filter : module_type rule_map_dispatcher) =
 		let last_add_to_module = gen.gadd_to_module in
 		let added_types = ref [] in
 		gen.gadd_to_module <- (fun md_type priority ->
@@ -830,13 +776,11 @@ let run_filters gen =
 							gen.gcurrent_class <- Some c
 						| _ ->
 							gen.gcurrent_class <- None);
-					let new_hd = filter#run_f hd in
+					let new_hd = filter#run hd in
 
 					let added_types_new = !added_types in
 					added_types := [];
-					let added_types = List.map (fun (t,p) ->
-						get (filter#run_from p t)
-					) added_types_new in
+					let added_types = List.map (fun (t,p) -> filter#run_from p t) added_types_new in
 
 					loop ( added_types @ (new_hd :: processed) ) tl
 				| [] ->
@@ -856,7 +800,6 @@ let run_filters gen =
 	gen.gadd_to_module <- last_add_to_module;
 
 	List.iter (fun fn -> fn()) gen.gafter_expr_filters_ended;
-	(* Codegen.post_process gen.gtypes_list [gen.gexpr_filters#run_f]; *)
 	gen.gtypes_list <- run_filters gen.gsyntax_filters;
 	List.iter (fun fn -> fn()) gen.gafter_filters_ended;
 
@@ -1042,28 +985,16 @@ let get_real_fun gen t =
 	| TFun(args,t) -> TFun(List.map (fun (n,o,t) -> n,o,gen.greal_type t) args, gen.greal_type t)
 	| _ -> t
 
-let mk_return e = { eexpr = TReturn (Some e); etype = e.etype; epos = e.epos }
-
-let mk_temp gen name t =
-	incr tmp_count;
-	let name = gen.gmk_internal_name "temp" (name ^ (string_of_int !tmp_count)) in
-	alloc_var name t
-
-let v_nativearray = alloc_var "__array__" t_dynamic
 let mk_nativearray_decl gen t el pos =
-	{
-		eexpr = TCall(mk_local v_nativearray pos, el);
-		etype = gen.gclasses.nativearray t;
-		epos = pos;
-	}
+	mk (TCall (mk (TIdent "__array__") t_dynamic pos, el)) (gen.gclasses.nativearray t) pos
 
-let ensure_local gen block name e =
+let ensure_local com block name e =
 	match e.eexpr with
-		| TLocal _ -> e
-		| _ ->
-			let var = mk_temp gen name e.etype in
-			block := { e with eexpr = TVar(var, Some e); etype = gen.gcon.basic.tvoid; } :: !block;
-			{ e with eexpr = TLocal var }
+	| TLocal _ -> e
+	| _ ->
+		let v = mk_temp name e.etype in
+		block := (mk (TVar (v, Some e)) com.basic.tvoid e.epos) :: !block;
+		mk_local v e.epos
 
 let follow_module follow_func md = match md with
 	| TClassDecl _
@@ -1143,24 +1074,16 @@ let rec replace_mono t =
 	| TAnon _
 	| TDynamic _ -> ()
 	| TLazy f ->
-		replace_mono (!f())
+		replace_mono (lazy_type f)
 
 (* helper *)
 let mk_class_field name t public pos kind params =
-	{
-		cf_name = name;
-		cf_type = t;
-		cf_public = public;
-		cf_pos = pos;
-		cf_name_pos = null_pos;
-		cf_doc = None;
-		cf_meta = [ Meta.CompilerGenerated, [], null_pos ]; (* annotate that this class field was generated by the compiler *)
-		cf_kind = kind;
-		cf_params = params;
-		cf_expr = None;
-		cf_expr_unoptimized = None;
-		cf_overloads = [];
-	}
+	let f = mk_field name t pos null_pos in
+	f.cf_public <- public;
+	f.cf_meta <- [ Meta.CompilerGenerated, [], null_pos ]; (* annotate that this class field was generated by the compiler *)
+	f.cf_kind <- kind;
+	f.cf_params <- params;
+	f
 
 (* this helper just duplicates the type parameter class, which is assumed that cl is. *)
 (* This is so we can use class parameters on function parameters, without running the risk of name clash *)
@@ -1236,7 +1159,7 @@ let find_first_declared_field gen orig_cl ?get_vmtype ?exact_field field =
 		| _ -> gen.greal_type params_t in
 		Some(f,actual_t,declared_t,params_t,c,tl,tlch)
 
-let field_access gen (t:t) (field:string) : (tfield_access) =
+let rec field_access gen (t:t) (field:string) : (tfield_access) =
 	(*
 		t can be either an haxe-type as a real-type;
 		'follow' should be applied here since we can generalize that a TType will be accessible as its
@@ -1290,8 +1213,16 @@ let field_access gen (t:t) (field:string) : (tfield_access) =
 					| None -> not_found()
 					| Some (cf, actual_t, declared_t, declared_cl) ->
 						FClassField(orig_cl, orig_params, declared_cl, cf, false, actual_t, declared_t))
-		| TEnum _ | TAbstract _ ->
-			(* enums have no field *) FNotFound
+		| TEnum (en,params) when Meta.has Meta.Class en.e_meta ->
+			(* A field access to an enum could mean accessing field of its generated class (e.g. `index` for switches).
+			   Ideally, we should change all TEnum instances to relevant TInst instances so we never reach this case,
+			   but for now, we're going to find the generated class and make a field access to it instead. *)
+			(try
+				let cl_enum = List.find (function TClassDecl cl when cl.cl_path = en.e_path && Meta.has Meta.Enum cl.cl_meta -> true | _ -> false) gen.gtypes_list in
+				let cl_enum = match cl_enum with TClassDecl cl -> TInst (cl,params) | _ -> assert false in
+				field_access gen cl_enum field
+			with Not_found ->
+				FNotFound)
 		| TAnon anon ->
 			(try match !(anon.a_status) with
 				| Statics cl ->
@@ -1379,8800 +1310,6 @@ let get_type gen path =
 	try Hashtbl.find gen.gtypes path with | Not_found -> raise (TypeNotFound path)
 
 
-(* ******************************************* *)
-(* set hxgen module *)
-(* ******************************************* *)
-(*
-	Goes through all module types and adds the @:hxGen or @:nativeGen meta to them.
-	Basically, everything that is extern is assumed to not be hxgen, unless meta :hxGen is set,
-	and everything that is not extern is assumed to be hxgen, unless meta :nativeGgen is set.
-*)
-module SetHXGen =
-struct
-	(*
-		The only option is to run this filter eagerly, because it must be one of the first filters to run,
-		since many others depend of it.
-	*)
-	let run_filter gen =
-		let rec is_hxgen md =
-			match md with
-			| TClassDecl { cl_kind = KAbstractImpl a } ->
-				is_hxgen (TAbstractDecl a)
-			| TClassDecl cl ->
-				let rec is_hxgen_class (c,_) =
-					if c.cl_extern then begin
-						if Meta.has Meta.HxGen c.cl_meta then
-							true
-						else
-							Option.map_default (is_hxgen_class) false c.cl_super || List.exists is_hxgen_class c.cl_implements
-					end else begin
-						if Meta.has Meta.NativeChildren c.cl_meta || Meta.has Meta.NativeGen c.cl_meta then
-							Option.map_default is_hxgen_class false c.cl_super || List.exists is_hxgen_class c.cl_implements
-						else
-							let rec has_nativec (c,p) =
-								if is_hxgen_class (c,p) then
-									false
-								else
-									(Meta.has Meta.NativeChildren c.cl_meta && not (Option.map_default is_hxgen_class false c.cl_super || List.exists is_hxgen_class c.cl_implements))
-									|| Option.map_default has_nativec false c.cl_super
-							in
-							if Option.map_default has_nativec false c.cl_super && not (List.exists is_hxgen_class c.cl_implements) then
-								false
-							else
-								true
-					end
-				in
-				is_hxgen_class (cl,[])
-			| TEnumDecl e ->
-				if e.e_extern then
-					Meta.has Meta.HxGen e.e_meta
-				else if Meta.has Meta.NativeGen e.e_meta then
-					if Meta.has Meta.FlatEnum e.e_meta then
-						false
-					else begin
-						gen.gcon.error "Only flat enums may be @:nativeGen" e.e_pos;
-						true
-					end
-				else
-					true
-			| TAbstractDecl a when Meta.has Meta.CoreType a.a_meta ->
-				not (Meta.has Meta.NativeGen a.a_meta)
-			| TAbstractDecl a ->
-				(match follow a.a_this with
-				| TInst _ | TEnum _ | TAbstract _ ->
-					is_hxgen (t_to_md (follow a.a_this))
-				| _ ->
-					not (Meta.has Meta.NativeGen a.a_meta))
-			| TTypeDecl t -> (* TODO see when would we use this *)
-				false
-		in
-
-		let filter md =
-			let meta = if is_hxgen md then Meta.HxGen else Meta.NativeGen in
-			match md with
-			| TClassDecl cl -> cl.cl_meta <- (meta, [], cl.cl_pos) :: cl.cl_meta
-			| TEnumDecl e -> e.e_meta <- (meta, [], e.e_pos) :: e.e_meta
-			| TTypeDecl t -> t.t_meta <- (meta, [], t.t_pos) :: t.t_meta
-			| TAbstractDecl a -> a.a_meta <- (meta, [], a.a_pos) :: a.a_meta
-		in
-
-		List.iter filter gen.gtypes_list
-end;;
-
-
-(* ******************************************* *)
-(* overloading reflection constructors *)
-(* ******************************************* *)
-(*
-	this module works on languages that support function overloading and
-	enable function hiding via static functions.
-	it takes the constructor body out of the constructor and adds it to a special ctor
-	static function. The static function will receive the same parameters as the constructor,
-	plus the special "me" var, which will replace "this"
-
-	Then it always adds two constructors to the class: one that receives a special marker class,
-	indicating that the object should be constructed without executing constructor body,
-	and one that executes its normal constructor.
-	Both will only include a super() call to the superclasses' emtpy constructor.
-
-	This enables two things:
-		empty construction without the need of incompatibility with the platform's native construction method
-		the ability to call super() constructor in any place in the constructor
-
-	This will insert itself in the default reflection-related module filter
-*)
-module OverloadingConstructor =
-struct
-	let priority = 0.0
-	let name = "overloading_constructor"
-
-	let rec cur_ctor c tl =
-		match c.cl_constructor with
-		| Some ctor ->
-			ctor, c, tl
-		| None ->
-			match c.cl_super with
-			| None ->
-				raise Not_found
-			| Some (sup,stl) ->
-				cur_ctor sup (List.map (apply_params c.cl_params tl) stl)
-
-	let rec prev_ctor c tl =
-		match c.cl_super with
-		| None ->
-			raise Not_found
-		| Some (sup,stl) ->
-			let stl = List.map (apply_params c.cl_params tl) stl in
-			match sup.cl_constructor with
-			| None -> prev_ctor sup stl
-			| Some ctor -> ctor, sup, stl
-
-	let make_static_ctor_name gen cl =
-		let name = gen.gmk_internal_name "hx" "ctor" in
-		name ^ "_" ^ (String.concat "_" (fst cl.cl_path)) ^ "_" ^ (snd cl.cl_path)
-
-	(* replaces super() call with last static constructor call *)
-	let replace_super_call gen c tl with_params me p =
-		let rec loop_super c tl =
-			match c.cl_super with
-			| None ->
-				raise Not_found
-			| Some(sup,stl) ->
-				let stl = List.map (apply_params c.cl_params tl) stl in
-				try
-					let static_ctor_name = make_static_ctor_name gen sup in
-					sup, stl, PMap.find static_ctor_name sup.cl_statics
-				with Not_found ->
-					loop_super sup stl
-		in
-		let sup, stl, cf = loop_super c tl in
-		let with_params = (mk (TLocal me) me.v_type p) :: with_params in
-		let cf =
-			try
-				(* choose best super function *)
-				List.iter (fun e -> replace_mono e.etype) with_params;
-				List.find (fun cf ->
-					replace_mono cf.cf_type;
-					let args, _ = get_fun (apply_params cf.cf_params stl cf.cf_type) in
-					try
-						List.for_all2 (fun (_,_,t) e -> try
-							let e_etype = run_follow gen e.etype in
-							let t = run_follow gen t in
-							unify e_etype t; true
-						with Unify_error _ ->
-							false
-						) args with_params
-					with Invalid_argument("List.for_all2") ->
-						false
-				) (cf :: cf.cf_overloads)
-			with Not_found ->
-				gen.gcon.error "No suitable overload for the super call arguments was found" p; cf
-		in
-		{
-			eexpr = TCall(
-				{
-					eexpr = TField(ExprBuilder.make_static_this sup p, FStatic(sup,cf));
-					etype = apply_params cf.cf_params stl cf.cf_type;
-					epos = p
-				},
-				with_params
-			);
-			etype = gen.gcon.basic.tvoid;
-			epos = p;
-		}
-
-	(* will create a static counterpart of 'ctor', and replace its contents to a call to the static version*)
-	let create_static_ctor gen ~empty_ctor_expr cl ctor =
-		match Meta.has Meta.SkipCtor ctor.cf_meta with
-		| true -> ()
-		| false when is_none ctor.cf_expr -> ()
-		| false ->
-			let static_ctor_name = make_static_ctor_name gen cl in
-			(* create the static constructor *)
-			let basic = gen.gcon.basic in
-			let ctor_types = List.map (fun (s,t) -> (s, TInst(map_param (get_cl_t t), []))) cl.cl_params in
-			let me = alloc_var "__hx_this" (TInst(cl, List.map snd ctor_types)) in
-			me.v_capture <- true;
-
-			let fn_args, _ = get_fun ctor.cf_type in
-			let ctor_params = List.map snd ctor_types in
-			let fn_type = TFun((me.v_name,false, me.v_type) :: List.map (fun (n,o,t) -> (n,o,apply_params cl.cl_params ctor_params t)) fn_args, basic.tvoid) in
-			let cur_tf_args = match ctor.cf_expr with
-			| Some { eexpr = TFunction(tf) } -> tf.tf_args
-			| _ -> assert false
-			in
-
-			let changed_tf_args = List.map (fun (v,_) -> (v,None)) cur_tf_args in
-
-			let local_map = Hashtbl.create (List.length cur_tf_args) in
-			let static_tf_args = (me, None) :: List.map (fun (v,b) ->
-				let new_v = alloc_var v.v_name (apply_params cl.cl_params ctor_params v.v_type) in
-				new_v.v_capture <- v.v_capture;
-				Hashtbl.add local_map v.v_id new_v;
-				(new_v, b)
-			) cur_tf_args in
-
-			let static_ctor = mk_class_field static_ctor_name fn_type false ctor.cf_pos (Method MethNormal) ctor_types in
-
-			(* change ctor contents to reference the 'me' var instead of 'this' *)
-			let actual_super_call = ref None in
-			let rec map_expr ~is_first e = match e.eexpr with
-				| TCall (({ eexpr = TConst TSuper } as tsuper), params) -> (try
-					let params = List.map (fun e -> map_expr ~is_first:false e) params in
-					actual_super_call := Some { e with eexpr = TCall(tsuper, [empty_ctor_expr]) };
-					replace_super_call gen cl ctor_params params me e.epos
-				with | Not_found ->
-					(* last static function was not found *)
-					actual_super_call := Some e;
-					if not is_first then
-						gen.gcon.error "Super call must be the first call when extending native types" e.epos;
-					{ e with eexpr = TBlock([]) })
-				| TFunction tf when is_first ->
-					do_map ~is_first:true e
-				| TConst TThis ->
-					mk_local me e.epos
-				| TBlock (fst :: bl) ->
-					let fst = map_expr ~is_first:is_first fst in
-					{ e with eexpr = TBlock(fst :: List.map (fun e -> map_expr ~is_first:false e) bl); etype = apply_params cl.cl_params ctor_params e.etype }
-				| _ ->
-					do_map e
-			and do_map ?(is_first=false) e =
-				let do_t = apply_params cl.cl_params ctor_params in
-				let do_v v = try
-						Hashtbl.find local_map v.v_id
-					with | Not_found ->
-						v.v_type <- do_t v.v_type; v
-				in
-				Type.map_expr_type (map_expr ~is_first:is_first) do_t do_v e
-			in
-
-			let expr = do_map ~is_first:true (get ctor.cf_expr) in
-			let expr = match expr.eexpr with
-			| TFunction(tf) ->
-				{ expr with etype = fn_type; eexpr = TFunction({ tf with tf_args = static_tf_args }) }
-			| _ -> assert false in
-			static_ctor.cf_expr <- Some expr;
-			(* add to the statics *)
-			(try
-				let stat = PMap.find static_ctor_name cl.cl_statics in
-				stat.cf_overloads <- static_ctor :: stat.cf_overloads
-			with | Not_found ->
-				cl.cl_ordered_statics <- static_ctor :: cl.cl_ordered_statics;
-				cl.cl_statics <- PMap.add static_ctor_name static_ctor cl.cl_statics);
-			(* change current super call *)
-			match ctor.cf_expr with
-			| Some({ eexpr = TFunction(tf) } as e) ->
-				let block_contents, p = match !actual_super_call with
-				| None -> [], ctor.cf_pos
-				| Some super -> [super], super.epos
-				in
-				let block_contents = block_contents @ [{
-					eexpr = TCall(
-						{
-							eexpr = TField(
-								ExprBuilder.make_static_this cl p,
-								FStatic(cl, static_ctor));
-							etype = apply_params static_ctor.cf_params (List.map snd cl.cl_params) static_ctor.cf_type;
-							epos = p
-						},
-						[{ eexpr = TConst TThis; etype = TInst(cl, List.map snd cl.cl_params); epos = p }]
-						@ List.map (fun (v,_) -> mk_local v p) cur_tf_args
-					);
-					etype = basic.tvoid;
-					epos = p
-				}] in
-				ctor.cf_expr <- Some { e with eexpr = TFunction({ tf with tf_expr = { tf.tf_expr with eexpr = TBlock block_contents }; tf_args = changed_tf_args }) }
-			| _ -> assert false
-
-	(* makes constructors that only call super() for the 'ctor' argument *)
-	let clone_ctors gen ctor sup stl cl =
-		let basic = gen.gcon.basic in
-		let rec clone cf =
-			let ncf = mk_class_field "new" (apply_params sup.cl_params stl cf.cf_type) cf.cf_public cf.cf_pos cf.cf_kind cf.cf_params in
-			let args, ret = get_fun ncf.cf_type in
-			(* single expression: call to super() *)
-			let tf_args = List.map (fun (name,_,t) ->
-				(* the constructor will have no optional arguments, as presumably this will be handled by the underlying expr *)
-				alloc_var name t, None
-			) args in
-			let super_call =
-			{
-				eexpr = TCall(
-					{ eexpr = TConst TSuper; etype = TInst(cl, List.map snd cl.cl_params); epos = ctor.cf_pos },
-					List.map (fun (v,_) -> mk_local v ctor.cf_pos) tf_args);
-				etype = basic.tvoid;
-				epos = ctor.cf_pos;
-			} in
-			ncf.cf_expr <- Some
-			{
-				eexpr = TFunction {
-					tf_args = tf_args;
-					tf_type = basic.tvoid;
-					tf_expr = mk_block super_call;
-				};
-				etype = ncf.cf_type;
-				epos = ctor.cf_pos;
-			};
-			ncf
-		in
-		(* take off createEmpty *)
-		let all = List.filter (fun cf -> replace_mono cf.cf_type; not (Meta.has Meta.SkipCtor cf.cf_meta)) (ctor :: ctor.cf_overloads) in
-		let clones = List.map clone all in
-		match clones with
-		| [] ->
-			(* raise Not_found *)
-			assert false (* should never happen *)
-		| cf :: [] -> cf
-		| cf :: overl ->
-			cf.cf_meta <- (Meta.Overload,[],cf.cf_pos) :: cf.cf_meta;
-			cf.cf_overloads <- overl; cf
-
-	let rec descends_from_native_or_skipctor cl =
-		not (is_hxgen (TClassDecl cl)) || Meta.has Meta.SkipCtor cl.cl_meta || match cl.cl_super with
-		| None -> false
-		| Some(c,_) -> descends_from_native_or_skipctor c
-
-	let ensure_super_is_first gen cf =
-		let rec loop e =
-			match e.eexpr with
-			| TBlock (b :: block) ->
-				loop b
-			| TBlock []
-			| TCall({ eexpr = TConst TSuper },_) -> ()
-			| _ ->
-				gen.gcon.error "Types that derive from a native class must have its super() call as the first statement in the constructor" cf.cf_pos
-		in
-		match cf.cf_expr with
-		| None -> ()
-		| Some e -> Type.iter loop e
-
-	let configure ~(empty_ctor_type : t) ~(empty_ctor_expr : texpr) gen =
-		gen.gtools.r_create_empty <- (fun cl params pos -> mk (TNew(cl,params,[empty_ctor_expr])) (TInst(cl,params)) pos);
-
-		let basic = gen.gcon.basic in
-		let should_change cl = not cl.cl_interface && (not cl.cl_extern || is_hxgen (TClassDecl cl)) && (match cl.cl_kind with KAbstractImpl _ -> false | _ -> true) in
-		let msize = List.length gen.gtypes_list in
-		let processed, empty_ctors = Hashtbl.create msize, Hashtbl.create msize in
-
-
-		let rec get_last_empty cl =
-			try
-				Hashtbl.find empty_ctors cl.cl_path
-			with | Not_found ->
-				match cl.cl_super with
-				| None -> raise Not_found
-				| Some (sup,_) -> get_last_empty sup
-		in
-
-		let rec change cl =
-			if not (Hashtbl.mem processed cl.cl_path) then begin
-				Hashtbl.add processed cl.cl_path true;
-
-				(* make sure we've processed the super types *)
-				Option.may (fun (super,_) -> if should_change super then change super) cl.cl_super;
-
-				(* implement static hx_ctor and reimplement constructors *)
-				(try
-					let ctor =
-						match cl.cl_constructor with
-						| Some ctor ->
-							ctor
-						| None ->
-							try
-								let sctor, sup, stl = prev_ctor cl (List.map snd cl.cl_params) in
-								(* we'll make constructors that will only call super() *)
-								let ctor = clone_ctors gen sctor sup stl cl in
-								cl.cl_constructor <- Some ctor;
-								ctor
-							with Not_found -> (* create default constructor *)
-								let ctor = mk_class_field "new" (TFun ([], basic.tvoid)) false cl.cl_pos (Method MethNormal) [] in
-								ctor.cf_expr <- Some {
-									eexpr = TFunction {
-										tf_args = [];
-										tf_type = basic.tvoid;
-										tf_expr = mk (TBlock []) basic.tvoid cl.cl_pos;
-									};
-									etype = ctor.cf_type;
-									epos = ctor.cf_pos;
-								};
-								cl.cl_constructor <- Some ctor;
-								ctor
-					in
-					(* now that we made sure we have a constructor, exit if native gen *)
-					if not (is_hxgen (TClassDecl cl)) || Meta.has Meta.SkipCtor cl.cl_meta then begin
-						if descends_from_native_or_skipctor cl && is_some cl.cl_super then
-							List.iter (fun cf -> ensure_super_is_first gen cf) (ctor :: ctor.cf_overloads);
-						raise Exit
-					end;
-
-					(* if cl descends from a native class, we cannot use the static constructor strategy *)
-					if descends_from_native_or_skipctor cl && is_some cl.cl_super then
-						List.iter (fun cf -> ensure_super_is_first gen cf) (ctor :: ctor.cf_overloads)
-					else
-						(* now that we have a current ctor, create the static counterparts *)
-						List.iter (fun cf -> create_static_ctor gen ~empty_ctor_expr:empty_ctor_expr cl cf) (ctor :: ctor.cf_overloads)
-				with Exit -> ());
-
-				(* implement empty ctor *)
-				(try
-					(* now that we made sure we have a constructor, exit if native gen *)
-					if not (is_hxgen (TClassDecl cl)) then raise Exit;
-
-					(* get first *)
-					let empty_type = TFun (["empty",false,empty_ctor_type],basic.tvoid) in
-					let super =
-						match cl.cl_super with
-						| None -> (* implement empty *)
-								[]
-						| Some (sup,_) ->
-							try
-								ignore (get_last_empty sup);
-								let esuper = mk (TConst TSuper) (TInst (cl, List.map snd cl.cl_params)) cl.cl_pos in
-								[mk (TCall (esuper, [empty_ctor_expr])) basic.tvoid cl.cl_pos]
-							with Not_found ->
-								try
-									(* super type is native: find super constructor with least arguments *)
-									let sctor, sup, stl = prev_ctor cl (List.map snd cl.cl_params) in
-									let rec loop remaining (best,n) =
-										match remaining with
-										| [] -> best
-										| cf :: r ->
-											let args,_ = get_fun cf.cf_type in
-											if (List.length args) < n then
-												loop r (cf,List.length args)
-											else
-												loop r (best,n)
-									in
-									let args,_ = get_fun sctor.cf_type in
-									let best = loop sctor.cf_overloads (sctor, List.length args) in
-									let args,_ = get_fun (apply_params sup.cl_params stl best.cf_type) in
-									let esuper = mk (TConst TSuper) (TInst (sup, stl)) cl.cl_pos in
-									[mk (TCall (esuper, List.map (fun (n,o,t) -> null t cl.cl_pos) args)) basic.tvoid cl.cl_pos]
-								with Not_found ->
-									(* extends native type, but no ctor found *)
-									[]
-					in
-					let ctor = mk_class_field "new" empty_type false cl.cl_pos (Method MethNormal) [] in
-					ctor.cf_expr <- Some {
-						eexpr = TFunction {
-							tf_type = basic.tvoid;
-							tf_args = [alloc_var "empty" empty_ctor_type, None];
-							tf_expr = mk (TBlock super) basic.tvoid cl.cl_pos
-						};
-						etype = empty_type;
-						epos = cl.cl_pos;
-					};
-					ctor.cf_meta <- [Meta.SkipCtor, [], ctor.cf_pos];
-					Hashtbl.add empty_ctors cl.cl_path ctor;
-					match cl.cl_constructor with
-					| None ->
-						cl.cl_constructor <- Some ctor
-					| Some c ->
-						c.cf_overloads <- ctor :: c.cf_overloads
-				with Exit -> ());
-			end
-		in
-
-		let module_filter md =
-			(match md with
-			| TClassDecl cl when should_change cl ->
-				change cl;
-			| _ ->
-				());
-			None
-		in
-		gen.gmodule_filters#add ~name:name ~priority:(PCustom priority) module_filter
-
-end;;
-
-(* ******************************************* *)
-(* init function module *)
-(* ******************************************* *)
-(*
-	This module will take proper care of the init function, by taking off all expressions from static vars and putting them
-	in order in the init function.
-	It will also initialize dynamic functions, both by putting them in the constructor and in the init function
-
-	depends on:
-		(syntax) must run before ExprStatement module
-		(ok) must run before OverloadingConstructor module so the constructor can be in the correct place
-		(syntax) must run before FunctionToClass module
-*)
-module InitFunction =
-struct
-	let name = "init_funcs"
-	let priority = solve_deps name [DBefore OverloadingConstructor.priority]
-
-	let ensure_simple_expr gen e =
-		let rec iter e = match e.eexpr with
-			| TConst _ | TLocal _ | TArray _ | TBinop _
-			| TField _ | TTypeExpr _ | TParenthesis _ | TCast _ | TMeta _
-			| TCall _ | TNew _ | TUnop _ ->
-				Type.iter iter e
-			| _ ->
-				print_endline (debug_expr e);
-				gen.gcon.error "Expression is too complex for a readonly variable initialization" e.epos
-		in
-		iter e
-
-	let configure gen =
-		let handle_override_dynfun acc e this field =
-			let add_expr = ref None in
-			let v = mk_temp gen ("super_" ^ field) e.etype in
-			v.v_capture <- true;
-
-			let rec loop e =
-				match e.eexpr with
-					| TField({ eexpr = TConst(TSuper) }, f) ->
-						let n = field_name f in
-						(if n <> field then assert false);
-						let local = mk_local v e.epos in
-						(match !add_expr with
-							| None ->
-								add_expr := Some { e with eexpr = TVar(v, Some this) }
-							| Some _ -> ());
-						local
-					| TConst TSuper -> assert false
-					| _ -> Type.map_expr loop e
-			in
-			let e = loop e in
-
-			match !add_expr with
-				| None -> e :: acc
-				| Some add_expr -> add_expr :: e :: acc
-		in
-
-		let handle_class cl =
-			let init = match cl.cl_init with
-				| None -> []
-				| Some i -> [i]
-			in
-			let init = List.fold_left (fun acc cf ->
-				match cf.cf_kind with
-					| Var v when Meta.has Meta.ReadOnly cf.cf_meta ->
-							if v.v_write <> AccNever && not (Meta.has Meta.CoreApi cl.cl_meta) then gen.gcon.warning "@:readOnly variable declared without `never` setter modifier" cf.cf_pos;
-							(match cf.cf_expr with
-								| None -> gen.gcon.warning "Uninitialized readonly variable" cf.cf_pos; acc
-								| Some e -> ensure_simple_expr gen e; acc)
-					| Var _
-					| Method MethDynamic when not (Type.is_extern_field cf) ->
-						(match cf.cf_expr with
-							| Some e ->
-								(match cf.cf_params with
-									| [] ->
-										let var = { eexpr = TField(ExprBuilder.make_static_this cl cf.cf_pos, FStatic(cl,cf)); etype = cf.cf_type; epos = cf.cf_pos } in
-										let ret = ({ eexpr = TBinop(Ast.OpAssign, var, e); etype = cf.cf_type; epos = cf.cf_pos; }) in
-										cf.cf_expr <- None;
-
-										ret :: acc
-									| _ ->
-										let params = List.map (fun _ -> t_dynamic) cf.cf_params in
-										let fn = apply_params cf.cf_params params in
-										let var = { eexpr = TField(ExprBuilder.make_static_this cl cf.cf_pos, FStatic(cl,cf)); etype = fn cf.cf_type; epos = cf.cf_pos } in
-										let rec change_expr e =
-											Type.map_expr_type (change_expr) fn (fun v -> v.v_type <- fn v.v_type; v) e
-										in
-
-										let ret = ({ eexpr = TBinop(Ast.OpAssign, var, change_expr e); etype = fn cf.cf_type; epos = cf.cf_pos; }) in
-										cf.cf_expr <- None;
-										ret :: acc
-								)
-							| None -> acc)
-					| _ -> acc
-			) init cl.cl_ordered_statics
-			in
-			let init = List.rev init in
-			(match init with
-				| [] -> cl.cl_init <- None
-				| _ -> cl.cl_init <- Some { eexpr = TBlock(init); epos = cl.cl_pos; etype = gen.gcon.basic.tvoid; });
-
-			(* FIXME: find a way to tell OverloadingConstructor to execute this code even with empty constructors *)
-			let vars, funs = List.fold_left (fun (acc_vars,acc_funs) cf ->
-				match cf.cf_kind with
-					| Var v when Meta.has Meta.ReadOnly cf.cf_meta ->
-							if v.v_write <> AccNever && not (Meta.has Meta.CoreApi cl.cl_meta) then gen.gcon.warning "@:readOnly variable declared without `never` setter modifier" cf.cf_pos;
-							(match cf.cf_expr with
-								| None -> (acc_vars,acc_funs)
-								| Some e -> ensure_simple_expr gen e; (acc_vars,acc_funs))
-					| Var _
-					| Method MethDynamic ->
-						let is_var = match cf.cf_kind with | Var _ -> true | _ -> false in
-						(match cf.cf_expr, cf.cf_params with
-							| Some e, [] ->
-								let var = { eexpr = TField({ eexpr = TConst(TThis); epos = cf.cf_pos; etype = TInst(cl, List.map snd cl.cl_params); }, FInstance(cl, List.map snd cl.cl_params, cf)); etype = cf.cf_type; epos = cf.cf_pos } in
-								let ret = ({ eexpr = TBinop(Ast.OpAssign, var, e); etype = cf.cf_type; epos = cf.cf_pos; }) in
-								cf.cf_expr <- None;
-								let is_override = List.memq cf cl.cl_overrides in
-
-								if is_override then begin
-									cl.cl_ordered_fields <- List.filter (fun f -> f.cf_name <> cf.cf_name) cl.cl_ordered_fields;
-									cl.cl_fields <- PMap.remove cf.cf_name cl.cl_fields;
-									acc_vars, handle_override_dynfun acc_funs ret var cf.cf_name
-								end else if is_var then
-									ret :: acc_vars, acc_funs
-								else
-									acc_vars, ret :: acc_funs
-							| Some e, _ ->
-								let params = List.map (fun _ -> t_dynamic) cf.cf_params in
-								let fn = apply_params cf.cf_params params in
-								let var = { eexpr = TField({ eexpr = TConst(TThis); epos = cf.cf_pos; etype = TInst(cl, List.map snd cl.cl_params); }, FInstance(cl, List.map snd cl.cl_params, cf)); etype = cf.cf_type; epos = cf.cf_pos } in
-								let rec change_expr e =
-									Type.map_expr_type (change_expr) fn (fun v -> v.v_type <- fn v.v_type; v) e
-								in
-
-								let ret = ({ eexpr = TBinop(Ast.OpAssign, var, change_expr e); etype = fn cf.cf_type; epos = cf.cf_pos; }) in
-								cf.cf_expr <- None;
-								let is_override = List.memq cf cl.cl_overrides in
-
-								if is_override then begin
-									cl.cl_ordered_fields <- List.filter (fun f -> f.cf_name <> cf.cf_name) cl.cl_ordered_fields;
-									cl.cl_fields <- PMap.remove cf.cf_name cl.cl_fields;
-									acc_vars, handle_override_dynfun acc_funs ret var cf.cf_name
-								end else if is_var then
-									ret :: acc_vars, acc_funs
-								else
-									acc_vars, ret :: acc_funs
-							| None, _ -> acc_vars,acc_funs)
-					| _ -> acc_vars,acc_funs
-			) ([],[]) cl.cl_ordered_fields
-			in
-			(* let vars = List.rev vars in *)
-			(* let funs = List.rev funs in *)
-			(* see if there is any *)
-			(match vars, funs with
-				| [], [] -> ()
-				| _ ->
-					(* if there is, we need to find the constructor *)
-					let ctors = match cl.cl_constructor with
-					| Some ctor -> ctor
-					| None -> try
-						let sctor, sup, stl = OverloadingConstructor.prev_ctor cl (List.map snd cl.cl_params) in
-						let ctor = OverloadingConstructor.clone_ctors gen sctor sup stl cl in
-						cl.cl_constructor <- Some ctor;
-						ctor
-					with | Not_found ->
-						let basic = gen.gcon.basic in
-						let ctor = mk_class_field "new" (TFun([], basic.tvoid)) false cl.cl_pos (Method MethNormal) [] in
-						ctor.cf_expr <- Some
-						{
-							eexpr = TFunction {
-								tf_args = [];
-								tf_type = basic.tvoid;
-								tf_expr = { eexpr = TBlock[]; etype = basic.tvoid; epos = cl.cl_pos };
-							};
-							etype = ctor.cf_type;
-							epos = ctor.cf_pos;
-						};
-						cl.cl_constructor <- Some ctor;
-						ctor
-					in
-
-					let process ctor =
-						let func = match ctor.cf_expr with
-							| Some({eexpr = TFunction(tf)} as e) ->
-								let rec add_fn e = match e.eexpr with
-									| TBlock(hd :: tl) -> (match hd.eexpr with
-										| TCall({ eexpr = TConst TSuper }, _) ->
-											if not (OverloadingConstructor.descends_from_native_or_skipctor cl) then
-												{ e with eexpr = TBlock(vars @ (hd :: (funs @ tl))) }
-											else
-												{ e with eexpr = TBlock(hd :: (vars @ funs @ tl)) }
-										| TBlock(_) ->
-											{ e with eexpr = TBlock( (add_fn hd) :: tl ) }
-										| _ ->
-											{ e with eexpr = TBlock( vars @ funs @ (hd :: tl) ) })
-									| _ -> Type.concat { e with eexpr = TBlock(vars @ funs) } e
-								in
-								let tf_expr = add_fn (mk_block tf.tf_expr) in
-								{ e with eexpr = TFunction({ tf with tf_expr = tf_expr }) }
-							| _ -> assert false
-						in
-						ctor.cf_expr <- Some(func)
-					in
-					List.iter process (ctors :: ctors.cf_overloads)
-			)
-		in
-
-		let mod_filter = function
-			| TClassDecl cl -> (if not cl.cl_extern then handle_class cl); None
-			| _ -> None in
-
-		gen.gmodule_filters#add ~name:"init_funcs" ~priority:(PCustom priority) mod_filter
-
-end;;
-
-
-(* ******************************************* *)
-(* Dynamic Binop/Unop handler *)
-(* ******************************************* *)
-(*
-	On some languages there is limited support for operations on
-	dynamic variables, so those operations must be changed.
-
-	There are 5 types of binary operators:
-		1 - can take any variable and returns a bool (== and !=)
-		2 - can take either a string, or a number and returns either a bool or the underlying type ( >, < for bool and + for returning its type)
-		3 - take numbers and return a number ( *, /, ...)
-		4 - take ints and return an int (bit manipulation)
-		5 - take a bool and returns a bool ( &&, || ...)
-
-	On the default implementation, type 1 and the plus function will be handled with a function call;
-	Type 2 will be handled with the parameter "compare_handler", which will do something like Reflect.compare(x1, x2);
-	Types 3, 4 and 5 will perform a cast to double, int and bool, which will then be handled normally by the platform
-
-	Unary operators are the most difficult to handle correctly.
-	With unary operators, there are 2 types:
-
-		1 - can take a number, changes and returns the result (++, --, ~)
-		2 - can take a number (-) or bool (!), and returns the result
-
-	The first case is much trickier, because it doesn't seem a good idea to change any variable to double just because it is dynamic,
-	but this is how we will handle right now.
-	something like that:
-
-	var x:Dynamic = 10;
-	x++;
-
-	will be:
-	object x = 10;
-	x = ((IConvertible)x).ToDouble(null) + 1;
-
-	depends on:
-		(syntax) must run before expression/statment normalization because it may generate complex expressions
-		must run before OverloadingConstructor due to later priority conflicts. Since ExpressionUnwrap is only
-		defined afterwards, we will set this value with absolute values
-*)
-module DynamicOperators =
-struct
-	let name = "dyn_ops"
-	let priority = 0.0
-
-	let configure gen ?(handle_strings = true) (should_change:texpr->bool) (equals_handler:texpr->texpr->texpr) (dyn_plus_handler:texpr->texpr->texpr->texpr) (compare_handler:texpr->texpr->texpr) =
-		let get_etype_one e =
-			if like_int e.etype then
-				ExprBuilder.make_int gen.gcon 1 e.epos
-			else
-				ExprBuilder.make_float gen.gcon "1.0" e.epos
-		in
-
-		let basic = gen.gcon.basic in
-
-		let rec run e =
-			match e.eexpr with
-				| TBinop (OpAssignOp op, e1, e2) when should_change e -> (* e1 will never contain another TBinop *)
-					(match e1.eexpr with
-						| TLocal _ ->
-							mk_paren { e with eexpr = TBinop(OpAssign, e1, run { e with eexpr = TBinop(op, e1, e2) }) }
-						| TField _ | TArray _ ->
-							let eleft, rest = match e1.eexpr with
-								| TField(ef, f) ->
-									let v = mk_temp gen "dynop" ef.etype in
-									{ e1 with eexpr = TField(mk_local v ef.epos, f) }, [ { eexpr = TVar(v,Some (run ef)); etype = basic.tvoid; epos = ef.epos } ]
-								| TArray(e1a, e2a) ->
-									let v = mk_temp gen "dynop" e1a.etype in
-									let v2 = mk_temp gen "dynopi" e2a.etype in
-									{ e1 with eexpr = TArray(mk_local v e1a.epos, mk_local v2 e2a.epos) }, [
-										{ eexpr = TVar(v,Some (run e1a)); etype = basic.tvoid; epos = e1.epos };
-										{ eexpr = TVar(v2, Some (run e2a)); etype = basic.tvoid; epos = e1.epos }
-									]
-								| _ -> assert false
-							in
-							{ e with
-								eexpr = TBlock (rest @ [ { e with eexpr = TBinop(OpAssign, eleft, run { e with eexpr = TBinop(op, eleft, e2) }) } ]);
-							}
-						| _ ->
-							assert false
-					)
-
-				| TBinop (OpAssign, e1, e2)
-				| TBinop (OpInterval, e1, e2) -> Type.map_expr run e
-				| TBinop (op, e1, e2) when should_change e->
-					(match op with
-						| OpEq -> (* type 1 *)
-							equals_handler (run e1) (run e2)
-						| OpNotEq -> (* != -> !equals() *)
-							mk_paren { eexpr = TUnop(Ast.Not, Prefix, (equals_handler (run e1) (run e2))); etype = gen.gcon.basic.tbool; epos = e.epos }
-						| OpAdd  ->
-							if handle_strings && (is_string e.etype || is_string e1.etype || is_string e2.etype) then
-								{ e with eexpr = TBinop(op, mk_cast gen.gcon.basic.tstring (run e1), mk_cast gen.gcon.basic.tstring (run e2)) }
-							else
-								dyn_plus_handler e (run e1) (run e2)
-						| OpGt | OpGte | OpLt | OpLte  -> (* type 2 *)
-							{ eexpr = TBinop(op, compare_handler (run e1) (run e2), { eexpr = TConst(TInt(Int32.zero)); etype = gen.gcon.basic.tint; epos = e.epos} ); etype = gen.gcon.basic.tbool; epos = e.epos }
-						| OpMult | OpDiv | OpSub | OpMod -> (* always cast everything to double *)
-							let etype = (get_etype_one e).etype in
-							{ e with eexpr = TBinop(op, mk_cast etype (run e1), mk_cast etype (run e2)) }
-						| OpBoolAnd | OpBoolOr ->
-							{ e with eexpr = TBinop(op, mk_cast gen.gcon.basic.tbool (run e1), mk_cast gen.gcon.basic.tbool (run e2)) }
-						| OpAnd | OpOr | OpXor | OpShl | OpShr | OpUShr ->
-							{ e with eexpr = TBinop(op, mk_cast gen.gcon.basic.tint (run e1), mk_cast gen.gcon.basic.tint (run e2)) }
-						| OpAssign | OpAssignOp _ | OpInterval | OpArrow -> assert false)
-				| TUnop (Increment as op, flag, e1)
-				| TUnop (Decrement as op, flag, e1) when should_change e ->
-					(*
-						some naming definitions:
-						* ret => the returning variable
-						* _g => the get body
-						* getvar => the get variable expr
-
-						This will work like this:
-							- if e1 is a TField, set _g = get body, getvar = (get body).varname
-							- if Prefix, return getvar = getvar + 1.0
-							- if Postfix, set ret = getvar; getvar = getvar + 1.0; ret;
-					*)
-					let one = get_etype_one e in
-					let etype = one.etype in
-					let op = (match op with Increment -> OpAdd | Decrement -> OpSub | _ -> assert false) in
-
-					let var, getvar =
-						match e1.eexpr with
-							| TField(fexpr, field) ->
-								let tmp = mk_temp gen "getvar" fexpr.etype in
-								let var = { eexpr = TVar(tmp, Some(run fexpr)); etype = gen.gcon.basic.tvoid; epos = e.epos } in
-								(Some var, { eexpr = TField( { fexpr with eexpr = TLocal(tmp) }, field); etype = etype; epos = e1.epos })
-							| _ ->
-								(None, e1)
-					in
-
-					(match flag with
-						| Prefix ->
-							let block = (match var with | Some e -> [e] | None -> []) @
-							[
-								mk_cast etype { e with eexpr = TBinop(OpAssign, getvar,{ eexpr = TBinop(op, mk_cast etype getvar, one); etype = etype; epos = e.epos }); etype = getvar.etype; }
-							]
-							in
-							{ eexpr = TBlock(block); etype = etype; epos = e.epos }
-						| Postfix ->
-							let ret = mk_temp gen "ret" etype in
-							let vars = (match var with Some e -> [e] | None -> []) @ [{ eexpr = TVar(ret, Some (mk_cast etype getvar)); etype = gen.gcon.basic.tvoid; epos = e.epos }] in
-							let retlocal = { eexpr = TLocal(ret); etype = etype; epos = e.epos } in
-							let block = vars @
-							[
-							{ e with eexpr = TBinop(OpAssign, getvar, { eexpr = TBinop(op, retlocal, one); etype = getvar.etype; epos = e.epos }) };
-							retlocal
-						] in
-						{ eexpr = TBlock(block); etype = etype; epos = e.epos }
-				)
-			| TUnop (op, flag, e1) when should_change e ->
-				let etype = match op with | Not -> gen.gcon.basic.tbool | _ -> gen.gcon.basic.tint in
-				mk_paren { eexpr = TUnop(op, flag, mk_cast etype (run e1)); etype = etype; epos = e.epos }
-			| _ -> Type.map_expr run e
-		in
-		let map e = Some(run e) in
-		gen.gexpr_filters#add ~name:"dyn_ops" ~priority:(PCustom priority) map
-end;;
-
-
-(* ******************************************* *)
-(* Dynamic Field Access *)
-(* ******************************************* *)
-(*
-	This module will filter every dynamic field access in haxe.
-
-	On platforms that do not support dynamic access, it is with this that you should
-	replace dynamic calls with x.field / Reflect.setField calls, and guess what -
-	this is the default implemenation!
-	Actually there is a problem with Reflect.setField because it returns void, which is a bad thing for us,
-	so even in the default implementation, the function call should be specified to a Reflect.setField version that returns
-	the value that was set
-
-	(TODO: should it be separated?)
-	As a plus, the default implementation adds something that doesn't hurt anybody, it looks for
-	TAnon with Statics / EnumStatics field accesses and transforms them into real static calls.
-	This means it will take this
-
-	var m = Math;
-	for (i in 0...1000) m.cos(10);
-
-	which is an optimization in dynamic platforms, but performs horribly on strongly typed platforms
-	and transform into:
-
-	var m = Math;
-	for (i in 0...1000) Math.cos(10);
-
-	depends on:
-		(ok) must run AFTER Binop/Unop handler - so Unops / Binops are already unrolled
-*)
-module DynamicFieldAccess =
-struct
-	let name = "dynamic_field_access"
-	let priority = solve_deps name [DAfter DynamicOperators.priority]
-
-	(*
-		is_dynamic (expr) (field_access_expr) (field) : a function that indicates if the field access should be changed
-		change_expr (expr) (field_access_expr) (field) (setting expr) (is_unsafe) : changes the expression
-		call_expr (expr) (field_access_expr) (field) (call_params) : changes a call expression
-	*)
-	let configure gen (is_dynamic:texpr->texpr->Type.tfield_access->bool) (change_expr:texpr->texpr->string->texpr option->bool->texpr) (call_expr:texpr->texpr->string->texpr list->texpr) =
-		let rec run e =
-			match e.eexpr with
-			(* class types *)
-			| TField(fexpr, f) when is_some (anon_class fexpr.etype) ->
-				let decl = get (anon_class fexpr.etype) in
-				let name = field_name f in
-				(try
-					match decl with
-						| TClassDecl cl ->
-								let cf = PMap.find name cl.cl_statics in
-								{ e with eexpr = TField({ fexpr with eexpr = TTypeExpr decl }, FStatic(cl, cf)) }
-						| TEnumDecl en ->
-								let ef = PMap.find name en.e_constrs in
-								{ e with eexpr = TField({ fexpr with eexpr = TTypeExpr decl }, FEnum(en, ef)) }
-						| TAbstractDecl _ -> (* abstracts don't have TFields *) assert false
-						| TTypeDecl _ -> (* anon_class doesn't return TTypeDecl *) assert false
-					with
-						| Not_found -> match f with
-							| FStatic(cl,cf) when Meta.has Meta.Extern cf.cf_meta ->
-								{ e with eexpr = TField({ fexpr with eexpr = TTypeExpr decl }, FStatic(cl, cf)) }
-							| _ ->
-								change_expr e { fexpr with eexpr = TTypeExpr decl } (field_name f) None true
-				)
-			| TField(fexpr, f) when is_dynamic e fexpr (f) ->
-				change_expr e (run fexpr) (field_name f) None true
-			| TCall(
-				{ eexpr = TField(_, FStatic({ cl_path = ([], "Reflect") }, { cf_name = "field" })) } ,
-					[obj; { eexpr = TConst(TString(field)) }]
-				) ->
-				let t = match gen.greal_type obj.etype with
-					| TDynamic _ | TAnon _ | TMono _ -> t_dynamic
-					| t -> t
-				in
-				change_expr (mk_field_access gen { obj with etype = t } field obj.epos) (run obj) field None false
-			| TCall(
-				{ eexpr = TField(_, FStatic({ cl_path = ([], "Reflect") }, { cf_name = "setField" } )) },
-					[obj; { eexpr = TConst(TString(field)) }; evalue]
-				) ->
-				change_expr (mk_field_access gen obj field obj.epos) (run obj) field (Some (run evalue)) false
-			| TBinop(OpAssign, ({eexpr = TField(fexpr, f)}), evalue) when is_dynamic e fexpr (f) ->
-				change_expr e (run fexpr) (field_name f) (Some (run evalue)) true
-			| TBinop(OpAssign, { eexpr = TField(fexpr, f) }, evalue) ->
-					(match field_access_esp gen fexpr.etype (f) with
-						| FClassField(_,_,_,cf,false,t,_) when (try PMap.find cf.cf_name gen.gbase_class_fields == cf with Not_found -> false) ->
-								change_expr e (run fexpr) (field_name f) (Some (run evalue)) true
-						| _ -> Type.map_expr run e
-					)
-			(* #if debug *)
-			| TBinop(OpAssignOp op, ({eexpr = TField(fexpr, f)}), evalue) when is_dynamic e fexpr (f) -> assert false (* this case shouldn't happen *)
-			| TUnop(Increment, _, ({eexpr = TField( ( { eexpr=TLocal(local) } as fexpr ), f)}))
-			| TUnop(Decrement, _, ({eexpr = TField( ( { eexpr=TLocal(local) } as fexpr ), f)})) when is_dynamic e fexpr (f) -> assert false (* this case shouldn't happen *)
-			(* #end *)
-			| TCall( ({ eexpr = TField(fexpr, f) }), params ) when is_dynamic e fexpr (f) ->
-				call_expr e (run fexpr) (field_name f) (List.map run params)
-			| _ -> Type.map_expr run e
-		in
-		let map e = Some(run e) in
-		gen.gexpr_filters#add ~name:"dynamic_field_access" ~priority:(PCustom(priority)) map
-end;;
-
-
-(* ******************************************* *)
-(* Closure Detection *)
-(* ******************************************* *)
-(*
-	Just a small utility filter that detects when a closure must be created.
-	On the default implementation, this means when a function field is being accessed
-	not via reflection and not to be called instantly
-
-	dependencies:
-		must run after DynamicFieldAccess, so any TAnon { Statics / EnumStatics } will be changed to the corresponding TTypeExpr
-*)
-module FilterClosures =
-struct
-	let name = "filter_closures"
-	let priority = solve_deps name [DAfter DynamicFieldAccess.priority]
-
-	let configure gen (should_change:texpr->string->bool) (filter:texpr->texpr->string->bool->texpr) =
-		let rec run e =
-			match e.eexpr with
-				(*(* this is precisely the only case where we won't even ask if we should change, because it is a direct use of TClosure *)
-				| TCall ( {eexpr = TClosure(e1,s)} as clos, args ) ->
-					{ e with eexpr = TCall({ clos with eexpr = TClosure(run e1, s) }, List.map run args ) }
-				| TCall ( clos, args ) ->
-					let rec loop clos = match clos.eexpr with
-						| TClosure(e1,s) -> Some (clos, e1, s)
-						| TParenthesis p -> loop p
-						| _ -> None
-					in
-					let clos = loop clos in
-					(match clos with
-						| Some (clos, e1, s) -> { e with eexpr = TCall({ clos with eexpr = TClosure(run e1, s) }, List.map run args ) }
-						| None -> Type.map_expr run e)*)
-					| TCall({ eexpr = TLocal{ v_name = "__delegate__" } } as local, [del]) ->
-						{ e with eexpr = TCall(local, [Type.map_expr run del]) }
-					| TCall(({ eexpr = TField(_, _) } as ef), params) ->
-						{ e with eexpr = TCall(Type.map_expr run ef, List.map run params) }
-					| TField(ef, FEnum(en, field)) ->
-							(* FIXME replace t_dynamic with actual enum Anon field *)
-							let ef = run ef in
-							(match follow field.ef_type with
-								| TFun _ when should_change ef field.ef_name ->
-									filter e ef field.ef_name true
-								| _ ->
-										{ e with eexpr = TField(ef, FEnum(en,field)) }
-							)
-					| TField(({ eexpr = TTypeExpr _ } as tf), f) ->
-						(match field_access_esp gen tf.etype (f) with
-							| FClassField(_,_,_,cf,_,_,_) ->
-								(match cf.cf_kind with
-									| Method(MethDynamic)
-									| Var _ ->
-										e
-									| _ when should_change tf cf.cf_name ->
-										filter e tf cf.cf_name true
-									| _ ->
-										e
-							 )
-							| _ -> e)
-					| TField(e1, FClosure (Some _, cf)) when should_change e1 cf.cf_name ->
-						(match cf.cf_kind with
-						| Method MethDynamic | Var _ ->
-							Type.map_expr run e
-						| _ ->
-							filter e (run e1) cf.cf_name false)
-					| _ -> Type.map_expr run e
-		in
-		let map e = Some(run e) in
-		gen.gexpr_filters#add ~name:name ~priority:(PCustom priority) map
-end;;
-
-
-(* ******************************************* *)
-(* Dynamic TArray Handling *)
-(* ******************************************* *)
-(*
-	In some languages you cannot overload the [] operator,
-	so we need to decide what is kept as TArray and what gets mapped.
-
-	depends on:
-		(syntax) must run before expression/statment normalization because it may generate complex expressions
-		(ok) must run before binop transformations because it may generate some untreated binop ops
-		(ok) must run before dynamic field access is transformed into reflection
-*)
-module TArrayTransform =
-struct
-	let name = "dyn_tarray"
-	let priority = solve_deps name [DBefore DynamicOperators.priority; DBefore DynamicFieldAccess.priority]
-
-	(* should change signature: tarray expr -> binop operation -> should change? *)
-	let configure gen (should_change:texpr->Ast.binop option->bool) (get_fun:string) (set_fun:string) =
-		let basic = gen.gcon.basic in
-		let mk_get e e1 e2 =
-			let efield = mk_field_access gen e1 get_fun e.epos in
-			{ e with eexpr = TCall(efield, [e2]) }
-		in
-		let mk_set e e1 e2 evalue =
-			let efield = mk_field_access gen e1 set_fun e.epos in
-			{ e with eexpr = TCall(efield, [e2; evalue]) }
-		in
-		let rec run e =
-			match e.eexpr with
-				| TArray(e1, e2) ->
-					(* e1 should always be a var; no need to map there *)
-					if should_change e None then mk_get e (run e1) (run e2) else Type.map_expr run e
-				| TBinop (Ast.OpAssign, ({ eexpr = TArray(e1a,e2a) } as earray), evalue) when should_change earray (Some Ast.OpAssign) ->
-					mk_set e (run e1a) (run e2a) (run evalue)
-				| TBinop (Ast.OpAssignOp op,({ eexpr = TArray(e1a,e2a) } as earray) , evalue) when should_change earray (Some (Ast.OpAssignOp op)) ->
-					(* cache all arguments in vars so they don't get executed twice *)
-					(* let ensure_local gen block name e = *)
-					let block = ref [] in
-
-					let arr_local = ensure_local gen block "array" (run e1a) in
-					let idx_local = ensure_local gen block "index" (run e2a) in
-					block := (mk_set e arr_local idx_local ( { e with eexpr=TBinop(op, mk_get earray arr_local idx_local, run evalue) } )) :: !block;
-
-					{ e with eexpr = TBlock (List.rev !block) }
-				| TUnop(op, flag, ({ eexpr = TArray(e1a, e2a) } as earray)) ->
-					if should_change earray None && match op with | Not | Neg -> false | _ -> true then begin
-
-						let block = ref [] in
-
-						let actual_t = match op with
-							| Ast.Increment | Ast.Decrement -> (match follow earray.etype with
-								| TInst _ | TAbstract _ | TEnum _ -> earray.etype
-								| _ -> basic.tfloat)
-							| Ast.Not -> basic.tbool
-							| _ -> basic.tint
-						in
-
-						let val_v = mk_temp gen "arrVal" actual_t in
-						let ret_v = mk_temp gen "arrRet" actual_t in
-
-						let arr_local = ensure_local gen block "arr" (run e1a) in
-						let idx_local = ensure_local gen block "arrIndex" (run e2a) in
-
-						let val_local = { earray with eexpr = TLocal(val_v) } in
-						let ret_local = { earray with eexpr = TLocal(ret_v) } in
-						(* var idx = 1; var val = x._get(idx); var ret = val++; x._set(idx, val); ret; *)
-						block := { eexpr = TVar(val_v, Some(mk_get earray arr_local idx_local)); (* var val = x._get(idx) *)
-											 etype = gen.gcon.basic.tvoid;
-											 epos = e2a.epos
-										 } :: !block;
-						block := { eexpr = TVar(ret_v, Some { e with eexpr = TUnop(op, flag, val_local) }); (* var ret = val++ *)
-												etype = gen.gcon.basic.tvoid;
-												epos = e2a.epos
-										 } :: !block;
-						block := (mk_set e arr_local idx_local val_local) (*x._set(idx,val)*) :: !block;
-						block := ret_local :: !block;
-						{ e with eexpr = TBlock (List.rev !block) }
-					end else
-						Type.map_expr run e
-				| _ -> Type.map_expr run e
-		in
-		let map e = Some(run e) in
-		gen.gexpr_filters#add ~name:"dyn_tarray" ~priority:(PCustom priority) map
-end;;
+let fun_args (l : (tvar * tconstant option) list)=
+	List.map (fun (v,s) -> (v.v_name, (s <> None), v.v_type)) l
 
-
-(* ******************************************* *)
-(* Try / Catch + throw native types handling *)
-(* ******************************************* *)
-(*
-	Some languages/vm's do not support throwing any kind of value. For them, only
-	special kinds of objects can be thrown. Because of this, we must wrap some throw
-	statements with an expression, and also we must unwrap it on the catch() phase, and
-	maybe manually test with Std.is()
-
-	dependencies:
-		must run before dynamic field access (?) TODO review
-		It's a syntax filter, as it alters types (throw wrapper)
-*)
-module TryCatchWrapper =
-struct
-	let priority = solve_deps "try_catch" [DBefore DynamicFieldAccess.priority]
-
-	(*
-		should_wrap : does the type should be wrapped? This of course works on the reverse way, so it tells us if the type should be unwrapped as well
-		wrap_throw : the wrapper for throw (throw expr->expr inside throw->returning wrapped expression)
-		unwrap_expr : the other way around : given the catch var (maybe will need casting to wrapper_type) , return the unwrap expr
-		rethrow_expr : how to rethrow ane exception in the platform
-		catchall_type : the class used for catchall (e:Dynamic)
-		wrapper_type : the wrapper type, so we can test if exception is of type 'wrapper'
-		catch_map : maps the catch expression to include some intialization code (e.g. setting up Stack.exceptionStack)
-	*)
-	let configure gen (should_wrap:t->bool) (wrap_throw:texpr->texpr->texpr) (unwrap_expr:tvar->pos->texpr) (rethrow_expr:texpr->texpr) (catchall_type:t) (wrapper_type:t) (catch_map:tvar->texpr->texpr) =
-		let rec run e =
-			match e.eexpr with
-					| TThrow texpr when should_wrap texpr.etype -> wrap_throw e (run texpr)
-					| TTry (ttry, catches) ->
-						let nowrap_catches, must_wrap_catches, catchall = List.fold_left (fun (nowrap_catches, must_wrap_catches, catchall) (v, catch) ->
-							(* first we'll see if the type is Dynamic (catchall) *)
-							match follow v.v_type with
-								| TDynamic _ ->
-									assert (is_none catchall);
-									(nowrap_catches, must_wrap_catches, Some(v,run catch))
-								(* see if we should unwrap it *)
-								| _ when should_wrap (follow v.v_type) ->
-									(nowrap_catches, (v,run catch) :: must_wrap_catches, catchall)
-								| _ ->
-									( (v,catch_map v (run catch)) :: nowrap_catches, must_wrap_catches, catchall )
-						) ([], [], None) catches
-						in
-						(* temp (?) fix for https://github.com/HaxeFoundation/haxe/issues/4134 *)
-						let must_wrap_catches = List.rev must_wrap_catches in
-						(*
-							1st catch all nowrap "the easy way"
-							2nd see if there are any must_wrap or catchall. If there is,
-								do a catchall first with a temp var.
-								then get catchall var (as dynamic) (or create one), and declare it = catchall exception
-								then test if it is of type wrapper_type. If it is, unwrap it
-								then start doing Std.is() tests for each catch type
-								if there is a catchall in the end, end with it. If there isn't, rethrow
-						*)
-						let dyn_catch = match (catchall, must_wrap_catches) with
-							| Some (v,c), _
-							| _, (v, c) :: _ ->
-								let pos = c.epos in
-								let temp_var = mk_temp gen "catchallException" catchall_type in
-								let temp_local = { eexpr=TLocal(temp_var); etype = temp_var.v_type; epos = pos } in
-								let catchall_var = (*match catchall with
-									| None -> *) mk_temp gen "catchall" t_dynamic
-									(*| Some (v,_) -> v*)
-								in
-								let catchall_decl = { eexpr = TVar(catchall_var, Some(temp_local)); etype=gen.gcon.basic.tvoid; epos = pos } in
-								let catchall_local = { eexpr = TLocal(catchall_var); etype = t_dynamic; epos = pos } in
-								(* if it is of type wrapper_type, unwrap it *)
-								let std_is = mk_static_field_access (get_cl (get_type gen ([],"Std"))) "is" (TFun(["v",false,t_dynamic;"cl",false,mt_to_t (get_type gen ([], "Class")) [t_dynamic]],gen.gcon.basic.tbool)) pos in
-								let mk_std_is t pos = { eexpr = TCall(std_is, [catchall_local; mk_mt_access (t_to_mt t) pos]); etype = gen.gcon.basic.tbool; epos = pos } in
-
-								let if_is_wrapper_expr = { eexpr = TIf(mk_std_is wrapper_type pos,
-									{ eexpr = TBinop(OpAssign, catchall_local, unwrap_expr temp_var pos); etype = t_dynamic; epos = pos }
-								, None); etype = gen.gcon.basic.tvoid; epos = pos } in
-								let rec loop must_wrap_catches = match must_wrap_catches with
-									| (vcatch,catch) :: tl ->
-										{ eexpr = TIf(mk_std_is vcatch.v_type catch.epos,
-											{ eexpr = TBlock({ eexpr=TVar(vcatch, Some(mk_cast vcatch.v_type catchall_local)); etype=gen.gcon.basic.tvoid; epos=catch.epos } :: [catch] ); etype = catch.etype; epos = catch.epos },
-											Some (loop tl));
-										etype = catch.etype; epos = catch.epos }
-									| [] ->
-										match catchall with
-											| Some (v,s) ->
-												Type.concat { eexpr = TVar(v, Some(catchall_local)); etype = gen.gcon.basic.tvoid; epos = pos } s
-											| None ->
-												mk_block (rethrow_expr temp_local)
-								in
-								[ ( temp_var, catch_map temp_var { e with eexpr = TBlock([ catchall_decl; if_is_wrapper_expr; loop must_wrap_catches ]) } ) ]
-							| _ ->
-								[]
-						in
-						{ e with eexpr = TTry(run ttry, (List.rev nowrap_catches) @ dyn_catch) }
-					| _ -> Type.map_expr run e
-		in
-		let map e = Some(run e) in
-		gen.gsyntax_filters#add ~name:"try_catch" ~priority:(PCustom priority) map
-end;;
-
-
-let fun_args = List.map (function | (v,s) -> (v.v_name, (match s with | None -> false | Some _ -> true), v.v_type))
-
-(* ******************************************* *)
-(* Closures To Class *)
-(* ******************************************* *)
-
-(*
-
-	This is a very important filter. It will take all anonymous functions from the AST, will search for all captured variables, and will create a class
-	that implements an abstract interface for calling functions. This is very important for targets that don't support anonymous functions to work correctly.
-	Also it is possible to implement some strategies to avoid value type boxing, such as NaN tagging or double/object arguments. All this will be abstracted away
-	from this interface.
-
-
-	dependencies:
-		must run after dynamic field access, because of conflicting ways to deal with invokeField
-		(module filter) must run after OverloadingConstructor so we can also change the dynamic function expressions
-
-		uses TArray expressions for array. TODO see interaction
-		uses TThrow expressions.
-*)
-
-module ClosuresToClass =
-struct
-	let name = "closures_to_class"
-	let priority = solve_deps name [ DAfter DynamicFieldAccess.priority ]
-
-	type closures_ctx = {
-		func_class : tclass;
-
-		(*
-			this is what will actually turn the function into class field.
-			The standard implementation by default will already take care of creating the class, and setting the captured variables.
-
-			It will also return the super arguments to be called
-		*)
-		closure_to_classfield : tfunc->t->pos->tclass_field * (texpr list);
-
-		(*
-			when a dynamic function call is made, we need to convert it as if it were calling the dynamic function interface.
-
-			TCall expr -> new TCall expr
-		*)
-		dynamic_fun_call : texpr->texpr;
-
-		(*
-			Provide a toolchain so we can easily create classes that extend Function and add more functionality on top of it.
-
-			arguments:
-				tclass -> subject (so we know the type of this)
-				( int -> (int->t->tconstant option->texpr) -> ( (tvar * tconstant option) list * texpr) )
-					int -> current arity of the function whose member will be mapped; -1 for dynamic function. It is guaranteed that dynamic function will be called last
-					t -> the return type of the function
-					(int->t->tconstant option->texpr) -> api to get exprs that unwrap arguments correctly
-						int -> argument wanted to unwrap
-						t -> solicited type
-						tconstant option -> map to this default value if null
-						returns a texpr that tells how the default
-					should return a list with additional arguments (only works if is_function_base = true)
-					and the underlying function expression
-		*)
-		map_base_classfields : tclass->( int -> t -> (tvar list) -> (int->t->tconstant option->texpr) -> texpr )->tclass_field list;
-	}
-
-	type map_info = {
-		in_unsafe : bool;
-		in_unused : bool;
-	}
-
-	let null_map_info = { in_unsafe = false; in_unused = false; }
-
-	(*
-		the default implementation will take 3 transformation functions:
-			* one that will transform closures that are not called immediately (instance.myFunc).
-				normally on this case it's best to have a runtime handler that will take the instance, the function and call its invokeField when invoked
-			* one that will actually handle the anonymous functions themselves.
-			* one that will transform calling a dynamic function. So for example, dynFunc(arg1, arg2) might turn into dynFunc.apply2(arg1, arg2);
-			( suspended ) * an option to match papplied functions
-			* handling parameterized anonymous function declaration (optional - tparam_anon_decl and tparam_anon_acc)
-	*)
-
-	let rec cleanup_delegate e = match e.eexpr with
-		| TParenthesis e | TMeta(_,e)
-		| TCast(e,_) -> cleanup_delegate e
-		| _ -> e
-
-	let funct gen t = match follow (run_follow gen t) with
-		| TFun(args,ret) -> args,ret
-		| _ -> raise Not_found
-
-	let mk_conversion_fun gen e =
-		let args, ret = funct gen e.etype in
-		let tf_args = List.map (fun (n,o,t) -> alloc_var n t,None) args in
-		let block, local = match e.eexpr with
-			| TLocal v ->
-				v.v_capture <- true;
-				[],e
-			| _ ->
-				let tmp = mk_temp gen "delegate_conv" e.etype in
-				tmp.v_capture <- true;
-				[{ eexpr = TVar(tmp,Some e); etype = gen.gcon.basic.tvoid; epos = e.epos }], mk_local tmp e.epos
-		in
-		let body = {
-			eexpr = TCall(local, List.map (fun (v,_) -> mk_local v e.epos) tf_args);
-			etype = ret;
-			epos = e.epos;
-		} in
-		let body = if not (ExtType.is_void ret) then
-			{ body with eexpr = TReturn( Some body ) }
-		else
-			body
-		in
-		let body = {
-			eexpr = TBlock([body]);
-			etype = body.etype;
-			epos = body.epos;
-		} in
-		block, {
-			tf_args = tf_args;
-			tf_expr = body;
-			tf_type = ret;
-		}
-
-	let traverse gen ?tparam_anon_decl ?tparam_anon_acc (handle_anon_func:texpr->tfunc->map_info->t option->texpr) (dynamic_func_call:texpr->texpr) e =
-		let info = ref null_map_info in
-		let rec run e =
-			match e.eexpr with
-				| TCast({ eexpr = TCall({ eexpr = TLocal{ v_name = "__delegate__" } } as local, [del] ) } as e2, _) ->
-					let e2 = { e2 with etype = e.etype } in
-					let replace_delegate ex =
-						{ e with eexpr = TCast({ e2 with eexpr = TCall(local, [ex]) }, None) }
-					in
-					(* found a delegate; let's see if it's a closure or not *)
-					let clean = cleanup_delegate del in
-					(match clean.eexpr with
-						| TField( ef, (FClosure _ as f)) | TField( ef, (FStatic _ as f)) ->
-							(* a closure; let's leave this unchanged for FilterClosures to handle it *)
-							replace_delegate { clean with eexpr = TField( run ef, f ) }
-						| TFunction tf ->
-							(* handle like we'd handle a normal function, but create an unchanged closure field for it *)
-							let ret = handle_anon_func clean { tf with tf_expr = run tf.tf_expr } !info (Some e.etype) in
-							replace_delegate ret
-						| _ -> try
-							let block, tf = mk_conversion_fun gen del in
-							let block = List.map run block in
-							let tf = { tf with tf_expr = run tf.tf_expr } in
-							let ret = handle_anon_func { clean with eexpr = TFunction(tf) } { tf with tf_expr = run tf.tf_expr } !info (Some e.etype) in
-							let ret = replace_delegate ret in
-							if block = [] then
-								ret
-							else
-								{ ret with eexpr = TBlock(block @ [ret]) }
-						with Not_found ->
-							gen.gcon.error "This delegate construct is unsupported" e.epos;
-							replace_delegate (run clean))
-
-				| TCall(({ eexpr = TLocal{ v_name = "__unsafe__" } } as local), [arg]) ->
-					let old = !info in
-					info := { !info with in_unsafe = true };
-					let arg2 = run arg in
-					info := old;
-					{ e with eexpr = TCall(local,[arg2]) }
-				(* parameterized functions handling *)
-				| TVar(vv, ve) -> (match tparam_anon_decl with
-					| None -> Type.map_expr run e
-					| Some tparam_anon_decl ->
-						(match (vv, ve) with
-							| ({ v_extra = Some( _ :: _, _) } as v), Some ({ eexpr = TFunction tf } as f)
-							| ({ v_extra = Some( _ :: _, _) } as v), Some { eexpr = TArrayDecl([{ eexpr = TFunction tf } as f]) | TCall({ eexpr = TLocal { v_name = "__array__" } }, [{ eexpr = TFunction tf } as f]) } -> (* captured transformation *)
-								ignore(tparam_anon_decl v f { tf with tf_expr = run tf.tf_expr });
-								{ e with eexpr = TBlock([]) }
-							| _ ->
-								Type.map_expr run { e with eexpr = TVar(vv, ve) })
-						)
-				| TLocal ({ v_extra = Some( _ :: _, _) } as v)
-				| TArray ({ eexpr = TLocal ({ v_extra = Some( _ :: _, _) } as v) }, _) -> (* captured transformation *)
-					(match tparam_anon_acc with
-					| None -> Type.map_expr run e
-					| Some tparam_anon_acc -> tparam_anon_acc v e)
-				| TCall( { eexpr = TField(_, FEnum _) }, _ ) ->
-					Type.map_expr run e
-				(* if a TClosure is being call immediately, there's no need to convert it to a TClosure *)
-				| TCall(( { eexpr = TField(ecl,f) } as e1), params) ->
-					(* check to see if called field is known and if it is a MethNormal (only MethNormal fields can be called directly) *)
-					(* let name = field_name f in *)
-					(match field_access_esp gen (gen.greal_type ecl.etype) f with
-						| FClassField(_,_,_,cf,_,_,_) ->
-							(match cf.cf_kind with
-								| Method MethNormal
-								| Method MethInline ->
-									{ e with eexpr = TCall({ e1 with eexpr = TField(run ecl, f) }, List.map run params) }
-								| _ ->
-									match gen.gfollow#run_f e1.etype with
-										| TFun _ ->
-											dynamic_func_call { e with eexpr = TCall(run e1, List.map run params) }
-										| _ ->
-											let i = ref 0 in
-											let t = TFun(List.map (fun e -> incr i; "arg" ^ (string_of_int !i), false, e.etype) params, e.etype) in
-											dynamic_func_call { e with eexpr = TCall( mk_castfast t (run e1), List.map run params ) }
-							)
-						(* | FNotFound ->
-							{ e with eexpr = TCall({ e1 with eexpr = TField(run ecl, f) }, List.map run params) }
-								(* expressions by now may have generated invalid expressions *) *)
-						| _ ->
-							match gen.gfollow#run_f e1.etype with
-								| TFun _ ->
-									dynamic_func_call { e with eexpr = TCall(run e1, List.map run params) }
-								| _ ->
-									let i = ref 0 in
-									let t = TFun(List.map (fun e -> incr i; "arg" ^ (string_of_int !i), false, e.etype) params, e.etype) in
-									dynamic_func_call { e with eexpr = TCall( mk_castfast t (run e1), List.map run params ) }
-					)
-				| TFunction tf ->
-					handle_anon_func e { tf with tf_expr = run tf.tf_expr } !info None
-				| TCall({ eexpr = TConst(TSuper) }, _) ->
-					Type.map_expr run e
-				| TCall({ eexpr = TLocal(v) }, args) when String.get v.v_name 0 = '_' && Hashtbl.mem gen.gspecial_vars v.v_name ->
-					Type.map_expr run e
-				| TCall(tc,params) ->
-					let i = ref 0 in
-					let may_cast = match gen.gfollow#run_f tc.etype with
-						| TFun _ -> fun e -> e
-						| _ ->
-							let t = TFun(List.map (fun e ->
-									incr i;
-									("p" ^ (string_of_int !i), false, e.etype)
-								) params, e.etype)
-							in
-							fun e -> mk_castfast t e
-					in
-					dynamic_func_call { e with eexpr = TCall(run (may_cast tc), List.map run params) }
-				| _ -> Type.map_expr run e
-		in
-
-		(match e.eexpr with
-			| TFunction(tf) -> Type.map_expr run e
-			| _ -> run e)
-
-	let rec get_type_params acc t =
-		match t with
-			| TInst(( { cl_kind = KTypeParameter _ } as cl), []) ->
-				if List.memq cl acc then acc else cl :: acc
-			| TFun (params,tret) ->
-				List.fold_left get_type_params acc ( tret :: List.map (fun (_,_,t) -> t) params )
-			| TDynamic t ->
-				(match t with | TDynamic _ -> acc | _ -> get_type_params acc t)
-			| TAbstract (a, pl) when not (Meta.has Meta.CoreType a.a_meta) ->
-					get_type_params acc ( Abstract.get_underlying_type a pl)
-			| TAnon a ->
-				PMap.fold (fun cf acc ->
-					let params = List.map (fun (_,t) -> match follow t with
-						| TInst(c,_) -> c
-						| _ -> assert false) cf.cf_params
-					in
-					List.filter (fun t -> not (List.memq t params)) (get_type_params acc cf.cf_type)
-				) a.a_fields acc
-			| TType(_, [])
-			| TAbstract (_, [])
-			| TInst(_, [])
-			| TEnum(_, []) ->
-				acc
-			| TType(_, params)
-			| TAbstract(_, params)
-			| TEnum(_, params)
-			| TInst(_, params) ->
-				List.fold_left get_type_params acc params
-			| TMono r -> (match !r with
-				| Some t -> get_type_params acc t
-				| None -> acc)
-			| _ -> get_type_params acc (follow_once t)
-
-	let get_captured expr =
-		let ret = Hashtbl.create 1 in
-		let ignored = Hashtbl.create 0 in
-
-		let params = ref [] in
-		let check_params t = params := get_type_params !params t in
-		let rec traverse expr =
-			match expr.eexpr with
-				| TFor (v, _, _) ->
-					Hashtbl.add ignored v.v_id v;
-					check_params v.v_type;
-					Type.iter traverse expr
-				| TFunction(tf) ->
-					List.iter (fun (v,_) -> Hashtbl.add ignored v.v_id v) tf.tf_args;
-					(match follow expr.etype with
-						| TFun(args,ret) ->
-							List.iter (fun (_,_,t) ->
-								check_params t
-							) args;
-							check_params ret
-						| _ -> ());
-					Type.iter traverse expr
-				| TVar (v, opt) ->
-					(match v.v_extra with
-						| Some(_ :: _, _) -> ()
-						| _ ->
-							check_params v.v_type);
-					Hashtbl.add ignored v.v_id v;
-					ignore(Option.map traverse opt)
-				| TLocal { v_extra = Some( (_ :: _ ),_) } ->
-					()
-				| TLocal(( { v_capture = true } ) as v) ->
-					(if not (Hashtbl.mem ignored v.v_id || Hashtbl.mem ret v.v_id) then begin check_params v.v_type; Hashtbl.replace ret v.v_id expr end);
-				| _ -> Type.iter traverse expr
-		in traverse expr;
-		ret, !params
-
-	(*
-		OPTIMIZEME:
-
-		Take off from Codegen the code that wraps captured variables,
-
-		traverse through all variables, looking for their use (just like local_usage)
-		three possible outcomes for captured variables:
-			- become a function member variable <- best performance.
-				Will not work on functions that can be created more than once (functions inside a loop or functions inside functions)
-				The function will have to be created on top of the block, so its variables can be filled in instead of being declared
-			- single-element array - the most compatible way, though also creates a slight overhead.
-		- we'll have some labels for captured variables:
-			- used in loop
-	*)
-
-	(*
-		The default implementation will impose a naming convention:
-			invoke(arity)_(o for returning object/d for returning double) when arity < max_arity
-			invoke_dynamic_(o/d) when arity > max_arity
-
-		This means that it also imposes that the dynamic function return types may only be Dynamic or Float, and all other basic types must be converted to/from it.
-	*)
-	let configure gen ft =
-
-		let handle_anon_func fexpr tfunc mapinfo delegate_type : texpr * (tclass * texpr list) =
-			let in_unsafe = mapinfo.in_unsafe || match gen.gcurrent_class, gen.gcurrent_classfield with
-				| Some c, _ when Meta.has Meta.Unsafe c.cl_meta -> true
-				| _, Some cf when Meta.has Meta.Unsafe cf.cf_meta -> true
-				| _ -> false
-			in
-			(* get all captured variables it uses *)
-			let captured_ht, tparams = get_captured fexpr in
-			let captured = Hashtbl.fold (fun _ e acc -> e :: acc) captured_ht [] in
-			let captured = List.sort (fun e1 e2 -> match e1, e2 with
-				| { eexpr = TLocal v1 }, { eexpr = TLocal v2 } ->
-					compare v1.v_name v2.v_name
-				| _ -> assert false) captured
-			in
-
-			(*let cltypes = List.map (fun cl -> (snd cl.cl_path, TInst(map_param cl, []) )) tparams in*)
-			let cltypes = List.map (fun cl -> (snd cl.cl_path, TInst(cl, []) )) tparams in
-
-			(* create a new class that extends abstract function class, with a ctor implementation that will setup all captured variables *)
-			let cfield = match gen.gcurrent_classfield with
-				| None -> "Anon"
-				| Some cf -> cf.cf_name
-			in
-			let cur_line = Lexer.get_error_line fexpr.epos in
-			let path = (fst gen.gcurrent_path, Printf.sprintf "%s_%s_%d__Fun" (snd gen.gcurrent_path) cfield cur_line) in
-			let cls = mk_class (get gen.gcurrent_class).cl_module path tfunc.tf_expr.epos in
-			if in_unsafe then cls.cl_meta <- (Meta.Unsafe,[],null_pos) :: cls.cl_meta;
-
-			if Common.defined gen.gcon Define.EraseGenerics then begin
-				cls.cl_meta <- (Meta.HaxeGeneric,[],null_pos) :: cls.cl_meta
-			end;
-			cls.cl_module <- (get gen.gcurrent_class).cl_module;
-			cls.cl_params <- cltypes;
-
-			let mk_this v pos =
-				{
-					(mk_field_access gen { eexpr = TConst TThis; etype = TInst(cls, List.map snd cls.cl_params); epos = pos } v.v_name pos)
-					with etype = v.v_type
-				}
-			in
-
-			let mk_this_assign v pos =
-			{
-				eexpr = TBinop(OpAssign, mk_this v pos, { eexpr = TLocal(v); etype = v.v_type; epos = pos });
-				etype = v.v_type;
-				epos = pos
-			} in
-
-			(* mk_class_field name t public pos kind params *)
-			let ctor_args, ctor_sig, ctor_exprs = List.fold_left (fun (ctor_args, ctor_sig, ctor_exprs) lexpr ->
-				match lexpr.eexpr with
-					| TLocal(v) ->
-						let cf = mk_class_field v.v_name v.v_type false lexpr.epos (Var({ v_read = AccNormal; v_write = AccNormal; })) [] in
-						cls.cl_fields <- PMap.add v.v_name cf cls.cl_fields;
-						cls.cl_ordered_fields <- cf :: cls.cl_ordered_fields;
-
-						let ctor_v = alloc_var v.v_name v.v_type in
-						((ctor_v, None) :: ctor_args, (v.v_name, false, v.v_type) :: ctor_sig, (mk_this_assign v cls.cl_pos) :: ctor_exprs)
-					| _ -> assert false
-			) ([],[],[]) captured in
-
-			(* change all captured variables to this.capturedVariable *)
-			let rec change_captured e =
-				match e.eexpr with
-					| TLocal( ({ v_capture = true }) as v ) when Hashtbl.mem captured_ht v.v_id ->
-						mk_this v e.epos
-					| _ -> Type.map_expr change_captured e
-			in
-			let func_expr = change_captured tfunc.tf_expr in
-
-			let invokecf, invoke_field, super_args = match delegate_type with
-				| None -> (* no delegate *)
-					let ifield, sa = ft.closure_to_classfield { tfunc with tf_expr = func_expr } fexpr.etype fexpr.epos in
-					ifield,ifield,sa
-				| Some _ ->
-					let pos = cls.cl_pos in
-					let cf = mk_class_field "Delegate" (TFun(fun_args tfunc.tf_args, tfunc.tf_type)) true pos (Method MethNormal) [] in
-					cf.cf_expr <- Some { fexpr with eexpr = TFunction { tfunc with tf_expr = func_expr }; };
-					cf.cf_meta <- (Meta.Final,[],pos) :: cf.cf_meta;
-					cls.cl_ordered_fields <- cf :: cls.cl_ordered_fields;
-					cls.cl_fields <- PMap.add cf.cf_name cf cls.cl_fields;
-					(* invoke function body: call Delegate function *)
-					let ibody = {
-						eexpr = TCall({
-							eexpr = TField({
-								eexpr = TConst TThis;
-								etype = TInst(cls, List.map snd cls.cl_params);
-								epos = pos;
-							}, FInstance(cls, List.map snd cls.cl_params, cf));
-							etype = cf.cf_type;
-							epos = pos;
-						}, List.map (fun (v,_) -> mk_local v pos) tfunc.tf_args);
-						etype = tfunc.tf_type;
-						epos = pos
-					} in
-					let ibody = if not (ExtType.is_void tfunc.tf_type) then
-						{ ibody with eexpr = TReturn( Some ibody ) }
-					else
-						ibody
-					in
-					let ifield, sa = ft.closure_to_classfield { tfunc with tf_expr = ibody } fexpr.etype fexpr.epos in
-					cf,ifield,sa
-			in
-
-			(* create the constructor *)
-			(* todo properly abstract how type var is set *)
-
-			cls.cl_super <- Some(ft.func_class, []);
-			let pos = cls.cl_pos in
-			let super_call =
-			{
-				eexpr = TCall({ eexpr = TConst(TSuper); etype = TInst(ft.func_class,[]); epos = pos }, super_args);
-				etype = gen.gcon.basic.tvoid;
-				epos = pos;
-			} in
-
-			let ctor_type = (TFun(ctor_sig, gen.gcon.basic.tvoid)) in
-			let ctor = mk_class_field "new" ctor_type true cls.cl_pos (Method(MethNormal)) [] in
-			ctor.cf_expr <- Some(
-			{
-				eexpr = TFunction(
-				{
-					tf_args = ctor_args;
-					tf_type = gen.gcon.basic.tvoid;
-					tf_expr = { eexpr = TBlock(super_call :: ctor_exprs); etype = gen.gcon.basic.tvoid; epos = cls.cl_pos }
-				});
-				etype = ctor_type;
-				epos = cls.cl_pos;
-			});
-			cls.cl_constructor <- Some(ctor);
-
-			(* add invoke function to the class *)
-			cls.cl_ordered_fields <- invoke_field :: cls.cl_ordered_fields;
-			cls.cl_fields <- PMap.add invoke_field.cf_name invoke_field cls.cl_fields;
-			cls.cl_overrides <- invoke_field :: cls.cl_overrides;
-
-			gen.gadd_to_module (TClassDecl cls) priority;
-
-			(* if there are no captured variables, we can create a cache so subsequent calls don't need to create a new function *)
-			let expr, clscapt =
-				match captured, tparams with
-				| [], [] ->
-					let cache_var = gen.gmk_internal_name "hx" "current" in
-					let cache_cf = mk_class_field cache_var (TInst(cls,[])) false func_expr.epos (Var({ v_read = AccNormal; v_write = AccNormal })) [] in
-					cls.cl_ordered_statics <- cache_cf :: cls.cl_ordered_statics;
-					cls.cl_statics <- PMap.add cache_var cache_cf cls.cl_statics;
-
-					(* if (FuncClass.hx_current != null) FuncClass.hx_current; else (FuncClass.hx_current = new FuncClass()); *)
-
-					(* let mk_static_field_access cl field fieldt pos = *)
-					let hx_current = mk_static_field_access cls cache_var (TInst(cls,[])) func_expr.epos in
-
-					let pos = func_expr.epos in
-					{ fexpr with
-						etype = hx_current.etype;
-						eexpr = TIf(
-							{
-								eexpr = TBinop(OpNotEq, hx_current, null (TInst(cls,[])) pos);
-								etype = gen.gcon.basic.tbool;
-								epos = pos;
-							},
-							hx_current,
-							Some(
-							{
-								eexpr = TBinop(OpAssign, hx_current, { fexpr with eexpr = TNew(cls, [], captured) });
-								etype = (TInst(cls,[]));
-								epos = pos;
-							}))
-					}, (cls,captured)
-				| _ ->
-					(* change the expression so it will be a new "added class" ( captured variables arguments ) *)
-					{ fexpr with eexpr = TNew(cls, List.map (fun cl -> TInst(cl,[])) tparams, List.rev captured) }, (cls,captured)
-			in
-			match delegate_type with
-			| None ->
-				expr,clscapt
-			| Some _ ->
-				{
-					eexpr = TField(expr, FClosure(Some (cls,[]),invokecf)); (* TODO: FClosure change *)
-					etype = invokecf.cf_type;
-					epos = cls.cl_pos
-				}, clscapt
-		in
-
-		let tvar_to_cdecl = Hashtbl.create 0 in
-
-		let run = traverse
-			gen
-			~tparam_anon_decl:(fun v e fn ->
-				let _, info = handle_anon_func e fn null_map_info None in
-				Hashtbl.add tvar_to_cdecl v.v_id info
-			)
-			~tparam_anon_acc:(fun v e -> try
-				let cls, captured = Hashtbl.find tvar_to_cdecl v.v_id in
-				let types = match v.v_extra with
-					| Some(t,_) -> t
-					| _ -> assert false
-				in
-				let monos = List.map (fun _ -> mk_mono()) types in
-				let vt = match follow v.v_type with
-					| TInst(_, [v]) -> v
-					| v -> v
-				in
-				let et = match follow e.etype with
-					| TInst(_, [v]) -> v
-					| v -> v
-				in
-				let original = apply_params types monos vt in
-				unify et original;
-
-				let monos = List.map (fun t -> apply_params types (List.map (fun _ -> t_dynamic) types) t) monos in
-
-				let same_cl t1 t2 = match follow t1, follow t2 with
-					| TInst(c,_), TInst(c2,_) -> c == c2
-					| _ -> false
-				in
-				let passoc = List.map2 (fun (_,t) m -> t,m) types monos in
-				let cltparams = List.map (fun (_,t) ->
-					try
-						snd (List.find (fun (t2,_) -> same_cl t t2) passoc)
-					with | Not_found -> t) cls.cl_params
-				in
-				{ e with eexpr = TNew(cls, cltparams, captured) }
-			with
-				| Not_found ->
-				gen.gcon.warning "This expression may be invalid" e.epos;
-				e
-				| Unify_error el ->
-					List.iter (fun el -> gen.gcon.warning (Error.unify_error_msg (print_context()) el) e.epos) el;
-				gen.gcon.warning "This expression may be invalid" e.epos;
-				e
-			)
-			(* (handle_anon_func:texpr->tfunc->texpr) (dynamic_func_call:texpr->texpr->texpr list->texpr) *)
-			(fun e f info delegate_type -> fst (handle_anon_func e f info delegate_type))
-			ft.dynamic_fun_call
-			(* (dynamic_func_call:texpr->texpr->texpr list->texpr) *)
-		in
-		let map e = Some(run e) in
-		gen.gexpr_filters#add ~name:name ~priority:(PCustom priority) map
-
-
-	(*
-		this submodule will provide the default implementation for the C# and Java targets.
-
-		it will have two return types: double and dynamic, and
-	*)
-	module DoubleAndDynamicClosureImpl =
-	struct
-		let get_ctx gen parent_func_class max_arity (* e.g. new haxe.lang.ClassClosure *) =
-			let basic = gen.gcon.basic in
-
-			let func_args_i i =
-				let rec loop i (acc) =
-					if i = 0 then (acc) else begin
-						let vfloat = alloc_var (gen.gmk_internal_name "fn" ("float" ^ string_of_int i)) basic.tfloat in
-						let vdyn = alloc_var (gen.gmk_internal_name "fn" ("dyn" ^ string_of_int i)) t_dynamic in
-
-						loop (i - 1) ((vfloat, None) :: (vdyn, None) :: acc)
-					end
-				in
-				loop i []
-			in
-
-			let args_real_to_func args =
-				let arity = List.length args in
-				if arity >= max_arity then
-					[ alloc_var (gen.gmk_internal_name "fn" "dynargs") (basic.tarray t_dynamic), None ]
-				else func_args_i arity
-			in
-
-			let func_sig_i i =
-				let rec loop i acc =
-					if i = 0 then acc else begin
-						let vfloat = gen.gmk_internal_name "fn" ("float" ^ string_of_int i) in
-						let vdyn = gen.gmk_internal_name "fn" ("dyn" ^ string_of_int i) in
-
-						loop (i - 1) ( (vfloat,false,basic.tfloat) :: (vdyn,false,t_dynamic) :: acc )
-					end
-				in
-				loop i []
-			in
-
-			let args_real_to_func_sig args =
-				let arity = List.length args in
-				if arity >= max_arity then
-					[gen.gmk_internal_name "fn" "dynargs", false, basic.tarray t_dynamic]
-				else begin
-					func_sig_i arity
-				end
-			in
-
-			let rettype_real_to_func t = match run_follow gen t with
-				| TType({ t_path = [],"Null" }, _) ->
-					0,t_dynamic
-				| _ when like_float t && not (like_i64 t) ->
-					(1, basic.tfloat)
-				| _ ->
-					(0, t_dynamic)
-			in
-
-			let args_real_to_func_call el (pos:pos) =
-				if List.length el >= max_arity then
-					[{ eexpr = TArrayDecl el; etype = basic.tarray t_dynamic; epos = pos }]
-				else begin
-					List.fold_left (fun acc e ->
-						if like_float (gen.greal_type e.etype) && not (like_i64 (gen.greal_type e.etype)) then
-							( e :: undefined e.epos :: acc )
-						else
-							( null basic.tfloat e.epos :: e :: acc )
-					) ([]) (List.rev el)
-				end
-			in
-
-			let const_type c def =
-				match c with
-				| TString _ -> basic.tstring
-				| TInt _ -> basic.tint
-				| TFloat _ -> basic.tfloat
-				| TBool _ -> basic.tbool
-				| _ -> def
-			in
-
-			let get_args_func args changed_args pos =
-				let arity = List.length args in
-				let mk_const const elocal t =
-					match const with
-					| None ->
-						mk_cast t elocal
-					| Some const ->
-						{ eexpr = TIf(
-							{ elocal with eexpr = TBinop(Ast.OpEq, elocal, null elocal.etype elocal.epos); etype = basic.tbool },
-							{ elocal with eexpr = TConst(const); etype = const_type const t },
-							Some ( mk_cast t elocal )
-						); etype = t; epos = elocal.epos }
-				in
-
-				if arity >= max_arity then begin
-					let varray = match changed_args with | [v,_] -> v | _ -> assert false in
-					let varray_local = mk_local varray pos in
-					let mk_varray i = { eexpr = TArray(varray_local, { eexpr = TConst(TInt(Int32.of_int i)); etype = basic.tint; epos = pos }); etype = t_dynamic; epos = pos } in
-
-					snd (List.fold_left (fun (count,acc) (v,const) ->
-						(count + 1, (mk (TVar(v, Some(mk_const const (mk_varray count) v.v_type))) basic.tvoid pos) :: acc)
-					) (0,[]) args)
-				end else begin
-					let _, dyn_args, float_args = List.fold_left (fun (count,fargs, dargs) arg ->
-						if count land 1 = 0 then
-							(count + 1, fargs, arg :: dargs)
-						else
-							(count + 1, arg :: fargs, dargs)
-					) (1,[],[]) (List.rev changed_args) in
-
-					let rec loop acc args fargs dargs =
-						match args, fargs, dargs with
-							| [], [], [] -> acc
-							| (v,const) :: args, (vf,_) :: fargs, (vd,_) :: dargs ->
-								let acc = { eexpr = TVar(v, Some(
-									{
-										eexpr = TIf(
-											{ eexpr = TBinop(Ast.OpEq, mk_local vd pos, undefined pos); etype = basic.tbool; epos = pos },
-											mk_cast v.v_type (mk_local vf pos),
-											Some ( mk_const const (mk_local vd pos) v.v_type )
-										);
-										etype = v.v_type;
-										epos = pos
-									} )); etype = basic.tvoid; epos = pos } :: acc in
-								loop acc args fargs dargs
-							| _ -> assert false
-					in
-
-					loop [] args float_args dyn_args
-				end
-			in
-
-			let closure_to_classfield tfunc old_sig pos =
-				(* change function signature *)
-				let old_args = tfunc.tf_args in
-				let changed_args = args_real_to_func old_args in
-
-				(*
-					FIXME properly handle int64 cases, which will break here (because of inference to int)
-					UPDATE: the fix will be that Int64 won't be a typedef to Float/Int
-				*)
-				let changed_sig, arity, type_number, changed_sig_ret, is_void, is_dynamic_func = match follow old_sig with
-					| TFun(_sig, ret) ->
-						let type_n, ret_t = rettype_real_to_func ret in
-						let arity = List.length _sig in
-						let is_dynamic_func = arity >= max_arity in
-						let ret_t = if is_dynamic_func then t_dynamic else ret_t in
-
-						(TFun(args_real_to_func_sig _sig, ret_t), arity, type_n, ret_t, ExtType.is_void ret, is_dynamic_func)
-					| _ -> (print_endline (s_type (print_context()) (follow old_sig) )); assert false
-				in
-
-				let tf_expr = if is_void then begin
-					let rec map e =
-						match e.eexpr with
-							| TReturn None -> { e with eexpr = TReturn (Some (null t_dynamic e.epos)) }
-							| _ -> Type.map_expr map e
-					in
-					let e = mk_block (map tfunc.tf_expr) in
-					match e.eexpr with
-						| TBlock(bl) ->
-							{ e with eexpr = TBlock(bl @ [{ eexpr = TReturn (Some (null t_dynamic e.epos)); etype = t_dynamic; epos = e.epos }]) }
-						| _ -> assert false
-				end else tfunc.tf_expr in
-
-				let changed_sig_ret = if is_dynamic_func then t_dynamic else changed_sig_ret in
-
-				(* get real arguments on top of function body *)
-				let get_args = get_args_func tfunc.tf_args changed_args pos in
-				(*
-					FIXME HACK: in order to be able to run the filters that have already ran for this piece of code,
-					we will cheat and run it as if it was the whole code
-					We could just make ClosuresToClass run before TArrayTransform, but we cannot because of the
-					dependency between ClosuresToClass (after DynamicFieldAccess, and before TArrayTransform)
-
-					maybe a way to solve this would be to add an "until" field to run_from
-				*)
-				let real_get_args = gen.gexpr_filters#run_f { eexpr = TBlock(get_args); etype = basic.tvoid; epos = pos } in
-
-				let func_expr = Type.concat real_get_args tf_expr in
-
-				(* set invoke function *)
-				(* todo properly abstract how naming for invoke is made *)
-				let invoke_name = if is_dynamic_func then "invokeDynamic" else ("invoke" ^ (string_of_int arity) ^ (if type_number = 0 then "_o" else "_f")) in
-				let invoke_name = gen.gmk_internal_name "hx" invoke_name in
-				let invoke_field = mk_class_field invoke_name changed_sig false func_expr.epos (Method(MethNormal)) [] in
-				let invoke_fun = {
-					eexpr = TFunction {
-						tf_args = changed_args;
-						tf_type = changed_sig_ret;
-						tf_expr = func_expr;
-					};
-					etype = changed_sig;
-					epos = func_expr.epos;
-				} in
-				invoke_field.cf_expr <- Some invoke_fun;
-
-				invoke_field, [
-					ExprBuilder.make_int gen.gcon arity pos;
-					ExprBuilder.make_int gen.gcon type_number pos;
-				]
-			in
-
-			let dynamic_fun_call call_expr =
-				let tc, params = match call_expr.eexpr with
-					| TCall(tc, params) -> tc, params
-					| _ -> assert false
-				in
-				let ct = gen.greal_type call_expr.etype in
-				let postfix, ret_t =
-					if like_float ct && not (like_i64 ct) then
-							"_f", gen.gcon.basic.tfloat
-					else
-						"_o", t_dynamic
-				in
-				let params_len = List.length params in
-				let ret_t = if params_len >= max_arity then t_dynamic else ret_t in
-
-				let invoke_fun = if params_len >= max_arity then "invokeDynamic" else "invoke" ^ (string_of_int params_len) ^ postfix in
-				let invoke_fun = gen.gmk_internal_name "hx" invoke_fun in
-				let fun_t = match follow tc.etype with
-					| TFun(_sig, _) ->
-						TFun(args_real_to_func_sig _sig, ret_t)
-					| _ ->
-						let i = ref 0 in
-						let _sig = List.map (fun p -> let name = "arg" ^ (string_of_int !i) in incr i; (name,false,p.etype) ) params in
-						TFun(args_real_to_func_sig _sig, ret_t)
-				in
-
-				let may_cast = match follow call_expr.etype with
-					| TAbstract ({ a_path = ([], "Void") },[]) -> (fun e -> e)
-					| _ -> mk_cast call_expr.etype
-				in
-
-				may_cast
-				{
-					eexpr = TCall(
-						{ (mk_field_access gen { tc with etype = gen.greal_type tc.etype } invoke_fun tc.epos) with etype = fun_t },
-						args_real_to_func_call params call_expr.epos
-					);
-					etype = ret_t;
-					epos = call_expr.epos
-				}
-			in
-
-			let iname i is_float =
-				let postfix = if is_float then "_f" else "_o" in
-				gen.gmk_internal_name "hx" ("invoke" ^ string_of_int i) ^ postfix
-			in
-
-			let map_base_classfields cl map_fn =
-				let pos = cl.cl_pos in
-				let this_t = TInst(cl,List.map snd cl.cl_params) in
-				let this = { eexpr = TConst(TThis); etype = this_t; epos = pos } in
-				let mk_this field t = { (mk_field_access gen this field pos) with etype = t } in
-
-				let mk_invoke_i i is_float =
-					let cf = mk_class_field (iname i is_float) (TFun(func_sig_i i, if is_float then basic.tfloat else t_dynamic)) false pos (Method MethNormal) [] in
-					cf
-				in
-
-				let type_name = gen.gmk_internal_name "fn" "type" in
-				let dynamic_arg = alloc_var (gen.gmk_internal_name "fn" "dynargs") (basic.tarray t_dynamic) in
-
-				let mk_invoke_complete_i i is_float =
-
-					(* let arity = i in *)
-					let args = func_args_i i in
-
-					(* api fn *)
-
-					(* only cast if needed *)
-					let mk_cast tto efrom = gen.ghandle_cast (gen.greal_type tto) (gen.greal_type efrom.etype) efrom in
-					let api i t const =
-						let vf, _ = List.nth args (i * 2) in
-						let vo, _ = List.nth args (i * 2 + 1) in
-
-						let needs_cast, is_float = match t, like_float t && not (like_i64 t) with
-							| TAbstract({ a_path = ([], "Float") },[]), _ -> false, true
-							| _, true -> true, true
-							| _ -> false,false
-						in
-
-						let olocal = mk_local vo pos in
-						let flocal = mk_local vf pos in
-
-						let get_from_obj e = match const with
-							| None -> mk_cast t e
-							| Some tc ->
-								{
-									eexpr = TIf(
-										{ eexpr = TBinop(Ast.OpEq, olocal, null t_dynamic pos); etype = basic.tbool; epos = pos } ,
-										{ eexpr = TConst(tc); etype = t; epos = pos },
-										Some (mk_cast t e)
-									);
-									etype = t;
-									epos = pos;
-								}
-						in
-
-						{
-							eexpr = TIf(
-								{ eexpr = TBinop(Ast.OpEq, olocal, undefined pos); etype = basic.tbool; epos = pos },
-								(if needs_cast then mk_cast t flocal else flocal),
-								Some ( get_from_obj olocal )
-							);
-							etype = t;
-							epos = pos
-						}
-					in
-					(* end of api fn *)
-
-					let ret = if is_float then basic.tfloat else t_dynamic in
-
-					let fn_expr = map_fn i ret (List.map fst args) api in
-
-					let t = TFun(fun_args args, ret) in
-
-					let tfunction =
-						{
-							eexpr = TFunction({
-								tf_args = args;
-								tf_type = ret;
-								tf_expr =
-								mk_block fn_expr
-							});
-							etype = t;
-							epos = pos;
-						}
-					in
-
-					let cf = mk_invoke_i i is_float in
-					cf.cf_expr <- Some tfunction;
-					cf
-				in
-
-				let rec loop i cfs =
-					if i < 0 then cfs else begin
-						(*let mk_invoke_complete_i i is_float =*)
-						(mk_invoke_complete_i i false) :: (mk_invoke_complete_i i true) :: (loop (i-1) cfs)
-					end
-				in
-
-				let cfs = loop max_arity [] in
-
-				let switch =
-					let api i t const =
-						match i with
-							| -1 ->
-								mk_local dynamic_arg pos
-							| _ ->
-								mk_cast t {
-									eexpr = TArray(
-										mk_local dynamic_arg pos,
-										{ eexpr = TConst(TInt(Int32.of_int i)); etype = basic.tint; epos = pos });
-									etype = t;
-									epos = pos;
-								}
-					in
-					map_fn (-1) t_dynamic [dynamic_arg] api
-				in
-
-				let args = [dynamic_arg, None] in
-				let dyn_t = TFun(fun_args args, t_dynamic) in
-				let dyn_cf = mk_class_field (gen.gmk_internal_name "hx" "invokeDynamic") dyn_t false pos (Method MethNormal) [] in
-
-				dyn_cf.cf_expr <- Some {
-					eexpr = TFunction {
-						tf_args = args;
-						tf_type = t_dynamic;
-						tf_expr = mk_block switch
-					};
-					etype = dyn_t;
-					epos = pos;
-				};
-
-				let additional_cfs = begin
-					let new_t = TFun(["arity", false, basic.tint; "type", false, basic.tint],basic.tvoid) in
-					let new_cf = mk_class_field "new" (new_t) true pos (Method MethNormal) [] in
-					let v_arity, v_type = alloc_var "arity" basic.tint, alloc_var "type" basic.tint in
-					let mk_assign v field = mk (TBinop (OpAssign, mk_this field v.v_type, mk_local v pos)) v.v_type pos in
-
-					let arity_name = gen.gmk_internal_name "hx" "arity" in
-					new_cf.cf_expr <- Some {
-						eexpr = TFunction({
-							tf_args = [v_arity, None; v_type, None];
-							tf_type = basic.tvoid;
-							tf_expr =
-							{
-								eexpr = TBlock([
-									mk_assign v_type type_name;
-									mk_assign v_arity arity_name
-								]);
-								etype = basic.tvoid;
-								epos = pos;
-							}
-						});
-						etype = new_t;
-						epos = pos;
-					};
-
-					[
-						new_cf;
-						mk_class_field type_name basic.tint true pos (Var { v_read = AccNormal; v_write = AccNormal }) [];
-						mk_class_field arity_name basic.tint true pos (Var { v_read = AccNormal; v_write = AccNormal }) [];
-					]
-				end in
-
-				dyn_cf :: (additional_cfs @ cfs)
-			in
-
-			begin
-				(*
-					setup fields for the abstract implementation of the Function class
-
-					new(arity, type)
-					{
-						this.arity = arity;
-						this.type = type;
-					}
-
-					hx::invokeX_f|o (where X is from 0 to max_arity) (args)
-					{
-						if (this.type == 0|1) return invokeX_o|f(args); else throw "Invalid number of arguments."
-					}
-
-					hx::invokeDynamic, which will work in the same way
-				*)
-				let cl = parent_func_class in
-				let pos = cl.cl_pos in
-
-				let rec mk_dyn_call arity api =
-					let zero = ExprBuilder.make_float gen.gcon "0.0" pos in
-					let rec loop i acc =
-						if i = 0 then
-							acc
-						else begin
-							let arr = api (i - 1) t_dynamic None in
-							loop (i - 1) (zero :: arr :: acc)
-						end
-					in
-					loop arity []
-				in
-
-				let this = mk (TConst TThis) (TInst (cl, List.map snd cl.cl_params)) pos in
-				let mk_this field t = { (mk_field_access gen this field pos) with etype = t } in
-
-				let mk_invoke_switch i api =
-					let t = TFun (func_sig_i i, t_dynamic) in
-					(* case i: return this.invokeX_o(0, 0, 0, 0, 0, ... arg[0], args[1]....); *)
-					[ExprBuilder.make_int gen.gcon i pos], mk_return (mk (TCall(mk_this (iname i false) t, mk_dyn_call i api)) t_dynamic pos)
-				in
-				let rec loop_cases api arity acc =
-					if arity < 0 then
-						acc
-					else
-						loop_cases api (arity - 1) (mk_invoke_switch arity api :: acc)
-				in
-
-				let type_name = gen.gmk_internal_name "fn" "type" in
-				let mk_expr i is_float vars =
-					let call_expr =
-						let call_t = TFun(List.map (fun v -> (v.v_name, false, v.v_type)) vars, if is_float then t_dynamic else basic.tfloat) in
-						{
-							eexpr = TCall(mk_this (iname i (not is_float)) call_t, List.map (fun v -> mk_local v pos) vars);
-							etype = if is_float then t_dynamic else basic.tfloat;
-							epos = pos
-						}
-					in
-					{
-						eexpr = TIf(
-							mk (TBinop (Ast.OpNotEq, mk_this type_name basic.tint, (ExprBuilder.make_int gen.gcon (if is_float then 0 else 1) pos))) basic.tbool pos,
-							mk (TThrow (ExprBuilder.make_string gen.gcon "Wrong number of arguments" pos)) t_dynamic pos,
-							Some (mk_return call_expr)
-						);
-						etype = t_dynamic;
-						epos = pos;
-					}
-				in
-
-				let arities_processed = Hashtbl.create 10 in
-				let max_arity = ref 0 in
-
-				let map_fn cur_arity fun_ret_type vars (api:int->t->tconstant option->texpr) =
-					let is_float = like_float fun_ret_type && not (like_i64 fun_ret_type) in
-					match cur_arity with
-					| -1 ->
-						let dynargs = api (-1) t_dynamic None in
-
-						(* (dynargs == null) ? 0 : dynargs.length *)
-						let switch_cond = {
-							eexpr = TIf(
-								mk (TBinop (OpEq, dynargs, null dynargs.etype pos)) basic.tbool pos,
-								mk (TConst (TInt Int32.zero)) basic.tint pos,
-								Some (mk_field_access gen dynargs "length" pos));
-							etype = basic.tint;
-							epos = pos;
-						} in
-
-						{
-							eexpr = TSwitch(
-								switch_cond,
-								loop_cases api !max_arity [],
-								Some(mk (TThrow (ExprBuilder.make_string gen.gcon "Too many arguments" pos)) basic.tvoid pos));
-							etype = basic.tvoid;
-							epos = pos;
-						}
-					| _ ->
-						if not (Hashtbl.mem arities_processed cur_arity) then begin
-							Hashtbl.add arities_processed cur_arity true;
-							if cur_arity > !max_arity then max_arity := cur_arity
-						end;
-
-						mk_expr cur_arity is_float vars
-				in
-
-				let cfs = map_base_classfields cl map_fn in
-				List.iter (fun cf ->
-					if cf.cf_name = "new" then
-						parent_func_class.cl_constructor <- Some cf
-					else
-						parent_func_class.cl_fields <- PMap.add cf.cf_name cf parent_func_class.cl_fields
-				) cfs;
-				parent_func_class.cl_ordered_fields <- (List.filter (fun cf -> cf.cf_name <> "new") cfs) @ parent_func_class.cl_ordered_fields
-			end;
-
-			{
-				func_class = parent_func_class;
-				closure_to_classfield = closure_to_classfield;
-				dynamic_fun_call = dynamic_fun_call;
-				map_base_classfields = map_base_classfields;
-			}
-	end;;
-end;;
-
-
-(* ******************************************* *)
-(* Type Parameters *)
-(* ******************************************* *)
-
-(*
-
-	This module will handle type parameters. There are lots of changes we need to do to correctly support type parameters:
-
-	traverse will:
-		V Detect when parameterized function calls are made
-		* Detect when a parameterized class instance is being cast to another parameter
-		* Change new<> parameterized function calls
-		*
-
-	extras:
-		* On languages that support "real" type parameters, a Cast function is provided that will convert from a <Dynamic> to the requested type.
-			This cast will call createEmpty with the correct type, and then set each variable to the new form. Some types will be handled specially, namely the Native Array.
-			Other implementations may be delegated to the runtime.
-		* parameterized classes will implement a new interface (with only a Cast<> function added to it), so we can access the <Dynamic> type parameter for them. Also any reference to <Dynamic> will be replaced by a reference to this interface. (also on TTypeExpr - Std.is())
-		* Type parameter renaming to avoid name clash
-		* Detect type parameter casting and call Cast<> instead
-
-	for java:
-		* for specially assigned classes, parameters will be replaced by _d and _i versions of parameterized functions. This will only work for parameterized classes, not functions.
-
-	dependencies:
-		must run after casts are detected. This will be ensured at CastDetect module.
-
-*)
-
-module TypeParams =
-struct
-
-	let name = "type_params"
-
-	let priority = max_dep -. 20.
-
-	(* this function will receive the original function argument, the applied function argument and the original function parameters. *)
-	(* from this info, it will infer the applied tparams for the function *)
-	(* this function is used by CastDetection module *)
-	let infer_params gen pos (original_args:((string * bool * t) list * t)) (applied_args:((string * bool * t) list * t)) (params:(string * t) list) calls_parameters_explicitly : tparams =
-		match params with
-		| [] -> []
-		| _ ->
-			let args_list args = (if not calls_parameters_explicitly then t_dynamic else snd args) :: (List.map (fun (n,o,t) -> t) (fst args)) in
-
-			let monos = List.map (fun _ -> mk_mono()) params in
-			let original = args_list (get_fun (apply_params params monos (TFun(fst original_args,snd original_args)))) in
-			let applied = args_list applied_args in
-
-			(try
-				List.iter2 (fun a o ->
-					unify a o
-					(* type_eq EqStrict a o *)
-				) applied original
-				(* unify applied original *)
-			with | Unify_error el ->
-					(* List.iter (fun el -> gen.gcon.warning (Typecore.unify_error_msg (print_context()) el) pos) el; *)
-					gen.gcon.warning ("This expression may be invalid") pos
-		 | Invalid_argument("List.map2") ->
-					gen.gcon.warning ("This expression may be invalid") pos
-			);
-
-			List.map (fun t ->
-				match follow t with
-					| TMono _ ->	t_empty
-					| t -> t
-			) monos
-
-	(* ******************************************* *)
-	(* Real Type Parameters Module *)
-	(* ******************************************* *)
-
-	(*
-		This submodule is by now specially made for the .NET platform. There might be other targets that will
-		make use of this, but it IS very specific.
-
-		On the .NET platform, generics are real specialized classes that are JIT compiled. For this reason, we cannot
-		cast from one type parameter to another. Also there is no common type for the type parameters, so for example
-		an instance of type Array<Int> will return false for instance is Array<object> .
-
-		So we need to:
-			1. create a common interface (without type parameters) (e.g. "Array") which will only contain a __Cast<> function, which will cast from one type into another
-			2. Implement the __Cast function. This part is a little hard, as we must identify all type parameter-dependent fields contained in the class and convert them.
-			In most cases the conversion will just be to call .__Cast<>() on the instances, or just a simple cast. But when the instance is a @:nativegen type, there will be no .__Cast
-			function, and we will need to deal with this case either at compile-time (added handlers - specially for NativeArray), or at runtime (adding new runtime handlers)
-			3. traverse the AST looking for casts involving type parameters, and replace them with .__Cast<>() calls. If type is @:nativegen, throw a warning. If really casting from one type parameter to another on a @:nativegen context, throw an error.
-
-
-		special literals:
-			it will use the special literal __typehandle__ that the target must implement in order to run this. This literal is a way to get the typehandle of e.g. the type parameters,
-			so we can compare them. In C# it's the equivalent of typeof(T).TypeHandle (TypeHandle compare is faster than System.Type.Equals())
-
-		dependencies:
-			(module filter) Interface creation must run AFTER enums are converted into classes, otherwise there is no way to tell parameterized enums to implement an interface
-			Must run AFTER CastDetect. This will be ensured per CastDetect
-
-	*)
-
-	module RealTypeParams =
-	struct
-
-		let name = "real_type_params"
-
-		let priority = priority
-
-		let rec has_type_params t =
-			match follow t with
-				| TInst( { cl_kind = KTypeParameter _ }, _) -> true
-				| TAbstract(_, params)
-				| TEnum(_, params)
-				| TInst(_, params) -> List.exists (fun t -> has_type_params t) params
-				| TFun(args,ret) ->
-					List.exists (fun (n,o,t) -> has_type_params t) args || has_type_params ret
-				| _ -> false
-
-		let rec follow_all_md md =
-			let t = match md with
-				| TClassDecl { cl_kind = KAbstractImpl a } ->
-					TAbstract(a, List.map snd a.a_params)
-				| TClassDecl c ->
-					TInst(c, List.map snd c.cl_params)
-				| TEnumDecl e ->
-					TEnum(e, List.map snd e.e_params)
-				| TTypeDecl t ->
-					TType(t, List.map snd t.t_params)
-				| TAbstractDecl a ->
-					TAbstract(a, List.map snd a.a_params)
-			in
-			Abstract.follow_with_abstracts t
-
-		let rec is_hxgeneric md =
-			match md with
-			| TClassDecl { cl_kind = KAbstractImpl a } ->
-				is_hxgeneric (TAbstractDecl a)
-			| TClassDecl(cl) ->
-				not (Meta.has Meta.NativeGeneric cl.cl_meta)
-			| TEnumDecl(e) ->
-				not (Meta.has Meta.NativeGeneric e.e_meta)
-			| TAbstractDecl(a) when Meta.has Meta.NativeGeneric a.a_meta ->
-				not (Meta.has Meta.NativeGeneric a.a_meta)
-			| md -> match follow_all_md md with
-				| TInst(cl,_) -> is_hxgeneric (TClassDecl cl)
-				| TEnum(e,_) -> is_hxgeneric (TEnumDecl e)
-				| TAbstract(a,_) -> not (Meta.has Meta.NativeGeneric a.a_meta)
-				| _ -> true
-
-		let rec set_hxgeneric gen mds isfirst md =
-			let path = t_path md in
-			if List.exists (fun m -> path = t_path m) mds then begin
-				if isfirst then
-					None (* we still can't determine *)
-				else
-					Some true (* if we're in second pass and still can't determine, it's because it can be hxgeneric *)
-			end else begin
-				let has_unresolved = ref false in
-				let is_false v =
-					match v with
-						| Some false -> true
-						| None -> has_unresolved := true; false
-						| Some true -> false
-				in
-
-				let mds = md :: mds in
-				match md with
-					| TClassDecl(cl)	->
-						(* first see if any meta is present (already processed) *)
-						if Meta.has Meta.NativeGeneric cl.cl_meta then
-							Some false
-						else if Meta.has Meta.HaxeGeneric cl.cl_meta then
-							Some true
-						else if cl.cl_params = [] && is_hxgen md then
-							(cl.cl_meta <- (Meta.HaxeGeneric,[],cl.cl_pos) :: cl.cl_meta;
-							Some true)
-						else if cl.cl_params = [] then
-							(cl.cl_meta <- (Meta.NativeGeneric, [], cl.cl_pos) :: cl.cl_meta;
-							Some false)
-						else if not (is_hxgen md) then
-							(cl.cl_meta <- (Meta.NativeGeneric, [], cl.cl_pos) :: cl.cl_meta;
-							Some false)
-						else begin
-							(*
-								if it's not present, see if any superclass is nativegeneric.
-								nativegeneric is inherited, while hxgeneric can be later changed to nativegeneric
-							*)
-							(* on the first pass, our job is to find any evidence that makes it not be hxgeneric. Otherwise it will be hxgeneric *)
-							match cl.cl_super with
-								| Some (c,_) when is_false (set_hxgeneric gen mds isfirst (TClassDecl c)) ->
-									cl.cl_meta <- (Meta.NativeGeneric, [], cl.cl_pos) :: cl.cl_meta;
-									Some false
-								| _ ->
-									(* see if it's a generic class *)
-									match cl.cl_params with
-										| [] ->
-											(* if it's not, then it will follow hxgen *)
-											if is_hxgen (TClassDecl cl) then
-												cl.cl_meta <- (Meta.HaxeGeneric, [], cl.cl_pos) :: cl.cl_meta
-											else
-												cl.cl_meta <- (Meta.NativeGeneric, [], cl.cl_pos) :: cl.cl_meta;
-											Some true
-										| _ ->
-											(* if it is, loop through all fields + statics and look for non-hxgeneric
-												generic classes that have KTypeParameter as params *)
-											let rec loop cfs =
-												match cfs with
-													| [] -> false
-													| cf :: cfs ->
-														let t = follow (gen.greal_type cf.cf_type) in
-														match t with
-															| TInst( { cl_kind = KTypeParameter _ }, _ ) -> loop cfs
-															| TInst(cl,p) when has_type_params t && is_false (set_hxgeneric gen mds isfirst (TClassDecl cl)) ->
-																if not (Hashtbl.mem gen.gtparam_cast cl.cl_path) then true else loop cfs
-															| TEnum(e,p) when has_type_params t && is_false (set_hxgeneric gen mds isfirst (TEnumDecl e)) ->
-																if not (Hashtbl.mem gen.gtparam_cast e.e_path) then true else loop cfs
-															| _ -> loop cfs (* TAbstracts / Dynamics can't be generic *)
-											in
-											if loop cl.cl_ordered_fields then begin
-												cl.cl_meta <- (Meta.NativeGeneric, [], cl.cl_pos) :: cl.cl_meta;
-												Some false
-											end else if isfirst && !has_unresolved then
-												None
-											else begin
-												cl.cl_meta <- (Meta.HaxeGeneric, [], cl.cl_pos) :: cl.cl_meta;
-												Some true
-											end
-						end
-					| TEnumDecl e ->
-						if Meta.has Meta.NativeGeneric e.e_meta then
-							Some false
-						else if Meta.has Meta.HaxeGeneric e.e_meta then
-							Some true
-						else if not (is_hxgen (TEnumDecl e)) then begin
-							e.e_meta <- (Meta.NativeGeneric, [], e.e_pos) :: e.e_meta;
-							Some false
-						end else begin
-							(* if enum is not generic, then it's hxgeneric *)
-							match e.e_params with
-								| [] ->
-									e.e_meta <- (Meta.HaxeGeneric, [], e.e_pos) :: e.e_meta;
-									Some true
-								| _ ->
-									let rec loop efs =
-										match efs with
-											| [] -> false
-											| ef :: efs ->
-												let t = follow (gen.greal_type ef.ef_type) in
-												match t with
-													| TFun(args, _) ->
-														if List.exists (fun (n,o,t) ->
-															let t = follow t in
-															match t with
-																| TInst( { cl_kind = KTypeParameter _ }, _ ) ->
-																	false
-																| TInst(cl,p) when has_type_params t && is_false (set_hxgeneric gen mds isfirst (TClassDecl cl)) ->
-																	not (Hashtbl.mem gen.gtparam_cast cl.cl_path)
-																| TEnum(e,p) when has_type_params t && is_false (set_hxgeneric gen mds isfirst (TEnumDecl e)) ->
-																	not (Hashtbl.mem gen.gtparam_cast e.e_path)
-																| _ -> false
-														) args then
-															true
-														else
-															loop efs
-													| _ -> loop efs
-									in
-									let efs = PMap.fold (fun ef acc -> ef :: acc) e.e_constrs [] in
-									if loop efs then begin
-										e.e_meta <- (Meta.NativeGeneric, [], e.e_pos) :: e.e_meta;
-										Some false
-									end else if isfirst && !has_unresolved then
-										None
-									else begin
-										e.e_meta <- (Meta.HaxeGeneric, [], e.e_pos) :: e.e_meta;
-										Some true
-									end
-						end
-					| _ -> assert false
-			end
-
-		let set_hxgeneric gen md =
-			let ret = match md with
-				| TClassDecl { cl_kind = KAbstractImpl a } -> (match follow_all_md md with
-					| (TInst _ | TEnum _ as t) -> (
-						let md = match t with
-							| TInst(cl,_) -> TClassDecl cl
-							| TEnum(e,_) -> TEnumDecl e
-							| _ -> assert false
-						in
-						let ret = set_hxgeneric gen [] true md in
-						if ret = None then get (set_hxgeneric gen [] false md) else get ret)
-					| TAbstract(a,_) -> true
-					| _ -> true)
-				| _ -> match set_hxgeneric gen [] true md with
-					| None ->
-						get (set_hxgeneric gen [] false md)
-					| Some v ->
-						v
-			in
-			if not ret then begin
-				match md with
-				| TClassDecl c ->
-					let set_hxgeneric (_,param) = match follow param with
-						| TInst(c,_) ->
-							c.cl_meta <- (Meta.NativeGeneric, [], c.cl_pos) :: c.cl_meta
-						| _ -> ()
-					in
-					List.iter set_hxgeneric c.cl_params;
-					let rec handle_field cf =
-						List.iter set_hxgeneric cf.cf_params;
-						List.iter handle_field cf.cf_overloads
-					in
-					(match c.cl_kind with
-						| KAbstractImpl a ->
-							List.iter set_hxgeneric a.a_params;
-						| _ -> ());
-					List.iter handle_field c.cl_ordered_fields;
-					List.iter handle_field c.cl_ordered_statics
-				| _ -> ()
-			end;
-			ret
-
-		let params_has_tparams params =
-			List.fold_left (fun acc t -> acc || has_type_params t) false params
-
-		(* ******************************************* *)
-		(* RealTypeParamsModf *)
-		(* ******************************************* *)
-
-		(*
-
-			This is the module filter of Real Type Parameters. It will traverse through all types and look for hxgeneric classes (only classes).
-			When found, a parameterless interface will be created and associated via the "ifaces" Hashtbl to the original class.
-			Also a "cast" function will be automatically generated which will handle unsafe downcasts to more specific type parameters (necessary for serialization)
-
-			dependencies:
-				Anything that may create hxgeneric classes must run before it.
-				Should run before ReflectionCFs (this dependency will be added to ReflectionCFs), so the added interfaces also get to be real IHxObject's
-
-		*)
-
-		module RealTypeParamsModf =
-		struct
-
-			let set_only_hxgeneric gen =
-				let rec run md =
-					match md with
-						| TTypeDecl _ | TAbstractDecl _ -> md
-						| _ -> ignore (set_hxgeneric gen md); md
-				in
-				run
-
-			let name = "real_type_params_modf"
-
-			let priority = solve_deps name []
-
-			let rec get_fields gen cl params_cl params_cf acc =
-				let fields = List.fold_left (fun acc cf ->
-					match follow (gen.greal_type (gen.gfollow#run_f (cf.cf_type))) with
-						| TInst(cli, ((_ :: _) as p)) when (not (is_hxgeneric (TClassDecl cli))) && params_has_tparams p ->
-							(cf, apply_params cl.cl_params params_cl cf.cf_type, apply_params cl.cl_params params_cf cf.cf_type) :: acc
-						| TEnum(e, ((_ :: _) as p)) when not (is_hxgeneric (TEnumDecl e)) && params_has_tparams p ->
-							(cf, apply_params cl.cl_params params_cl cf.cf_type, apply_params cl.cl_params params_cf cf.cf_type) :: acc
-						| _ -> acc
-				) [] cl.cl_ordered_fields in
-				match cl.cl_super with
-					| Some(cs, tls) ->
-						get_fields gen cs (List.map (apply_params cl.cl_params params_cl) tls) (List.map (apply_params cl.cl_params params_cf) tls) (fields @ acc)
-					| None -> (fields @ acc)
-
-			let get_cast_name cl = String.concat "_" ((fst cl.cl_path) @ [snd cl.cl_path; "cast"]) (* explicitly define it *)
-
-			(* overrides all needed cast functions from super classes / interfaces to call the new cast function *)
-			let create_stub_casts gen cl cast_cfield =
-				(* go through superclasses and interfaces *)
-				let p = cl.cl_pos in
-				let this = { eexpr = TConst TThis; etype = (TInst(cl, List.map snd cl.cl_params)); epos = p } in
-
-				let rec loop cls tls level reverse_params =
-					if (level <> 0 || cls.cl_interface) && tls <> [] && is_hxgeneric (TClassDecl cls) then begin
-						let cparams = List.map (fun (s,t) -> (s, TInst (map_param (get_cl_t t), []))) cls.cl_params in
-						let name = get_cast_name cls in
-						if not (PMap.mem name cl.cl_fields) then begin
-							let reverse_params = List.map (apply_params cls.cl_params (List.map snd cparams)) reverse_params in
-							let cfield = mk_class_field name (TFun([], t_dynamic)) false cl.cl_pos (Method MethNormal) cparams in
-							let field = { eexpr = TField(this, FInstance(cl,List.map snd cl.cl_params, cast_cfield)); etype = apply_params cast_cfield.cf_params reverse_params cast_cfield.cf_type; epos = p } in
-							let call =
-							{
-								eexpr = TCall(field, []);
-								etype = t_dynamic;
-								epos = p;
-							} in
-							let call = gen.gparam_func_call call field reverse_params [] in
-							let delay () =
-								cfield.cf_expr <-
-								Some {
-									eexpr = TFunction(
-									{
-										tf_args = [];
-										tf_type = t_dynamic;
-										tf_expr =
-										{
-											eexpr = TReturn( Some call );
-											etype = t_dynamic;
-											epos = p;
-										}
-									});
-									etype = cfield.cf_type;
-									epos = p;
-								}
-							in
-							gen.gafter_filters_ended <- delay :: gen.gafter_filters_ended; (* do not let filters alter this expression content *)
-							cl.cl_ordered_fields <- cfield :: cl.cl_ordered_fields;
-							cl.cl_fields <- PMap.add cfield.cf_name cfield cl.cl_fields;
-							if level <> 0 then cl.cl_overrides <- cfield :: cl.cl_overrides
-						end
-					end;
-					let get_reverse super supertl =
-						let kv = List.map2 (fun (_,tparam) applied -> (follow applied, follow tparam)) super.cl_params supertl in
-						List.map (fun t ->
-							try
-								List.assq (follow t) kv
-							with | Not_found -> t
-						) reverse_params
-					in
-					(match cls.cl_super with
-					| None -> ()
-					| Some(super, supertl) ->
-						loop super supertl (level + 1) (get_reverse super supertl));
-					List.iter (fun (iface, ifacetl) ->
-						loop iface ifacetl level (get_reverse iface ifacetl)
-					) cls.cl_implements
-				in
-				loop cl (List.map snd cl.cl_params) 0 (List.map snd cl.cl_params)
-
-			(*
-				Creates a cast classfield, with the desired name
-
-				Will also look for previous cast() definitions and override them, to reflect the current type and fields
-
-				FIXME: this function still doesn't support generics that extend generics, and are cast as one of its subclasses. This needs to be taken care, by
-				looking at previous superclasses and whenever a generic class is found, its cast argument must be overriden. the toughest part is to know how to type
-				the current type correctly.
-			*)
-			let create_cast_cfield gen cl name =
-				let basic = gen.gcon.basic in
-				let cparams = List.map (fun (s,t) -> (s, TInst (map_param (get_cl_t t), []))) cl.cl_params in
-				let cfield = mk_class_field name (TFun([], t_dynamic)) false cl.cl_pos (Method MethNormal) cparams in
-				let params = List.map snd cparams in
-
-				let fields = get_fields gen cl (List.map snd cl.cl_params) params [] in
-
-				(* now create the contents of the function *)
-				(*
-					it will look something like:
-					if (typeof(T) == typeof(T2)) return this;
-
-					var new_me = new CurrentClass<T2>(EmptyInstnace);
-
-					for (field in Reflect.fields(this))
-					{
-						switch(field)
-						{
-							case "aNativeArray":
-								var newArray = new NativeArray(this.aNativeArray.Length);
-
-							default:
-								Reflect.setField(new_me, field, Reflect.field(this, field));
-						}
-					}
-				*)
-
-				let new_t = TInst(cl, params) in
-				let pos = cl.cl_pos in
-
-				let new_me_var = alloc_var "new_me" new_t in
-				let local_new_me = { eexpr = TLocal(new_me_var); etype = new_t; epos = pos } in
-				let this = { eexpr = TConst(TThis); etype = (TInst(cl, List.map snd cl.cl_params)); epos = pos } in
-				let field_var = alloc_var "field" gen.gcon.basic.tstring in
-				let local_field = { eexpr = TLocal(field_var); etype = field_var.v_type; epos = pos } in
-				let i_var = alloc_var "i" gen.gcon.basic.tint in
-				let local_i = { eexpr = TLocal(i_var); etype = gen.gcon.basic.tint; epos = pos } in
-				let incr_i = { eexpr = TUnop(Ast.Increment, Ast.Postfix, local_i); etype = basic.tint; epos = pos } in
-				let fields_var = alloc_var "fields" (gen.gcon.basic.tarray gen.gcon.basic.tstring) in
-				let local_fields = { eexpr = TLocal(fields_var); etype = (gen.gcon.basic.tarray gen.gcon.basic.tstring); epos = pos } in
-
-				let get_path t =
-					match follow t with
-						| TInst(cl,_) -> cl.cl_path
-						| TEnum(e,_) -> e.e_path
-						| TAbstract(a,_) -> a.a_path
-						| TMono _
-						| TDynamic _ -> ([], "Dynamic")
-						| _ -> assert false
-				in
-
-				(* this will take all fields that were *)
-				let fields_to_cases fields =
-					List.map (fun (cf, t_cl, t_cf) ->
-						let this_field = { eexpr = TField(this, FInstance(cl, List.map snd cl.cl_params, cf)); etype = t_cl; epos = pos } in
-						let expr =
-						{
-							eexpr = TBinop(OpAssign, { eexpr = TField(local_new_me, FInstance(cl, List.map snd cl.cl_params, cf) ); etype = t_cf; epos = pos },
-								try (Hashtbl.find gen.gtparam_cast (get_path t_cf)) this_field t_cf with | Not_found -> (* if not found tparam cast, it shouldn't be a valid hxgeneric *) assert false
-							);
-							etype = t_cf;
-							epos = pos;
-						} in
-
-						[ExprBuilder.make_string gen.gcon cf.cf_name pos], expr
-					) fields
-				in
-
-				let mk_typehandle =
-					let thandle = alloc_var "__typeof__" t_dynamic in
-					(fun cl -> { eexpr = TCall(mk_local thandle pos, [ ExprBuilder.make_static_this cl pos ]); etype = t_dynamic; epos = pos })
-				in
-				let mk_eq cl1 cl2 =
-					{ eexpr = TBinop(Ast.OpEq, mk_typehandle cl1, mk_typehandle cl2); etype = basic.tbool; epos = pos }
-				in
-
-				let rec mk_typehandle_cond thisparams cfparams =
-					match thisparams, cfparams with
-						| TInst(cl_this,[]) :: [], TInst(cl_cf,[]) :: [] ->
-							mk_eq cl_this cl_cf
-						| TInst(cl_this,[]) :: hd, TInst(cl_cf,[]) :: hd2 ->
-							{ eexpr = TBinop(Ast.OpBoolAnd, mk_eq cl_this cl_cf, mk_typehandle_cond hd hd2); etype = basic.tbool; epos = pos }
-						| v :: hd, v2 :: hd2 ->
-							(match follow v, follow v2 with
-								| (TInst(cl1,[]) as v), (TInst(cl2,[]) as v2) ->
-									mk_typehandle_cond (v :: hd) (v2 :: hd2)
-								| _ ->
-									assert false
-							)
-						| _ -> assert false
-				in
-
-				let fn =
-				{
-					tf_args = [];
-					tf_type = t_dynamic;
-					tf_expr =
-						{
-							eexpr = TBlock([
-								(* if (typeof(T) == typeof(T2)) return this *)
-								{
-									eexpr = TIf(
-										mk_typehandle_cond (List.map snd cl.cl_params) params,
-										mk_return this,
-										None);
-									etype = basic.tvoid;
-									epos = pos;
-								};
-								(* var new_me = /*special create empty with tparams construct*/ *)
-								{
-									eexpr = TVar(new_me_var, Some(gen.gtools.r_create_empty cl params pos));
-									etype = gen.gcon.basic.tvoid;
-									epos = pos
-								};
-								(* var fields = Reflect.fields(this); *)
-								{
-									eexpr = TVar(fields_var, Some(gen.gtools.r_fields true this));
-									etype = gen.gcon.basic.tvoid;
-									epos = pos
-								};
-								(* var i = 0; *)
-								{
-									eexpr = TVar(i_var, Some(ExprBuilder.make_int gen.gcon 0 pos));
-									etype = gen.gcon.basic.tvoid;
-									epos = pos
-								};
-								{
-									eexpr = TWhile( (* while (i < fields.length) *)
-										{
-											eexpr = TBinop(Ast.OpLt,
-												local_i,
-												mk_field_access gen local_fields "length" pos);
-											etype = gen.gcon.basic.tbool;
-											epos = pos
-										},
-										{
-											eexpr = TBlock [
-												(* var field = fields[i++]; *)
-												{
-													eexpr = TVar(field_var, Some { eexpr = TArray (local_fields, incr_i); etype = gen.gcon.basic.tstring; epos = pos });
-													etype = gen.gcon.basic.tvoid;
-													epos = pos
-												};
-												(
-													(* default: Reflect.setField(new_me, field, Reflect.field(this, field)) *)
-													let edef = gen.gtools.r_set_field gen.gcon.basic.tvoid local_new_me local_field (gen.gtools.r_field false gen.gcon.basic.tvoid this local_field) in
-													if fields <> [] then
-														(* switch(field) { ... } *)
-														{
-															eexpr = TSwitch(local_field, fields_to_cases fields, Some(edef));
-															etype = gen.gcon.basic.tvoid;
-															epos = pos;
-														}
-													else
-														edef;
-												)
-											];
-											etype = gen.gcon.basic.tvoid;
-											epos = pos
-										},
-										Ast.NormalWhile
-									);
-									etype = gen.gcon.basic.tvoid;
-									epos = pos;
-								};
- 								(* return new_me *)
-								mk_return local_new_me
-							]);
-							etype = t_dynamic;
-							epos = pos;
-						};
-				}
-				in
-
-				cfield.cf_expr <- Some( { eexpr = TFunction(fn); etype = cfield.cf_type; epos = pos } );
-
-				cfield
-
-			let create_static_cast_cf gen iface cf =
-				let p = iface.cl_pos in
-				let basic = gen.gcon.basic in
-				let cparams = List.map (fun (s,t) -> ("To_" ^ s, TInst (map_param (get_cl_t t), []))) cf.cf_params in
-				let me_type = TInst(iface,[]) in
-				let cfield = mk_class_field "__hx_cast" (TFun(["me",false,me_type], t_dynamic)) false iface.cl_pos (Method MethNormal) (cparams) in
-				let params = List.map snd cparams in
-
-				let me = alloc_var "me" me_type in
-				let field = { eexpr = TField(mk_local me p, FInstance(iface, List.map snd iface.cl_params, cf)); etype = apply_params cf.cf_params params cf.cf_type; epos = p } in
-				let call =
-				{
-					eexpr = TCall(field, []);
-					etype = t_dynamic;
-					epos = p;
-				} in
-				let call = gen.gparam_func_call call field params [] in
-
-				(* since object.someCall<ExplicitParameterDefinition>() isn't allowed on Haxe, we need to directly apply the params and delay this call *)
-				let delay () =
-					cfield.cf_expr <-
-					Some {
-						eexpr = TFunction(
-						{
-							tf_args = [me,None];
-							tf_type = t_dynamic;
-							tf_expr =
-							{
-								eexpr = TReturn( Some
-								{
-									eexpr = TIf(
-										{ eexpr = TBinop(Ast.OpNotEq, mk_local me p, null me.v_type p); etype = basic.tbool; epos = p },
-										call,
-										Some( null me.v_type p )
-									);
-									etype = t_dynamic;
-									epos = p;
-								});
-								etype = basic.tvoid;
-								epos = p;
-							}
-						});
-						etype = cfield.cf_type;
-						epos = p;
-					}
-				in
-				cfield, delay
-
-			let default_implementation gen ifaces base_generic =
-				let add_iface cl =
-					gen.gadd_to_module (TClassDecl cl) (max_dep);
-				in
-
-				let implement_stub_cast cthis iface tl =
-					let name = get_cast_name iface in
-					if not (PMap.mem name cthis.cl_fields) then begin
-						let cparams = List.map (fun (s,t) -> ("To_" ^ s, TInst(map_param (get_cl_t t), []))) iface.cl_params in
-						let field = mk_class_field name (TFun([],t_dynamic)) false iface.cl_pos (Method MethNormal) cparams in
-						let this = { eexpr = TConst TThis; etype = TInst(cthis, List.map snd cthis.cl_params); epos = cthis.cl_pos } in
-						field.cf_expr <- Some {
-							etype = TFun([],t_dynamic);
-							epos = this.epos;
-							eexpr = TFunction {
-								tf_type = t_dynamic;
-								tf_args = [];
-								tf_expr = mk_block { this with
-									eexpr = TReturn (Some this)
-								}
-							}
-						};
-						cthis.cl_ordered_fields <- field :: cthis.cl_ordered_fields;
-						cthis.cl_fields <- PMap.add name field cthis.cl_fields
-					end
-				in
-
-				let rec run md =
-					match md with
-						| TClassDecl ({ cl_params = [] } as cl) ->
-							(* see if we're implementing any generic interface *)
-							let rec check (iface,tl) =
-								if tl <> [] && set_hxgeneric gen (TClassDecl iface) then
-									(* implement cast stub *)
-									implement_stub_cast cl iface tl;
-								List.iter (fun (s,stl) -> check (s, List.map (apply_params iface.cl_params tl) stl)) iface.cl_implements;
-							in
-							List.iter (check) cl.cl_implements;
-							md
-						| TClassDecl ({ cl_params = hd :: tl } as cl) when set_hxgeneric gen md ->
-							let iface = mk_class cl.cl_module cl.cl_path cl.cl_pos in
-							iface.cl_array_access <- Option.map (apply_params (cl.cl_params) (List.map (fun _ -> t_dynamic) cl.cl_params)) cl.cl_array_access;
-							iface.cl_extern <- cl.cl_extern;
-							iface.cl_module <- cl.cl_module;
-							iface.cl_private <- cl.cl_private;
-							iface.cl_meta <-
-								(Meta.HxGen, [], cl.cl_pos)
-								::
-								(Meta.Custom "generic_iface", [(EConst(Int(string_of_int(List.length cl.cl_params))), cl.cl_pos)], cl.cl_pos)
-								::
-								iface.cl_meta;
-							Hashtbl.add ifaces cl.cl_path iface;
-
-							iface.cl_implements <- (base_generic, []) :: iface.cl_implements;
-							iface.cl_interface <- true;
-							cl.cl_implements <- (iface, []) :: cl.cl_implements;
-
-							let name = get_cast_name cl in
-							let cast_cf = create_cast_cfield gen cl name in
-							if not cl.cl_interface then create_stub_casts gen cl cast_cf;
-
-							let rec loop c = match c.cl_super with
-								| None -> ()
-								| Some(sup,_) -> try
-									let siface = Hashtbl.find ifaces sup.cl_path in
-									iface.cl_implements <- (siface,[]) :: iface.cl_implements;
-									()
-								with | Not_found -> loop sup
-							in
-							loop cl;
-
-							(if not cl.cl_interface then cl.cl_ordered_fields <- cast_cf :: cl.cl_ordered_fields);
-							let iface_cf = mk_class_field name cast_cf.cf_type false cast_cf.cf_pos (Method MethNormal) cast_cf.cf_params in
-							let cast_static_cf, delay = create_static_cast_cf gen iface iface_cf in
-
-							cl.cl_ordered_statics <- cast_static_cf :: cl.cl_ordered_statics;
-							cl.cl_statics <- PMap.add cast_static_cf.cf_name cast_static_cf cl.cl_statics;
-							gen.gafter_filters_ended <- delay :: gen.gafter_filters_ended; (* do not let filters alter this expression content *)
-
-							iface_cf.cf_type <- cast_cf.cf_type;
-							iface.cl_fields <- PMap.add name iface_cf iface.cl_fields;
-							let fields = List.filter (fun cf -> match cf.cf_kind with
-								| Var _ | Method MethDynamic -> false
-								| _ ->
-									let is_override = List.memq cf cl.cl_overrides in
-									let cf_type = if is_override && not (Meta.has Meta.Overload cf.cf_meta) then
-										match find_first_declared_field gen cl cf.cf_name with
-											| Some(_,_,declared_t,_,_,_,_) -> declared_t
-											| _ -> assert false
-									else
-										cf.cf_type
-									in
-
-									not (has_type_params cf_type)
-								) cl.cl_ordered_fields
-							in
-							let fields = List.map (fun f -> mk_class_field f.cf_name f.cf_type f.cf_public f.cf_pos f.cf_kind f.cf_params) fields in
-							let fields = iface_cf :: fields in
-							iface.cl_ordered_fields <- fields;
-							List.iter (fun f -> iface.cl_fields <- PMap.add f.cf_name f iface.cl_fields) fields;
-
-							add_iface iface;
-							md
-						| TTypeDecl _ | TAbstractDecl _ -> md
-						| TEnumDecl _ ->
-							ignore (set_hxgeneric gen md);
-							md
-						| _ -> ignore (set_hxgeneric gen md); md
-				in
-				run
-
-			let configure gen mapping_func =
-				let map e = Some(mapping_func e) in
-				gen.gmodule_filters#add ~name:name ~priority:(PCustom priority) map
-
-		end;;
-
-		(* create a common interface without type parameters and only a __Cast<> function *)
-		let default_implementation gen (dyn_tparam_cast:texpr->t->texpr) ifaces =
-			let change_expr e cl iface params =
-				let field = mk_static_field_access_infer cl "__hx_cast" e.epos params in
-				let elist = [mk_cast (TInst(iface,[])) e] in
-				let call = { eexpr = TCall(field, elist); etype = t_dynamic; epos = e.epos } in
-
-				gen.gparam_func_call call field params elist
-			in
-
-			let rec run e =
-				match e.eexpr with
-						| TCast(cast_expr, _) ->
-							(* see if casting to a native generic class *)
-							let t = gen.greal_type e.etype in
-							let unifies =
-								let ctype = gen.greal_type cast_expr.etype in
-								match follow ctype with
-								| TInst(cl,_) -> (try
-									unify ctype t;
-									true
-								with | Unify_error el ->
-									false)
-								| _ -> false
-							in
-							let unifies = unifies && not (PMap.mem "cs_safe_casts" gen.gcon.defines) in
-							(match follow t with
-								| TInst(cl, p1 :: pl) when is_hxgeneric (TClassDecl cl) && not unifies && not (Meta.has Meta.Enum cl.cl_meta) ->
-									let iface = Hashtbl.find ifaces cl.cl_path in
-									mk_cast e.etype (change_expr (Type.map_expr run cast_expr) cl iface (p1 :: pl))
-								| _ -> Type.map_expr run e
-							)
-						| _ -> Type.map_expr run e
-			in
-			run
-
-		let configure gen (dyn_tparam_cast:texpr->t->texpr) ifaces base_generic =
-			gen.ghas_tparam_cast_handler <- true;
-			let traverse = default_implementation gen dyn_tparam_cast ifaces in
-			let map e = Some(traverse e) in
-			gen.gsyntax_filters#add ~name:name ~priority:(PCustom priority) map;
-			RealTypeParamsModf.configure gen (RealTypeParamsModf.default_implementation gen ifaces base_generic)
-
-	end;;
-
-	(* ******************************************* *)
-	(* Rename Type Parameters *)
-	(* ******************************************* *)
-
-	(*
-
-		This module should run after everything is already applied,
-		it will look for possible type parameter name clashing and change the classes names to a
-
-		dependencies:
-			should run after everything is already applied. There's no configure on this module, only 'run'.
-
-	*)
-
-	module RenameTypeParameters =
-	struct
-
-		let name = "rename_type_parameters"
-
-		let run gen =
-			let i = ref 0 in
-			let found_types = ref PMap.empty in
-			let check_type name on_changed =
-				let rec loop name =
-					incr i;
-					let changed_name = (name ^ (string_of_int !i)) in
-					if PMap.mem changed_name !found_types then loop name else changed_name
-				in
-				if PMap.mem name !found_types then begin
-					let new_name = loop name in
-					found_types := PMap.add new_name true !found_types;
-					on_changed new_name
-				end else found_types := PMap.add name true !found_types
-			in
-
-			let get_cls t =
-				match follow t with
-					| TInst(cl,_) -> cl
-					| _ -> assert false
-			in
-
-			let iter_types (nt,t) =
-				let cls = get_cls t in
-				let orig = cls.cl_path in
-				check_type (snd orig) (fun name -> cls.cl_path <- (fst orig, name))
-			in
-
-			let save_params save params =
-				List.fold_left (fun save (_,t) ->
-					let cls = get_cls t in
-					(cls.cl_path,t) :: save) save params
-			in
-
-			List.iter (function
-				| TClassDecl cl ->
-					i := 0;
-
-					let save = [] in
-
-					found_types := PMap.empty;
-					let save = save_params save cl.cl_params in
-					List.iter iter_types cl.cl_params;
-					let cur_found_types = !found_types in
-					let save = ref save in
-					List.iter (fun cf ->
-						found_types := cur_found_types;
-						save := save_params !save cf.cf_params;
-						List.iter iter_types cf.cf_params
-					) (cl.cl_ordered_fields @ cl.cl_ordered_statics);
-
-					if !save <> [] then begin
-						let save = !save in
-						let res = cl.cl_restore in
-						cl.cl_restore <- (fun () ->
-							res();
-							List.iter (fun (path,t) ->
-								let cls = get_cls t in
-								cls.cl_path <- path) save
-						);
-					end
-
-				| TEnumDecl ( ({ e_params = hd :: tl }) ) ->
-					i := 0;
-					found_types := PMap.empty;
-					List.iter iter_types (hd :: tl)
-
-				| TAbstractDecl { a_params = hd :: tl } ->
-					i := 0;
-					found_types := PMap.empty;
-					List.iter iter_types (hd :: tl)
-
-				| _ -> ()
-
-			) gen.gtypes_list
-
-	end;;
-
-end;;
-
-(**************************************************************************************************************************)
-(*																									 SYNTAX FILTERS																												*)
-(**************************************************************************************************************************)
-
-(* ******************************************* *)
-(* Expression Unwrap *)
-(* ******************************************* *)
-(*
-	This is the most important module for source-code based targets. It will follow a convention of what's an expression and what's a statement,
-	and will unwrap statements where expressions are expected, and vice-versa.
-
-	It should be one of the first syntax filters to be applied. As a consequence, it's applied after all filters that add code to the AST, and by being
-	the first of the syntax filters, it will also have the AST retain most of the meaning of normal Haxe code. So it's easier to detect cases which are
-	side-effects free, for example
-
-	Any target can make use of this, but there is one requirement: The target must accept null to be set to any kind of variable. For example,
-	var i:Int = null; must be accepted. The best way to deal with this is to (like it's done in C#) make null equal to "default(Type)"
-
-	dependencies:
-		While it's best for Expression Unwrap to delay its execution as much as possible, since theoretically any
-		filter can return an expression that needs to be unwrapped, it is also desirable for ExpresionUnwrap to have
-		the AST as close as possible as Haxe's, so it can make some correct predictions (for example, so it can
-		more accurately know what can be side-effects-free and what can't).
-		This way, it will run slightly after the Normal priority, so if you don't say that a syntax filter must run
-		before Expression Unwrap, it will run after it.
-
-	TODO : While statement must become do / while, with the actual block inside an if for the condition, and else for 'break'
-*)
-module ExpressionUnwrap =
-struct
-	let name = "expression_unwrap"
-
-	(* priority: first syntax filter *)
-	let priority = -10.0
-
-	(*
-		We always need to rely on Blocks to be able to unwrap expressions correctly.
-		So the the standard traverse will always be based on blocks.
-		Normal block statements, like for(), while(), if(), ... will be mk_block'ed so there is always a block inside of them.
-
-			At the block level, we'll define an "add_statement" function, which will allow the current expression to
-			add statements to the block. This statement may or may not contain statements as expressions, so the texpr will be evaluated recursively before being added.
-
-			- traverse will always evaluate TBlocks
-			- for each texpr in a TBlock list,
-				check shallow type
-					if type is Statement or Both when it has problematic expression (var problematic_expr = count_problematic_expressions),
-						if we can eagerly call unwrap_statement on the whole expression (try_call_unwrap_statement), use the return expression
-						else
-							check expr_type of each underlying type (with expr_stat_map)
-								if it has ExprWithStatement or Statement,
-									call problematic_expression_unwrap in it
-									problematic_expr--
-								else if problematic_expr == 0, just add the unchanged expression
-								else if NoSideEffects and doesn't have short-circuit, just add the unchanged expression
-								else call problematic_expression_unwrap in it
-					if type is Expression, check if there are statements or Both inside.
-						if there are, problematic_expression_unwrap in it
-					aftewards, use on_expr_as_statement to get it
-
-		helpers:
-			try_call_unwrap_statement: (returns texpr option)
-				if underlying statement is TBinop(OpAssign/OpAssignOp), or TVar, with the right side being a Statement or a short circuit op, we can call apply_assign.
-
-			apply_assign:
-				if is TVar, first declare the tvar with default expression = null;
-				will receive the left and right side of the assignment; right-side must be Statement
-				see if right side is a short-circuit operation, call short_circuit_op_unwrap
-				else see eexpr of the right side
-					if it's void, just add the statement with add_statement, and set the right side as null;
-					if not, it will have a block inside. set the left side = to the last expression on each block inside. add_statement for it.
-
-			short_circuit_op_unwrap: x() && (1 + {var x = 0; x + 1;} == 2) && z()
-				-> var x = x();
-					 var y = false;
-					 var z = false;
-					 if (x) //for &&, neg for ||
-					 {
-						var temp = null;
-						{
-							var x = 0;
-							temp = x + 1;
-						}
-
-						y = (1 + temp) == 2;
-						if (y)
-						{
-							z = z();
-						}
-					 }
-				expects to receive a texpr with TBinop(OpBoolAnd/OpBoolOr)
-				will traverse the AST while there is a TBinop(OpBoolAnd/OpBoolOr) as a right-side expr, and declare new temp vars in the	for each found.
-				will collect the return value, a mapped expr with all exprs as TLocal of the temp vars created
-
-
-			problematic_expression_unwrap:
-				check expr_kind:
-					if it is NoSideEffects and not short-circuit, leave it there
-					if it is ExprWithStatement and not short-circuit, call Type.map_expr problematic_expression_unwrap
-					if it is Statement or Expression or short-circuit expr, call add_assign for this expression
-
-			add_assign:
-				see if the type is void. If it is, just add_statement the expression argument, and return a null value
-				else create a new variable, set TVar with Some() with the expression argument, add TVar with add_statement, and return the TLocal of this expression.
-
-			map_problematic_expr:
-				call expr_stat_map on statement with problematic_expression_unwrap
-
-		types:
-			type shallow_expr_type = | Statement | Expression | Both (* shallow expression classification. Both means that they can be either Statements as Expressions *)
-
-			type expr_kind = | NormalExpr | ExprNoSideEffects (* -> short-circuit is considered side-effects *) | ExprWithStatement | Statement
-				evaluates an expression (as in not a statement) type. If it is ExprWithStatement or Statement, it means it contains errors
-
-		functions:
-			shallow_expr_type (expr:texpr) : shallow_expr_type
-
-			expr_kind (expr:texpr) : expr_kind
-				deeply evaluates an expression type
-
-			expr_stat_map (fn:texpr->texpr) (expr:texpr) : texpr
-				it will traverse the AST looking for places where an expression is expected, and map the value according to fn
-
-			aggregate_expr_type (is_side_effects_free:bool) (children:expr_type list) : expr_type
-				helper function to deal with expr_type aggregation (e.g. an Expression + a Statement as a children, is a ExprWithStatement)
-
-			check_statement_in_expression (expr:texpr) : texpr option :
-				will check
-
-	*)
-
-	type shallow_expr_type = | Statement | Expression of texpr | Both of texpr (* shallow expression classification. Both means that they can be either Statements as Expressions *)
-
-	type expr_kind = | KNormalExpr | KNoSideEffects (* -> short-circuit is considered side-effects *) | KExprWithStatement | KStatement
-
-	let rec no_paren e =
-		match e.eexpr with
-			| TParenthesis e -> no_paren e
-			| _ -> e
-
-	(* must be called in a statement. Will execute fn whenever an expression (not statement) is expected *)
-	let rec expr_stat_map fn (expr:texpr) =
-		match (no_paren expr).eexpr with
-			| TBinop ( (Ast.OpAssign as op), left_e, right_e )
-			| TBinop ( (Ast.OpAssignOp _ as op), left_e, right_e ) ->
-				{ expr with eexpr = TBinop(op, fn left_e, fn right_e) }
-			| TParenthesis _ -> assert false
-			| TCall(left_e, params) ->
-				{ expr with eexpr = TCall(fn left_e, List.map fn params) }
-			| TNew(cl, tparams, params) ->
-				{ expr with eexpr = TNew(cl, tparams, List.map fn params) }
-			| TVar(v,eopt) ->
-				{ expr with eexpr = TVar(v, Option.map fn eopt) }
-			| TFor (v,cond,block) ->
-				{ expr with eexpr = TFor(v, fn cond, block) }
-			| TIf(cond,eif,eelse) ->
-				{ expr with eexpr = TIf(fn cond, eif, eelse) }
-			| TWhile(cond, block, flag) ->
-				{ expr with eexpr = TWhile(fn cond, block, flag) }
-			| TSwitch(cond, el_block_l, default) ->
-				{ expr with eexpr = TSwitch( fn cond, List.map (fun (el,block) -> (List.map fn el, block)) el_block_l, default ) }
-			| TReturn(eopt) ->
-				{ expr with eexpr = TReturn(Option.map fn eopt) }
-			| TThrow (texpr) ->
-				{ expr with eexpr = TThrow(fn texpr) }
-			| TBreak
-			| TContinue
-			| TTry _
-			| TUnop (Ast.Increment, _, _)
-			| TUnop (Ast.Decrement, _, _) (* unop is a special case because the haxe compiler won't let us generate complex expressions with Increment/Decrement *)
-			| TBlock _ -> expr (* there is no expected expression here. Only statements *)
-			| TMeta(m,e) ->
-				{ expr with eexpr = TMeta(m,expr_stat_map fn e) }
-			| _ -> assert false (* we only expect valid statements here. other expressions aren't valid statements *)
-
-	let is_expr = function | Expression _ -> true | _ -> false
-
-	let aggregate_expr_type map_fn side_effects_free children =
-		let rec loop acc children =
-			match children with
-				| [] -> acc
-				| hd :: children ->
-					match acc, map_fn hd with
-						| _, KExprWithStatement
-						| _, KStatement
-						| KExprWithStatement, _
-						| KStatement, _ -> KExprWithStatement
-						| KNormalExpr, KNoSideEffects
-						| KNoSideEffects, KNormalExpr
-						| KNormalExpr, KNormalExpr -> loop KNormalExpr children
-						| KNoSideEffects, KNoSideEffects -> loop KNoSideEffects children
-		in
-		loop (if side_effects_free then KNoSideEffects else KNormalExpr) children
-
-	(* statements: *)
-	(* Error CS0201: Only assignment, call, increment,					 *)
-	(* decrement, and new object expressions can be used as a		 *)
-	(* statement (CS0201). *)
-	let rec shallow_expr_type expr : shallow_expr_type =
-		match expr.eexpr with
-			| TCall _ when not (ExtType.is_void expr.etype) -> Both expr
-			| TNew _
-			| TUnop (Ast.Increment, _, _)
-			| TUnop (Ast.Decrement, _, _)
-			| TBinop (Ast.OpAssign, _, _)
-			| TBinop (Ast.OpAssignOp _, _, _) -> Both expr
-			| TIf (cond, eif, Some(eelse)) -> (match aggregate_expr_type expr_kind true [cond;eif;eelse] with
-				| KExprWithStatement -> Statement
-				| _ -> Both expr)
-			| TConst _
-			| TLocal _
-			| TArray _
-			| TBinop _
-			| TField _
-			| TEnumParameter _
-			| TTypeExpr _
-			| TObjectDecl _
-			| TArrayDecl _
-			| TFunction _
-			| TCast _
-			| TUnop _ -> Expression (expr)
-			| TParenthesis p | TMeta(_,p) -> shallow_expr_type p
-			| TBlock ([e]) -> shallow_expr_type e
-			| TCall _
-			| TVar _
-			| TBlock _
-			| TFor _
-			| TWhile _
-			| TSwitch _
-			| TTry _
-			| TReturn _
-			| TBreak
-			| TContinue
-			| TIf _
-			| TThrow _ -> Statement
-
-	and expr_kind expr =
-		match shallow_expr_type expr with
-			| Statement -> KStatement
-			| Both expr | Expression expr ->
-				let aggregate = aggregate_expr_type expr_kind in
-				match expr.eexpr with
-					| TConst _
-					| TLocal _
-					| TFunction _
-					| TTypeExpr _ ->
-						KNoSideEffects
-					| TCall (ecall, params) ->
-						aggregate false (ecall :: params)
-					| TNew (_,_,params) ->
-						aggregate false params
-					| TUnop (Increment,_,e)
-					| TUnop (Decrement,_,e) ->
-						aggregate false [e]
-					| TUnop (_,_,e) ->
-						aggregate true [e]
-					| TBinop (Ast.OpBoolAnd, e1, e2)
-					| TBinop (Ast.OpBoolOr, e1, e2) ->	(* TODO: should OpBool never be side-effects free? *)
-						aggregate true [e1;e2]
-					| TBinop (Ast.OpAssign, e1, e2)
-					| TBinop (Ast.OpAssignOp _, e1, e2) ->
-						aggregate false [e1;e2]
-					| TBinop (_, e1, e2) ->
-						aggregate true [e1;e2]
-					| TIf (cond, eif, Some(eelse)) -> (match aggregate true [cond;eif;eelse] with
-						| KExprWithStatement -> KStatement
-						| k -> k)
-					| TArray (e1,e2) ->
-						aggregate true [e1;e2]
-					| TParenthesis e
-					| TMeta(_,e)
-					| TField (e,_) ->
-						aggregate true [e]
-					| TArrayDecl (el) ->
-						aggregate true el
-					| TObjectDecl (sel) ->
-						aggregate true (List.map snd sel)
-					| TCast (e,_) ->
-						aggregate true [e]
-					| _ -> trace (debug_expr expr); assert false (* should have been read as Statement by shallow_expr_type *)
-
-	let is_side_effects_free e =
-		match expr_kind e with | KNoSideEffects -> true | _ -> false
-
-	let get_kinds (statement:texpr) =
-		let kinds = ref [] in
-		ignore (expr_stat_map (fun e ->
-			kinds := (expr_kind e) :: !kinds;
-			e
-		) statement);
-		List.rev !kinds
-
-	let has_problematic_expressions (kinds:expr_kind list) =
-		let rec loop kinds =
-			match kinds with
-				| [] -> false
-				| KStatement :: _
-				| KExprWithStatement :: _ -> true
-				| _ :: tl -> loop tl
-		in
-		loop kinds
-
-	let count_problematic_expressions (statement:texpr) =
-		let count = ref 0 in
-		ignore (expr_stat_map (fun e ->
-			(match expr_kind e with
-				| KStatement | KExprWithStatement -> incr count
-				| _ -> ()
-			);
-			e
-		) statement);
-		!count
-
-	let apply_assign_block assign_fun elist =
-		let rec assign acc elist =
-			match elist with
-				| [] -> acc
-				| last :: [] ->
-					(assign_fun last) :: acc
-				| hd :: tl ->
-					assign (hd :: acc) tl
-		in
-		List.rev (assign [] elist)
-
-	let mk_get_block assign_fun e =
-		match e.eexpr with
-			| TBlock [] -> e
-			| TBlock (el) ->
-				{ e with eexpr = TBlock(apply_assign_block assign_fun el) }
-			| _ ->
-				{ e with eexpr = TBlock([ assign_fun e ]) }
-
-	let add_assign gen add_statement expr =
-		match expr.eexpr, follow expr.etype with
-			| _, TAbstract ({ a_path = ([],"Void") },[])
-			| TThrow _, _ ->
-				add_statement expr;
-				null expr.etype expr.epos
-			| _ ->
-				let var = mk_temp gen "stmt" expr.etype in
-				let tvars = { expr with eexpr = TVar(var,Some(expr)) } in
-				let local = { expr with eexpr = TLocal(var) } in
-				add_statement tvars;
-				local
-
-	(* requirement: right must be a statement *)
-	let rec apply_assign assign_fun right =
-		match right.eexpr with
-			| TBlock el ->
-				{ right with eexpr = TBlock(apply_assign_block assign_fun el) }
-			| TSwitch (cond, elblock_l, default) ->
-				{ right with eexpr = TSwitch(cond, List.map (fun (el,block) -> (el, mk_get_block assign_fun block)) elblock_l, Option.map (mk_get_block assign_fun) default) }
-			| TTry (block, catches) ->
-				{ right with eexpr = TTry(mk_get_block assign_fun block, List.map (fun (v,block) -> (v,mk_get_block assign_fun block) ) catches) }
-			| TIf (cond,eif,eelse) ->
-				{ right with eexpr = TIf(cond, mk_get_block assign_fun eif, Option.map (mk_get_block assign_fun) eelse) }
-			| TThrow _
-			| TWhile _
-			| TFor _
-			| TReturn _
-			| TBreak
-			| TContinue -> right
-			| TParenthesis p | TMeta(_,p) ->
-				apply_assign assign_fun p
-			| TVar _ ->
-				right
-			| _ ->
-				match follow right.etype with
-					| TAbstract ({ a_path = ([], "Void") },[]) ->
-						right
-					| _ -> trace (debug_expr right); assert false (* a statement is required *)
-
-	let short_circuit_op_unwrap gen add_statement expr :texpr =
-		let do_not expr =
-			{ expr with eexpr = TUnop(Ast.Not, Ast.Prefix, expr) }
-		in
-
-		(* loop will always return its own TBlock, and the mapped expression *)
-		let rec loop acc expr =
-			match expr.eexpr with
-				| TBinop ( (Ast.OpBoolAnd as op), left, right) ->
-					let var = mk_temp gen "boolv" right.etype in
-					let tvars = { right with eexpr = TVar(var, Some( { right with eexpr = TConst(TBool false); etype = gen.gcon.basic.tbool } )); etype = gen.gcon.basic.tvoid } in
-					let local = { right with eexpr = TLocal(var) } in
-
-					let mapped_left, ret_acc = loop ( (local, { right with eexpr = TBinop(Ast.OpAssign, local, right) } ) :: acc) left in
-
-					add_statement tvars;
-					({ expr with eexpr = TBinop(op, mapped_left, local) }, ret_acc)
-				(* we only accept OpBoolOr when it's the first to be evaluated *)
-				| TBinop ( (Ast.OpBoolOr as op), left, right) when acc = [] ->
-					let left = match left.eexpr with
-						| TLocal _ | TConst _ -> left
-						| _ -> add_assign gen add_statement left
-					in
-
-					let var = mk_temp gen "boolv" right.etype in
-					let tvars = { right with eexpr = TVar(var, Some( { right with eexpr = TConst(TBool false); etype = gen.gcon.basic.tbool } )); etype = gen.gcon.basic.tvoid } in
-					let local = { right with eexpr = TLocal(var) } in
-					add_statement tvars;
-
-					({ expr with eexpr = TBinop(op, left, local) }, [ do_not left, { right with eexpr = TBinop(Ast.OpAssign, local, right) } ])
-				| _ when acc = [] -> assert false
-				| _ ->
-					let var = mk_temp gen "boolv" expr.etype in
-					let tvars = { expr with eexpr = TVar(var, Some( { expr with etype = gen.gcon.basic.tbool } )); etype = gen.gcon.basic.tvoid } in
-					let local = { expr with eexpr = TLocal(var) } in
-
-					let last_local = ref local in
-					let acc = List.map (fun (local, assign) ->
-						let l = !last_local in
-						last_local := local;
-						(l, assign)
-					) acc in
-
-					add_statement tvars;
-					(local, acc)
-		in
-
-		let mapped_expr, local_assign_list = loop [] expr in
-
-		let rec loop local_assign_list : texpr =
-			match local_assign_list with
-				| [local, assign] ->
-					{ eexpr = TIf(local, assign, None); etype = gen.gcon.basic.tvoid; epos = assign.epos }
-				| (local, assign) :: tl ->
-					{ eexpr = TIf(local,
-						{
-							eexpr = TBlock ( assign :: [loop tl] );
-							etype = gen.gcon.basic.tvoid;
-							epos = assign.epos;
-						},
-					None); etype = gen.gcon.basic.tvoid; epos = assign.epos }
-				| [] -> assert false
-		in
-
-		add_statement (loop local_assign_list);
-		mapped_expr
-
-	(* there are two short_circuit fuctions as I'm still testing the best way to do it *)
-	(*let short_circuit_op_unwrap gen add_statement expr :texpr =
-		let block = ref [] in
-		let rec short_circuit_op_unwrap is_first last_block expr =
-			match expr.eexpr with
-				| TBinop ( (Ast.OpBoolAnd as op), left, right)
-				| TBinop ( (Ast.OpBoolOr as op), left, right) ->
-					let var = mk_temp gen "boolv" left.etype in
-					let tvars = { left with eexpr = TVar([var, if is_first then Some(left) else Some( { left with eexpr = TConst(TBool false) } )]); etype = gen.gcon.basic.tvoid } in
-					let local = { left with eexpr = TLocal(var) } in
-					if not is_first then begin
-						last_block := !last_block @ [ { left with eexpr = TBinop(Ast.OpAssign, local, left) } ]
-					end;
-
-					add_statement tvars;
-					let local_op = match op with | Ast.OpBoolAnd -> local | Ast.OpBoolOr -> { local with eexpr = TUnop(Ast.Not, Ast.Prefix, local) } | _ -> assert false in
-
-					let new_block = ref [] in
-					let new_right = short_circuit_op_unwrap false new_block right in
-					last_block := !last_block @ [ { expr with eexpr = TIf(local_op, { right with eexpr = TBlock(!new_block) }, None) } ];
-
-					{ expr with eexpr = TBinop(op, local, new_right) }
-				| _ when is_first -> assert false
-				| _ ->
-					let var = mk_temp gen "boolv" expr.etype in
-					let tvars = { expr with eexpr = TVar([var, Some ( { expr with eexpr = TConst(TBool false) } ) ]); etype = gen.gcon.basic.tvoid } in
-					let local = { expr with eexpr = TLocal(var) } in
-					last_block := !last_block @ [ { expr with eexpr = TBinop(Ast.OpAssign, local, expr) } ];
-					add_statement tvars;
-
-					local
-		in
-		let mapped_expr = short_circuit_op_unwrap true block expr in
-		add_statement { eexpr = TBlock(!block); etype = gen.gcon.basic.tvoid; epos = expr.epos };
-		mapped_expr*)
-
-	let twhile_with_condition_statement gen add_statement twhile cond e1 flag =
-		(* when a TWhile is found with a problematic condition *)
-		let basic = gen.gcon.basic in
-
-		let block = if flag = Ast.NormalWhile then
-			{ e1 with eexpr = TIf(cond, e1, Some({ e1 with eexpr = TBreak; etype = basic.tvoid })) }
-		else
-			Type.concat e1 { e1 with
-				eexpr = TIf({
-					eexpr = TUnop(Ast.Not, Ast.Prefix, mk_paren cond);
-					etype = basic.tbool;
-					epos = cond.epos
-				}, { e1 with eexpr = TBreak; etype = basic.tvoid }, None);
-				etype = basic.tvoid
-			}
-		in
-
-		add_statement { twhile with
-			eexpr = TWhile(
-				{ eexpr = TConst(TBool true); etype = basic.tbool; epos = cond.epos },
-				block,
-				Ast.DoWhile
-			);
-		}
-
-	let try_call_unwrap_statement gen problematic_expression_unwrap (add_statement:texpr->unit) (expr:texpr) : texpr option =
-		let check_left left =
-			match expr_kind left with
-				| KExprWithStatement ->
-					problematic_expression_unwrap add_statement left KExprWithStatement
-				| KStatement -> assert false (* doesn't make sense a KStatement as a left side expression *)
-				| _ -> left
-		in
-
-		let handle_assign op left right =
-			let left = check_left left in
-			Some (apply_assign (fun e -> { e with eexpr = TBinop(op, left, if ExtType.is_void left.etype then e else gen.ghandle_cast left.etype e.etype e) }) right )
-		in
-
-		let handle_return e =
-			Some( apply_assign (fun e ->
-				match e.eexpr with
-					| TThrow _ -> e
-					| _ when ExtType.is_void e.etype ->
-							{ e with eexpr = TBlock([e; { e with eexpr = TReturn None }]) }
-					| _ ->
-							{ e with eexpr = TReturn( Some e ) }
-			) e )
-		in
-
-		let is_problematic_if right =
-			match expr_kind right with
-				| KStatement | KExprWithStatement -> true
-				| _ -> false
-		in
-
-		match expr.eexpr with
-			| TBinop((Ast.OpAssign as op),left,right)
-			| TBinop((Ast.OpAssignOp _ as op),left,right) when shallow_expr_type right = Statement ->
-				handle_assign op left right
-			| TReturn( Some right ) when shallow_expr_type right = Statement ->
-				handle_return right
-			| TBinop((Ast.OpAssign as op),left, ({ eexpr = TBinop(Ast.OpBoolAnd,_,_) } as right) )
-			| TBinop((Ast.OpAssign as op),left,({ eexpr = TBinop(Ast.OpBoolOr,_,_) } as right))
-			| TBinop((Ast.OpAssignOp _ as op),left,({ eexpr = TBinop(Ast.OpBoolAnd,_,_) } as right) )
-			| TBinop((Ast.OpAssignOp _ as op),left,({ eexpr = TBinop(Ast.OpBoolOr,_,_) } as right) ) ->
-				let right = short_circuit_op_unwrap gen add_statement right in
-				Some { expr with eexpr = TBinop(op, check_left left, right) }
-			| TVar(v,Some({ eexpr = TBinop(Ast.OpBoolAnd,_,_) } as right))
-			| TVar(v,Some({ eexpr = TBinop(Ast.OpBoolOr,_,_) } as right)) ->
-				let right = short_circuit_op_unwrap gen add_statement right in
-				Some { expr with eexpr = TVar(v, Some(right)) }
-			| TVar(v,Some(right)) when shallow_expr_type right = Statement ->
-				add_statement ({ expr with eexpr = TVar(v, Some(null right.etype right.epos)) });
-				handle_assign Ast.OpAssign { expr with eexpr = TLocal(v); etype = v.v_type } right
-			(* TIf handling *)
-			| TBinop((Ast.OpAssign as op),left, ({ eexpr = TIf _ } as right))
-			| TBinop((Ast.OpAssignOp _ as op),left,({ eexpr = TIf _ } as right)) when is_problematic_if right ->
-				handle_assign op left right
-			| TVar(v,Some({ eexpr = TIf _ } as right)) when is_problematic_if right ->
-				add_statement ({ expr with eexpr = TVar(v, Some(null right.etype right.epos)) });
-				handle_assign Ast.OpAssign { expr with eexpr = TLocal(v); etype = v.v_type } right
-			| TWhile(cond, e1, flag) when is_problematic_if cond ->
-				twhile_with_condition_statement gen add_statement expr cond e1 flag;
-				Some (null expr.etype expr.epos)
-			| _ -> None
-
-	let configure gen (on_expr_as_statement:texpr->texpr option) =
-		let add_assign = add_assign gen in
-
-		let problematic_expression_unwrap add_statement expr e_type =
-			let rec problematic_expression_unwrap is_first expr e_type =
-				match e_type, expr.eexpr with
-					| _, TBinop(Ast.OpBoolAnd, _, _)
-					| _, TBinop(Ast.OpBoolOr, _, _) -> add_assign add_statement expr (* add_assign so try_call_unwrap_expr *)
-					| KNoSideEffects, _ -> expr
-					| KStatement, _
-					| KNormalExpr, _ -> add_assign add_statement expr
-					| KExprWithStatement, TCall _
-					| KExprWithStatement, TNew _
-					| KExprWithStatement, TBinop (Ast.OpAssign,_,_)
-					| KExprWithStatement, TBinop (Ast.OpAssignOp _,_,_)
-					| KExprWithStatement, TUnop (Ast.Increment,_,_) (* all of these may have side-effects, so they must also be add_assign'ed . is_first avoids infinite loop *)
-					| KExprWithStatement, TUnop (Ast.Decrement,_,_) when not is_first -> add_assign add_statement expr
-
-					(* bugfix: Type.map_expr doesn't guarantee the correct order of execution *)
-					| KExprWithStatement, TBinop(op,e1,e2) ->
-						let e1 = problematic_expression_unwrap false e1 (expr_kind e1) in
-						let e2 = problematic_expression_unwrap false e2 (expr_kind e2) in
-						{ expr with eexpr = TBinop(op, e1, e2) }
-					| KExprWithStatement, TArray(e1,e2) ->
-						let e1 = problematic_expression_unwrap false e1 (expr_kind e1) in
-						let e2 = problematic_expression_unwrap false e2 (expr_kind e2) in
-						{ expr with eexpr = TArray(e1, e2) }
-					(* bugfix: calls should not be transformed into closure calls *)
-					| KExprWithStatement, TCall(( { eexpr = TField (ef_left, f) } as ef ), eargs) ->
-						{ expr with eexpr = TCall(
-							{ ef with eexpr = TField(problematic_expression_unwrap false ef_left (expr_kind ef_left), f) },
-							List.map (fun e -> problematic_expression_unwrap false e (expr_kind e)) eargs)
-						}
-					| KExprWithStatement, _ -> Type.map_expr (fun e -> problematic_expression_unwrap false e (expr_kind e)) expr
-			in
-			problematic_expression_unwrap true expr e_type
-		in
-
-		let rec traverse e =
-			match e.eexpr with
-				| TBlock el ->
-					let new_block = ref [] in
-					let rec process_statement e =
-						let e = no_paren e in
-						match e.eexpr, shallow_expr_type e with
-							| TCall( { eexpr = TLocal v } as elocal, elist ), _ when String.get v.v_name 0 = '_' && Hashtbl.mem gen.gspecial_vars v.v_name ->
-								new_block := { e with eexpr = TCall( elocal, List.map (fun e ->
-									match e.eexpr with
-										| TBlock _ -> traverse e
-										| _ -> e
-								) elist ) } :: !new_block
-							| _, Statement | _, Both _ ->
-								let e = match e.eexpr with | TReturn (Some ({ eexpr = TThrow _ } as ethrow)) -> ethrow | _ -> e in
-								let kinds = get_kinds e in
-								if has_problematic_expressions kinds then begin
-									match try_call_unwrap_statement gen problematic_expression_unwrap add_statement e with
-										| Some { eexpr = TConst(TNull) } (* no op *)
-										| Some { eexpr = TBlock [] } -> ()
-										| Some e ->
-											if has_problematic_expressions (get_kinds e) then begin
-												process_statement e
-											end else
-												new_block := (traverse e) :: !new_block
-										| None ->
-										(
-											let acc = ref kinds in
-											let new_e = expr_stat_map (fun e ->
-												match !acc with
-													| hd :: tl ->
-														acc := tl;
-														if has_problematic_expressions (hd :: tl) then begin
-															problematic_expression_unwrap add_statement e hd
-														end else
-															e
-													| [] -> assert false
-											) e in
-
-											new_block := (traverse new_e) :: !new_block
-										)
-								end else begin new_block := (traverse e) :: !new_block end
-							| _, Expression e ->
-								match on_expr_as_statement e with
-									| None -> ()
-									| Some e -> process_statement e
-					and add_statement expr =
-						process_statement expr
-					in
-
-					List.iter (process_statement) el;
-					let block = List.rev !new_block in
-					{ e with eexpr = TBlock(block) }
-				| TTry (block, catches) ->
-					{ e with eexpr = TTry(traverse (mk_block block), List.map (fun (v,block) -> (v, traverse (mk_block block))) catches) }
-				| TSwitch (cond,el_e_l, default) ->
-					{ e with eexpr = TSwitch(cond, List.map (fun (el,e) -> (el, traverse (mk_block e))) el_e_l, Option.map (fun e -> traverse (mk_block e)) default) }
-				| TWhile (cond,block,flag) ->
-					{e with eexpr = TWhile(cond,traverse (mk_block block), flag) }
-				| TIf (cond, eif, eelse) ->
-					{ e with eexpr = TIf(cond, traverse (mk_block eif), Option.map (fun e -> traverse (mk_block e)) eelse) }
-				| TFor (v,it,block) ->
-					{ e with eexpr = TFor(v,it, traverse (mk_block block)) }
-				| TFunction (tfunc) ->
-					{ e with eexpr = TFunction({ tfunc with tf_expr = traverse (mk_block tfunc.tf_expr) }) }
-				| _ -> e (* if expression doesn't have a block, we will exit *)
-		in
-		let map e = Some(traverse e) in
-		gen.gsyntax_filters#add ~name:name ~priority:(PCustom priority) map
-end;;
-
-(* ******************************************* *)
-(* Casts detection v2 *)
-(* ******************************************* *)
-
-(*
-
-	Will detect implicit casts and add TCast for them. Since everything is already followed by follow_all, typedefs are considered a new type altogether
-
-	Types shouldn't be cast if:
-		* When an instance is being coerced to a superclass or to an implemented interface
-		* When anything is being coerced to Dynamic
-
-	edit:
-		As a matter of performance, we will also run the type parameters casts in here. Otherwise the exact same computation would have to be performed twice,
-		with maybe even some loss of information
-
-		* TAnon / TDynamic will call
-		* Type parameter handling will be abstracted
-
-	dependencies:
-		Must run before ExpressionUnwrap
-
-*)
-
-module CastDetect =
-struct
-
-	let name = "cast_detect_2"
-
-	let priority = solve_deps name [DBefore TypeParams.priority; DBefore ExpressionUnwrap.priority]
-
-	(* ******************************************* *)
-	(* ReturnCast *)
-	(* ******************************************* *)
-
-	(*
-
-		Cast detection for return types can't be done at CastDetect time, since we need an
-		unwrapped expression to make sure we catch all return cast detections. So this module
-		is specifically to deal with that, and is configured automatically by CastDetect
-
-		dependencies:
-
-
-	*)
-
-	module ReturnCast =
-	struct
-
-		let name = "return_cast"
-
-		let priority = solve_deps name [DAfter priority; DAfter ExpressionUnwrap.priority]
-
-		let default_implementation gen =
-			let rec extract_expr e = match e.eexpr with
-				| TParenthesis e
-				| TMeta (_,e)
-				| TCast(e,_) -> extract_expr e
-				| _ -> e
-			in
-			let current_ret_type = ref None in
-			let handle e tto tfrom = gen.ghandle_cast (gen.greal_type tto) (gen.greal_type tfrom) e in
-			let in_value = ref false in
-
-			let rec run e =
-				let was_in_value = !in_value in
-				in_value := true;
-				match e.eexpr with
-				| TReturn (eopt) ->
-					(* a return must be inside a function *)
-					let ret_type = match !current_ret_type with | Some(s) -> s | None -> gen.gcon.error "Invalid return outside function declaration." e.epos; assert false in
-					(match eopt with
-					| None when not (ExtType.is_void ret_type) ->
-						{ e with eexpr = TReturn( Some(null ret_type e.epos)) }
-					| None -> e
-					| Some eret ->
-						{ e with eexpr = TReturn( Some(handle (run eret) ret_type eret.etype ) ) })
-				| TFunction(tfunc) ->
-					let last_ret = !current_ret_type in
-					current_ret_type := Some(tfunc.tf_type);
-					let ret = Type.map_expr run e in
-					current_ret_type := last_ret;
-					ret
-				| TBlock el ->
-					{ e with eexpr = TBlock ( List.map (fun e -> in_value := false; run e) el ) }
-				| TBinop ( (Ast.OpAssign as op),e1,e2)
-				| TBinop ( (Ast.OpAssignOp _ as op),e1,e2) when was_in_value ->
-					let e1 = extract_expr (run e1) in
-					let r = { e with eexpr = TBinop(op, e1, handle (run e2) e1.etype e2.etype); etype = e1.etype } in
-					handle r e.etype e1.etype
-				| TBinop ( (Ast.OpAssign as op),({ eexpr = TField(tf, f) } as e1), e2 )
-				| TBinop ( (Ast.OpAssignOp _ as op),({ eexpr = TField(tf, f) } as e1), e2 ) ->
-					(match field_access_esp gen (gen.greal_type tf.etype) (f) with
-						| FClassField(cl,params,_,_,is_static,actual_t,_) ->
-							let actual_t = if is_static then actual_t else apply_params cl.cl_params params actual_t in
-							let e1 = extract_expr (run e1) in
-							{ e with eexpr = TBinop(op, e1, handle (run e2) actual_t e2.etype); etype = e1.etype }
-						| _ ->
-							let e1 = extract_expr (run e1) in
-							{ e with eexpr = TBinop(op, e1, handle (run e2) e1.etype e2.etype); etype = e1.etype }
-					)
-				| TBinop ( (Ast.OpAssign as op),e1,e2)
-				| TBinop ( (Ast.OpAssignOp _ as op),e1,e2) ->
-					let e1 = extract_expr (run e1) in
-					{ e with eexpr = TBinop(op, e1, handle (run e2) e1.etype e2.etype); etype = e1.etype }
-				| _ -> Type.map_expr run e
-			in
-			run
-
-		let configure gen =
-			let map e = Some(default_implementation gen e) in
-			gen.gsyntax_filters#add ~name:name ~priority:(PCustom priority) map
-
-	end;;
-
-	let get_args t = match follow t with
-		| TFun(args,ret) -> args,ret
-		| _ -> trace (debug_type t); assert false
-
-	(*
-		Since this function is applied under native-context only, the type paraters will already be changed
-	*)
-	let map_cls gen also_implements fn super =
-		let rec loop c tl =
-			if c == super then
-				fn c tl
-			else
-				(match c.cl_super with
-					| None -> false
-					| Some (cs,tls) ->
-						let tls = gen.greal_type_param (TClassDecl cs) tls in
-						loop cs (List.map (apply_params c.cl_params tl) tls)
-				)
-				||
-				(if also_implements then
-					List.exists (fun (cs,tls) -> loop cs (List.map (apply_params c.cl_params tl) tls)) c.cl_implements
-				else
-					false)
-		in
-		loop
-
-	let follow_dyn t = match follow t with
-		| TMono _ | TLazy _ -> t_dynamic
-		| t -> t
-
-	(*
-		this has a slight change from the type.ml version, in which it doesn't
-		change a TMono into the other parameter
-	*)
-	let rec type_eq gen param a b =
-		if a == b then
-			()
-		else match follow_dyn (gen.greal_type a) , follow_dyn (gen.greal_type b) with
-		| TEnum (e1,tl1) , TEnum (e2,tl2) ->
-			if e1 != e2 && not (param = EqCoreType && e1.e_path = e2.e_path) then Type.error [cannot_unify a b];
-			List.iter2 (type_eq gen param) tl1 tl2
-		| TAbstract (a1,tl1) , TAbstract (a2,tl2) ->
-			if a1 != a2 && not (param = EqCoreType && a1.a_path = a2.a_path) then Type.error [cannot_unify a b];
-			List.iter2 (type_eq gen param) tl1 tl2
-		| TInst (c1,tl1) , TInst (c2,tl2) ->
-			if c1 != c2 && not (param = EqCoreType && c1.cl_path = c2.cl_path) && (match c1.cl_kind, c2.cl_kind with KExpr _, KExpr _ -> false | _ -> true) then Type.error [cannot_unify a b];
-			List.iter2 (type_eq gen param) tl1 tl2
-		| TFun (l1,r1) , TFun (l2,r2) when List.length l1 = List.length l2 ->
-			(try
-				type_eq gen param r1 r2;
-				List.iter2 (fun (n,o1,t1) (_,o2,t2) ->
-					if o1 <> o2 then Type.error [Not_matching_optional n];
-					type_eq gen param t1 t2
-				) l1 l2
-			with
-				Unify_error l -> Type.error (cannot_unify a b :: l))
-		| TDynamic a , TDynamic b ->
-			type_eq gen param a b
-		| TAnon a1, TAnon a2 ->
-			(try
-				PMap.iter (fun n f1 ->
-					try
-						let f2 = PMap.find n a2.a_fields in
-						if f1.cf_kind <> f2.cf_kind && (param = EqStrict || param = EqCoreType || not (unify_kind f1.cf_kind f2.cf_kind)) then Type.error [invalid_kind n f1.cf_kind f2.cf_kind];
-						try
-							type_eq gen param f1.cf_type f2.cf_type
-						with
-							Unify_error l -> Type.error (invalid_field n :: l)
-					with
-						Not_found ->
-							if is_closed a2 then Type.error [has_no_field b n];
-							if not (link (ref None) b f1.cf_type) then Type.error [cannot_unify a b];
-							a2.a_fields <- PMap.add n f1 a2.a_fields
-				) a1.a_fields;
-				PMap.iter (fun n f2 ->
-					if not (PMap.mem n a1.a_fields) then begin
-						if is_closed a1 then Type.error [has_no_field a n];
-						if not (link (ref None) a f2.cf_type) then Type.error [cannot_unify a b];
-						a1.a_fields <- PMap.add n f2 a1.a_fields
-					end;
-				) a2.a_fields;
-			with
-				Unify_error l -> Type.error (cannot_unify a b :: l))
-		| _ , _ ->
-			if b == t_dynamic && (param = EqRightDynamic || param = EqBothDynamic) then
-				()
-			else if a == t_dynamic && param = EqBothDynamic then
-				()
-			else
-				Type.error [cannot_unify a b]
-
-	let type_iseq gen a b =
-		try
-			type_eq gen EqStrict a b;
-			true
-		with
-			Unify_error _ -> false
-
-	(* will return true if both arguments are compatible. If it's not the case, a runtime error is very likely *)
-	let is_cl_related gen cl tl super superl =
-		let is_cl_related cl tl super superl = map_cls gen (match cl.cl_kind,super.cl_kind with KTypeParameter _, _ | _,KTypeParameter _ -> false | _ -> true) (fun _ _ -> true) super cl tl in
-		is_cl_related cl tl super superl || is_cl_related super superl cl tl
-
-	let is_exactly_basic gen t1 t2 =
-		match gen.gfollow#run_f t1, gen.gfollow#run_f t2 with
-			| TAbstract(a1, []), TAbstract(a2, []) ->
-				a1 == a2 && Common.defined gen.gcon Define.FastCast
-			| TInst(c1, []), TInst(c2, []) ->
-				c1 == c2 && Common.defined gen.gcon Define.FastCast
-			| TEnum(e1, []), TEnum(e2, []) ->
-				e1 == e2 && Common.defined gen.gcon Define.FastCast
-			| _ ->
-				false
-
-	let rec is_unsafe_cast gen to_t from_t =
-		match (follow to_t, follow from_t) with
-			| TInst(cl_to, to_params), TInst(cl_from, from_params) ->
-				not (is_cl_related gen cl_from from_params cl_to to_params)
-			| TEnum(e_to, _), TEnum(e_from, _) ->
-				e_to.e_path <> e_from.e_path
-			| TFun _, TFun _ ->
-				(* functions are never unsafe cast by default. This behavior might be changed *)
-				(* with a later AST pass which will run through TFun to TFun casts *)
-				false
-			| TMono _, _
-			| _, TMono _
-			| TDynamic _, _
-			| _, TDynamic _ ->
-				false
-			| TAnon _, _
-			| _, TAnon _ ->
-				(* anonymous are never unsafe also. *)
-				(* Though they will generate a cast, so if this cast is unneeded it's better to avoid them by tweaking gen.greal_type *)
-				false
-			| TAbstract _, _
-			| _, TAbstract _ ->
-				(try
-					unify from_t to_t;
-					false
-				with | Unify_error _ ->
-					try
-						unify to_t from_t; (* still not unsafe *)
-						false
-					with | Unify_error _ ->
-						true)
-			| _ -> true
-
-	let unifies tfrom tto = try
-		unify tfrom tto;
-		true
-	with | _ ->
-		false
-
-	let do_unsafe_cast gen from_t to_t e	=
-		let t_path t =
-			match t with
-				| TInst(cl, _) -> cl.cl_path
-				| TEnum(e, _) -> e.e_path
-				| TType(t, _) -> t.t_path
-				| TAbstract(a, _) -> a.a_path
-				| TDynamic _ -> ([], "Dynamic")
-				| _ -> raise Not_found
-		in
-		match gen.gfollow#run_f from_t, gen.gfollow#run_f to_t with
-		| TInst({ cl_kind = KTypeParameter tl },_), t2 when List.exists (fun t -> unifies t t2) tl ->
-			mk_cast to_t (mk_cast t_dynamic e)
-		| _ ->
-			let do_default () =
-				gen.gon_unsafe_cast to_t e.etype e.epos;
-				mk_cast to_t (mk_cast t_dynamic e)
-			in
-			(* TODO: there really should be a better way to write that *)
-			try
-				if (Hashtbl.find gen.gsupported_conversions (t_path from_t)) from_t to_t then
-					mk_cast to_t e
-				else
-					do_default()
-			with
-				| Not_found ->
-					try
-						if (Hashtbl.find gen.gsupported_conversions (t_path to_t)) from_t to_t then
-							mk_cast to_t e
-						else
-							do_default()
-					with
-						| Not_found -> do_default()
-
-	(* ****************************** *)
-	(* cast handler *)
-	(* decides if a cast should be emitted, given a from and a to type *)
-	(*
-		this function is like a mini unify, without e.g. subtyping, which makes sense
-		at the backend level, since most probably Anons and TInst will have a different representation there
-	*)
-	let rec handle_cast gen e real_to_t real_from_t =
-		let do_unsafe_cast () = do_unsafe_cast gen real_from_t real_to_t { e with etype = real_from_t } in
-		let to_t, from_t = real_to_t, real_from_t in
-
-		let mk_cast fast t e =
-			match e.eexpr with
-				(* TThrow is always typed as Dynamic, we just need to type it accordingly *)
-				| TThrow _ -> { e with etype = t }
-				| _ -> if fast then mk_castfast t e else mk_cast t e
-		in
-
-		let e = { e with etype = real_from_t } in
-		if try fast_eq real_to_t real_from_t with Invalid_argument("List.for_all2") -> false then e else
-		match real_to_t, real_from_t with
-			(* string is the only type that can be implicitly converted from any other *)
-			| TInst( { cl_path = ([], "String") }, []), TInst( { cl_path = ([], "String") }, [] ) ->
-				mk_cast true to_t e
-			| TInst( { cl_path = ([], "String") }, []), _ ->
-				mk_cast false to_t e
-			| TInst(cl_to, params_to), TInst(cl_from, params_from) ->
-				let ret = ref None in
-				(*
-					this is a little confusing:
-					we are here mapping classes until we have the same to and from classes, applying the type parameters in each step, so we can
-					compare the type parameters;
-
-					If a class is found - meaning that the cl_from can be converted without a cast into cl_to,
-					we still need to check their type parameters.
-				*)
-				ignore (map_cls gen (match cl_from.cl_kind,cl_to.cl_kind with KTypeParameter _, _ | _,KTypeParameter _ -> false | _ -> true) (fun _ tl ->
-					try
-						(* type found, checking type parameters *)
-						List.iter2 (type_eq gen EqStrict) tl params_to;
-						ret := Some e;
-						true
-					with | Unify_error _ ->
-						(* type parameters need casting *)
-						if gen.ghas_tparam_cast_handler then begin
-							(*
-								if we are already handling type parameter casts on other part of code (e.g. RealTypeParameters),
-								we'll just make a cast to indicate that this place needs type parameter-involved casting
-							*)
-							ret := Some (mk_cast true to_t e);
-							true
-						end else
-							(*
-								if not, we're going to check if we only need a simple cast,
-								or if we need to first cast into the dynamic version of it
-							*)
-							try
-								List.iter2 (type_eq gen EqRightDynamic) tl params_to;
-								ret := Some (mk_cast true to_t e);
-								true
-							with | Unify_error _ ->
-								ret := Some (mk_cast true to_t (mk_cast true (TInst(cl_to, List.map (fun _ -> t_dynamic) params_to)) e));
-								true
-				) cl_to cl_from params_from);
-				if is_some !ret then
-					get !ret
-				else if is_cl_related gen cl_from params_from cl_to params_to then
-					mk_cast true to_t e
-				else
-					(* potential unsafe cast *)
-					(do_unsafe_cast ())
-			| TMono _, TMono _
-			| TMono _, TDynamic _
-			| TDynamic _, TDynamic _
-			| TDynamic _, TMono _ ->
-				e
-			| TMono _, _
-			| TDynamic _, _
-			| TAnon _, _ when gen.gneeds_box real_from_t ->
-				mk_cast false to_t e
-			| TMono _, _
-			| TDynamic _, _ -> e
-			| _, TMono _
-			| _, TDynamic _ -> mk_cast false to_t e
-			| TAnon (a_to), TAnon (a_from) ->
-				if a_to == a_from then
-					e
-				else if type_iseq gen to_t from_t then (* FIXME apply unify correctly *)
-					e
-				else
-					mk_cast true to_t e
-			| _, TAnon(anon) -> (try
-				let p2 = match !(anon.a_status) with
-				| Statics c -> TInst(c,List.map (fun _ -> t_dynamic) c.cl_params)
-				| EnumStatics e -> TEnum(e, List.map (fun _ -> t_dynamic) e.e_params)
-				| AbstractStatics a -> TAbstract(a, List.map (fun _ -> t_dynamic) a.a_params)
-				| _ -> raise Not_found
-				in
-				let tclass = match get_type gen ([],"Class") with
-				| TAbstractDecl(a) -> a
-				| _ -> assert false in
-				handle_cast gen e real_to_t (gen.greal_type (TAbstract(tclass, [p2])))
-			with | Not_found ->
-				mk_cast false to_t e)
-			| TAbstract (a_to, _), TAbstract(a_from, _) when a_to == a_from ->
-				e
-			| TAbstract _, TInst({ cl_kind = KTypeParameter _ }, _)
-			| TInst({ cl_kind = KTypeParameter _ }, _), TAbstract _ ->
-				do_unsafe_cast()
-			| TAbstract _, _
-			| _, TAbstract _ ->
-				(try
-					unify from_t to_t;
-					mk_cast true to_t e
-				with | Unify_error _ ->
-					try
-						unify to_t from_t;
-						mk_cast true to_t e
-					with | Unify_error _ ->
-						do_unsafe_cast())
-			| TEnum(e_to, []), TEnum(e_from, []) ->
-				if e_to == e_from then
-					e
-				else
-					(* potential unsafe cast *)
-					(do_unsafe_cast ())
-			| TEnum(e_to, params_to), TEnum(e_from, params_from) when e_to.e_path = e_from.e_path ->
-				(try
-						List.iter2 (type_eq gen (if gen.gallow_tp_dynamic_conversion then EqRightDynamic else EqStrict)) params_from params_to;
-						e
-					with
-						| Unify_error _ -> do_unsafe_cast ()
-				)
-			| TEnum(en, params_to), TInst(cl, params_from)
-				| TInst(cl, params_to), TEnum(en, params_from) ->
-					(* this is here for max compatibility with EnumsToClass module *)
-				if en.e_path = cl.cl_path && Meta.has Meta.Class en.e_meta then begin
-					(try
-						List.iter2 (type_eq gen (if gen.gallow_tp_dynamic_conversion then EqRightDynamic else EqStrict)) params_from params_to;
-						e
-					with
-						| Invalid_argument("List.iter2") ->
-							(*
-								this is a hack for RealTypeParams. Since there is no way at this stage to know if the class is the actual
-								EnumsToClass derived from the enum, we need to imply from possible ArgumentErrors (because of RealTypeParams interfaces),
-								that they would only happen if they were a RealTypeParams created interface
-							*)
-							e
-						| Unify_error _ -> do_unsafe_cast ()
-					)
-				end else
-					do_unsafe_cast ()
-			| TType(t_to, params_to), TType(t_from, params_from) when t_to == t_from ->
-				if gen.gspecial_needs_cast real_to_t real_from_t then
-					(try
-						List.iter2 (type_eq gen (if gen.gallow_tp_dynamic_conversion then EqRightDynamic else EqStrict)) params_from params_to;
-						e
-					with
-						| Unify_error _ -> do_unsafe_cast ()
-					)
-				else
-					e
-			| TType(t_to, _), TType(t_from,_) ->
-				if gen.gspecial_needs_cast real_to_t real_from_t then
-					mk_cast false to_t e
-				else
-					e
-			| TType _, _ when gen.gspecial_needs_cast real_to_t real_from_t ->
-				mk_cast false to_t e
-			| _, TType _ when gen.gspecial_needs_cast real_to_t real_from_t ->
-				mk_cast false to_t e
-			(*| TType(t_to, _), TType(t_from, _) ->
-				if t_to.t_path = t_from.t_path then
-					e
-				else if is_unsafe_cast gen real_to_t real_from_t then (* is_unsafe_cast will already follow both *)
-					(do_unsafe_cast ())
-				else
-					mk_cast to_t e*)
-			| TType _, _
-			| _, TType _ ->
-				if is_unsafe_cast gen real_to_t real_from_t then (* is_unsafe_cast will already follow both *)
-					(do_unsafe_cast ())
-				else
-					mk_cast false to_t e
-			| TAnon anon, _ ->
-				if PMap.is_empty anon.a_fields then
-					e
-				else
-					mk_cast true to_t e
-			| TFun(args, ret), TFun(args2, ret2) ->
-				let get_args = List.map (fun (_,_,t) -> t) in
-				(try List.iter2 (type_eq gen (EqBothDynamic)) (ret :: get_args args) (ret2 :: get_args args2); e with | Unify_error _ | Invalid_argument("List.iter2") -> mk_cast true to_t e)
-			| _, _ ->
-				do_unsafe_cast ()
-
-	(* end of cast handler *)
-	(* ******************* *)
-
-	let is_static_overload c name =
-		match c.cl_super with
-		| None -> false
-		| Some (sup,_) ->
-			let rec loop c =
-				(PMap.mem name c.cl_statics) || (match c.cl_super with
-					| None -> false
-					| Some (sup,_) -> loop sup)
-			in
-			loop sup
-
-	(* this is a workaround for issue #1743, as FInstance() is returning the incorrect classfield *)
-	let rec clean_t t = match follow t with
-		| TAbstract(a,tl) when not (Meta.has Meta.CoreType a.a_meta) ->
-			clean_t (Abstract.get_underlying_type a tl)
-		| t -> t
-
-	let select_overload gen applied_f overloads types params =
-		let rec check_arg arglist elist =
-			match arglist, elist with
-				| [], [] -> true (* it is valid *)
-				| (_,_,TAbstract({ a_path = (["haxe";"extern"],"Rest") }, [t])) :: [], elist ->
-					List.for_all (fun (_,_,et) -> Type.type_iseq (clean_t et) (clean_t t)) elist
-				| (_,_,t) :: arglist, (_,_,et) :: elist when Type.type_iseq (clean_t et) (clean_t t) ->
-					check_arg arglist elist
-				| _ -> false
-		in
-		match follow applied_f with
-		| TFun _ ->
-			replace_mono applied_f;
-			let args, _ = get_fun applied_f in
-			let elist = List.rev args in
-			let rec check_overload overloads =
-				match overloads with
-				| (t, cf) :: overloads ->
-						let cft = apply_params types params t in
-						let cft = monomorphs cf.cf_params cft in
-						let args, _ = get_fun cft in
-						if check_arg (List.rev args) elist then
-							cf,t,false
-						else if overloads = [] then
-							cf,t,true (* no compatible overload was found *)
-						else
-							check_overload overloads
-				| [] -> assert false
-			in
-			check_overload overloads
-		| _ -> match overloads with  (* issue #1742 *)
-		| (t,cf) :: [] -> cf,t,true
-		| (t,cf) :: _ -> cf,t,false
-		| _ -> assert false
-
-	let choose_ctor gen cl tparams etl maybe_empty_t p =
-		let ctor, sup, stl = OverloadingConstructor.cur_ctor cl tparams in
-		(* get returned stl, with Dynamic as t_empty *)
-		let rec get_changed_stl c tl =
-			if c == sup then
-				tl
-			else match c.cl_super with
-			| None -> stl
-			| Some(sup,stl) -> get_changed_stl sup (List.map (apply_params c.cl_params tl) stl)
-		in
-		let ret_tparams = List.map (fun t -> match follow t with
-		| TDynamic _ | TMono _ -> t_empty
-		| _ -> t) tparams in
-		let ret_stl = get_changed_stl cl ret_tparams in
-		let ctors = ctor :: ctor.cf_overloads in
-		List.iter replace_mono etl;
-		(* first filter out or select outright maybe_empty *)
-		let ctors, is_overload = match etl, maybe_empty_t with
-		| [t], Some empty_t ->
-			let count = ref 0 in
-			let is_empty_call = Type.type_iseq t empty_t in
-			let ret = List.filter (fun cf -> match follow cf.cf_type with
-			(* | TFun([_,_,t],_) -> incr count; true *)
-			| TFun([_,_,t],_) ->
-				replace_mono t; incr count; is_empty_call = (Type.type_iseq t empty_t)
-			| _ -> false) ctors in
-			ret, !count > 1
-		| _ ->
-			let len = List.length etl in
-			let ret = List.filter (fun cf -> List.length (fst (get_fun cf.cf_type)) = len) ctors in
-			ret, (match ret with | _ :: [] -> false | _ -> true)
-		in
-		let rec check_arg arglist elist =
-			match arglist, elist with
-			| [], [] -> true
-			| (_,_,t) :: arglist, et :: elist -> (try
-				let t = run_follow gen t in
-				unify et t;
-				check_arg arglist elist
-			with | Unify_error el ->
-				(* List.iter (fun el -> gen.gcon.warning (Typecore.unify_error_msg (print_context()) el) p) el; *)
-				false)
-			| _ ->
-				false
-		in
-		let rec check_cf cf =
-			let t = apply_params sup.cl_params stl cf.cf_type in
-			replace_mono t;
-			let args, _ = get_fun t in
-			check_arg args etl
-		in
-		match is_overload, ctors with
-			| false, [c] ->
-				false, c, sup, ret_stl
-			| _ ->
-				is_overload, List.find check_cf ctors, sup, ret_stl
-
-	let change_rest tfun elist =
-		let rec loop acc arglist elist = match arglist, elist with
-			| (_,_,TAbstract({ a_path = (["haxe";"extern"],"Rest") },[t])) :: [], elist ->
-				List.rev (List.map (fun _ -> "rest",false,t) elist @ acc)
-			| (n,o,t) :: arglist, _ :: elist ->
-				loop ((n,o,t) :: acc) arglist elist
-			| _, _ ->
-				List.rev acc
-		in
-		let args,ret = get_fun tfun in
-		TFun(loop [] args elist, ret)
-
-	let fastcast_if_needed gen expr real_to_t real_from_t =
-		if Common.defined gen.gcon Define.FastCast then begin
-			if type_iseq gen real_to_t real_from_t then
-				{ expr with etype = real_to_t }
-			else
-				mk_castfast real_to_t { expr with etype=real_from_t }
-		end else
-			handle_cast gen expr real_to_t real_from_t
-
-	(*
-		Type parameter handling
-		It will detect if/what type parameters were used, and call the cast handler
-		It will handle both TCall(TField) and TCall by receiving a texpr option field: e
-		Also it will transform the type parameters with greal_type_param and make
-
-		handle_impossible_tparam - should cases where the type parameter is impossible to be determined from the called parameters be Dynamic?
-		e.g. static function test<T>():T {}
-	*)
-
-	(* match e.eexpr with | TCall( ({ eexpr = TField(ef, f) }) as e1, elist ) -> *)
-	let handle_type_parameter gen e e1 ef ~clean_ef ~overloads_cast_to_base f elist calls_parameters_explicitly =
-		(* the ONLY way to know if this call has parameters is to analyze the calling field. *)
-		(* To make matters a little worse, on both C# and Java only in some special cases that type parameters will be used *)
-		(* Namely, when using reflection type parameters are useless, of course. This also includes anonymous types *)
-		(* this will have to be handled by gparam_func_call *)
-
-		let return_var efield =
-			match e with
-				| None ->
-					efield
-				| Some ecall ->
-					match follow efield.etype with
-						| TFun(_,ret) ->
-							(* closures will be handled by the closure handler. So we will just hint what's the expected type *)
-							(* FIXME: should closures have also its arguments cast correctly? In the current implementation I think not. TO_REVIEW *)
-							handle_cast gen { ecall with eexpr = TCall(efield, elist) } (gen.greal_type ecall.etype) ret
-						| _ ->
-							{ ecall with eexpr = TCall(efield, elist) }
-		in
-
-		let real_type = gen.greal_type ef.etype in
-		(* this part was rewritten at roughly r6477 in order to correctly support overloads *)
-		(match field_access_esp gen real_type (f) with
-		| FClassField (cl, params, _, cf, is_static, actual_t, declared_t) when e <> None && (cf.cf_kind = Method MethNormal || cf.cf_kind = Method MethInline) ->
-				(* C# target changes params with a real_type function *)
-				let params = match follow clean_ef.etype with
-					| TInst(_,params) -> params
-					| _ -> params
-				in
-				let local_mk_cast t expr =
-					(* handle_cast gen expr t expr.etype *)
-					if is_exactly_basic gen t expr.etype then
-						expr
-					else
-						mk_castfast t expr
-				in
-
-				let ecall = get e in
-				let ef = ref ef in
-				let is_overload = cf.cf_overloads <> [] || Meta.has Meta.Overload cf.cf_meta || (is_static && is_static_overload cl (field_name f)) in
-				let cf, actual_t, error = match is_overload with
-					| false ->
-							(* since actual_t from FClassField already applies greal_type, we're using the get_overloads helper to get this info *)
-							let t = if cf.cf_params = [] then (* this if statement must be eliminated - it's a workaround for #3516 + infer params. *)
-								actual_t
-							else
-								declared_t
-							in
-							cf,t,false
-					| true ->
-					let (cf, actual_t, error), is_static = match f with
-						| FInstance(c,_,cf) | FClosure(Some (c,_),cf) ->
-							(* get from overloads *)
-							(* FIXME: this is a workaround for issue #1743 . Uncomment this code after it was solved *)
-							(* let t, cf = List.find (fun (t,cf2) -> cf == cf2) (Overloads.get_overloads cl (field_name f)) in *)
-							(* cf, t, false *)
-							select_overload gen e1.etype (Overloads.get_overloads cl (field_name f)) cl.cl_params params, false
-						| FStatic(c,f) ->
-							(* workaround for issue #1743 *)
-							(* f,f.cf_type, false *)
-							select_overload gen e1.etype ((f.cf_type,f) :: List.map (fun f -> f.cf_type,f) f.cf_overloads) [] [], true
-						| _ ->
-							gen.gcon.warning "Overloaded classfield typed as anonymous" ecall.epos;
-							(cf, actual_t, true), true
-					in
-
-					if not (is_static || error) then match find_first_declared_field gen cl ~exact_field:{ cf with cf_type = actual_t } cf.cf_name with
-					| Some(cf_orig,actual_t,_,_,declared_cl,tl,tlch) ->
-						let rec is_super e = match e.eexpr with
-							| TConst TSuper -> true
-							| TParenthesis p | TMeta(_,p) -> is_super p
-							| _ -> false
-						in
-						if declared_cl != cl && overloads_cast_to_base && not (is_super !ef) then begin
-							let pos = (!ef).epos in
-							ef := {
-								eexpr = TCall(
-									{ eexpr = TLocal(alloc_var "__as__" t_dynamic); etype = t_dynamic; epos = pos },
-									[!ef]);
-								etype = TInst(declared_cl,List.map (apply_params cl.cl_params params) tl);
-								epos = pos
-							}
-						end;
-						{ cf_orig with cf_name = cf.cf_name },actual_t,false
-					| None ->
-						gen.gcon.warning "Cannot find matching overload" ecall.epos;
-						cf, actual_t, true
-					else
-						cf,actual_t,error
-				in
-
-				(* take off Rest param *)
-				let actual_t = change_rest actual_t elist in
-				(* set the real (selected) class field *)
-				let f = match f with
-					| FInstance(c,tl,_) -> FInstance(c,tl,cf)
-					| FClosure(c,_) -> FClosure(c,cf)
-					| FStatic(c,_) -> FStatic(c,cf)
-					| f -> f
-				in
-				let error = error || (match follow actual_t with | TFun _ -> false | _ -> true) in
-				if error then (* if error, ignore arguments *)
-					if ExtType.is_void ecall.etype then
-						{ ecall with eexpr = TCall({ e1 with eexpr = TField(!ef, f) }, elist ) }
-					else
-						local_mk_cast ecall.etype { ecall with eexpr = TCall({ e1 with eexpr = TField(!ef, f) }, elist ) }
-				else begin
-					(* infer arguments *)
-					(* let called_t = TFun(List.map (fun e -> "arg",false,e.etype) elist, ecall.etype) in *)
-					let called_t = match follow e1.etype with | TFun _ -> e1.etype | _ -> TFun(List.map (fun e -> "arg",false,e.etype) elist, ecall.etype)	in (* workaround for issue #1742 *)
-					let called_t = change_rest called_t elist in
-					let fparams = TypeParams.infer_params gen ecall.epos (get_fun (apply_params cl.cl_params params actual_t)) (get_fun called_t) cf.cf_params calls_parameters_explicitly in
-					(* get what the backend actually sees *)
-					(* actual field's function *)
-					let actual_t = get_real_fun gen actual_t in
-					let real_params = gen.greal_type_param (TClassDecl cl) params in
-					let function_t = apply_params cl.cl_params real_params actual_t in
-					let real_fparams = if calls_parameters_explicitly then
-						gen.greal_type_param (TClassDecl cl) fparams
-					else
-						gen.greal_type_param (TClassDecl cl) (TypeParams.infer_params gen ecall.epos (get_fun function_t) (get_fun (get_real_fun gen called_t)) cf.cf_params calls_parameters_explicitly) in
-					let function_t = get_real_fun gen (apply_params cf.cf_params real_fparams function_t) in
-					let args_ft, ret_ft = get_fun function_t in
-					(* applied function *)
-					let applied = elist in
-					(* check types list *)
-					let new_ecall, elist = try
-						let elist = List.map2 (fun applied (_,_,funct) ->
-							match is_overload || real_fparams <> [], applied.eexpr with
-							| true, TConst TNull ->
-								mk_castfast (gen.greal_type funct) applied
-							| true, _ -> (* when not (type_iseq gen (gen.greal_type applied.etype) funct) -> *)
-								let ret = handle_cast gen applied (funct) (gen.greal_type applied.etype) in
-								(match ret.eexpr with
-								| TCast _ -> ret
-								| _ -> local_mk_cast (funct) ret)
-							| _ ->
-								handle_cast gen applied (funct) (gen.greal_type applied.etype)
-						) applied args_ft in
-						{ ecall with
-							eexpr = TCall(
-								{ e1 with eexpr = TField(!ef, f) },
-								elist);
-						}, elist
-					with | Invalid_argument("List.map2") ->
-						gen.gcon.warning ("This expression may be invalid" ) ecall.epos;
-						{ ecall with eexpr = TCall({ e1 with eexpr = TField(!ef, f) }, elist) }, elist
-					in
-					let new_ecall = if fparams <> [] then gen.gparam_func_call new_ecall { e1 with eexpr = TField(!ef, f) } fparams elist else new_ecall in
-					let ret = handle_cast gen new_ecall (gen.greal_type ecall.etype) (gen.greal_type ret_ft) in
-					(match gen.gcon.platform, cf.cf_params, ret.eexpr with
-						| _, _, TCast _ -> ret
-						| Java, _ :: _, _ ->
-							(* this is a workaround for a javac openjdk issue with unused type parameters and return type inference *)
-							(* see more at issue #3123 *)
-							mk_cast (gen.greal_type ret_ft) new_ecall
-						| _ -> ret)
-				end
-		| FClassField (cl,params,_,{ cf_kind = (Method MethDynamic | Var _) },_,actual_t,_) ->
-			(* if it's a var, we will just try to apply the class parameters that have been changed with greal_type_param *)
-			let t = apply_params cl.cl_params (gen.greal_type_param (TClassDecl cl) params) (gen.greal_type actual_t) in
-			return_var (handle_cast gen { e1 with eexpr = TField(ef, f) } (gen.greal_type e1.etype) (gen.greal_type t))
-		| FClassField (cl,params,_,cf,_,actual_t,_) ->
-			return_var (handle_cast gen { e1 with eexpr = TField({ ef with etype = t_dynamic }, f) } e1.etype t_dynamic) (* force dynamic and cast back to needed type *)
-		| FEnumField (en, efield, true) ->
-			let ecall = match e with | None -> trace (field_name f); trace efield.ef_name; gen.gcon.error "This field should be called immediately" ef.epos; assert false | Some ecall -> ecall in
-			(match en.e_params with
-				(*
-				| [] ->
-					let args, ret = get_args (efield.ef_type) in
-					let ef = { ef with eexpr = TTypeExpr( TEnumDecl en ); etype = TEnum(en, []) } in
-					handle_cast gen { ecall with eexpr = TCall({ e1 with eexpr = TField(ef, FEnum(en, efield)) }, List.map2 (fun param (_,_,t) -> handle_cast gen param (gen.greal_type t) (gen.greal_type param.etype)) elist args) } (gen.greal_type ecall.etype) (gen.greal_type ret)
-			*)
-				| _ ->
-					let pt = match e with | None -> real_type | Some _ -> snd (get_fun e1.etype) in
-					let _params = match follow pt with | TEnum(_, p) -> p | _ -> gen.gcon.warning (debug_expr e1) e1.epos; assert false in
-					let args, ret = get_args efield.ef_type in
-					let actual_t = TFun(List.map (fun (n,o,t) -> (n,o,gen.greal_type t)) args, gen.greal_type ret) in
-					(*
-						because of differences on how <Dynamic> is handled on the platforms, this is a hack to be able to
-						correctly use class field type parameters with RealTypeParams
-					*)
-					let cf_params = List.map (fun t -> match follow t with | TDynamic _ -> t_empty | _ -> t) _params in
-					let t = apply_params en.e_params (gen.greal_type_param (TEnumDecl en) cf_params) actual_t in
-					let t = apply_params efield.ef_params (List.map (fun _ -> t_dynamic) efield.ef_params) t in
-
-					let args, ret = get_args t in
-
-					let elist = List.map2 (fun param (_,_,t) -> handle_cast gen (param) (gen.greal_type t) (gen.greal_type param.etype)) elist args in
-					let e1 = { e1 with eexpr = TField({ ef with eexpr = TTypeExpr( TEnumDecl en ); etype = TEnum(en, _params) }, FEnum(en, efield) ) } in
-					let new_ecall = gen.gparam_func_call ecall e1 _params elist in
-
-					handle_cast gen new_ecall (gen.greal_type ecall.etype) (gen.greal_type ret)
-			)
-		| FEnumField _ when is_some e -> assert false
-		| FEnumField (en,efield,_) ->
-				return_var { e1 with eexpr = TField({ ef with eexpr = TTypeExpr( TEnumDecl en ); },FEnum(en,efield)) }
-		(* no target by date will uses this.so this code may not be correct at all *)
-		| FAnonField cf ->
-			let t = gen.greal_type cf.cf_type in
-			return_var (handle_cast gen { e1 with eexpr = TField(ef, f) } (gen.greal_type e1.etype) t)
-		| FNotFound
-		| FDynamicField _ ->
-			if is_some e then
-				return_var { e1 with eexpr = TField(ef, f) }
-			else
-				return_var (handle_cast gen { e1 with eexpr = TField({ ef with etype = t_dynamic }, f) } e1.etype t_dynamic) (* force dynamic and cast back to needed type *)
-		)
-
-	(* end of type parameter handling *)
-	(* ****************************** *)
-
-	(** overloads_cast_to_base argument will cast overloaded function types to the class that declared it. **)
-	(**			This is necessary for C#, and if true, will require the target to implement __as__, as a `quicker` form of casting **)
-	let configure gen ?(overloads_cast_to_base = false) maybe_empty_t calls_parameters_explicitly =
-		let handle e t1 t2 = handle_cast gen e (gen.greal_type t1) (gen.greal_type t2) in
-
-		let in_value = ref false in
-
-		let rec clean_cast e = match e.eexpr with
-		 | TCast(e,_) -> clean_cast e
-		 | TParenthesis(e) | TMeta(_,e) -> clean_cast e
-		 | _ -> e
-		in
-
-		let get_abstract_impl t = match t with
-			| TAbstract(a,pl) when not (Meta.has Meta.CoreType a.a_meta) ->
-				Abstract.get_underlying_type a pl
-			| t -> t
-		in
-
-		let rec is_abstract_to_struct t = match t with
-			| TAbstract(a,pl) when not (Meta.has Meta.CoreType a.a_meta) ->
-				is_abstract_to_struct (Abstract.get_underlying_type a pl)
-			| TInst(c,_) when Meta.has Meta.Struct c.cl_meta ->
-				true
-			| _ -> false
-		in
-
-		let binop_type op main_expr e1 e2 =
-			let name = platform_name gen.gcon.platform in
-			let basic = gen.gcon.basic in
-			(* If either operand is of type decimal, the other operand is converted to type decimal, or a compile-time error occurs if the other operand is of type float or double.
-			 * Otherwise, if either operand is of type double, the other operand is converted to type double.
-			 * Otherwise, if either operand is of type float, the other operand is converted to type float.
-			 * Otherwise, if either operand is of type ulong, the other operand is converted to type ulong, or a compile-time error occurs if the other operand is of type sbyte, short, int, or long.
-			 * Otherwise, if either operand is of type long, the other operand is converted to type long.
-			 * Otherwise, if either operand is of type uint and the other operand is of type sbyte, short, or int, both operands are converted to type long.
-			 * Otherwise, if either operand is of type uint, the other operand is converted to type uint.
-			 * Otherwise, both operands are converted to type int.
-			 *  *)
-			let t1, t2 = follow (run_follow gen e1.etype), follow (run_follow gen e2.etype) in
-			match t1, t2 with
-				| TAbstract(a1,[]), TAbstract(a2,[]) when a1 == a2 ->
-					{ main_expr with eexpr = TBinop(op, e1, e2); etype = e1.etype }
-				| TInst(i1,[]), TInst(i2,[]) when i1 == i2 ->
-					{ main_expr with eexpr = TBinop(op, e1, e2); etype = e1.etype }
-				| TInst({ cl_path = ([],"String") },[]), _ when op = OpAdd ->
-					{ main_expr with eexpr = TBinop(op, e1, mk_cast basic.tstring e2); etype = basic.tstring }
-				| _, TInst({ cl_path = ([],"String") },[]) when op = OpAdd ->
-					{ main_expr with eexpr = TBinop(op, mk_cast basic.tstring e1, e2); etype = basic.tstring }
-				| TAbstract({ a_path = ([], "Float") }, []), _ ->
-					{ main_expr with eexpr = TBinop(op, e1, e2); etype = e1.etype }
-				| _, TAbstract({ a_path = ([], "Float") }, []) ->
-					{ main_expr with eexpr = TBinop(op, e1, e2); etype = e2.etype }
-				| TAbstract({ a_path = ([], "Single") }, []), _ ->
-					{ main_expr with eexpr = TBinop(op, e1, e2); etype = e1.etype }
-				| _, TAbstract({ a_path = ([], "Single") }, []) ->
-					{ main_expr with eexpr = TBinop(op, e1, e2); etype = e2.etype }
-				| TAbstract({ a_path = ([pf], "UInt64") }, []), _ when pf = name ->
-					{ main_expr with eexpr = TBinop(op, e1, e2); etype = e1.etype }
-				| _, TAbstract({ a_path = ([pf], "UInt64") }, []) when pf = name ->
-					{ main_expr with eexpr = TBinop(op, e1, e2); etype = e2.etype }
-				| TAbstract({ a_path = ([pf], "Int64") }, []), _ when pf = name ->
-					{ main_expr with eexpr = TBinop(op, e1, e2); etype = e1.etype }
-				| _, TAbstract({ a_path = ([pf], "Int64") }, []) when pf = name ->
-					{ main_expr with eexpr = TBinop(op, e1, e2); etype = e2.etype }
-				| TAbstract({ a_path = ([], "UInt") }, []), tother when like_int tother ->
-					let ti64 = mt_to_t_dyn ( get_type gen ([name], "Int64") ) in
-					let ret = { main_expr with eexpr = TBinop(op, e1, e2); etype = ti64 } in
-					if op <> OpDiv then
-						mk_cast t1 ret
-					else
-						ret
-				| tother, TAbstract({ a_path = ([], "UInt") }, []) when like_int tother ->
-					let ti64 = mt_to_t_dyn ( get_type gen ([name], "Int64") ) in
-					let ret = { main_expr with eexpr = TBinop(op, e1, e2); etype = ti64 } in
-					if op <> OpDiv then
-						mk_cast t2 ret
-					else
-						ret
-				| TAbstract({ a_path = ([], "UInt") }, []), _ ->
-					{ main_expr with eexpr = TBinop(op, e1, e2); etype = e1.etype }
-				| _, TAbstract({ a_path = ([], "UInt") }, []) ->
-					{ main_expr with eexpr = TBinop(op, e1, e2); etype = e2.etype }
-				| TAbstract(a1,[]), TAbstract(a2,[]) ->
-					{ main_expr with eexpr = TBinop(op, e1, e2); etype = basic.tint }
-				| _ ->
-					{ main_expr with eexpr = TBinop(op, e1, e2) }
-		in
-		let binop_type = if Common.defined gen.gcon Define.FastCast then
-			binop_type
-		else
-			fun op main_expr e1 e2 -> { main_expr with eexpr = TBinop(op, e1, e2) }
-		in
-
-		let rec run ?(just_type = false) e =
-			let handle = if not just_type then handle else fun e t1 t2 -> { e with etype = gen.greal_type t2 } in
-			let was_in_value = !in_value in
-			in_value := true;
-			match e.eexpr with
-				| TConst ( TInt _ | TFloat _ | TBool _ as const ) ->
-					(* take off any Null<> that it may have *)
-					let t = follow (run_follow gen e.etype) in
-					(* do not allow constants typed as Single - need to cast them *)
-					let real_t = match const with
-						| TInt _ -> gen.gcon.basic.tint
-						| TFloat _ -> gen.gcon.basic.tfloat
-						| TBool _ -> gen.gcon.basic.tbool
-						| _ -> assert false
-					in
-					handle e t real_t
-				| TCast( { eexpr = TConst TNull }, _ ) ->
-					{ e with eexpr = TConst TNull }
-				| TCast( { eexpr = TCall( { eexpr = TLocal { v_name = "__delegate__" } } as local, [del] ) } as e2, _) ->
-					{ e with eexpr = TCast({ e2 with eexpr = TCall(local, [Type.map_expr run del]) }, None) }
-
-				| TBinop ( (Ast.OpAssign | Ast.OpAssignOp _ as op), e1, e2 ) ->
-					let e1 = run ~just_type:true e1 in
-					let e2 = handle (run e2) e1.etype e2.etype in
-					{ e with eexpr = TBinop(op, clean_cast e1, e2) }
-				| TBinop ( (Ast.OpShl | Ast.OpShr | Ast.OpUShr as op), e1, e2 ) ->
-					let e1 = run e1 in
-					let e2 = handle (run e2) (gen.gcon.basic.tint) e2.etype in
-					let rett = binop_type op e e1 e2 in
-					{ e with eexpr = TBinop(op, e1, e2); etype = rett.etype }
-				| TBinop( (OpAdd | OpMult | OpDiv | OpSub | OpAnd | OpOr | OpXor | OpMod) as op, e1, e2 ) ->
-					binop_type op e (run e1) (run e2)
-				| TBinop( (OpEq | OpNotEq | OpGt | OpGte | OpLt | OpLte | OpBoolAnd | OpBoolOr) as op, e1, e2 ) ->
-					handle { e with eexpr = TBinop(op, run e1, run e2) } e.etype gen.gcon.basic.tbool
-				| TField(ef, f) ->
-					handle_type_parameter gen None e (run ef) ~clean_ef:ef ~overloads_cast_to_base:overloads_cast_to_base f [] calls_parameters_explicitly
-				| TArrayDecl el ->
-					let et = e.etype in
-					let base_type = match follow et with
-						| TInst({ cl_path = ([], "Array") } as cl, bt) -> gen.greal_type_param (TClassDecl cl) bt
-						| _ ->
-							gen.gcon.warning (debug_type et) e.epos;
-							(match gen.gcurrent_class with
-								| Some cl -> print_endline (s_type_path cl.cl_path)
-								| _ -> ());
-							assert false
-					in
-					let base_type = List.hd base_type in
-					{ e with eexpr = TArrayDecl( List.map (fun e -> handle (run e) base_type e.etype) el ); etype = et }
-				| TCall ({ eexpr = TLocal { v_name = "__array__" } } as arr_local, el) ->
-					let et = e.etype in
-					let base_type = match follow et with
-						| TInst(cl, bt) -> gen.greal_type_param (TClassDecl cl) bt
-						| _ -> assert false
-					in
-					let base_type = List.hd base_type in
-					{ e with eexpr = TCall(arr_local, List.map (fun e -> handle (run e) base_type e.etype) el ); etype = et }
-				| TCall( ({ eexpr = TLocal v } as local), params ) when String.get v.v_name 0 = '_' && String.get v.v_name 1 = '_' && Hashtbl.mem gen.gspecial_vars v.v_name ->
-					{ e with eexpr = TCall(local, List.map (fun e -> (match e.eexpr with TBlock _ -> in_value := false | _ -> ()); run e) params) }
-				| TCall( ({ eexpr = TField(ef, f) }) as e1, elist ) ->
-					handle_type_parameter gen (Some e) (e1) (run ef) ~clean_ef:ef ~overloads_cast_to_base:overloads_cast_to_base f (List.map run elist) calls_parameters_explicitly
-
-				(* the TNew and TSuper code was modified at r6497 *)
-				| TCall( { eexpr = TConst TSuper } as ef, eparams ) ->
-					let cl, tparams = match follow ef.etype with
-					| TInst(cl,p) ->
-						cl,p
-					| _ -> assert false in
-					(try
-						let is_overload, cf, sup, stl = choose_ctor gen cl tparams (List.map (fun e -> e.etype) eparams) maybe_empty_t e.epos in
-						let handle e t1 t2 =
-							if is_overload then
-								let ret = handle e t1 t2 in
-								match ret.eexpr with
-								| TCast _ -> ret
-								| _ -> mk_cast (gen.greal_type t1) e
-							else
-								handle e t1 t2
-						in
-						let stl = gen.greal_type_param (TClassDecl sup) stl in
-						let args, _ = get_fun (apply_params sup.cl_params stl cf.cf_type) in
-						let eparams = List.map2 (fun e (_,_,t) ->
-							handle (run e) t e.etype
-						) eparams args in
-						{ e with eexpr = TCall(ef, eparams) }
-					with | Not_found ->
-						gen.gcon.warning "No overload found for this constructor call" e.epos;
-						{ e with eexpr = TCall(ef, List.map run eparams) })
-				| TCall (ef, eparams) ->
-					(match ef.etype with
-						| TFun(p, ret) ->
-							handle ({ e with eexpr = TCall(run ef, List.map2 (fun param (_,_,t) -> handle (run param) t param.etype) eparams p) }) e.etype ret
-						| _ -> Type.map_expr run e
-					)
-				(* the TNew and TSuper code was modified at r6497 *)
-				| TNew ({ cl_kind = KTypeParameter _ }, _, _) ->
-					Type.map_expr run e
-				| TNew (cl, tparams, eparams) -> (try
-					let is_overload, cf, sup, stl = choose_ctor gen cl tparams (List.map (fun e -> e.etype) eparams) maybe_empty_t e.epos in
-					let handle e t1 t2 =
-						if is_overload then
-							let ret = handle e t1 t2 in
-							match ret.eexpr with
-							| TCast _ -> ret
-							| _ -> mk_cast (gen.greal_type t1) e
-						else
-							handle e t1 t2
-					in
-					let stl = gen.greal_type_param (TClassDecl sup) stl in
-					let args, _ = get_fun (apply_params sup.cl_params stl cf.cf_type) in
-					let eparams = List.map2 (fun e (_,_,t) ->
-						handle (run e) t e.etype
-					) eparams args in
-					{ e with eexpr = TNew(cl, tparams, eparams) }
-				with | Not_found ->
-					gen.gcon.warning "No overload found for this constructor call" e.epos;
-					{ e with eexpr = TNew(cl, tparams, List.map run eparams) })
-				| TArray(arr, idx) ->
-					let arr_etype = match follow arr.etype with
-						| (TInst _ as t) -> t
-						| TAbstract (a, pl) when not (Meta.has Meta.CoreType a.a_meta) ->
-							follow (Abstract.get_underlying_type a pl)
-						| t -> t
-					in
-					let idx = run idx in
-					let idx = match gen.greal_type idx.etype with
-						| TAbstract({ a_path = [],"Int" },_) -> idx
-						| _ -> match handle idx gen.gcon.basic.tint (gen.greal_type idx.etype) with
-							| ({ eexpr = TCast _ } as idx) -> idx
-							| idx -> mk_cast gen.gcon.basic.tint idx
-					in
-					let e = { e with eexpr = TArray(run arr, idx) } in
-					(* get underlying class (if it's a class *)
-					(match arr_etype with
-						| TInst(cl, params) ->
-							(* see if it implements ArrayAccess *)
-							(match cl.cl_array_access with
-								| None -> e
-								| Some t ->
-									(* if it does, apply current parameters (and change them) *)
-									(* let real_t = apply_params_internal (List.map (gen.greal_type_param (TClassDecl cl))) cl params t in *)
-									let param = apply_params cl.cl_params (gen.greal_type_param (TClassDecl cl) params) t in
-									let real_t = apply_params cl.cl_params params param in
-									(* see if it needs a cast *)
-
-									fastcast_if_needed gen e (gen.greal_type e.etype) (gen.greal_type real_t)
-									(* handle (e) (gen.greal_type e.etype) (gen.greal_type real_t) *)
-							)
-						| _ -> Type.map_expr run e)
-				| TVar (v, eopt) ->
-					{ e with eexpr = TVar (v, match eopt with
-							| None -> eopt
-							| Some e -> Some( handle (run e) v.v_type e.etype ))
-					}
-				(* FIXME deal with in_value when using other statements that may not have a TBlock wrapped on them *)
-				| TIf (econd, ethen, Some(eelse)) when was_in_value ->
-					{ e with eexpr = TIf (handle (run econd) gen.gcon.basic.tbool econd.etype, handle (run ethen) e.etype ethen.etype, Some( handle (run eelse) e.etype eelse.etype ) ) }
-				| TIf (econd, ethen, eelse) ->
-					{ e with eexpr = TIf (handle (run econd) gen.gcon.basic.tbool econd.etype, (in_value := false; run (mk_block ethen)), Option.map (fun e -> in_value := false; run (mk_block e)) eelse) }
-				| TWhile (econd, e1, flag) ->
-					{ e with eexpr = TWhile (handle (run econd) gen.gcon.basic.tbool econd.etype, (in_value := false; run (mk_block e1)), flag) }
-				| TSwitch (cond, el_e_l, edef) ->
-					{ e with eexpr = TSwitch(run cond, List.map (fun (el,e) -> (List.map run el, (in_value := false; run (mk_block e)))) el_e_l, Option.map (fun e -> in_value := false; run (mk_block e)) edef) }
-				| TFor (v,cond,e1) ->
-					{ e with eexpr = TFor(v, run cond, (in_value := false; run (mk_block e1))) }
-				| TTry (e, ve_l) ->
-					{ e with eexpr = TTry((in_value := false; run (mk_block e)), List.map (fun (v,e) -> in_value := false; (v, run (mk_block e))) ve_l) }
-				| TBlock el ->
-					let i = ref 0 in
-					let len = List.length el in
-					{ e with eexpr = TBlock ( List.map (fun e ->
-						incr i;
-						if !i <> len || not was_in_value then
-							in_value := false;
-						run e
-					) el ) }
-				| TCast (expr, md) when ExtType.is_void (follow e.etype) ->
-					run expr
-				| TCast (expr, md) ->
-					let rec get_null e =
-						match e.eexpr with
-						| TConst TNull -> Some e
-						| TParenthesis e | TMeta(_,e) -> get_null e
-						| _ -> None
-					in
-
-					(match get_null expr with
-					| Some enull ->
-							if gen.gcon.platform = Cs then
-								{ enull with etype = gen.greal_type e.etype }
-							else
-								mk_cast (gen.greal_type e.etype) enull
-					| _ when is_abstract_to_struct expr.etype && type_iseq gen e.etype (get_abstract_impl expr.etype) ->
-						run { expr with etype = expr.etype }
-					| _ when is_exactly_basic gen expr.etype e.etype ->
-						run { expr with etype = expr.etype }
-					| _ ->
-						match gen.greal_type e.etype, gen.greal_type expr.etype with
-							| (TInst(c,tl) as tinst1), TAbstract({ a_path = ["cs"],"Pointer" }, [tinst2]) when type_iseq gen tinst1 (gen.greal_type tinst2) ->
-								run expr
-							| _ ->
-								let last_unsafe = gen.gon_unsafe_cast in
-								gen.gon_unsafe_cast <- (fun t t2 pos -> ());
-								let ret = handle (run expr) e.etype expr.etype in
-								gen.gon_unsafe_cast <- last_unsafe;
-								match ret.eexpr with
-									| TCast _ -> { ret with etype = gen.greal_type e.etype }
-									| _ -> { e with eexpr = TCast(ret,md); etype = gen.greal_type e.etype }
-					)
-				(*| TCast _ ->
-					(* if there is already a cast, we should skip this cast check *)
-					Type.map_expr run e*)
-				| TFunction f ->
-					in_value := false;
-					Type.map_expr run e
-
-				| _ -> Type.map_expr run e
-		in
-		gen.ghandle_cast <- (fun tto tfrom expr -> handle_cast gen expr (gen.greal_type tto) (gen.greal_type tfrom));
-		let map e = match gen.gcurrent_classfield with
-			| Some(cf) when Meta.has (Meta.Custom ":skipCastDetect") cf.cf_meta ->
-				None
-			| _ ->
-				Some(run e)
-		in
-		gen.gsyntax_filters#add ~name:name ~priority:(PCustom priority) map;
-		ReturnCast.configure gen
-
-end;;
-
-(* ******************************************* *)
-(* Reflection-enabling Class fields *)
-(* ******************************************* *)
-
-(*
-	This is the most hardcore codegen part of the code. There's much to improve so this code can be more readable, but at least it's running correctly right now! This will be improved. (TODO)
-
-	This module will create class fields that enable reflection for targets that have a slow or inexistent reflection abilities. Because of the similarity
-	of strategies between what should have been different modules, they are all unified in this reflection-enabling class fields.
-
-	They include:
-		* Get(throwErrors, isCheck) / Set fields . Remember to allow implements Dynamic also.
-		* Invoke fields() -> You need to configure how many invoke_field fields there will be. + invokeDynamic
-		* Has field -> parameter in get field that returns __undefined__ if it doesn't exist.
-
-		* GetType -> return the current Class<> / Enum<>
-		* Fields() -> returns all the fields / static fields. Remember to allow implements Dynamic also
-
-		* Create(arguments array), CreateEmpty - calls new() or create empty
-		* getInstanceFields / getClassFields -> show even function fields, everything!
-
-		* deleteField -> only for implements Dynamic
-
-		for enums:
-		* createEnum -> invokeField for classes
-		* createEnumIndex -> use invokeField as well, and use numbers e.g. "0", "1", "2" .... For this, use "@:alias" metadata
-		* getEnumConstructs -> fields()
-
-		need to be solved outside:
-		* getEnumName
-		* enumIndex
-		*
-
-		need to be solved by haxe code:
-		* enumParameters -> for (field in Reflect.fields(enum)) arr.push(Reflect.field(enum, field))
-
-	Standard:
-		if a class contains a @:$enum metadata, it's treated as a converted enum to class
-
-
-	Optimizations:
-		* if optimize is true, all fields will be hashed by the same hashing function as neko (31 bits int : always positive). Every function that expects a string for the field will expect also an int, for the hash
-			a string (which is nullable for compile-time hashes) + an int.
-			At compile-time, a collision will throw an error (like neko).
-			At runtime, a collision will make a negative int. Negative ints will always resolve to a special Hash<> field which takes a string.
-		* if optimize is true, Reflect.field/setField will be replaced by either the runtime version (with already hashed string), either by the own .Field()/.SetField() HxObject's version,
-			if the type is detected to already be hxgen
-		* TODO: if for() optimization for arrays is disabled, we can replace for(field in Reflect.fields(obj)) to:
-			for (field in ( (Std.is(obj, HxObject) ? ((HxObject)obj).Fields() : Reflect.fields(obj)) )) // no array copying . for further optimization this could be guaranteed to return
-			the already hashed fields.
-
-	Mappings:
-		* if create Dynamic class is true, TObjectDecl will be mapped to new DynamicClass(fields, [hashedFields], values)
-		*
-
-	dependencies:
-		There is no big dependency from this target. Though it should be a syntax filter, mainly one of the first so most expression generation has already been done,
-		while the AST has its meaning close to haxe's.
-		Should run before InitFunction so it detects variables containing expressions as "always-execute" expressions, even when using CreateEmpty
-
-		* Must run before switch() syntax changes
-
-*)
-
-open ClosuresToClass;;
-module ReflectionCFs =
-struct
-
-	let name = "reflection_cfs"
-
-	type rcf_hash_conflict_ctx = {
-		t : t;
-		add_names : texpr->texpr->texpr;
-		get_conflict : texpr->texpr->texpr->texpr;
-		set : texpr->texpr->texpr->texpr->texpr;
-		delete : texpr->texpr->texpr->texpr;
-	}
-
-	type rcf_ctx =
-	{
-		rcf_gen : generator_ctx;
-		rcf_ft : ClosuresToClass.closures_ctx;
-		rcf_optimize : bool;
-
-		rcf_object_iface : tclass;
-
-		rcf_max_func_arity : int;
-
-		(*
-			the hash lookup function. can be an inlined expr or simply a function call.
-			its only needed features is that it should return the index of the key if found, and the
-			complement of the index of where it should be inserted if not found (Ints).
-
-			hash->hash_array->length->returning expression
-		*)
-		rcf_hash_function : texpr->texpr->texpr->texpr;
-
-		rcf_lookup_function : texpr->texpr;
-
-		(* hash_array->length->pos->value *)
-		rcf_insert_function : texpr->texpr->texpr->texpr->texpr;
-
-		(* hash_array->length->pos->value *)
-		rcf_remove_function : texpr->texpr->texpr->texpr;
-
-		rcf_hash_fields : (int, string) Hashtbl.t;
-
-		rcf_hash_paths : (path * int, string) Hashtbl.t;
-
-		rcf_hash_conflict_ctx : rcf_hash_conflict_ctx option;
-
-		(*
-			main expr -> field expr -> field string -> possible hash int (if optimize) -> possible set expr -> should_throw_exceptions -> changed expression
-
-			Changes a get / set field to the runtime resolution function
-		*)
-		rcf_on_getset_field : texpr->texpr->string->int32 option->texpr option->bool->texpr;
-
-		rcf_on_call_field : texpr->texpr->string->int32 option->texpr list->texpr;
-	}
-
-	let new_ctx gen ft object_iface optimize dynamic_getset_field dynamic_call_field hash_function lookup_function insert_function remove_function hash_conflict_ctx =
-		{
-			rcf_gen = gen;
-			rcf_ft = ft;
-
-			rcf_optimize = optimize;
-
-			rcf_object_iface = object_iface;
-
-			rcf_max_func_arity = 10;
-
-			rcf_hash_function = hash_function;
-			rcf_lookup_function = lookup_function;
-
-			rcf_insert_function = insert_function;
-			rcf_remove_function = remove_function;
-
-			rcf_hash_fields = Hashtbl.create 100;
-			rcf_hash_paths = Hashtbl.create 100;
-
-			rcf_on_getset_field = dynamic_getset_field;
-			rcf_on_call_field = dynamic_call_field;
-			rcf_hash_conflict_ctx = hash_conflict_ctx;
-		}
-
-	(*
-		methods as a bool option is a little laziness of my part.
-			None means that methods are included with normal fields;
-			Some(true) means collect only methods
-			Some(false) means collect only fields (and MethDynamic fields)
-	*)
-	let collect_fields cl (methods : bool option) =
-		let collected = Hashtbl.create 0 in
-		let collect cf acc =
-			if Meta.has Meta.CompilerGenerated cf.cf_meta || Meta.has Meta.SkipReflection cf.cf_meta then
-				acc
-			else match methods, cf.cf_kind with
-				| None, _ when not (Hashtbl.mem collected cf.cf_name) -> Hashtbl.add collected cf.cf_name true; ([cf.cf_name], cf) :: acc
-				| Some true, Method MethDynamic -> acc
-				| Some true, Method _ when not (Hashtbl.mem collected cf.cf_name) -> Hashtbl.add collected cf.cf_name true; ([cf.cf_name], cf) :: acc
-				| Some false, Method MethDynamic
-				| Some false, Var _ when not (Hashtbl.mem collected cf.cf_name) -> Hashtbl.add collected cf.cf_name true; ([cf.cf_name], cf) :: acc
-				| _ -> acc
-		in
-		let collect_cfs cfs acc =
-			let rec loop cfs acc =
-				match cfs with
-					| [] -> acc
-					| hd :: tl -> loop tl (collect hd acc)
-			in
-			loop cfs acc
-		in
-		let rec loop cl acc =
-			let acc = collect_cfs cl.cl_ordered_fields acc in
-			match cl.cl_super with
-				| None -> acc
-				| Some(cl,_) ->
-					if not (is_hxgen (TClassDecl cl)) then loop cl acc else acc
-		in
-
-		loop cl []
-
-	let hash f =
-		let h = ref 0 in
-		for i = 0 to String.length f - 1 do
-			h := !h * 223 + int_of_char (String.unsafe_get f i);
-		done;
-		if Sys.word_size = 64 then Int32.to_int (Int32.shift_right (Int32.shift_left (Int32.of_int !h) 1) 1) else !h
-
-	let hash_field ctx f pos =
-		let h = hash f in
-		(try
-			let f2 = Hashtbl.find ctx.rcf_hash_paths (ctx.rcf_gen.gcurrent_path, h) in
-			if f <> f2 then ctx.rcf_gen.gcon.error ("Field conflict between " ^ f ^ " and " ^ f2) pos
-		with Not_found ->
-			Hashtbl.add ctx.rcf_hash_paths (ctx.rcf_gen.gcurrent_path, h) f;
-			Hashtbl.replace ctx.rcf_hash_fields h f);
-		h
-
-	(* ( tf_args, switch_var ) *)
-	let field_type_args ctx pos =
-		match ctx.rcf_optimize with
-			| true ->
-				let field_name, field_hash = alloc_var "field" ctx.rcf_gen.gcon.basic.tstring, alloc_var "hash" ctx.rcf_gen.gcon.basic.tint in
-
-				[field_name, None; field_hash, None], field_hash
-			| false ->
-				let field_name = alloc_var "field" ctx.rcf_gen.gcon.basic.tstring in
-				[field_name, None], field_name
-
-	let hash_field_i32 ctx pos field_name =
-		let i = hash_field ctx field_name pos in
-		let i = Int32.of_int (i) in
-		if i < Int32.zero then
-			Int32.logor (Int32.logand i (Int32.of_int 0x3FFFFFFF)) (Int32.shift_left Int32.one 30)
-		else i
-
-	let switch_case ctx pos field_name =
-		match ctx.rcf_optimize with
-			| true ->
-				let i = hash_field_i32 ctx pos field_name in
-				mk (TConst (TInt i)) ctx.rcf_gen.gcon.basic.tint pos
-			| false ->
-				ExprBuilder.make_string ctx.rcf_gen.gcon field_name pos
-
-	let call_super ctx fn_args ret_t cf cl this_t pos =
-		{
-			eexpr = TCall({
-				eexpr = TField({ eexpr = TConst(TSuper); etype = this_t; epos = pos }, FInstance(cl,List.map snd cl.cl_params,cf));
-				etype = TFun(fun_args fn_args, ret_t);
-				epos = pos;
-			}, List.map (fun (v,_) -> mk_local v pos) fn_args);
-			etype = ret_t;
-			epos = pos;
-		}
-
-
-	let mk_throw ctx str pos = { eexpr = TThrow (ExprBuilder.make_string ctx.rcf_gen.gcon str pos); etype = ctx.rcf_gen.gcon.basic.tvoid; epos = pos }
-
-	let enumerate_dynamic_fields ctx cl when_found base_arr =
-		let gen = ctx.rcf_gen in
-		let basic = gen.gcon.basic in
-		let pos = cl.cl_pos in
-
-		let vtmp = alloc_var "i" basic.tint in
-
-		let mk_for arr len =
-			let t = if ctx.rcf_optimize then basic.tint else basic.tstring in
-			let convert_str e = if ctx.rcf_optimize then ctx.rcf_lookup_function e else e in
-			let tmpinc = { eexpr = TUnop(Ast.Increment, Ast.Postfix, mk_local vtmp pos); etype = basic.tint; epos = pos } in
-			[
-				{ eexpr = TBinop(OpAssign, mk_local vtmp pos, ExprBuilder.make_int ctx.rcf_gen.gcon 0 pos); etype = basic.tint; epos = pos };
-				{
-					eexpr = TWhile (
-						{ eexpr = TBinop(Ast.OpLt, mk_local vtmp pos, len); etype = basic.tbool; epos = pos },
-						mk_block (when_found (convert_str { eexpr = TArray (arr, tmpinc); etype = t; epos = pos })),
-						Ast.NormalWhile
-					);
-					etype = basic.tvoid;
-					epos = pos
-				}
-			]
- 		in
-
-		let this_t = TInst(cl, List.map snd cl.cl_params) in
-		let this = { eexpr = TConst(TThis); etype = this_t; epos = pos } in
-		let mk_this field t = { (mk_field_access gen this field pos) with etype = t } in
-
-		{ eexpr = TVar (vtmp,None); etype = basic.tvoid; epos = pos }
-		::
-		if ctx.rcf_optimize then
-			mk_for (mk_this (gen.gmk_internal_name "hx" "hashes") (gen.gclasses.nativearray basic.tint)) (mk_this (gen.gmk_internal_name "hx" "length") basic.tint)
-			@
-			mk_for (mk_this (gen.gmk_internal_name "hx" "hashes_f") (gen.gclasses.nativearray basic.tint)) (mk_this (gen.gmk_internal_name "hx" "length_f") basic.tint)
-			@
-			(
-				let conflict_ctx = Option.get ctx.rcf_hash_conflict_ctx in
-				let ehead = mk_this (gen.gmk_internal_name "hx" "conflicts") conflict_ctx.t in
-				[conflict_ctx.add_names ehead base_arr]
-			)
-		else
-			mk_for (mk_this (gen.gmk_internal_name "hx" "hashes") (gen.gclasses.nativearray basic.tstring)) (mk_this (gen.gmk_internal_name "hx" "length") basic.tint)
-			@
-			mk_for (mk_this (gen.gmk_internal_name "hx" "hashes_f") (gen.gclasses.nativearray basic.tstring)) (mk_this (gen.gmk_internal_name "hx" "length_f") basic.tint)
-
-	(* *********************
-		 Dynamic lookup
-		 *********************
-
-		 This is the behavior of standard <implements Dynamic> classes. It will replace the error throwing
-		 if a field doesn't exists when looking it up.
-
-		 In order for it to work, an implementation for hash_function must be created.
-		 hash_function is the function to be called/inlined that will allow us to lookup the hash into a sorted array of hashes.
-		 A binary search or linear search algorithm may be implemented. The only need is that if not found, the NegBits of
-		 the place where it should be inserted must be returned.
-	*)
-	let abstract_dyn_lookup_implementation ctx this field_local hash_local may_value is_float pos =
-		let gen = ctx.rcf_gen in
-		let basic = gen.gcon.basic in
-		let mk_this field t = { (mk_field_access gen this field pos) with etype = t } in
-		let a_t = if ctx.rcf_optimize then basic.tint else basic.tstring in
-		let hx_hashes = mk_this (gen.gmk_internal_name "hx" "hashes") (gen.gclasses.nativearray a_t) in
-		let hx_hashes_f = mk_this (gen.gmk_internal_name "hx" "hashes_f") (gen.gclasses.nativearray a_t) in
-		let hx_dynamics = mk_this (gen.gmk_internal_name "hx" "dynamics") (gen.gclasses.nativearray t_empty) in
-		let hx_dynamics_f = mk_this (gen.gmk_internal_name "hx" "dynamics_f") (gen.gclasses.nativearray basic.tfloat) in
-		let hx_length = mk_this (gen.gmk_internal_name "hx" "length") (basic.tint) in
-		let hx_length_f = mk_this (gen.gmk_internal_name "hx" "length_f") (basic.tint) in
-		let res = alloc_var "res" basic.tint in
-		let fst_hash, snd_hash, fst_dynamics, snd_dynamics, fst_length, snd_length =
-			if is_float then
-				hx_hashes_f, hx_hashes, hx_dynamics_f, hx_dynamics, hx_length_f, hx_length
-			else
-				hx_hashes, hx_hashes_f, hx_dynamics, hx_dynamics_f, hx_length, hx_length_f
-		in
-		let res_local = mk_local res pos in
-		let gte = {
-			eexpr = TBinop(Ast.OpGte, res_local, { eexpr = TConst(TInt(Int32.zero)); etype = basic.tint; epos = pos });
-			etype = basic.tbool;
-			epos = pos;
-		} in
-		let mk_tarray arr idx =
-			{
-				eexpr = TArray(arr, idx);
-				etype = gen.gclasses.nativearray_type arr.etype;
-				epos = pos;
-			}
-		in
-		let ret_t = if is_float then basic.tfloat else t_dynamic in
-
-		match may_value with
-			| None ->
-				(*
-					var res = lookup(this.__hx_hashes/f, hash);
-					if (res < 0)
-					{
-						res = lookup(this.__hx_hashes_f/_, hash);
-						if(res < 0)
-							return null;
-						else
-							return __hx_dynamics_f[res];
-					} else {
-						return __hx_dynamics[res];
-					}
-				*)
-				let block =
-				[
-					{ eexpr = TVar(res, Some(ctx.rcf_hash_function hash_local fst_hash fst_length)); etype = basic.tvoid; epos = pos };
-					{ eexpr = TIf(gte, mk_return (mk_tarray fst_dynamics res_local), Some({
-						eexpr = TBlock(
-						[
-							{ eexpr = TBinop(Ast.OpAssign, res_local, ctx.rcf_hash_function hash_local snd_hash snd_length); etype = basic.tint; epos = pos };
-							{ eexpr = TIf(gte, mk_return (mk_tarray snd_dynamics res_local), None); etype = ret_t; epos = pos }
-						]);
-						etype = ret_t;
-						epos = pos;
-					})); etype = ret_t; epos = pos }
-				] in
-
-				if ctx.rcf_optimize then
-					let conflict_ctx = Option.get ctx.rcf_hash_conflict_ctx in
-					let ehead = mk_this (gen.gmk_internal_name "hx" "conflicts") conflict_ctx.t in
-					let vconflict = alloc_var "conflict" conflict_ctx.t in
-					let local_conflict = mk_local vconflict pos in
-					[mk (TIf (
-						mk (TBinop (OpLt, hash_local, ExprBuilder.make_int gen.gcon 0 pos)) basic.tbool pos,
-						mk (TBlock [
-							mk (TVar (vconflict, Some (conflict_ctx.get_conflict ehead hash_local field_local))) basic.tvoid pos;
-							mk (TIf (
-								mk (TBinop (OpNotEq, local_conflict, mk (TConst TNull) local_conflict.etype pos)) basic.tbool pos,
-								mk_return (Codegen.field local_conflict "value" t_dynamic pos),
-								None
-							)) basic.tvoid pos;
-						]) basic.tvoid pos,
-						Some (mk (TBlock block) basic.tvoid pos)
-					)) basic.tvoid pos]
-				else
-					block
-			| Some value_local ->
-				(*
-					//if is not float:
-					//if (isNumber(value_local)) return this.__hx_setField_f(field, getNumber(value_local), false(not static));
-					var res = lookup(this.__hx_hashes/f, hash);
-					if (res >= 0)
-					{
-						return __hx_dynamics/f[res] = value_local;
-					} else {
-						res = lookup(this.__hx_hashes_f/_, hash);
-						if (res >= 0)
-						{
-							__hx_dynamics_f/_.splice(res,1);
-							__hx_hashes_f/_.splice(res,1);
-						}
-					}
-
-					__hx_hashses/_f.insert(~res, hash);
-					__hx_dynamics/_f.insert(~res, value_local);
-					return value_local;
-				*)
-				let neg_res = { eexpr = TUnop(Ast.NegBits, Ast.Prefix, res_local); etype = basic.tint; epos = pos } in
-
-				let res2 = alloc_var "res2" basic.tint in
-				let res2_local = mk_local res2 pos in
-				let gte2 = {
-					eexpr = TBinop(Ast.OpGte, res2_local, { eexpr = TConst(TInt(Int32.zero)); etype = basic.tint; epos = pos });
-					etype = basic.tbool;
-					epos = pos;
-				} in
-
-				let block =
-				[
-					{ eexpr = TVar(res, Some(ctx.rcf_hash_function hash_local fst_hash fst_length)); etype = basic.tvoid; epos = pos };
-					{
-						eexpr = TIf(gte,
-							mk_return { eexpr = TBinop(Ast.OpAssign, mk_tarray fst_dynamics res_local, value_local); etype = value_local.etype; epos = pos },
-							Some({ eexpr = TBlock([
-								{ eexpr = TVar( res2, Some(ctx.rcf_hash_function hash_local snd_hash snd_length)); etype = basic.tvoid; epos = pos };
-								{
-									eexpr = TIf(gte2, { eexpr = TBlock([
-										ctx.rcf_remove_function snd_hash snd_length res2_local;
-										ctx.rcf_remove_function snd_dynamics snd_length res2_local;
-										mk (TUnop(Decrement,Postfix,snd_length)) basic.tint pos
-									]); etype = t_dynamic; epos = pos }, None);
-									etype = t_dynamic;
-									epos = pos;
-								}
-							]); etype = t_dynamic; epos = pos }));
-						etype = t_dynamic;
-						epos = pos;
-					};
-					ctx.rcf_insert_function fst_hash fst_length neg_res hash_local;
-					ctx.rcf_insert_function fst_dynamics fst_length neg_res value_local;
-					mk (TUnop(Increment,Postfix,fst_length)) basic.tint pos;
-				] in
-
-				let block =
-					if ctx.rcf_optimize then
-						let conflict_ctx = Option.get ctx.rcf_hash_conflict_ctx in
-						let ehead = mk_this (gen.gmk_internal_name "hx" "conflicts") conflict_ctx.t in
-						[mk (TIf (
-							mk (TBinop (OpLt, hash_local, ExprBuilder.make_int gen.gcon 0 pos)) basic.tbool pos,
-							conflict_ctx.set ehead hash_local field_local value_local,
-							Some (mk (TBlock block) basic.tvoid pos)
-						)) basic.tvoid pos]
-					else
-						block
-				in
-				block @ [mk_return value_local]
-
-	let get_delete_field ctx cl is_dynamic =
-		let pos = cl.cl_pos in
-		let this_t = TInst(cl, List.map snd cl.cl_params) in
-		let this = { eexpr = TConst(TThis); etype = this_t; epos = pos } in
-		let gen = ctx.rcf_gen in
-		let basic = gen.gcon.basic in
-		let tf_args, switch_var = field_type_args ctx pos in
-		let local_switch_var = mk_local switch_var pos in
-		let fun_type = TFun(fun_args tf_args,basic.tbool) in
-		let cf = mk_class_field (gen.gmk_internal_name "hx" "deleteField") fun_type false pos (Method MethNormal) [] in
-		let body = if is_dynamic then begin
-			let mk_this field t = { (mk_field_access gen this field pos) with etype = t } in
-			let a_t = if ctx.rcf_optimize then basic.tint else basic.tstring in
-			let hx_hashes = mk_this (gen.gmk_internal_name "hx" "hashes") (gen.gclasses.nativearray a_t) in
-			let hx_hashes_f = mk_this (gen.gmk_internal_name "hx" "hashes_f") (gen.gclasses.nativearray a_t) in
-			let hx_dynamics = mk_this (gen.gmk_internal_name "hx" "dynamics") (gen.gclasses.nativearray t_empty) in
-			let hx_dynamics_f = mk_this (gen.gmk_internal_name "hx" "dynamics_f") (gen.gclasses.nativearray basic.tfloat) in
-			let hx_length = mk_this (gen.gmk_internal_name "hx" "length") (basic.tint) in
-			let hx_length_f = mk_this (gen.gmk_internal_name "hx" "length_f") (basic.tint) in
-			let res = alloc_var "res" basic.tint in
-			let res_local = mk_local res pos in
-			let gte = {
-				eexpr = TBinop(Ast.OpGte, res_local, { eexpr = TConst(TInt(Int32.zero)); etype = basic.tint; epos = pos });
-				etype = basic.tbool;
-				epos = pos;
-			} in
-			(*
-				var res = lookup(this.__hx_hashes, hash);
-				if (res >= 0)
-				{
-					__hx_dynamics.splice(res,1);
-					__hx_hashes.splice(res,1);
-
-					return true;
-				} else {
-					res = lookup(this.__hx_hashes_f, hash);
-					if (res >= 0)
-					{
-						__hx_dynamics_f.splice(res,1);
-						__hx_hashes_f.splice(res,1);
-
-						return true;
-					}
-				}
-
-				return false;
-			*)
-			let common = [
-				{ eexpr = TVar(res,Some(ctx.rcf_hash_function local_switch_var hx_hashes hx_length)); etype = basic.tvoid; epos = pos };
-				{
-					eexpr = TIf(gte, { eexpr = TBlock([
-						ctx.rcf_remove_function hx_hashes hx_length res_local;
-						ctx.rcf_remove_function hx_dynamics hx_length res_local;
-						mk (TUnop(Decrement,Postfix,hx_length)) basic.tint pos;
-						mk_return { eexpr = TConst(TBool true); etype = basic.tbool; epos = pos }
-					]); etype = t_dynamic; epos = pos }, Some({ eexpr = TBlock([
-						{ eexpr = TBinop(Ast.OpAssign, res_local, ctx.rcf_hash_function local_switch_var hx_hashes_f hx_length_f); etype = basic.tint; epos = pos };
-						{ eexpr = TIf(gte, { eexpr = TBlock([
-							ctx.rcf_remove_function hx_hashes_f hx_length_f res_local;
-							ctx.rcf_remove_function hx_dynamics_f hx_length_f res_local;
-							mk (TUnop(Decrement,Postfix,hx_length_f)) basic.tint pos;
-							mk_return { eexpr = TConst(TBool true); etype = basic.tbool; epos = pos }
-						]); etype = t_dynamic; epos = pos }, None); etype = t_dynamic; epos = pos }
-					]); etype = t_dynamic; epos = pos }));
-					etype = t_dynamic;
-					epos = pos;
-				};
-				mk_return { eexpr = TConst(TBool false); etype = basic.tbool; epos = pos }
-			] in
-
-			if ctx.rcf_optimize then
-				let v_name = match tf_args with (v,_) :: _ -> v | _ -> assert false in
-				let local_name = mk_local v_name pos in
-				let conflict_ctx = Option.get ctx.rcf_hash_conflict_ctx in
-				let ehead = mk_this (gen.gmk_internal_name "hx" "conflicts") conflict_ctx.t in
-				(mk (TIf (
-					mk (TBinop (OpLt, local_switch_var, ExprBuilder.make_int gen.gcon 0 pos)) basic.tbool pos,
-					mk (TReturn (Some (conflict_ctx.delete ehead local_switch_var local_name))) basic.tvoid pos,
-					None
-				)) basic.tvoid pos) :: common
-			else
-				common
-		end else
-		[
-			mk_return { eexpr = TConst(TBool false); etype = basic.tbool; epos = pos }
-		] in
-
-		(* create function *)
-		let fn =
-		{
-			tf_args = tf_args;
-			tf_type = basic.tbool;
-			tf_expr = { eexpr = TBlock(body); etype = t_dynamic; epos = pos }
-		} in
-		cf.cf_expr <- Some({ eexpr = TFunction(fn); etype = fun_type; epos = pos });
-		cf
-
-	let rec is_first_dynamic cl =
-		match cl.cl_super with
-			| Some(cl,_) ->
-				if is_some cl.cl_dynamic then false else is_first_dynamic cl
-			| None -> true
-
-	let is_override cl = match cl.cl_super with
-		| Some (cl, _) when is_hxgen (TClassDecl cl) -> true
-		| _ -> false
-
-	let get_args t = match follow t with
-		| TFun(args,ret) -> args,ret
-		| _ -> assert false
-
-	(* WARNING: this will only work if overloading contructors is possible on target language *)
-	let implement_dynamic_object_ctor ctx cl =
-		let rec is_side_effects_free e =
-			match e.eexpr with
-				| TConst _
-				| TLocal _
-				| TFunction _
-				| TTypeExpr _ ->
-					true
-				| TNew(clnew,[],params) when clnew == cl ->
-					List.for_all is_side_effects_free params
-				| TUnop(Increment,_,_)
-				| TUnop(Decrement,_,_)
-				| TBinop(OpAssign,_,_)
-				| TBinop(OpAssignOp _,_,_) ->
-					false
-				| TUnop(_,_,e) ->
-					is_side_effects_free e
-				| TArray(e1,e2)
-				| TBinop(_,e1,e2) ->
-					is_side_effects_free e1 && is_side_effects_free e2
-				| TIf(cond,e1,Some e2) ->
-					is_side_effects_free cond && is_side_effects_free e1 && is_side_effects_free e2
-				| TField(e,_)
-				| TParenthesis e | TMeta(_,e) -> is_side_effects_free e
-				| TArrayDecl el -> List.for_all is_side_effects_free el
-				| TCast(e,_) -> is_side_effects_free e
-				| _ -> false
-		in
-
-		let pos = cl.cl_pos in
-		let gen = ctx.rcf_gen in
-		let basic = gen.gcon.basic in
-		let hasht = if ctx.rcf_optimize then basic.tint else basic.tstring in
-
-		let hashes_field = gen.gmk_internal_name "hx" "hashes", gen.gclasses.nativearray hasht in
-		let hashes_f_field = gen.gmk_internal_name "hx" "hashes_f", gen.gclasses.nativearray hasht in
-		let dynamics_field = gen.gmk_internal_name "hx" "dynamics", gen.gclasses.nativearray t_empty in
-		let dynamics_f_field = gen.gmk_internal_name "hx" "dynamics_f", gen.gclasses.nativearray basic.tfloat in
-		let fields =
-		[
-			hashes_field;
-			dynamics_field;
-			hashes_f_field;
-			dynamics_f_field;
-		] in
-
-		let hashes_var = alloc_var (fst hashes_field) (snd hashes_field) in
-		let hashes_f_var = alloc_var (fst hashes_f_field) (snd hashes_f_field) in
-		let tf_args = [
-			hashes_var, None;
-			alloc_var (fst dynamics_field) (snd dynamics_field), None;
-			hashes_f_var, None;
-			alloc_var (fst dynamics_f_field) (snd dynamics_f_field), None;
-		] in
-
-		let this = { eexpr = TConst TThis; etype = TInst(cl, List.map snd cl.cl_params); epos = pos } in
-		let mk_this field t = { (mk_field_access gen this field pos) with etype = t } in
-		let fun_t = TFun(fun_args tf_args,basic.tvoid) in
-		let ctor = mk_class_field "new" fun_t true pos (Method MethNormal) [] in
-		ctor.cf_expr <- Some(
-		{
-			eexpr = TFunction({
-				tf_args = tf_args;
-				tf_type = basic.tvoid;
-				tf_expr =
-				{
-					eexpr = TBlock(
-						List.map (fun (v,_) ->
-							{ eexpr = TBinop(Ast.OpAssign, mk_this v.v_name v.v_type, mk_local v pos); etype = v.v_type; epos = pos }
-						) tf_args
-						@
-						[
-							mk (TBinop(OpAssign, mk_this (gen.gmk_internal_name "hx" "length") basic.tint, gen.gclasses.nativearray_len (mk_local hashes_var pos) pos)) basic.tint pos;
-							mk (TBinop(OpAssign, mk_this (gen.gmk_internal_name "hx" "length_f") basic.tint, gen.gclasses.nativearray_len (mk_local hashes_f_var pos) pos)) basic.tint pos;
-						]
-					);
-					etype = basic.tvoid;
-					epos = pos
-				}
-			});
-			etype = fun_t;
-			epos = pos
-		});
-
-		add_constructor cl ctor;
-		(* default ctor also *)
-		let ctor = mk_class_field "new" (TFun([],basic.tvoid)) false pos (Method MethNormal) [] in
-		ctor.cf_expr <- Some {
-			eexpr = TFunction {
-				tf_type = basic.tvoid;
-				tf_args = [];
-				tf_expr = {
-					eexpr = TBlock(List.map (fun (f,t) ->
-						{ eexpr = TBinop(Ast.OpAssign, mk_this f t,{ eexpr = TCall(mk_local v_nativearray pos, []); etype = t; epos = pos; }); etype = t; epos = pos }
-					) fields);
-					etype = basic.tvoid;
-					epos = pos;
-				}
-			};
-			etype = ctor.cf_type;
-			epos = pos;
-		};
-		add_constructor cl ctor;
-		(* and finally we will return a function that transforms a TObjectDecl into a new DynamicObject() call *)
-		let rec loop objdecl acc acc_f =
-			match objdecl with
-				| [] -> acc,acc_f
-				| (name,expr) :: tl ->
-					let real_t = gen.greal_type expr.etype in
-					match follow expr.etype with
-						| TInst ( { cl_path = ["haxe"], "Int64" }, [] ) ->
-							loop tl ((name, gen.ghandle_cast t_dynamic real_t expr) :: acc) acc_f
-						| _ ->
-							if like_float real_t && not (like_i64 real_t) then
-								loop tl acc ((name, gen.ghandle_cast basic.tfloat real_t expr) :: acc_f)
-							else
-								loop tl ((name, gen.ghandle_cast t_dynamic real_t expr) :: acc) acc_f
-		in
-
-		let may_hash_field s =
-			if ctx.rcf_optimize then begin
-				mk (TConst (TInt (hash_field_i32 ctx pos s))) basic.tint pos
-			end else begin
-				ExprBuilder.make_string gen.gcon s pos
-			end
-		in
-
-		let do_objdecl e objdecl =
-			let exprs_before = ref [] in
-			let rec change_exprs decl acc = match decl with
-				| (name,expr) :: tl ->
-					if is_side_effects_free expr then
-						change_exprs tl ((name,expr) :: acc)
-					else begin
-						let var = mk_temp gen "odecl" expr.etype in
-						exprs_before := { eexpr = TVar(var,Some expr); etype = basic.tvoid; epos = expr.epos } :: !exprs_before;
-						change_exprs tl ((name,mk_local var expr.epos) :: acc)
-					end
-				| [] -> acc
-			in
-			let objdecl = change_exprs objdecl [] in
-
-			let odecl, odecl_f = loop objdecl [] [] in
-			let changed_expr = List.map (fun (s,e) -> (may_hash_field s,e)) in
-			let odecl, odecl_f = changed_expr odecl, changed_expr odecl_f in
-			let sort_fn (e1,_) (e2,_) =
-				match e1.eexpr, e2.eexpr with
-					| TConst(TInt i1), TConst(TInt i2) -> compare i1 i2
-					| TConst(TString s1), TConst(TString s2) -> compare s1 s2
-					| _ -> assert false
-			in
-
-			let odecl, odecl_f = List.sort sort_fn odecl, List.sort sort_fn odecl_f in
-
-			let ret = {
-				e with eexpr = TNew(cl,[],
-					[
-						mk_nativearray_decl gen hasht (List.map fst odecl) pos;
-						mk_nativearray_decl gen t_empty (List.map snd odecl) pos;
-						mk_nativearray_decl gen hasht (List.map fst odecl_f) pos;
-						mk_nativearray_decl gen basic.tfloat (List.map snd odecl_f) pos;
-					]);
-			} in
-			match !exprs_before with
-				| [] -> ret
-				| block ->
-					{
-						eexpr = TBlock(List.rev block @ [ret]);
-						etype = ret.etype;
-						epos = ret.epos;
-					}
-		in
-		do_objdecl
-
-	let implement_dynamics ctx cl =
-		let pos = cl.cl_pos in
-		let is_override = is_override cl in
-		if is_some cl.cl_dynamic then begin
-			if is_first_dynamic cl then begin
-				(*
-					* add hx_hashes, hx_hashes_f, hx_dynamics, hx_dynamics_f to class
-					* implement hx_deleteField
-				*)
-				let gen = ctx.rcf_gen in
-				let basic = gen.gcon.basic in
-				let hasht = if ctx.rcf_optimize then basic.tint else basic.tstring in
-
-				let new_fields =
-				[
-					mk_class_field (gen.gmk_internal_name "hx" "hashes") (gen.gclasses.nativearray hasht) false pos (Var { v_read = AccNormal; v_write = AccNormal }) [];
-					mk_class_field (gen.gmk_internal_name "hx" "dynamics") (gen.gclasses.nativearray t_empty) false pos (Var { v_read = AccNormal; v_write = AccNormal }) [];
-					mk_class_field (gen.gmk_internal_name "hx" "hashes_f") (gen.gclasses.nativearray hasht) false pos (Var { v_read = AccNormal; v_write = AccNormal }) [];
-					mk_class_field (gen.gmk_internal_name "hx" "dynamics_f") (gen.gclasses.nativearray basic.tfloat) false pos (Var { v_read = AccNormal; v_write = AccNormal }) [];
-				] in
-
-				(if cl.cl_path <> (["haxe"; "lang"], "DynamicObject") then
-					List.iter (fun cf -> cf.cf_expr <- Some { eexpr = TCall(mk_local v_nativearray pos, []); etype = cf.cf_type; epos = cf.cf_pos }) new_fields
-				);
-
-				let new_fields =
-					if ctx.rcf_optimize then
-						let f = mk_class_field (gen.gmk_internal_name "hx" "conflicts") (Option.get ctx.rcf_hash_conflict_ctx).t false pos (Var { v_read = AccNormal; v_write = AccNormal }) [] in
-						f :: new_fields
-					else
-						new_fields
-				in
-
-				let delete = get_delete_field ctx cl true in
-
-				let new_fields = new_fields @ [
-					mk_class_field (gen.gmk_internal_name "hx" "length") (basic.tint) false pos (Var { v_read = AccNormal; v_write = AccNormal }) [];
-					mk_class_field (gen.gmk_internal_name "hx" "length_f") (basic.tint) false pos (Var { v_read = AccNormal; v_write = AccNormal }) [];
-					delete;
-				] in
-
-				List.iter (fun cf ->
-					cl.cl_fields <- PMap.add cf.cf_name cf cl.cl_fields
-				) new_fields;
-
-		(*
-				let rec last_ctor cl =
-					match cl.cl_constructor with
-						| None -> (match cl.cl_super with | None -> None | Some (cl,_) -> last_ctor cl)
-						| Some c -> Some c
-				in
-		*)
-				(*
-					in order for the next to work, we need to execute our script before InitFunction, so the expressions inside the variables are initialized by the constructor
-				*)
-				(*
-					Now we need to add their initialization.
-					This will consist of different parts:
-						Check if there are constructors. If not, create one and add initialization to it (calling super, ok)
-						If there are, add as first statement (or second if there is a super() call in the first)
-						If class has @:dynamicObject meta, also create another new() class with its parameters as constructor arguments
-				*)
-
-				cl.cl_ordered_fields <- cl.cl_ordered_fields @ new_fields;
-				if is_override then cl.cl_overrides <- delete :: cl.cl_overrides
-			end
-		end else if not is_override then begin
-			let delete = get_delete_field ctx cl false in
-			cl.cl_ordered_fields <- cl.cl_ordered_fields @ [delete];
-			cl.cl_fields <- PMap.add delete.cf_name delete cl.cl_fields
-		end
-
-
-	(*
-		Implements:
-			__hx_lookupField(field:String, throwErrors:Bool, isCheck:Bool, handleProperties:Bool, isFirst:Bool):Dynamic
-
-			__hx_lookupField_f(field:String, throwErrors:Bool, handleProperties:Bool, isFirst:Bool):Float
-
-			__hx_lookupSetField(field:String, value:Dynamic, handleProperties:Bool, isFirst:Bool):Dynamic;
-
-			__hx_lookupSetField(field:String, value:Float, handleProperties:Bool, isFirst:Bool):Float;
-	*)
-	let implement_final_lookup ctx cl =
-		let gen = ctx.rcf_gen in
-		let basic = gen.gcon.basic in
-		let pos = cl.cl_pos in
-		let is_override = is_override cl in
-
-		let this = { eexpr = TConst(TThis); etype = TInst(cl, List.map snd cl.cl_params); epos = pos } in
-
-		(*
-			this function will create the class fields and call callback for each version
-
-			callback : is_float fields_args switch_var throw_errors_option is_check_option value_option : texpr list
-		*)
-		let create_cfs is_dynamic callback =
-			let create_cf is_float is_set =
-				let name = gen.gmk_internal_name "hx" ( (if is_set then "lookupSetField" else "lookupField") ^ (if is_float then "_f" else "") ) in
-				let field_args, switch_var = field_type_args ctx pos in
-				let ret_t = if is_float then basic.tfloat else t_dynamic in
-				let tf_args, throw_errors_opt =
-					if is_set then
-						field_args, None
-					else
-						let v = alloc_var "throwErrors" basic.tbool in
-						field_args @ [v,None], Some v
-				in
-				let tf_args, is_check_opt =
-					if is_set || is_float then
-						tf_args, None
-					else
-						let v = alloc_var "isCheck" basic.tbool in
-						tf_args @ [v,None], Some v
-				in
-				let tf_args, value_opt =
-					if not is_set then
-						tf_args, None
-					else
-						let v = alloc_var "value" ret_t in
-						field_args @ [v,None], Some v
-				in
-
-				let fun_t = TFun(fun_args tf_args, ret_t) in
-				let cf = mk_class_field name fun_t false pos (Method MethNormal) [] in
-				let block = callback is_float field_args switch_var throw_errors_opt is_check_opt value_opt in
-				let block = if not is_set then let tl = begin
-					let throw_errors_local = mk_local (get throw_errors_opt) pos in
-					let mk_check_throw msg =
-					{
-						eexpr = TIf(throw_errors_local, mk_throw ctx msg pos, Some (mk_return (null ret_t pos)));
-						etype = ret_t;
-						epos = pos
-					} in
-
-					let mk_may_check_throw msg = if is_dynamic then mk_return (null ret_t pos) else mk_check_throw msg in
-					if is_float then begin
-						[
-							mk_may_check_throw "Field not found or incompatible field type.";
-						]
-					end else begin
-						let is_check_local = mk_local (get is_check_opt) pos in
-						[
-							{
-								eexpr = TIf(is_check_local, mk_return (undefined pos), Some( mk_may_check_throw "Field not found." ));
-								etype = ret_t;
-								epos = pos;
-							}
-						]
-					end
-				end in block @ tl else block in
-				cf.cf_expr <- Some(
-					{
-						eexpr = TFunction({
-							tf_args = tf_args;
-							tf_type = ret_t;
-							tf_expr = { eexpr = TBlock(block); etype = ret_t; epos = pos }
-						});
-						etype = fun_t;
-						epos = pos
-					}
-				);
-				cf
-			in
-			let cfs =
-			[
-				create_cf false false;
-				create_cf true false;
-				create_cf false true;
-				create_cf true true
-			] in
-			cl.cl_ordered_fields <- cl.cl_ordered_fields @ cfs;
-			List.iter (fun cf ->
-				cl.cl_fields <- PMap.add cf.cf_name cf cl.cl_fields;
-				if is_override then cl.cl_overrides <- cf :: cl.cl_overrides
-			) cfs
-		in
-
-		if is_some cl.cl_dynamic then begin
-			(* let abstract_dyn_lookup_implementation ctx this hash_local may_value is_float pos = *)
-			(* callback : is_float fields_args switch_var throw_errors_option is_check_option value_option : texpr list *)
-			if is_first_dynamic cl then
-				create_cfs true (fun is_float fields_args switch_var _ _ value_opt ->
-					let v_name = match fields_args with (v,_) :: _ -> v | _ -> assert false in
-					abstract_dyn_lookup_implementation ctx this (mk_local v_name pos) (mk_local switch_var pos) (Option.map (fun v -> mk_local v pos) value_opt) is_float pos
-				)
-		end else if not is_override then begin
-			create_cfs false (fun is_float fields_args switch_var _ _ value_opt ->
-				match value_opt with
-					| None -> (* is not set *)
-						[]
-					| Some _ -> (* is set *)
-						if is_float then
-							[ mk_throw ctx "Cannot access field for writing or incompatible type." pos ]
-						else
-							[ mk_throw ctx "Cannot access field for writing." pos ]
-			)
-		end
-
-	(* *)
-	let implement_get_set ctx cl =
-		let gen = ctx.rcf_gen in
-		let mk_cfield is_set is_float =
-			let pos = cl.cl_pos in
-			let basic = ctx.rcf_gen.gcon.basic in
-			let tf_args, switch_var = field_type_args ctx pos in
-			let field_args = tf_args in
-			let local_switch_var = { eexpr = TLocal(switch_var); etype = switch_var.v_type; epos = pos } in
-
-			let handle_prop = alloc_var "handleProperties" basic.tbool in
-			let handle_prop_local = mk_local handle_prop pos in
-
-			let this = { eexpr = TConst TThis; etype = TInst(cl, List.map snd cl.cl_params); epos = pos } in
-			let mk_this_call_raw name fun_t params =
-				{ eexpr = TCall( { (mk_field_access gen this name pos) with etype = fun_t; }, params ); etype = snd (get_args fun_t); epos = pos }
-			in
-
-			let fun_type = ref (TFun([], basic.tvoid)) in
-			let fun_name = ctx.rcf_gen.gmk_internal_name "hx" ( (if is_set then "setField" else "getField") ^ (if is_float then "_f" else "") ) in
-			let cfield = mk_class_field fun_name !fun_type false pos (Method MethNormal) [] in
-
-			let maybe_cast e = e in
-
-			let t = TInst(cl, List.map snd cl.cl_params) in
-
-			(* if it's not latest hxgen class -> check super *)
-			let mk_do_default args do_default =
-				match cl.cl_super with
-					| None -> fun () -> maybe_cast (do_default ())
-					| Some (super, sparams) when not (is_hxgen (TClassDecl super)) ->
-						fun () -> maybe_cast (do_default ())
-					| _ ->
-						fun () ->
-							mk_return {
-								eexpr = TCall(
-									{ eexpr = TField({ eexpr = TConst TSuper; etype = t; epos = pos }, FInstance(cl, List.map snd cl.cl_params, cfield)); etype = !fun_type; epos = pos },
-									(List.map (fun (v,_) -> mk_local v pos) args) );
-								etype = if is_float then basic.tfloat else t_dynamic;
-								epos = pos;
-							};
-			in
-
-			(* if it is set function, there are some different set fields to do *)
-			let do_default, do_field, tf_args = if is_set then begin
-				let value_var = alloc_var "value" (if is_float then basic.tfloat else t_dynamic) in
-				let value_local = { eexpr = TLocal(value_var); etype = value_var.v_type; epos = pos } in
-				let tf_args = tf_args @ [value_var,None; handle_prop, None; ] in
-				let lookup_name = gen.gmk_internal_name "hx" ("lookupSetField" ^ if is_float then "_f" else "") in
-
-				let do_default =
-						fun () ->
-							mk_return (mk_this_call_raw lookup_name (TFun(fun_args (field_args @ [value_var,None]),value_var.v_type)) ( List.map (fun (v,_) -> mk_local v pos) field_args @ [ value_local ] ))
-				in
-
-				let do_field cf cf_type =
-					let get_field ethis = { eexpr = TField (ethis, FInstance(cl, List.map snd cl.cl_params, cf)); etype = cf_type; epos = pos } in
-					let this = { eexpr = TConst(TThis); etype = t; epos = pos } in
-					let value_local = if is_float then match follow cf_type with
-						| TInst({ cl_kind = KTypeParameter _ }, _) ->
-							mk_cast t_dynamic value_local
-						| _ ->
-							value_local
-						else
-							value_local
-					in
-
-					let ret =
-					{
-						eexpr = TBlock([
-							{
-								eexpr = TBinop(Ast.OpAssign,
-									get_field this,
-									mk_cast cf_type value_local);
-								etype = cf_type;
-								epos = pos;
-							};
-							mk_return value_local
-						]);
-						etype = cf_type;
-						epos = pos;
-					} in
-					match cf.cf_kind with
-						| Var { v_write = AccCall } ->
-							let bl =
-							[
-								mk_this_call_raw ("set_" ^ cf.cf_name) (TFun(["value",false,cf.cf_type], cf.cf_type)) [ value_local ];
-								mk_return value_local
-							] in
-							if Type.is_extern_field cf then
-								{ eexpr = TBlock bl; etype = value_local.etype; epos = pos }
-							else
-								{
-									eexpr = TIf(
-										handle_prop_local,
-										{ eexpr = TBlock bl; etype = value_local.etype; epos = pos },
-										Some ret);
-									etype = value_local.etype;
-									epos = pos;
-								}
-						| _ ->
-							ret
-				in
-
-				(mk_do_default tf_args do_default, do_field, tf_args)
-			end else begin
-				let throw_errors = alloc_var "throwErrors" basic.tbool in
-				let throw_errors_local = mk_local throw_errors pos in
-				let do_default, tf_args = if not is_float then begin
-					let is_check = alloc_var "isCheck" basic.tbool in
-					let is_check_local = mk_local is_check pos in
-
-					let tf_args = tf_args @ [ throw_errors,None; ] in
-
-					(* default: if (isCheck) return __undefined__ else if(throwErrors) throw "Field not found"; else return null; *)
-					let lookup_name = gen.gmk_internal_name "hx" "lookupField" in
-					let do_default =
-							fun () ->
-								mk_return (mk_this_call_raw lookup_name (TFun(fun_args (field_args @ [throw_errors,None;is_check,None; ]),t_dynamic)) ( List.map (fun (v,_) -> mk_local v pos) field_args @ [ throw_errors_local; is_check_local; ] ))
-					in
-
-					(do_default, tf_args @ [ is_check,None; handle_prop,None; ])
-				end else begin
-					let tf_args = tf_args @ [ throw_errors,None; ] in
-
-					let lookup_name = gen.gmk_internal_name "hx" "lookupField_f" in
-					let do_default =
-							fun () ->
-								mk_return (mk_this_call_raw lookup_name (TFun(fun_args (field_args @ [throw_errors,None; ]),basic.tfloat)) ( List.map (fun (v,_) -> mk_local v pos) field_args @ [ throw_errors_local; ] ))
-					in
-
-					(do_default, tf_args @ [ handle_prop,None; ])
-				end in
-
-				 let get_field cf cf_type ethis cl name =
-					match cf.cf_kind with
-						| Var { v_read = AccCall } when Type.is_extern_field cf ->
-							mk_return (mk_this_call_raw ("get_" ^ cf.cf_name) (TFun(["value",false,cf.cf_type], cf.cf_type)) [	])
-						| Var { v_read = AccCall } ->
-							{
-								eexpr = TIf(
-									handle_prop_local,
-									mk_return (mk_this_call_raw ("get_" ^ cf.cf_name) (TFun(["value",false,cf.cf_type], cf.cf_type)) [	]),
-									Some { eexpr = TField (ethis, FInstance(cl, List.map snd cl.cl_params, cf)); etype = cf_type; epos = pos }
-								);
-								etype = cf_type;
-								epos = pos;
-							}
-						| Var _
-						| Method MethDynamic -> { eexpr = TField (ethis, FInstance(cl,List.map snd cl.cl_params,cf)); etype = cf_type; epos = pos }
-						| _ ->
-								{ eexpr = TField (this, FClosure(Some (cl,[]), cf)); etype = cf_type; epos = pos } (* TODO: FClosure change *)
-				in
-
-				let do_field cf cf_type =
-					let this = { eexpr = TConst(TThis); etype = t; epos = pos } in
-					match is_float, follow cf_type with
-						| true, TInst( { cl_kind = KTypeParameter _ }, _ ) ->
-							mk_return (mk_cast basic.tfloat (mk_cast t_dynamic (get_field cf cf_type this cl cf.cf_name)))
-						| _ ->
-							mk_return (maybe_cast (get_field cf cf_type this cl cf.cf_name ))
-				in
-				(mk_do_default tf_args do_default, do_field, tf_args)
-			end in
-
-			let get_fields() =
-				let ret = collect_fields cl ( if is_float || is_set then Some (false) else None ) in
-				let ret = if is_set then List.filter (fun (_,cf) ->
-					match cf.cf_kind with
-					(* | Var { v_write = AccNever } -> false *)
-					| _ -> not (Meta.has Meta.ReadOnly cf.cf_meta)) ret
-				else
-					List.filter (fun (_,cf) ->
-					match cf.cf_kind with
-					(* | Var { v_read = AccNever } -> false *)
-					| _ -> true) ret in
-				if is_float then
-					List.filter (fun (_,cf) -> (* TODO: maybe really apply_params in cf.cf_type. The benefits would be limited, though *)
-						match follow (ctx.rcf_gen.greal_type (ctx.rcf_gen.gfollow#run_f cf.cf_type)) with
-							| TDynamic _ | TMono _
-							| TInst ({ cl_kind = KTypeParameter _ }, _) -> true
-							| t when like_float t && not (like_i64 t) -> true
-							| _ -> false
-					) ret
-				else
-					(* dynamic will always contain all references *)
-					ret
-			in
-
-			(* now we have do_default, do_field and tf_args *)
-			(* so create the switch expr *)
-			fun_type := TFun(List.map (fun (v,_) -> (v.v_name, false, v.v_type)) tf_args, if is_float then basic.tfloat else t_dynamic );
-			let has_fields = ref false in
-
-			let content =
-				let fields = get_fields() in
-				let fields = List.filter (fun (_, cf) -> match is_set, cf.cf_kind with
-					| true, Var { v_write = AccCall } -> true
-					| false, Var { v_read = AccCall } -> true
-					| _ -> not (Type.is_extern_field cf)) fields
-				in
-				(if fields <> [] then has_fields := true);
-				let cases = List.map (fun (names, cf) ->
-					(if names = [] then assert false);
-					(List.map (switch_case ctx pos) names, do_field cf cf.cf_type)
-				) fields in
-				let default = Some(do_default()) in
-
-				mk_block { eexpr = TSwitch(local_switch_var, cases, default); etype = basic.tvoid; epos = pos }
-			in
-
-			let is_override = match cl.cl_super with
-				| Some (cl, _) when is_hxgen (TClassDecl cl) -> true
-				| _ -> false
-			in
-
-			if !has_fields || (not is_override) then begin
-				let func =
-				{
-					tf_args = tf_args;
-					tf_type = if is_float then basic.tfloat else t_dynamic;
-					tf_expr = content;
-				} in
-
-				let func = { eexpr = TFunction(func); etype = !fun_type; epos = pos } in
-
-				cfield.cf_type <- !fun_type;
-				cfield.cf_expr <- Some func;
-
-				cl.cl_ordered_fields <- cl.cl_ordered_fields @ [cfield];
-				cl.cl_fields <- PMap.add fun_name cfield cl.cl_fields;
-
-				(if is_override then cl.cl_overrides <- cfield	:: cl.cl_overrides)
-			end else ()
-		in
-		mk_cfield true true;
-		mk_cfield true false;
-		mk_cfield false false;
-		mk_cfield false true
-
-	let implement_getFields ctx cl =
-		let gen = ctx.rcf_gen in
-		let basic = gen.gcon.basic in
-		let pos = cl.cl_pos in
-
-		(*
-			function __hx_getFields(baseArr:Array<String>)
-			{
-				//add all variable fields
-				//then:
-				super.__hx_getFields(baseArr);
-			}
-		*)
-		let name = gen.gmk_internal_name "hx" "getFields" in
-		let v_base_arr = alloc_var "baseArr" (basic.tarray basic.tstring) in
-		let base_arr = mk_local v_base_arr pos in
-
-		let tf_args = [(v_base_arr,None)] in
-		let t = TFun(fun_args tf_args, basic.tvoid) in
-		let cf = mk_class_field name t false pos (Method MethNormal) [] in
-
-		let e_pushfield = mk_field_access gen base_arr "push" pos in
-		let mk_push value = mk (TCall (e_pushfield, [value])) basic.tint pos in
-
-		let has_value = ref false in
-		let map_fields =
-			List.map (fun (_,cf) ->
-				match cf.cf_kind with
-					| Var _
-					| Method MethDynamic when not (List.memq cf cl.cl_overrides) ->
-						has_value := true;
-						mk_push (ExprBuilder.make_string gen.gcon cf.cf_name pos)
-					| _ -> null basic.tvoid pos
-			)
-		in
-
-		(*
-			if it is first_dynamic, then we need to enumerate the dynamic fields
-		*)
-		let exprs =
-			if is_some cl.cl_dynamic && is_first_dynamic cl then begin
-				has_value := true;
-				enumerate_dynamic_fields ctx cl mk_push base_arr
-			end else
-				[]
-		in
-
-		let exprs =
-			if is_override cl then
-				let tparams = List.map snd cl.cl_params in
-				let esuper = mk (TConst TSuper) (TInst(cl, tparams)) pos in
-				let efield = mk (TField (esuper, FInstance (cl, tparams, cf))) t pos in
-				exprs @ [mk (TCall (efield, [base_arr])) basic.tvoid pos]
-			else
-				exprs
-		in
-
-		let exprs = map_fields (collect_fields cl (Some false)) @ exprs in
-
-		cf.cf_expr <- Some {
-			eexpr = TFunction({
-				tf_args = tf_args;
-				tf_type = basic.tvoid;
-				tf_expr = mk (TBlock exprs) basic.tvoid pos
-			});
-			etype = t;
-			epos = pos
-		};
-
-		if !has_value || not (is_override cl) then begin
-			cl.cl_ordered_fields <- cl.cl_ordered_fields @ [cf];
-			cl.cl_fields <- PMap.add cf.cf_name cf cl.cl_fields;
-			(if is_override cl then cl.cl_overrides <- cf :: cl.cl_overrides)
-		end
-
-
-	let implement_invokeField ctx ~slow_invoke cl =
-		(*
-			There are two ways to implement an haxe reflection-enabled class:
-			When we extend a non-hxgen class, and when we extend the base HxObject class.
-
-			Because of the added boiler plate we'd add every time we extend a non-hxgen class to implement a big IHxObject
-			interface, we'll handle the cases differently when implementing each interface.
-
-			At the IHxObject interface, there's only invokeDynamic(field, args[]), while at the HxObject class there are
-			the other, more optimized methods, that follow the Function class interface.
-
-			Since this will only be called by the Closure class, this conversion can be properly dealt with later.
-
-			TODO: create the faster version. By now only invokeDynamic will be implemented
-		*)
-		let gen = ctx.rcf_gen in
-		let basic = gen.gcon.basic in
-		let pos = cl.cl_pos in
-
-		let has_method = ref false in
-
-		let is_override = ref false in
-		let rec extends_hxobject cl =
-			match cl.cl_super with
-				| None -> true
-				| Some (cl,_) when is_hxgen (TClassDecl cl) -> is_override := true; extends_hxobject cl
-				| _ -> false
-		in
-
-		let field_args, switch_var = field_type_args ctx cl.cl_pos in
-		let field_args_exprs = List.map (fun (v,_) -> mk_local v pos) field_args in
-
-		let dynamic_arg = alloc_var "dynargs" (basic.tarray t_dynamic) in
-		let all_args = field_args @ [ dynamic_arg, None ] in
-		let fun_t = TFun(fun_args all_args, t_dynamic) in
-
-		let this_t = TInst(cl, List.map snd cl.cl_params) in
-		let this = { eexpr = TConst(TThis); etype = this_t; epos = pos } in
-		let apply_object cf = apply_params cf.cf_params (List.map (fun _ -> t_dynamic) cf.cf_params) cf.cf_type in
-
-		let mk_this_call_raw name fun_t params =
-			{ eexpr = TCall( { (mk_field_access gen this name pos) with etype = fun_t }, params ); etype = snd (get_args fun_t); epos = pos }
-		in
-
-		let mk_this_call cf params =
-			let t = apply_object cf in
-			(* the return type transformation into Dynamic *)
-			(* is meant to avoid return-type casting after functions with *)
-			(* type parameters are properly inferred at TypeParams.infer_params *)
-			(* e.g. function getArray<T : SomeType>(t:T):Array<T>; after infer_params, *)
-			(* T will be inferred as SomeType, but the returned type will still be typed *)
-			(* as Array<Dynamic> *)
-			let args, ret = get_args t in
-			let ret = match follow ret with
-				| TAbstract ({ a_path = ([], "Void") },[]) -> ret
-				| _ -> ret
-			in
-			mk_this_call_raw cf.cf_name (TFun(args, ret)) params
-		in
-
-		let extends_hxobject = extends_hxobject cl in
-		ignore extends_hxobject;
-
-		(* creates a invokeField of the class fields listed here *)
-		(*
-			function invokeField(field, dynargs)
-			{
-				switch(field)
-				{
-					case "a": this.a(dynargs[0], dynargs[1], dynargs[2]...);
-					default: super.invokeField //or this.getField(field).invokeDynamic(dynargs)
-				}
-			}
-		*)
-
-		let dyn_fun = mk_class_field (ctx.rcf_gen.gmk_internal_name "hx" "invokeField") fun_t false cl.cl_pos (Method MethNormal) [] in
-
-		let mk_switch_dyn cfs old =
-			let get_case (names,cf) =
-				has_method := true;
-				let i = ref 0 in
-				let dyn_arg_local = mk_local dynamic_arg pos in
-				let cases = List.map (switch_case ctx pos) names in
-				(cases,
-					{ eexpr = TReturn(Some (mk_this_call cf (List.map (fun (name,_,t) ->
-							let ret = { eexpr = TArray(dyn_arg_local, ExprBuilder.make_int ctx.rcf_gen.gcon !i pos); etype = t_dynamic; epos = pos } in
-							incr i;
-							ret
-						) (fst (get_args (cf.cf_type))) ) ));
-						etype = basic.tvoid;
-						epos = pos
-					}
-				)
-			in
-
-			let cfs = List.filter (fun (_,cf) -> match cf.cf_kind with
-				| Method _ -> if List.memq cf cl.cl_overrides then false else true
-				| _ -> true) cfs
-			in
-
-			let cases = List.map get_case cfs in
-			let cases = match old with
-				| [] -> cases
-				| _ ->
-					let ncases = List.map (fun cf -> switch_case ctx pos cf.cf_name) old in
-					( ncases, mk_return ((get slow_invoke) this (mk_local (fst (List.hd field_args)) pos) (mk_local dynamic_arg pos)) ) :: cases
-			in
-
-			let default = if !is_override then
-				{ eexpr = TReturn(Some (call_super ctx all_args t_dynamic dyn_fun cl this_t pos) ); etype = basic.tvoid; epos = pos }
-			else (
-				let field = begin
-					let fun_name = ctx.rcf_gen.gmk_internal_name "hx" "getField" in
-					let tf_args, _ = field_type_args ctx pos in
-					let tf_args, args = fun_args tf_args, field_args_exprs in
-
-					let tf_args, args = tf_args @ ["throwErrors",false, basic.tbool],       args @ [ExprBuilder.make_bool gen.gcon true pos] in
-					let tf_args, args = tf_args @ ["isCheck", false, basic.tbool],          args @ [ExprBuilder.make_bool gen.gcon false pos] in
-					let tf_args, args = tf_args @ ["handleProperties", false, basic.tbool], args @ [ExprBuilder.make_bool gen.gcon false pos] in
-
-					mk (TCall ({ (mk_field_access gen this fun_name pos) with etype = TFun(tf_args, t_dynamic) }, args)) t_dynamic pos
-				end in
-				let field = mk_cast (TInst(ctx.rcf_ft.func_class,[])) field in
-				mk_return {
-					eexpr = TCall(
-						mk_field_access gen field (gen.gmk_internal_name "hx" "invokeDynamic") pos,
-						[mk_local dynamic_arg pos]);
-					etype = t_dynamic;
-					epos = pos
-				} )
-			in
-
-			{
-				eexpr = TSwitch(mk_local switch_var pos, cases, Some default);
-				etype = basic.tvoid;
-				epos = pos;
-			}
-		in
-
-		let contents =
-			let nonstatics = collect_fields cl (Some true) in
-
-			let old_nonstatics = ref [] in
-
-			let nonstatics = match slow_invoke with
-				| None -> nonstatics
-				| Some _ ->
-					List.filter (fun (n,cf) ->
-						let is_old = not (PMap.mem cf.cf_name cl.cl_fields) || List.memq cf cl.cl_overrides in
-						(if is_old then old_nonstatics := cf :: !old_nonstatics);
-						not is_old
-					) nonstatics
-			in
-
-			mk_switch_dyn nonstatics !old_nonstatics
-		in
-
-		dyn_fun.cf_expr <- Some
-			{
-				eexpr = TFunction(
-				{
-					tf_args = all_args;
-					tf_type = t_dynamic;
-					tf_expr = mk_block contents;
-				});
-				etype = TFun(fun_args all_args, t_dynamic);
-				epos = pos;
-			};
-		if !is_override && not (!has_method) then () else begin
-			cl.cl_ordered_fields <- cl.cl_ordered_fields @ [dyn_fun];
-			cl.cl_fields <- PMap.add dyn_fun.cf_name dyn_fun cl.cl_fields;
-			(if !is_override then cl.cl_overrides <- dyn_fun :: cl.cl_overrides)
-		end
-
-	let implement_varargs_cl ctx cl =
-		let pos = cl.cl_pos in
-		let gen = ctx.rcf_gen in
-		let basic = gen.gcon.basic in
-
-		let this_t = TInst(cl, List.map snd cl.cl_params) in
-		let this = { eexpr = TConst(TThis); etype = this_t ; epos = pos } in
-		let mk_this field t = { (mk_field_access gen this field pos) with etype = t } in
-
-		let invokedyn = gen.gmk_internal_name "hx" "invokeDynamic" in
-		let idyn_t = TFun([gen.gmk_internal_name "fn" "dynargs", false, basic.tarray t_dynamic], t_dynamic) in
-		let this_idyn = mk_this invokedyn idyn_t in
-
-		let map_fn arity ret vars api =
-
-			let rec loop i acc =
-				if i < 0 then
-					acc
-				else
-					let obj = api i t_dynamic None in
-					loop (i - 1) (obj :: acc)
-			in
-
-			let call_arg = if arity = (-1) then
-				api (-1) t_dynamic None
-			else if arity = 0 then
-				null (basic.tarray t_empty) pos
-			else
-				{ eexpr = TArrayDecl(loop (arity - 1) []); etype = basic.tarray t_empty; epos = pos }
-			in
-
-			let expr = {
-				eexpr = TCall(
-					this_idyn,
-					[ call_arg ]
-				);
-				etype = t_dynamic;
-				epos = pos
-			} in
-
-			let expr = if like_float ret && not (like_int ret) then mk_cast ret expr else expr in
-
-			mk_return expr
-		in
-
-		let all_cfs = List.filter (fun cf -> cf.cf_name <> "new" && cf.cf_name <> (invokedyn) && match cf.cf_kind with Method _ -> true | _ -> false) (ctx.rcf_ft.map_base_classfields cl map_fn) in
-
-		cl.cl_ordered_fields <- cl.cl_ordered_fields @ all_cfs;
-		List.iter (fun cf ->
-			cl.cl_fields <- PMap.add cf.cf_name cf cl.cl_fields
-		) all_cfs;
-
-		List.iter (fun cf ->
-			cl.cl_overrides <- cf :: cl.cl_overrides
-		) cl.cl_ordered_fields
-
-	let implement_closure_cl ctx cl =
-		let pos = cl.cl_pos in
-		let gen = ctx.rcf_gen in
-		let basic = gen.gcon.basic in
-
-		let field_args, _ = field_type_args ctx pos in
-		let obj_arg = alloc_var "target" (TInst(ctx.rcf_object_iface, [])) in
-
-		let this_t = TInst(cl, List.map snd cl.cl_params) in
-		let this = { eexpr = TConst(TThis); etype = this_t ; epos = pos } in
-		let mk_this field t = { (mk_field_access gen this field pos) with etype = t } in
-
-		let tf_args = field_args @ [obj_arg, None] in
-		let cfs, ctor_body = List.fold_left (fun (acc_cf,acc_expr) (v,_) ->
-			let cf = mk_class_field v.v_name v.v_type false pos (Var { v_read = AccNormal; v_write = AccNormal } ) [] in
-			let expr = { eexpr = TBinop(Ast.OpAssign, mk_this v.v_name v.v_type, mk_local v pos); etype = v.v_type; epos = pos } in
-			(cf :: acc_cf, expr :: acc_expr)
-		) ([], [])	tf_args in
-
-		let map_fn arity ret vars api =
-			let this_obj = mk_this "target" (TInst(ctx.rcf_object_iface, [])) in
-
-			let rec loop i acc =
-				if i < 0 then
-					acc
-				else
-					let obj = api i t_dynamic None in
-					loop (i - 1) (obj :: acc)
-			in
-
-			let call_arg = if arity = (-1) then
-				api (-1) t_dynamic None
-			else if arity = 0 then
-				null (basic.tarray t_empty) pos
-			else
-				{ eexpr = TArrayDecl(loop (arity - 1) []); etype = basic.tarray t_empty; epos = pos }
-			in
-
-			let expr = {
-				eexpr = TCall(
-					mk_field_access gen this_obj (gen.gmk_internal_name "hx" "invokeField") pos,
-					(List.map (fun (v,_) -> mk_this v.v_name v.v_type) field_args) @ [ call_arg ]
-				);
-				etype = t_dynamic;
-				epos = pos
-			} in
-
-			let expr = if like_float ret && not (like_int ret) then mk_cast ret expr else expr in
-
-			mk_return expr
-		in
-
-		let all_cfs = List.filter (fun cf -> cf.cf_name <> "new" && match cf.cf_kind with Method _ -> true | _ -> false) (ctx.rcf_ft.map_base_classfields cl map_fn) in
-
-		List.iter (fun cf ->
-			cl.cl_overrides <- cf :: cl.cl_overrides
-		) all_cfs;
-		let all_cfs = cfs @ all_cfs in
-
-		cl.cl_ordered_fields <- cl.cl_ordered_fields @ all_cfs;
-		List.iter (fun cf ->
-			cl.cl_fields <- PMap.add cf.cf_name cf cl.cl_fields
-		) all_cfs;
-
-		let ctor_t = TFun(fun_args tf_args, basic.tvoid) in
-		let ctor_cf = mk_class_field "new" ctor_t true pos (Method MethNormal) [] in
-		ctor_cf.cf_expr <- Some {
-			eexpr = TFunction({
-				tf_args = tf_args;
-				tf_type = basic.tvoid;
-				tf_expr = { eexpr = TBlock({
-					eexpr = TCall({ eexpr = TConst(TSuper); etype = TInst(cl,[]); epos = pos }, [ExprBuilder.make_int ctx.rcf_gen.gcon (-1) pos; ExprBuilder.make_int ctx.rcf_gen.gcon (-1) pos]);
-					etype = basic.tvoid;
-					epos = pos
-				} :: ctor_body); etype = basic.tvoid; epos = pos }
-			});
-			etype = ctor_t;
-			epos = pos
-		};
-
-		cl.cl_constructor <- Some ctor_cf;
-
-		let closure_fun eclosure e field is_static =
-			let f = ExprBuilder.make_string gen.gcon field eclosure.epos in
-			let args = if ctx.rcf_optimize then [ f; { eexpr = TConst(TInt (hash_field_i32 ctx eclosure.epos field)); etype = basic.tint; epos = eclosure.epos } ] else [ f ] in
-			let args = args @ [ mk_cast (TInst(ctx.rcf_object_iface, [])) e ] in
-
-			{ eclosure with eexpr = TNew(cl,[],args) }
-		in
-		closure_fun
-
-	let get_closure_func ctx closure_cl =
-		let gen = ctx.rcf_gen in
-		let basic = gen.gcon.basic in
-		let closure_func eclosure e field is_static =
-			mk_cast eclosure.etype { eclosure with
-				eexpr = TNew(closure_cl, [], [
-					e;
-					ExprBuilder.make_string gen.gcon field eclosure.epos
-				] @ (
-					if ctx.rcf_optimize then [ { eexpr = TConst(TInt (hash_field_i32 ctx eclosure.epos field)); etype = basic.tint; epos = eclosure.epos } ] else []
-				));
-				etype = TInst(closure_cl,[])
-			}
-		in
-		closure_func
-
-	(*
-			main expr -> field expr -> field string -> possible set expr -> should_throw_exceptions -> changed expression
-
-			Changes a get / set
-		*
-		mutable rcf_on_getset_field : texpr->texpr->string->texpr option->bool->texpr;*)
-
-	let configure_dynamic_field_access ctx =
-		let gen = ctx.rcf_gen in
-		let is_dynamic expr fexpr field =
-			match (field_access_esp gen (gen.greal_type fexpr.etype) field) with
-			| FEnumField _
-			| FClassField _ -> false
-			| _ -> true
-		in
-
-		let maybe_hash = if ctx.rcf_optimize then fun str pos -> Some (hash_field_i32 ctx pos str) else fun str pos -> None in
-		DynamicFieldAccess.configure gen is_dynamic
-			(fun expr fexpr field set is_unsafe ->
-				let hash = maybe_hash field fexpr.epos in
-				ctx.rcf_on_getset_field expr fexpr field hash set is_unsafe
-			)
-			(fun ecall fexpr field call_list ->
-				let hash = maybe_hash field fexpr.epos in
-				ctx.rcf_on_call_field ecall fexpr field hash call_list
-			);
-		()
-
-
-	(* ******************************************* *)
-	(* UniversalBaseClass *)
-	(* ******************************************* *)
-	(*
-		Sets the universal base class for hxgen types (HxObject / IHxObject)
-
-		dependencies:
-			As a rule, it should be one of the last module filters to run so any @:hxgen class created in the process
-			-Should- only run after TypeParams.RealTypeParams.Modf
-	*)
-	module UniversalBaseClass =
-	struct
-		let name = "rcf_universal_base_class"
-		let priority = min_dep +. 10.
-
-		let configure gen baseclass baseinterface basedynamic =
-			let rec run md =
-				if is_hxgen md then
-					match md with
-					| TClassDecl ({ cl_interface = true } as cl) when cl.cl_path <> baseclass.cl_path && cl.cl_path <> baseinterface.cl_path && cl.cl_path <> basedynamic.cl_path ->
-						cl.cl_implements <- (baseinterface, []) :: cl.cl_implements
-					| TClassDecl ({ cl_kind = KAbstractImpl _ }) ->
-						(* don't add any base classes to abstract implementations *)
-						()
-					| TClassDecl ({ cl_super = None } as cl) when cl.cl_path <> baseclass.cl_path && cl.cl_path <> baseinterface.cl_path && cl.cl_path <> basedynamic.cl_path ->
-						if is_some cl.cl_dynamic then
-							cl.cl_super <- Some (basedynamic,[])
-						else
-							cl.cl_super <- Some (baseclass,[])
-					| TClassDecl ({ cl_super = Some(super,_) } as cl) when cl.cl_path <> baseclass.cl_path && cl.cl_path <> baseinterface.cl_path && not (is_hxgen (TClassDecl super)) ->
-						cl.cl_implements <- (baseinterface, []) :: cl.cl_implements
-					| _ ->
-						()
-			in
-			let map md = Some(run md; md) in
-			gen.gmodule_filters#add ~name:name ~priority:(PCustom priority) map
-	end;;
-
-
-	(*
-		Priority: must run AFTER UniversalBaseClass
-	*)
-	let priority = solve_deps name [DAfter UniversalBaseClass.priority]
-
-	let configure ?slow_invoke ctx baseinterface =
-		let gen = ctx.rcf_gen in
-		let run = (fun md -> match md with
-			| TClassDecl cl when is_hxgen md && ( not cl.cl_interface || cl.cl_path = baseinterface.cl_path ) && (match cl.cl_kind with KAbstractImpl _ -> false | _ -> true) ->
-				(implement_dynamics ctx cl);
-				(if not (PMap.mem (gen.gmk_internal_name "hx" "lookupField") cl.cl_fields) then implement_final_lookup ctx cl);
-				(if not (PMap.mem (gen.gmk_internal_name "hx" "getField") cl.cl_fields) then implement_get_set ctx cl);
-				(if not (PMap.mem (gen.gmk_internal_name "hx" "invokeField") cl.cl_fields) then implement_invokeField ctx ~slow_invoke:slow_invoke cl);
-				(if not (PMap.mem (gen.gmk_internal_name "hx" "getFields") cl.cl_fields) then implement_getFields ctx cl);
-				None
-			| _ -> None)
-		in
-
-		gen.gmodule_filters#add ~name:name ~priority:(PCustom priority) run
-
-end;;
-
-(* ******************************************* *)
-(* Object Declaration Mapper *)
-(* ******************************************* *)
-module ObjectDeclMap =
-struct
-	let name = "object_decl_map"
-	let priority = solve_deps name []
-
-	let configure gen map_fn =
-		let rec run e =
-			match e.eexpr with
-			| TObjectDecl odecl ->
-				let e = Type.map_expr run e in
-				(match e.eexpr with TObjectDecl odecl -> map_fn e odecl | _ -> assert false)
-			| _ ->
-				Type.map_expr run e
-		in
-		let map e = Some(run e) in
-		gen.gsyntax_filters#add ~name:name ~priority:(PCustom priority) map
-end;;
-
-
-(* ******************************************* *)
-(* EnumToClass *)
-(* ******************************************* *)
-(*
-	For languages that don't support parameterized enums and/or metadata in enums, we need to transform
-	enums into normal classes. This is done at the first module pass by creating new classes with the same
-	path inside the modules, and removing the actual enum module by setting it as en extern.
-
-	* The target must create its own strategy to deal with reflection. As it is right now, we will have a base class
-	which the class will extend, create @:$IsEnum metadata for the class, and create @:alias() metadatas for the fields,
-	with their tag order (as a string) as their alias. If you are using ReflectionCFs, then you don't have to worry
-	about that, as it's already generating all information needed by the haxe runtime.
-	so they can be
-*)
-module EnumToClass =
-struct
-	let name = "enum_to_class"
-	let priority = solve_deps name []
-
-	type t = {
-		ec_tbl : (path, tclass) Hashtbl.t;
-	}
-
-	let new_t () = {
-		ec_tbl = Hashtbl.create 10
-	}
-
-	(* ******************************************* *)
-	(* EnumToClassModf *)
-	(* ******************************************* *)
-	(*
-		The actual Module Filter that will transform the enum into a class
-
-		dependencies:
-			Should run before ReflectionCFs, in order to enable proper reflection access.
-			Should run before TypeParams.RealTypeParams.RealTypeParamsModf, since generic enums must be first converted to generic classes
-			It needs that the target platform implements __array__() as a shortcut to declare haxe.ds.Vector
-	*)
-	module EnumToClassModf =
-	struct
-		let name = "enum_to_class_mod"
-		let priority = solve_deps name [DBefore ReflectionCFs.priority; DBefore TypeParams.RealTypeParams.RealTypeParamsModf.priority]
-
-		let pmap_exists fn pmap = try PMap.iter (fun a b -> if fn a b then raise Exit) pmap; false with | Exit -> true
-
-		let has_any_meta en =
-			let has_meta meta = List.exists (fun (m,_,_) -> match m with Meta.Custom _ -> true | _ -> false) meta in
-			has_meta en.e_meta || pmap_exists (fun _ ef -> has_meta ef.ef_meta) en.e_constrs
-
-		let convert gen t base_class base_param_class en =
-			let handle_type_params = false in (* TODO: look into this *)
-			let basic = gen.gcon.basic in
-			let pos = en.e_pos in
-
-			(* create the class *)
-			let cl = mk_class en.e_module en.e_path pos in
-			Hashtbl.add t.ec_tbl en.e_path cl;
-
-			(match Codegen.build_metadata gen.gcon (TEnumDecl en) with
-				| Some expr ->
-					let cf = mk_class_field "__meta__" expr.etype false expr.epos (Var { v_read = AccNormal; v_write = AccNormal }) [] in
-					cf.cf_expr <- Some expr;
-					cl.cl_statics <- PMap.add "__meta__" cf cl.cl_statics;
-					cl.cl_ordered_statics <- cf :: cl.cl_ordered_statics
-				| _ -> ()
-			);
-
-			let super, has_params = if Meta.has Meta.FlatEnum en.e_meta then base_class, false else base_param_class, true in
-
-			cl.cl_super <- Some(super,[]);
-			cl.cl_extern <- en.e_extern;
-			en.e_meta <- (Meta.Class, [], pos) :: en.e_meta;
-			cl.cl_module <- en.e_module;
-			cl.cl_meta <- ( Meta.Enum, [], pos ) :: cl.cl_meta;
-
-			(match gen.gcon.platform with
-				| Cs when Common.defined gen.gcon Define.CoreApiSerialize ->
-					cl.cl_meta <- ( Meta.Meta, [ (EField( (EConst (Ident "System"), null_pos ), "Serializable" ), null_pos) ], null_pos ) :: cl.cl_meta
-				| _ -> ());
-			let c_types =
-				if handle_type_params then
-					List.map (fun (s,t) -> (s, TInst (map_param (get_cl_t t), []))) en.e_params
-				else
-					[]
-			in
-
-			cl.cl_params <- c_types;
-
-			let i = ref 0 in
-			let cfs = List.map (fun name ->
-				let ef = PMap.find name en.e_constrs in
-				let pos = ef.ef_pos in
-				let old_i = !i in
-				incr i;
-
-				let cf = match follow ef.ef_type with
-					| TFun(params,ret) ->
-						let dup_types =
-							if handle_type_params then
-								List.map (fun (s,t) -> (s, TInst (map_param (get_cl_t t), []))) en.e_params
-							else
-								[]
-						in
-
-						let ef_type =
-							let fn, types = if handle_type_params then snd, dup_types else (fun _ -> t_dynamic), en.e_params in
-							let t = apply_params en.e_params (List.map fn types) ef.ef_type in
-							apply_params ef.ef_params (List.map fn ef.ef_params) t
-						in
-
-						let params, ret = get_fun ef_type in
-						let cf_params = if handle_type_params then dup_types @ ef.ef_params else [] in
-
-						let cf = mk_class_field name ef_type true pos (Method MethNormal) cf_params in
-						cf.cf_meta <- [];
-
-						let tf_args = List.map (fun (name,opt,t) ->  (alloc_var name t, if opt then Some TNull else None) ) params in
-						let arr_decl = mk_nativearray_decl gen t_dynamic (List.map (fun (v,_) -> mk_local v pos) tf_args) pos in
-						let expr = {
-							eexpr = TFunction({
-								tf_args = tf_args;
-								tf_type = ret;
-								tf_expr = mk_block ( mk_return { eexpr = TNew(cl,List.map snd dup_types, [ExprBuilder.make_int gen.gcon old_i pos; arr_decl] ); etype = TInst(cl, List.map snd dup_types); epos = pos } );
-							});
-							etype = ef_type;
-							epos = pos
-						} in
-						cf.cf_expr <- Some expr;
-						cf
-					| _ ->
-						let actual_t = match follow ef.ef_type with
-							| TEnum(e, p) -> TEnum(e, List.map (fun _ -> t_dynamic) p)
-							| _ -> assert false
-						in
-						let cf = mk_class_field name actual_t true pos (Var { v_read = AccNormal; v_write = AccNever }) [] in
-						let args = if has_params then
-							[ExprBuilder.make_int gen.gcon old_i pos; null (gen.gclasses.nativearray t_dynamic) pos]
-						else
-							[ExprBuilder.make_int gen.gcon old_i pos]
-						in
-						cf.cf_meta <- [Meta.ReadOnly,[],pos];
-						cf.cf_expr <- Some {
-							eexpr = TNew(cl, List.map (fun _ -> t_empty) cl.cl_params, args);
-							etype = TInst(cl, List.map (fun _ -> t_empty) cl.cl_params);
-							epos = pos;
-						};
-						cf
-				in
-				cl.cl_statics <- PMap.add cf.cf_name cf cl.cl_statics;
-				cf
-			) en.e_names in
-			let constructs_cf = mk_class_field "__hx_constructs" (gen.gclasses.nativearray basic.tstring) true pos (Var { v_read = AccNormal; v_write = AccNever }) [] in
-			constructs_cf.cf_meta <- [Meta.ReadOnly,[],pos];
-			constructs_cf.cf_expr <- Some (mk_nativearray_decl gen basic.tstring (List.map (fun s -> { eexpr = TConst(TString s); etype = basic.tstring; epos = pos }) en.e_names) pos);
-
-			cl.cl_ordered_statics <- constructs_cf :: cfs @ cl.cl_ordered_statics ;
-			cl.cl_statics <- PMap.add "__hx_constructs" constructs_cf cl.cl_statics;
-
-			let getTag_cf_type = tfun [] basic.tstring in
-			let getTag_cf = mk_class_field "getTag" getTag_cf_type true pos (Method MethNormal) [] in
-			getTag_cf.cf_meta <- [(Meta.Final, [], pos)];
-			getTag_cf.cf_expr <- Some {
-				eexpr = TFunction {
-					tf_args = [];
-					tf_type = basic.tstring;
-					tf_expr = {
-						eexpr = TReturn (Some (
-							let e_constructs = mk_static_field_access_infer cl "__hx_constructs" pos [] in
-							let e_this = mk (TConst TThis) (TInst (cl,[])) pos in
-							let e_index = mk_field_access gen e_this "index" pos in
-							{
-								eexpr = TArray(e_constructs,e_index);
-								etype = basic.tstring;
-								epos = pos;
-							}
-						));
-						epos = pos;
-						etype = basic.tvoid;
-					}
-				};
-				etype = getTag_cf_type;
-				epos = pos;
-			};
-
-			cl.cl_ordered_fields <- getTag_cf :: cl.cl_ordered_fields ;
-			cl.cl_fields <- PMap.add "getTag" getTag_cf cl.cl_fields;
-			cl.cl_overrides <- getTag_cf :: cl.cl_overrides;
-			cl.cl_meta <- (Meta.NativeGen,[],cl.cl_pos) :: cl.cl_meta;
-			gen.gadd_to_module (TClassDecl cl) (max_dep);
-
-			TEnumDecl en
-
-		(*
-			traverse
-				gen - gen context
-				convert_all : bool - should we convert all enums? If set, convert_if_has_meta will be ignored.
-				convert_if_has_meta : bool - should we convert only if it has meta?
-				enum_base_class : tclass - the enum base class.
-				should_be_hxgen : bool - should the created enum be hxgen?
-		*)
-		let configure gen t convert_all convert_if_has_meta enum_base_class param_enum_class =
-			let convert e = convert gen t enum_base_class param_enum_class e in
-			let run md =
-				match md with
-				| TEnumDecl e when is_hxgen md ->
-					if convert_all then
-						convert e
-					else if convert_if_has_meta && has_any_meta e then
-						convert e
-					else if not (Meta.has Meta.FlatEnum e.e_meta) then
-						convert e
-					else begin
-						(* take off the :hxgen meta from it, if there's any *)
-						e.e_meta <- List.filter (fun (n,_,_) -> not (n = Meta.HxGen)) e.e_meta;
-						md
-					end
-				| _ ->
-					md
-			in
-			let map md = Some(run md) in
-			gen.gmodule_filters#add ~name:name ~priority:(PCustom priority) map
-	end;;
-
-	(* ******************************************* *)
-	(* EnumToClassExprf *)
-	(* ******************************************* *)
-	(*
-		Enum to class Expression Filter
-
-		dependencies:
-			Should run before TArrayTransform, since it generates array access expressions
-	*)
-	module EnumToClassExprf =
-	struct
-		let name = "enum_to_class_exprf"
-		let priority = solve_deps name [DBefore TArrayTransform.priority]
-
-		let configure gen t opt_get_native_enum_tag =
-			let rec run e =
-				let get_converted_enum_type et =
-					let en, eparams = match follow (gen.gfollow#run_f et) with
-						| TEnum(en,p) -> en, p
-						| _ -> raise Not_found
-					in
-					let cl = Hashtbl.find t.ec_tbl en.e_path in
-					TInst(cl, eparams)
-				in
-
-				match e.eexpr with
-				| TCall (({eexpr = TField(_, FStatic({cl_path=[],"Type"},{cf_name="enumIndex"}))} as left), [f]) ->
-					let f = run f in
-					(try
-						mk_field_access gen {f with etype = get_converted_enum_type f.etype} "index" e.epos
-					with Not_found ->
-						{ e with eexpr = TCall(left, [f]) })
-				| TEnumParameter(f, _,i) ->
-					let f = run f in
-					(* check if en was converted to class *)
-					(* if it was, switch on tag field and change cond type *)
-					let f = try
-						{ f with etype = get_converted_enum_type f.etype }
-					with Not_found ->
-						f
-					in
-					let cond_array = { (mk_field_access gen f "params" f.epos) with etype = gen.gclasses.nativearray t_dynamic } in
-					Codegen.index gen.gcon cond_array i e.etype e.epos
-				| _ ->
-					Type.map_expr run e
-			in
-			let map e = Some(run e) in
-			gen.gexpr_filters#add ~name:name ~priority:(PCustom priority) map
-
-	end;;
-
-	let configure gen opt_get_native_enum_tag convert_all convert_if_has_meta enum_base_class param_enum_class =
-		let t = new_t () in
-		EnumToClassModf.configure gen t convert_all convert_if_has_meta enum_base_class param_enum_class;
-		EnumToClassExprf.configure gen t opt_get_native_enum_tag
-end;;
-
-(* ******************************************* *)
-(* IteratorsInterface *)
-(* ******************************************* *)
-
-(*
-
-	This module will handle with Iterators, Iterables and TFor() expressions.
-	At first, a module filter will receive a Iterator<T> and Iterable<T> interface, which will be implemented
-	if hasNext(), next() or iterator() fields are detected with the correct type.
-	At this part a custom function will be called which can adequate the class fields so they are compatible with
-	native Iterators as well
-
-	The expression filter part of this module will look for TFor() expressions, and transform like that:
-	for (anInt in value.iterator())
-	{
-
-	}
-
-	{
-		var s:haxe.lang.Iterator<Int> = ExternalFunction.getIterator(value.iterator());
-		while (s.hasNext())
-		{
-			var anInt:Int = s.next();
-
-		}
-	}
-
-	dependencies:
-		None.
-
-*)
-
-module IteratorsInterface =
-struct
-	let name = "iterators_interface"
-	(* TODO later
-	(* ******************************************* *)
-	(* IteratorsInterfaceModf *)
-	(* ******************************************* *)
-	(*
-		The module filter for Iterators Interface, which will implement the iterator/iterable interface on each
-		class that conforms with the typedefs Iterator<> and Iterable<>
-
-		It's a very simple module and it will rely on cast detection to work correctly. This is so that
-		when the
-
-		dependencies:
-			Must run at the Module Filters, so cast detection can detect a cast to the interface and we can
-	*)
-	module IteratorsInterfaceModf =
-	struct
-		let name = "iterators_interface_modf"
-
-		let conforms_cfs has_next next =
-			try (match follow has_next.cf_type with
-				| TFun([],ret) when
-					(match follow ret with | TAbstract({ a_path = ([], "Bool") }, []) -> () | _ -> raise Not_found) ->
-						()
-				| _ -> raise Not_found);
-			(match follow next.cf_type with
-				| TFun([], ret) -> ret
-				| _ -> raise Not_found
-			)
-
-		let conforms_type_iterator t =
-			try match follow t with
-				| TInst(cl,params) ->
-						let has_next = PMap.find "hasNext" cl.cl_fields in
-						let next = PMap.find "next" cl.cl_fields in
-						Some (conforms_cfs has_next next)
-				| TAnon(anon) ->
-						let has_next = PMap.find "hasNext" anon.a_fields in
-						let next = PMap.find "next" anon.a_fields in
-						Some (conforms_cfs has_next next)
-				| _ -> None
-			with | Not_found -> None
-
-		let conforms_as_iterable cl =
-			try
-				let iterator = PMap.find "iterator" cl.cl_fields in
-				match follow iterator.cf_type with
-					| TFun([], ret) -> conforms_type_iterator ret
-					| _ -> None
-			with | Not_found -> None
-
-		let conforms_as_iterator cl =
-			try
-				let has_next = PMap.find "hasNext" cl.cl_fields in
-				let next = PMap.find "next" cl.cl_fields in
-				Some (conforms_cfs has_next next)
-			with | Not_found -> None
-
-		let priority = solve_deps name []
-
-		let traverse gen iterator_iface iterable_iface on_found_iterator on_found_iterable =
-			let rec run md =
-				match md with
-					| TClassDecl cl when not cl.cl_extern && is_hxgen cl ->
-						let conforms_iterator = conforms_as_iterator cl in
-						let conforms_iterable = conforms_as_iterable cl in
-						if is_some conforms_iterator then begin
-							let it_t = get conforms_iterator in
-							cl.cl_interfaces <- (iterator_iface, [it_t]);
-							on_found_iterator cl
-						end;
-						if is_some conforms_iterable then begin
-							let it_t = get conforms_iterable in
-							cl.cl_interfaces <- (iterable_iface, [it_t]);
-							on_found_iterable cl
-						end;
-
-						md
-					| _ -> md
-			in
-			run
-
-		let configure gen (mapping_func:texpr->texpr) =
-			let map e = Some(mapping_func e) in
-			gen.gexpr_filters#add ~name:name ~priority:(PCustom priority) map
-
-	end;;
-	*)
-
-	(* ******************************************* *)
-	(* IteratorsInterfaceExprf *)
-	(* ******************************************* *)
-	(*
-		The expression filter for Iterators. Will look for TFor, transform it into
-		{
-			var iterator = // in expression here
-			while (iterator.hasNext())
-			{
-				var varName = iterator.next();
-			}
-		}
-
-		dependencies:
-			Must run before Dynamic fields access is run
-
-		TODO: I think TFor is always rewritten to TWhile before getting into the generator nowadays,
-		so this filter could probably be removed. Gotta ask Simon about it.
-	*)
-	module IteratorsInterfaceExprf =
-	struct
-		let name = "iterators_interface_exprf"
-		let priority = solve_deps name [DBefore DynamicFieldAccess.priority]
-
-		let mk_access gen v name pos =
-			let field_t =
-				try match follow v.v_type with
-					| TInst(cl, params) ->
-						let field = PMap.find name cl.cl_fields in
-						apply_params cl.cl_params params field.cf_type
-					| TAnon(anon) ->
-						let field = PMap.find name anon.a_fields in
-						field.cf_type
-					| _ -> t_dynamic
-				with | Not_found -> t_dynamic
-			in
-			{ (mk_field_access gen (mk_local v pos) name pos) with etype = field_t }
-
-		let configure gen =
-			let basic = gen.gcon.basic in
-			let rec run e =
-				match e.eexpr with
-				| TFor(var, in_expr, block) ->
-					let in_expr = run in_expr in
-					let temp = mk_temp gen "iterator" in_expr.etype in
-					let block = [
-						{ eexpr = TVar(temp, Some(in_expr)); etype = basic.tvoid; epos = in_expr.epos };
-						{
-							eexpr = TWhile(
-								{ eexpr = TCall(mk_access gen temp "hasNext" in_expr.epos, []); etype = basic.tbool; epos = in_expr.epos },
-								Type.concat ({
-									eexpr = TVar(var, Some({ eexpr = TCall(mk_access gen temp "next" in_expr.epos, []); etype = var.v_type; epos = in_expr.epos }));
-									etype = basic.tvoid;
-									epos = in_expr.epos
-								}) ( run block ),
-								Ast.NormalWhile);
-							etype = basic.tvoid;
-							epos = e.epos
-						}
-					] in
-					{ eexpr = TBlock(block); etype = e.etype; epos = e.epos }
-				| _ ->
-					Type.map_expr run e
-			in
-			let map e = Some(run e) in
-			gen.gexpr_filters#add ~name:name ~priority:(PCustom priority) map
-	end;;
-
-	let configure gen =
-		IteratorsInterfaceExprf.configure gen
-
-end;;
-
-
-(* ******************************************* *)
-(* SwitchToIf *)
-(* ******************************************* *)
-(*
-	Just a syntax filter which changes switch expressions to if() else if() else if() ...
-*)
-module SwitchToIf =
-struct
-	let name = "switch_to_if"
-	let priority = solve_deps name []
-
-	let rec simplify_expr e = match e.eexpr with
-		| TParenthesis e
-		| TMeta(_,e) -> simplify_expr e
-		| _ -> e
-
-	let configure gen (should_convert:texpr->bool) =
-		let basic = gen.gcon.basic in
-		let rec run e =
-			match e.eexpr with
-				| TSwitch(cond,cases,default) when should_convert e ->
-					let cond_etype, should_cache = match gen.gfollow#run_f cond.etype with
-						| TType({ t_path = ([], "Null") }, [t]) ->
-							let rec take_off_nullable t = match gen.gfollow#run_f t with
-								| TType({ t_path = ([], "Null") }, [t]) -> take_off_nullable t
-								| _ -> t
-							in
-
-							take_off_nullable t, true
-						| _ -> cond.etype, false
-					in
-
-					if should_cache && not (should_convert { e with eexpr = TSwitch({ cond with etype = cond_etype }, cases, default) }) then begin
-						{ e with eexpr = TSwitch(mk_cast cond_etype (run cond), List.map (fun (cs,e) -> (List.map run cs, run e)) cases, Option.map run default) }
-					end else begin
-						let local, fst_block = match cond.eexpr, should_cache with
-							| TLocal _, false -> cond, []
-							| _ ->
-								let var = mk_temp gen "switch" cond_etype in
-								let cond = run cond in
-								let cond = if should_cache then mk_cast cond_etype cond else cond in
-
-								mk_local var cond.epos, [ { eexpr = TVar(var,Some(cond)); etype = basic.tvoid; epos = cond.epos } ]
-						in
-
-						let mk_eq cond =
-							{ eexpr = TBinop(Ast.OpEq, local, cond); etype = basic.tbool; epos = cond.epos }
-						in
-
-						let rec mk_many_cond conds =
-							match conds with
-								| cond :: [] ->
-									mk_eq cond
-								| cond :: tl ->
-									{ eexpr = TBinop(Ast.OpBoolOr, mk_eq (run cond), mk_many_cond tl); etype = basic.tbool; epos = cond.epos }
-								| [] -> assert false
-						in
-
-						let mk_many_cond conds =
-							let ret = mk_many_cond conds in
-							(*
-								this might be considered a hack. But since we're on a syntax filter and
-								the condition is guaranteed to not have run twice, we can really run the
-								expr filters again for it (so to change e.g. OpEq accordingly
-							*)
-							gen.gexpr_filters#run_f ret
-						in
-
-						let rec loop cases = match cases with
-							| (conds,e) :: [] ->
-								{ eexpr = TIf(mk_many_cond conds, run e, Option.map run default); etype = e.etype; epos = e.epos }
-							| (conds,e) :: tl ->
-								{ eexpr = TIf(mk_many_cond conds, run e, Some(loop tl)); etype = e.etype; epos = e.epos }
-							| [] -> match default with
-								| None ->
-									raise Exit
-								| Some d -> run d
-						in
-
-						try
-							{ e with eexpr = TBlock(fst_block @ [loop cases]) }
-						with | Exit ->
-							{ e with eexpr = TBlock [] }
-					end
-				| TSwitch(cond,cases,default) -> (try
-					match (simplify_expr cond).eexpr with
-						| TCall( { eexpr = TField(_,FStatic({ cl_path = [],"Type" }, { cf_name = "enumIndex" })) }, [enum] ) ->
-							let real_enum = match enum.etype with
-								| TEnum(e,_) -> e
-								| _ -> raise Not_found
-							in
-							if Meta.has Meta.Class real_enum.e_meta then raise Not_found;
-							let enum_expr = mk_mt_access (TEnumDecl(real_enum)) e.epos in
-							let fields = Hashtbl.create (List.length real_enum.e_names) in
-							PMap.iter (fun _ ef -> Hashtbl.add fields ef.ef_index ef) real_enum.e_constrs;
-							let cases = List.map (fun (el,e) ->
-								List.map (fun e -> match e.eexpr with
-								| TConst(TInt i) ->
-									let ef = Hashtbl.find fields (Int32.to_int i) in
-									{ e with eexpr = TField(enum_expr, FEnum(real_enum,ef)); etype = TEnum(real_enum,List.map (fun _ -> t_dynamic) real_enum.e_params) }
-								| _ -> raise Not_found) el, run e
-							) cases in
-							{ e with eexpr = TSwitch(enum,cases,Option.map run default) }
-						| _ -> raise Not_found
-					with Not_found -> Type.map_expr run e)
-				| _ -> Type.map_expr run e
-		in
-		let map e = Some(run e) in
-		gen.gsyntax_filters#add ~name:name ~priority:(PCustom priority) map
-end;;
-
-
-(* ******************************************* *)
-(* Anonymous Class object handling *)
-(* ******************************************* *)
-(*
-	(syntax)
-	When we pass a class as an object, in some languages we will need a special construct to be able to
-	access its statics as if they were normal object fields. On C# and Java the way found to do that is
-	by handling statics reflection also by a normal instance. This also happens in hxcpp and neko, so I
-	guess it's a valid practice.
-	So if we want to handle the reflection of the static MyClass, here's roughly how it will be done:
-
-	var x = MyClass;
-	gets converted into
-	Haxe.Lang.Class x = Haxe.Lang.Runtime.GetType(typeof(MyClass).RuntimeHandle);
-
-	which will in turn look in its cache but roughly would do:
-	Haxe.Lang.Class x = new Haxe.Lang.Class(new MyClass(EmptyObject.EMPTY));
-
-	This module will of course let the caller choose how this will be implemented. It will just identify all
-	uses of class that will require it to be cast as an object.
-*)
-module ClassInstance =
-struct
-	let priority = solve_deps "class_instance" []
-
-	let configure gen (change_expr:texpr->module_type->texpr) =
-		let rec run e =
-			match e.eexpr with
-					| TCall( ({ eexpr = TLocal({ v_name = ("__is__" | "__as__" | "__typeof__") } as v) } as local), calls ) when Hashtbl.mem gen.gspecial_vars v.v_name ->
-						{ e with eexpr = TCall(local, List.map (fun e ->
-							match e.eexpr with
-							| TTypeExpr _ -> e
-							| _ -> run e) calls) }
-					| TField({ eexpr = TTypeExpr(mt) }, f) ->
-							e
-					| TField(ef, f) ->
-						(match anon_class ef.etype with
-							| None -> Type.map_expr run e
-							| Some t ->
-								{ e with eexpr = TField( { ef with eexpr = TTypeExpr(t) }, f) }
-						)
-					| TTypeExpr(mt) -> change_expr e mt
-					| _ -> Type.map_expr run e
-		in
-		let map e = Some(run e) in
-		gen.gsyntax_filters#add ~name:"class_instance" ~priority:(PCustom priority) map
-end;;
-
-(* ******************************************* *)
-(* HardNullableSynf *)
-(* ******************************************* *)
-(*
-	This module will handle Null<T> types for languages that offer a way of dealing with
-	stack-allocated structures or tuples and generics. Essentialy on those targets a Null<T>
-	will be a tuple ( 'a * bool ), where bool is whether the value is null or not.
-
-	At first (configure-time), we will modify the follow function so it can follow correctly nested Null<Null<T>>,
-	and do not follow Null<T> to its underlying type
-
-	Then we will run a syntax filter, which will look for casts to Null<T> and replace them by
-	a call to the new Null<T> creation;
-	Also casts from Null<T> to T or direct uses of Null<T> (call, field access, array access, closure)
-	will result in the actual value being accessed
-	For compatibility with the C# target, HardNullable will accept both Null<T> and haxe.lang.Null<T> types
-
-	dependencies:
-		Needs to be run after all cast detection modules
-*)
-module HardNullableSynf =
-struct
-	let name = "hard_nullable"
-	let priority = solve_deps name [DAfter CastDetect.ReturnCast.priority]
-
-	let rec is_null_t gen t = match gen.greal_type t with
-		| TType( { t_path = ([], "Null") }, [of_t])
-		| TInst( { cl_path = (["haxe";"lang"], "Null") }, [of_t]) ->
-			let rec take_off_null t =
-				match is_null_t gen t with | None -> t | Some s -> take_off_null s
-			in
-
-			Some (take_off_null of_t)
-		| TMono r -> (match !r with | Some t -> is_null_t gen t | None -> None)
-		| TLazy f -> is_null_t gen (!f())
-		| TType (t, tl) ->
-			is_null_t gen (apply_params t.t_params tl t.t_type)
-		| _ -> None
-
-	let follow_addon gen t =
-		let rec strip_off_nullable t =
-			let t = gen.gfollow#run_f t in
-			match t with
-				(* haxe.lang.Null<haxe.lang.Null<>> wouldn't be a valid construct, so only follow Null<> *)
-				| TType ( { t_path = ([], "Null") }, [of_t] ) -> strip_off_nullable of_t
-				| _ -> t
-		in
-
-		match t with
-			| TType( ({ t_path = ([], "Null") } as tdef), [of_t]) ->
-				Some( TType(tdef, [ strip_off_nullable of_t ]) )
-			| _ -> None
-
-	let configure gen unwrap_null wrap_val null_to_dynamic has_value opeq_handler =
-		gen.gfollow#add ~name:(name ^ "_follow") (follow_addon gen);
-
-		let is_null_t = is_null_t gen in
-		let is_string t = match gen.greal_type t with
-			| TInst({ cl_path=([],"String") },_) -> true
-			| _ -> false
-		in
-		let handle_unwrap to_t e =
-			let e_null_t = get (is_null_t e.etype) in
-			match gen.greal_type to_t with
-				| TDynamic _ | TMono _ | TAnon _ ->
-					(match e_null_t with
-						| TDynamic _ | TMono _ | TAnon _ ->
-							gen.ghandle_cast to_t e_null_t (unwrap_null e)
-						| _ -> null_to_dynamic e
-					)
-				| _ ->
-					gen.ghandle_cast to_t e_null_t (unwrap_null e)
-		in
-
-		let handle_wrap e t =
-			match e.eexpr with
-				| TConst(TNull) ->
-					wrap_val e t false
-				| _ ->
-					wrap_val e t true
-		in
-
-		let cur_block = ref [] in
-		let add_tmp v e p =
-			cur_block := { eexpr = TVar(v,e); etype = gen.gcon.basic.tvoid; epos = p } :: !cur_block
-		in
-		let get_local e = match e.eexpr with
-			| TLocal _ ->
-				e, e
-			| _ ->
-				let v = mk_temp gen "nulltmp" e.etype in
-				add_tmp v (Some (null e.etype e.epos)) e.epos;
-				let local = { e with eexpr = TLocal(v) } in
-				mk_paren { e with eexpr = TBinop(Ast.OpAssign, local, e) }, local
-		in
-		let rec run e =
-			match e.eexpr with
-				| TBlock(bl) ->
-					let lst = !cur_block in
-					cur_block := [];
-					List.iter (fun e ->
-						let e = run e in
-						cur_block := (e :: !cur_block)
-					) bl;
-					let ret = !cur_block in
-					cur_block := lst;
-					{ e with eexpr = TBlock(List.rev ret) }
-				| TCast(v, _) ->
-					let null_et = is_null_t e.etype in
-					let null_vt = is_null_t v.etype in
-					(match null_vt, null_et with
-						| Some(vt), None when is_string e.etype ->
-							let v = run v in
-							{ e with eexpr = TCast(null_to_dynamic v,None) }
-						| Some(vt), None ->
-							(match v.eexpr with
-								(* is there an unnecessary cast to Nullable? *)
-								| TCast(v2, _) ->
-									run { v with etype = e.etype }
-								| _ ->
-									handle_unwrap e.etype (run v)
-							)
-						| None, Some(et) ->
-							handle_wrap (run v) et
-						| Some(vt), Some(et) when not (type_iseq (run_follow gen vt) (run_follow gen et)) ->
-							(* check if has value and convert *)
-							let vlocal_fst, vlocal = get_local (run v) in
-							{
-								eexpr = TIf(
-									has_value vlocal_fst,
-									handle_wrap (mk_cast et (unwrap_null vlocal)) et,
-									Some( handle_wrap (null et e.epos) et ));
-								etype = e.etype;
-								epos = e.epos
-							}
-						| _ ->
-							Type.map_expr run e
-					)
-				| TField(ef, field) when is_some (is_null_t ef.etype) ->
-					let to_t = get (is_null_t ef.etype) in
-					{ e with eexpr = TField(handle_unwrap to_t (run ef), field) }
-				| TCall(ecall, params) when is_some (is_null_t ecall.etype) ->
-					let to_t = get (is_null_t ecall.etype) in
-					{ e with eexpr = TCall(handle_unwrap to_t (run ecall), List.map run params) }
-				| TArray(earray, p) when is_some (is_null_t earray.etype) ->
-					let to_t = get (is_null_t earray.etype) in
-					{ e with eexpr = TArray(handle_unwrap to_t (run earray), p) }
-				| TBinop(op, e1, e2) ->
-					let e1_t = is_null_t e1.etype in
-					let e2_t = is_null_t e2.etype in
-
-					(match op with
-						| Ast.OpAssign
-						| Ast.OpAssignOp _ ->
-							(match e1_t, e2_t with
-								| Some t1, Some t2 ->
-									(match op with
-										| Ast.OpAssign ->
-											Type.map_expr run e
-										| Ast.OpAssignOp op ->
-											(match e1.eexpr with
-												| TLocal _ ->
-													{ e with eexpr = TBinop( Ast.OpAssign, e1, handle_wrap { e with eexpr = TBinop (op, handle_unwrap t1 e1, handle_unwrap t2 (run e2) ) } t1 ) }
-												| _ ->
-													let v, e1, evars = match e1.eexpr with
-														| TField(ef, f) ->
-															let v = mk_temp gen "nullbinop" ef.etype in
-															v, { e1 with eexpr = TField(mk_local v ef.epos, f) }, ef
-														| _ ->
-															let v = mk_temp gen "nullbinop" e1.etype in
-															v, mk_local v e1.epos, e1
-													in
-													{ e with eexpr = TBlock([
-														{ eexpr = TVar(v, Some evars); etype = gen.gcon.basic.tvoid; epos = e.epos };
-														{ e with eexpr = TBinop( Ast.OpAssign, e1, handle_wrap { e with eexpr = TBinop (op, handle_unwrap t1 e1, handle_unwrap t2 (run e2) ) } t1 ) }
-													]) }
-											)
-										| _ -> assert false
-									)
-
-								| _ ->
-									Type.map_expr run e (* casts are already dealt with normal CastDetection module *)
-							)
-						| Ast.OpEq | Ast.OpNotEq ->
-							(match e1.eexpr, e2.eexpr with
-								| TConst(TNull), _ when is_some e2_t ->
-									let e = has_value (run e2) in
-									if op = Ast.OpEq then
-										{ e with eexpr = TUnop(Ast.Not, Ast.Prefix, e) }
-									else
-										e
-								| _, TConst(TNull) when is_some e1_t ->
-									let e = has_value (run e1) in
-									if op = Ast.OpEq then
-										{ e with eexpr = TUnop(Ast.Not, Ast.Prefix, e) }
-									else
-										e
-								| _ when is_some e1_t || is_some e2_t ->
-										let e1, e2 =
-											if not (is_some e1_t) then
-												run e2, handle_wrap (run e1) (get e2_t)
-											else if not (is_some e2_t) then
-												run e1, handle_wrap (run e2) (get e1_t)
-											else
-												run e1, run e2
-										in
-										let e = opeq_handler e1 e2 in
-										if op = Ast.OpEq then
-											{ e with eexpr = TUnop(Ast.Not, Ast.Prefix, e) }
-										else
-											e
-								| _ ->
-									Type.map_expr run e
-							)
-						| Ast.OpAdd when is_string e1.etype || is_string e2.etype ->
-							let e1 = if is_some e1_t then
-								null_to_dynamic (run e1)
-							else
-								run e1
-							in
-							let e2 = if is_some e2_t then
-								null_to_dynamic (run e2)
-							else
-								run e2
-							in
-							let e_t = is_null_t e.etype in
-							if is_some e_t then
-								wrap_val { eexpr = TBinop(op,e1,e2); etype = get e_t; epos = e.epos } (get e_t) true
-							else
-								{ e with eexpr = TBinop(op,e1,e2) }
-						| _ ->
-							let e1 = if is_some e1_t then
-								handle_unwrap (get e1_t) (run e1)
-							else run e1 in
-							let e2 = if is_some e2_t then
-								handle_unwrap (get e2_t) (run e2)
-							else
-								run e2 in
-
-							(* if it is Null<T>, we need to convert the result again to null *)
-							let e_t = (is_null_t e.etype) in
-							if is_some e_t then
-								wrap_val { eexpr = TBinop(op, e1, e2); etype = get e_t; epos = e.epos } (get e_t) true
-							else
-								{ e with eexpr = TBinop(op, e1, e2) }
-					)
-				(*| TUnop( (Ast.Increment as op)*)
-				| _ -> Type.map_expr run e
-		in
-		let run e = match e.eexpr with
-			| TFunction tf ->
-				run { e with eexpr = TFunction { tf with tf_expr = mk_block tf.tf_expr } }
-			| TBlock _ ->
-				run e
-			| _ -> match run (mk_block e) with
-				| { eexpr = TBlock([e]) } -> e
-				| e -> e
-		in
-		let map e = Some(run e) in
-		gen.gsyntax_filters#add ~name:name ~priority:(PCustom priority) map
-
-end;;
-
-(* ******************************************* *)
-(* ArrayDeclSynf *)
-(* ******************************************* *)
-(*
-	A syntax filter that will change array declarations to the actual native array declarations plus
-	the haxe array initialization
-
-	dependencies:
-		Must run after ObjectDeclMap since it can add TArrayDecl expressions
-*)
-module ArrayDeclSynf =
-struct
-	let name = "array_decl_synf"
-	let priority = solve_deps name [DAfter ObjectDeclMap.priority]
-
-	let configure gen native_array_cl =
-		let rec run e =
-			match e.eexpr with
-			| TArrayDecl el ->
-				let cl, params = match follow e.etype with
-					| TInst(({ cl_path = ([], "Array") } as cl), ( _ :: _  as params)) -> cl, params
-					| TInst(({ cl_path = ([], "Array") } as cl), []) -> cl, [t_dynamic]
-					| _ -> assert false
-				in
-				let changed_params = gen.greal_type_param (TClassDecl cl) params in
-				{ e with eexpr = TNew(cl, changed_params, [ { e with eexpr = TArrayDecl(List.map run el); etype = TInst(native_array_cl, changed_params) } ]	); }
-			| _ -> Type.map_expr run e
-		in
-		let map e = Some(run e) in
-		gen.gsyntax_filters#add ~name:name ~priority:(PCustom priority) map
-end;;
-
-
-(* ******************************************* *)
-(* SwitchBreakSynf *)
-(* ******************************************* *)
-(*
-	In most languages, 'break' is used as a statement also to break from switch statements.
-	This generates an incompatibility with haxe code, as we can use break to break from loops from inside a switch
-
-	This script will detect 'breaks' inside switch statements, and will offer the opportunity to change both
-	when this pattern is found.
-
-	Some options are possible:
-		On languages that support goto, 'break' may mean goto " after the loop ". There also can be special labels for
-			loops, so you can write "break label" (javascript, java, d)
-		On languages that do not support goto, a custom solution must be enforced
-
-	dependencies:
-		Since UnreachableCodeElimination must run before it, and Unreachable should be one of the
-		very last filters to run, we will make a fixed value which runs after UnreachableCodeElimination
-		(meaning: it's the very last filter)
-*)
-module SwitchBreakSynf =
-struct
-	let name = "switch_break_synf"
-	let priority = min_dep -. 150.0
-
-	type add_to_block_api = texpr->bool->unit
-
-	let configure gen (change_loop:texpr->int->add_to_block_api->texpr) (change_break:texpr->int->add_to_block_api->texpr) =
-		let in_switch = ref false in
-		let cur_block = ref [] in
-		let to_add = ref [] in
-		let did_found = ref (-1) in
-
-		let api expr before =
-			if before then cur_block := expr :: !cur_block else to_add := expr :: !to_add
-		in
-		let num = ref 0 in
-		let cur_num = ref 0 in
-
-		let rec run e =
-			match e.eexpr with
-			| TFunction _ ->
-				let old_num = !num in
-				num := 0;
-					let ret = Type.map_expr run e in
-				num := old_num;
-				ret
-			| TFor _
-			| TWhile _ ->
-				let last_switch = !in_switch in
-				let last_found = !did_found in
-				let last_num = !cur_num in
-				in_switch := false;
-				incr num;
-				cur_num := !num;
-				did_found := -1;
-					let new_e = Type.map_expr run e in (* assuming that no loop will be found in the condition *)
-					let new_e = if !did_found <> -1 then change_loop new_e !did_found api else new_e in
-				did_found := last_found;
-				in_switch := last_switch;
-				cur_num := last_num;
-
-				new_e
-			| TSwitch _ ->
-				let last_switch = !in_switch in
-				in_switch := true;
-
-					let new_e = Type.map_expr run e in
-
-				in_switch := last_switch;
-				new_e
-			| TBlock bl ->
-				let last_block = !cur_block in
-				let last_toadd = !to_add in
-				to_add := [];
-				cur_block := [];
-
-					List.iter (fun e ->
-						let new_e = run e in
-						cur_block := new_e :: !cur_block;
-						match !to_add with
-							| [] -> ()
-							| _ -> cur_block := !to_add @ !cur_block; to_add := []
-					) bl;
-
-				let ret = List.rev !cur_block in
-				cur_block := last_block;
-				to_add := last_toadd;
-
-				{ e with eexpr = TBlock(ret) }
-			| TBreak ->
-				if !in_switch then (did_found := !cur_num; change_break e !cur_num api) else e
-			| _ -> Type.map_expr run e
-		in
-		let map e = Some(run e) in
-		gen.gsyntax_filters#add ~name:name ~priority:(PCustom priority) map
-end;;
-
-
-(* ******************************************* *)
-(* Unreachable Code Elimination *)
-(* ******************************************* *)
-(*
-	In some source code platforms, the code won't compile if there is Unreachable code, so this filter will take off any unreachable code.
-		If the parameter "handle_switch_break" is set to true, it will already add a "break" statement on switch cases when suitable;
-			in order to not confuse with while break, it will be a special expression __sbreak__
-		If the parameter "handle_not_final_returns" is set to true, it will also add final returns when functions are detected to be lacking of them.
-			(Will respect __fallback__ expressions)
-		If the parameter "java_mode" is set to true, some additional checks following the java unreachable specs
-			(http://docs.oracle.com/javase/specs/jls/se7/html/jls-14.html#jls-14.21) will be added
-
-	dependencies:
-		This must run before SwitchBreakSynf (see SwitchBreakSynf dependecy value)
-		This must be the LAST syntax filter to run. It expects ExpressionUnwrap to have run correctly, since this will only work for source-code based targets
-*)
-module UnreachableCodeEliminationSynf =
-struct
-
-	let name = "unreachable_synf"
-
-	let priority = min_dep -. 100.0
-
-	type uexpr_kind =
-		| Normal
-		| BreaksLoop
-		| BreaksFunction
-
-	let aggregate_kind e1 e2 =
-		match e1, e2 with
-			| Normal, _
-			| _, Normal -> Normal
-			| BreaksLoop, _
-			| _, BreaksLoop -> BreaksLoop
-			| BreaksFunction, BreaksFunction -> BreaksFunction
-
-	let aggregate_constant op c1 c2=
-		match op, c1, c2 with
-			| OpEq, Some v1, Some v2 -> Some (TBool (v1 = v2))
-			| OpNotEq, Some v1, Some v2 -> Some (TBool (v1 <> v2))
-			| OpBoolOr, Some (TBool v1) , Some (TBool v2) -> Some (TBool (v1 || v2))
-			| OpBoolAnd, Some (TBool v1) , Some (TBool v2) -> Some (TBool (v1 && v2))
-			| OpAssign, _, Some v2 -> Some v2
-			| _ -> None
-
-	let rec get_constant_expr e =
-		match e.eexpr with
-			| TConst (v) -> Some v
-			| TBinop(op, v1, v2) -> aggregate_constant op (get_constant_expr v1) (get_constant_expr v2)
-			| TParenthesis(e) | TMeta(_,e) -> get_constant_expr e
-			| _ -> None
-
-	let traverse gen java_mode =
-		let basic = gen.gcon.basic in
-		let should_warn = false in
-
-		let do_warn =
-			if should_warn then gen.gcon.warning "Unreachable code" else (fun pos -> ())
-		in
-
-		let return_loop expr kind =
-			match kind with
-				| Normal | BreaksLoop -> expr, Normal
-				| _ -> expr, kind
-		in
-
-		let sbreak = alloc_var "__sbreak__" t_dynamic in
-		let mk_sbreak = mk_local sbreak in
-
-		let rec has_fallback expr = match expr.eexpr with
-			| TBlock(bl) -> (match List.rev bl with
-				| { eexpr = TLocal { v_name = "__fallback__" } } :: _ -> true
-				| ({ eexpr = TBlock(_) } as bl) :: _ -> has_fallback bl
-				| _ -> false)
-			| TLocal { v_name = "__fallback__" } -> true
-			| _ -> false
-		in
-
-		let handle_case = fun (expr,kind) ->
-			match kind with
-			| Normal when has_fallback expr -> expr
-			| Normal -> Type.concat expr (mk_sbreak expr.epos)
-			| BreaksLoop | BreaksFunction -> expr
-		in
-
-		let has_break = ref false in
-
-		let rec process_expr expr =
-			match expr.eexpr with
-				| TReturn _ | TThrow _ -> expr, BreaksFunction
-				| TContinue -> expr, BreaksLoop
-				| TBreak -> has_break := true; expr, BreaksLoop
-				| TCall( { eexpr = TLocal { v_name = "__goto__" } }, _ ) -> expr, BreaksLoop
-
-				| TBlock bl ->
-					let new_block = ref [] in
-					let is_unreachable = ref false in
-					let ret_kind = ref Normal in
-
-					List.iter (fun e ->
-						if !is_unreachable then
-							do_warn e.epos
-						else begin
-							let changed_e, kind = process_expr e in
-							new_block := changed_e :: !new_block;
-							match kind with
-								| BreaksLoop | BreaksFunction ->
-									ret_kind := kind;
-									is_unreachable := true
-								| _ -> ()
-						end
-					) bl;
-
-					{ expr with eexpr = TBlock(List.rev !new_block) }, !ret_kind
-				| TFunction tf ->
-					let changed, kind = process_expr tf.tf_expr in
-					let changed = if not (ExtType.is_void tf.tf_type) && kind <> BreaksFunction then
-						Type.concat changed { eexpr = TReturn( Some (null tf.tf_type expr.epos) ); etype = basic.tvoid; epos = expr.epos }
-					else
-						changed
-					in
-
-					{ expr with eexpr = TFunction({ tf with tf_expr = changed }) }, Normal
-				| TFor(var, cond, block) ->
-					let last_has_break = !has_break in
-					has_break := false;
-
-					let changed_block, _ = process_expr block in
-					has_break := last_has_break;
-					let expr = { expr with eexpr = TFor(var, cond, changed_block) } in
-					return_loop expr Normal
-				| TIf(cond, eif, None) ->
-					if java_mode then
-						match get_constant_expr cond with
-							| Some (TBool true) ->
-								process_expr eif
-							| _ ->
-								{ expr with eexpr = TIf(cond, fst (process_expr eif), None) }, Normal
-					else
-						{ expr with eexpr = TIf(cond, fst (process_expr eif), None) }, Normal
-				| TIf(cond, eif, Some eelse) ->
-					let eif, eif_k = process_expr eif in
-					let eelse, eelse_k = process_expr eelse in
-					let k = aggregate_kind eif_k eelse_k in
-					{ expr with eexpr = TIf(cond, eif, Some eelse) }, k
-				| TWhile(cond, block, flag) ->
-					let last_has_break = !has_break in
-					has_break := false;
-
-					let block, k = process_expr block in
-					if java_mode then
-						match get_constant_expr cond, flag, !has_break with
-							| Some (TBool true), _, false ->
-								has_break := last_has_break;
-								{ expr with eexpr = TWhile(cond, block, flag) }, BreaksFunction
-							| Some (TBool false), NormalWhile, _ ->
-								has_break := last_has_break;
-								do_warn expr.epos;
-								null expr.etype expr.epos, Normal
-							| _ ->
-								has_break := last_has_break;
-								return_loop { expr with eexpr = TWhile(cond,block,flag) } Normal
-					else begin
-						has_break := last_has_break;
-						return_loop { expr with eexpr = TWhile(cond,block,flag) } Normal
-					end
-				| TSwitch(cond, el_e_l, None) ->
-					{ expr with eexpr = TSwitch(cond, List.map (fun (el, e) -> (el, handle_case (process_expr e))) el_e_l, None) }, Normal
-				| TSwitch(cond, el_e_l, Some def) ->
-					let def, k = process_expr def in
-					let def = handle_case (def, k) in
-					let k = ref k in
-					let ret = { expr with eexpr = TSwitch(cond, List.map (fun (el, e) ->
-						let e, ek = process_expr e in
-						k := aggregate_kind !k ek;
-						(el, handle_case (e, ek))
-					) el_e_l, Some def) } in
-					ret, !k
-				| TTry (e, catches) ->
-					let e, k = process_expr e in
-					let k = ref k in
-					let ret = { expr with eexpr = TTry(e, List.map (fun (v, e) ->
-						let e, ek = process_expr e in
-						k := aggregate_kind !k ek;
-						(v, e)
-					) catches) } in
-					ret, !k
-				| _ -> expr, Normal
-		in
-
-		let run e = fst (process_expr e) in
-		run
-
-	let configure gen java_mode =
-		let run = traverse gen java_mode in
-		let map e = Some(run e) in
-		gen.gsyntax_filters#add ~name:name ~priority:(PCustom priority) map
-end;;
-
-
-(* ******************************************* *)
-(* DefaultArguments *)
-(* ******************************************* *)
-(*
-	This Module Filter will go through all defined functions in all modules and change them
-	so they set all default arguments to be of a Nullable type, and adds the unroll from nullable to
-	the not-nullable type in the beginning of the function.
-
-	dependencies:
-		It must run before OverloadingConstructor, since OverloadingConstructor will change optional structures behavior
-*)
-module DefaultArguments =
-struct
-	let name = "default_arguments"
-	let priority = solve_deps name [ DBefore OverloadingConstructor.priority ]
-
-	let gen_check basic t nullable_var const pos =
-		let is_null t = match t with TType({t_path = ([],"Null")}, _) -> true | _ -> false in
-		let needs_cast t1 t2 = match is_null t1, is_null t2 with
-			| true, false | false, true -> true
-			| _ -> false
-		in
-
-		let const_t = match const with
-			| TString _ -> basic.tstring | TInt _ -> basic.tint | TFloat _ -> basic.tfloat
-			| TNull -> t | TBool _ -> basic.tbool | _ -> assert false
-		in
-		let const = { eexpr = TConst(const); etype = const_t; epos = pos } in
-		let const = if needs_cast t const_t then mk_cast t const else const in
-
-		let arg = mk_local nullable_var pos in
-		let arg = if needs_cast t nullable_var.v_type then mk_cast t arg else arg in
-
-		{
-			eexpr = TIf(
-				{ eexpr = TBinop(Ast.OpEq, mk_local nullable_var pos, null nullable_var.v_type pos); etype = basic.tbool; epos = pos },
-				const,
-				Some(arg)
-			);
-			etype = t;
-			epos = pos;
-		}
-
-	let add_opt gen block pos (var,opt) =
-		match opt with
-		| None | Some TNull ->
-			(var,opt)
-		| Some (TString str) ->
-			block := Codegen.set_default gen.gcon var (TString str) pos :: !block;
-			(var, opt)
-		| Some const ->
-			let basic = gen.gcon.basic in
-			let nullable_var = mk_temp gen var.v_name (basic.tnull var.v_type) in
-			let orig_name = var.v_name in
-			var.v_name <- nullable_var.v_name;
-			nullable_var.v_name <- orig_name;
-			(* var v = (temp_var == null) ? const : cast temp_var; *)
-			let evar = mk (TVar(var, Some(gen_check basic var.v_type nullable_var const pos))) basic.tvoid pos in
-			block := evar :: !block;
-			(nullable_var, opt)
-
-	let rec change_func gen cf =
-		List.iter (change_func gen) cf.cf_overloads;
-		let is_ctor = cf.cf_name = "new" in
-		let basic = gen.gcon.basic in
-		match cf.cf_kind, follow cf.cf_type with
-			| Var _, _ | Method MethDynamic, _ -> ()
-			| _, TFun(args, ret) ->
-				let found = ref false in
-				let args = ref (List.map (fun (n,opt,t) ->
-					(n,opt, if opt then (found := true; basic.tnull t) else t)
-				) args) in
-				(match !found, cf.cf_expr with
-					| true, Some ({ eexpr = TFunction tf } as texpr) ->
-						let block = ref [] in
-						let tf_args = List.map (add_opt gen block tf.tf_expr.epos) tf.tf_args in
-						let arg_assoc = List.map2 (fun (v,o) (v2,_) -> v,(v2,o) ) tf.tf_args tf_args in
-						let rec extract_super e = match e.eexpr with
-							| TBlock(({ eexpr = TCall({ eexpr = TConst TSuper }, _) } as e2) :: tl) ->
-								e2, tl
-							| TBlock(hd :: tl) ->
-								let e2, tl2 = extract_super hd in
-								e2, tl2 @ tl
-							| _ -> raise Not_found
-						in
-						let block = try
-							if not is_ctor then raise Not_found;
-							(* issue #2570 *)
-							(* check if the class really needs the super as the first statement -
-							just to make sure we don't inadvertently break any existing code *)
-							let rec check cl =
-								if not (is_hxgen (TClassDecl cl)) then
-									()
-								else match cl.cl_super with
-									| None ->
-										raise Not_found
-									| Some (cl,_) ->
-										check cl
-							in
-							(match gen.gcurrent_class with
-								| Some cl -> check cl
-								| _ -> ());
-							let super, tl = extract_super tf.tf_expr in
-							(match super.eexpr with
-								| TCall({ eexpr = TConst TSuper } as e1, args) ->
-									(* any super argument will be replaced by an inlined version of the check *)
-									let found = ref false in
-									let rec replace_args e = match e.eexpr with
-										| TLocal(v) -> (try
-											let v2,o = List.assq v arg_assoc in
-											let o = match o with
-												| None -> raise Not_found
-												| Some o -> o
-											in
-											found := true;
-											gen_check gen.gcon.basic v.v_type v2 o e.epos
-										with | Not_found -> e)
-										| _ -> Type.map_expr replace_args e
-									in
-									let args = List.map (replace_args) args in
-									{ tf.tf_expr with eexpr = TBlock((if !found then { super with eexpr = TCall(e1,args) } else super) :: !block @ tl) }
-								| _ -> assert false)
-							with | Not_found ->
-								Type.concat { tf.tf_expr with eexpr = TBlock(!block); etype = basic.tvoid } tf.tf_expr
-						in
-
-						args := fun_args tf_args;
-						cf.cf_expr <- Some( {texpr with eexpr = TFunction( { tf with
-							tf_args = tf_args;
-							tf_expr = block
-						} ); etype = TFun(!args, ret) } );
-						cf.cf_type <- TFun(!args, ret)
-
-					| _ -> ()
-				);
-				(if !found then cf.cf_type <- TFun(!args, ret))
-			| _, _ -> assert false
-
-	let configure gen =
-		let run md =
-			(match md with
-			| TClassDecl cl ->
-				List.iter (change_func gen) cl.cl_ordered_fields;
-				List.iter (change_func gen) cl.cl_ordered_statics;
-				Option.may (change_func gen) cl.cl_constructor;
-			| _ -> ());
-			Some(md);
-		in
-		gen.gmodule_filters#add ~name:name ~priority:(PCustom priority) run
-
-end;;
-
-
-(* ******************************************* *)
-(* Interface Variables Removal Modf *)
-(* ******************************************* *)
-(*
-	This module filter will take care of sanitizing interfaces for targets that do not support
-	variables declaration in interfaces. By now this will mean that if anything is typed as the interface,
-	and a variable access is made, a FNotFound will be returned for the field_access, so
-	the field will be only accessible by reflection.
-	Speed-wise, ideally it would be best to create getProp/setProp functions in this case and change
-	the AST to call them when accessing by interface. (TODO)
-	But right now it will be accessed by reflection.
-*)
-module InterfaceVarsDeleteModf =
-struct
-	let name = "interface_vars"
-	let priority = solve_deps name []
-
-	let configure gen =
-		let run md =
-			match md with
-			| TClassDecl ({ cl_interface = true } as cl) ->
-				let to_add = ref [] in
-				let fields = List.filter (fun cf ->
-					match cf.cf_kind with
-					| Var _ when gen.gcon.platform = Cs && Meta.has Meta.Event cf.cf_meta ->
-						true
-					| Var vkind when not (Type.is_extern_field cf && Meta.has Meta.Property cf.cf_meta) ->
-						(match vkind.v_read with
-							| AccCall ->
-								let newcf = mk_class_field ("get_" ^ cf.cf_name) (TFun([],cf.cf_type)) true cf.cf_pos (Method MethNormal) [] in
-								to_add := newcf :: !to_add;
-							| _ -> ()
-						);
-						(match vkind.v_write with
-							| AccCall ->
-								let newcf = mk_class_field ("set_" ^ cf.cf_name) (TFun(["val",false,cf.cf_type],cf.cf_type)) true cf.cf_pos (Method MethNormal) [] in
-								to_add := newcf :: !to_add;
-							| _ -> ()
-						);
-						cl.cl_fields <- PMap.remove cf.cf_name cl.cl_fields;
-						false
-					| Method MethDynamic ->
-						(* TODO OPTIMIZATION - add a `_dispatch` method to the interface which will call the dynamic function itself *)
-						cl.cl_fields <- PMap.remove cf.cf_name cl.cl_fields;
-						false
-					| _ ->
-						true
-				) cl.cl_ordered_fields in
-
-				cl.cl_ordered_fields <- fields;
-
-				List.iter (fun cf ->
-					match field_access gen (TInst(cl,List.map snd cl.cl_params)) cf.cf_name with
-					| FNotFound | FDynamicField _ ->
-						cl.cl_ordered_fields <- cf :: cl.cl_ordered_fields;
-						cl.cl_fields <- PMap.add cf.cf_name cf cl.cl_fields
-					| _ ->
-						()
-				) !to_add
-			| _ -> ()
-		in
-		let map md = Some(run md; md) in
-		gen.gmodule_filters#add ~name:name ~priority:(PCustom priority) map
-end;;
-
-
-(* ******************************************* *)
-(* InterfaceProps *)
-(* ******************************************* *)
-(*
-	This module filter will go through all declared properties, and see if they are conforming to a native interface.
-	If they are, it will add Meta.Property to it.
-*)
-module InterfaceProps =
-struct
-	let name = "interface_props"
-	let priority = solve_deps name []
-
-	let configure gen =
-		let run md =
-			match md with
-			| TClassDecl ({ cl_interface = false; cl_extern = false } as cl) ->
-				let vars = List.fold_left (fun acc (iface,_) ->
-					if Meta.has Meta.CsNative iface.cl_meta then
-						List.filter (fun cf -> match cf.cf_kind with
-							| Var { v_read = AccCall } | Var { v_write = AccCall } -> true
-							| _ -> false
-						) iface.cl_ordered_fields @ acc
-					else
-						acc
-				) [] cl.cl_implements in
-				let vars = List.map (fun cf -> cf.cf_name) vars in
-				if vars <> [] then
-					List.iter (fun cf -> match cf.cf_kind with
-						| Var { v_read = AccCall } | Var { v_write = AccCall } when List.mem cf.cf_name vars ->
-							cf.cf_meta <- (Meta.Property, [], null_pos) :: cf.cf_meta
-						| _ -> ()
-					) cl.cl_ordered_fields
-			| _ ->
-				()
-		in
-		let map md = Some(run md; md) in
-		gen.gmodule_filters#add ~name:name ~priority:(PCustom priority) map
-end;;
-
-
-(* ******************************************* *)
-(* Int Division Synf *)
-(* ******************************************* *)
-(*
-	On targets that support int division, this module will force a float division to be performed.
-	It will also look for casts to int or use of Std.int() to optimize this kind of operation.
-
-	dependencies:
-		since it depends on nothing, but many modules might generate division expressions,
-		it will be one of the last modules to run
-*)
-module IntDivisionSynf =
-struct
-	let name = "int_division_synf"
-	let priority = solve_deps name [ DAfter ExpressionUnwrap.priority; DAfter ObjectDeclMap.priority; DAfter ArrayDeclSynf.priority ]
-
-	let configure gen =
-		let basic = gen.gcon.basic in
-		let rec is_int t = match follow t with
-			| TInst({ cl_path = (["haxe";"lang"],"Null") }, [t]) -> is_int t
-			| t ->
-				like_int t && not (like_i64 t)
-		in
-		let is_exactly_int t = match follow t with
-			| TAbstract ({ a_path=[],"Int" }, []) -> true
-			| _ -> false
-		in
-		let rec run e =
-			match e.eexpr with
-			| TBinop((Ast.OpDiv as op), e1, e2) when is_int e1.etype && is_int e2.etype ->
-				{ e with eexpr = TBinop(op, mk_cast basic.tfloat (run e1), run e2) }
-			| TCall(
-					{ eexpr = TField(_, FStatic({ cl_path = ([], "Std") }, { cf_name = "int" })) },
-					[ ({ eexpr = TBinop((Ast.OpDiv as op), e1, e2) } as ebinop ) ]
-				) when is_int e1.etype && is_int e2.etype ->
-				let e = { ebinop with eexpr = TBinop(op, run e1, run e2); etype = basic.tint } in
-				if not (is_exactly_int e1.etype && is_exactly_int e2.etype) then
-					mk_cast basic.tint e
-				else
-					e
-			| TCast( ({ eexpr = TBinop((Ast.OpDiv as op), e1, e2) } as ebinop ), _ )
-			| TCast( ({ eexpr = TBinop(( (Ast.OpAssignOp Ast.OpDiv) as op), e1, e2) } as ebinop ), _ ) when is_int e1.etype && is_int e2.etype && is_int e.etype ->
-				let ret = { ebinop with eexpr = TBinop(op, run e1, run e2); etype = e.etype } in
-				if not (is_exactly_int e1.etype && is_exactly_int e2.etype) then
-					mk_cast e.etype ret
-				else
-					e
-			| _ ->
-				Type.map_expr run e
-		in
-
-		let map e = Some(run e) in
-		gen.gsyntax_filters#add ~name:name ~priority:(PCustom priority) map
-end;;
-
-
-(* ******************************************* *)
-(* UnnecessaryCastsRemoval *)
-(* ******************************************* *)
-(*
-	This module will take care of simplifying unnecessary casts, specially those made by the compiler
-	when inlining. Right now, it will only take care of casts used as a statement, which are always useless;
-	TODO: Take care of more cases, e.g. when the to and from types are the same
-
-	dependencies:
-		This must run after CastDetection, but before ExpressionUnwrap
-*)
-module UnnecessaryCastsRemoval =
-struct
-	let name = "casts_removal"
-	let priority = solve_deps name [DAfter CastDetect.priority; DBefore ExpressionUnwrap.priority]
-
-	let configure gen =
-		let rec take_off_cast run e =
-			match e.eexpr with
-			| TCast (c, _) -> take_off_cast run c
-			| _ -> run e
-		in
-		let rec traverse e =
-			match e.eexpr with
-			| TBlock bl ->
-				let bl = List.map (take_off_cast traverse) bl in
-				{ e with eexpr = TBlock bl }
-			| TTry (block, catches) ->
-				{ e with eexpr = TTry(traverse (mk_block block), List.map (fun (v,block) -> (v, traverse (mk_block block))) catches) }
-			| TSwitch (cond,el_e_l, default) ->
-				{ e with eexpr = TSwitch(cond, List.map (fun (el,e) -> (el, traverse (mk_block e))) el_e_l, Option.map (fun e -> traverse (mk_block e)) default) }
-			| TWhile (cond,block,flag) ->
-				{e with eexpr = TWhile(cond,traverse (mk_block block), flag) }
-			| TIf (cond, eif, eelse) ->
-				{ e with eexpr = TIf(cond, traverse (mk_block eif), Option.map (fun e -> traverse (mk_block e)) eelse) }
-			| TFor (v,it,block) ->
-				{ e with eexpr = TFor(v,it, traverse (mk_block block)) }
-			| TFunction (tfunc) ->
-				{ e with eexpr = TFunction({ tfunc with tf_expr = traverse (mk_block tfunc.tf_expr) }) }
-			| _ -> e (* if expression doesn't have a block, we will exit *)
-		in
-		let map e = Some(traverse e) in
-		gen.gsyntax_filters#add ~name:name ~priority:(PCustom priority) map
-end;;
-
-
-(* ******************************************* *)
-(* AbstractImplementationFix *)
-(* ******************************************* *)
-(*
-	This module filter will map the compiler created classes from abstract
-	implementations to valid haxe code, as needed by gencommon
-
-	dependencies:
-		No dependencies
-*)
-module AbstractImplementationFix =
-struct
-	let name = "abstract_implementation_fix"
-	let priority = solve_deps name []
-
-	let configure gen =
-		let run md =
-			(match md with
-			| TClassDecl ({ cl_kind = KAbstractImpl a } as c) ->
-				List.iter (
-					function
-					| ({ cf_name = "_new" } as cf) ->
-						cf.cf_params <- cf.cf_params @ a.a_params
-					| cf when Meta.has Meta.Impl cf.cf_meta ->
-						(match cf.cf_expr with
-						| Some({ eexpr = TFunction({ tf_args = (v, _) :: _ }) }) when Meta.has Meta.This v.v_meta ->
-							cf.cf_params <- cf.cf_params @ a.a_params
-						| _ -> ())
-					| _ -> ()
-				) c.cl_ordered_statics
-			| _ -> ());
-			Some md
-		in
-		gen.gmodule_filters#add ~name:name ~priority:(PCustom priority) run
-end;;
-
-(* ******************************************* *)
-(* FixOverrides *)
-(* ******************************************* *)
-
-(*
-
-	Covariant return types, contravariant function arguments and applied type parameters may change
-	in a way that expected implementations / overrides aren't recognized as such.
-	This filter will fix that.
-
-	dependencies:
-		FixOverrides expects that the target platform is able to deal with overloaded functions
-		It must run after DefaultArguments, otherwise code added by the default arguments may be invalid
-
-*)
-
-module FixOverrides =
-struct
-
-	let name = "fix_overrides"
-
-	let priority = solve_deps name [DAfter DefaultArguments.priority]
-
-	(*
-		if the platform allows explicit interface implementation (C#),
-		specify a explicit_fn_name function (tclass->string->string)
-		Otherwise, it expects the platform to be able to handle covariant return types
-	*)
-	let run ~explicit_fn_name ~get_vmtype gen =
-		let implement_explicitly = is_some explicit_fn_name in
-		let run md = match md with
-			| TClassDecl ( { cl_interface = true; cl_extern = false } as c ) ->
-				(* overrides can be removed from interfaces *)
-				c.cl_ordered_fields <- List.filter (fun f ->
-					try
-						if Meta.has Meta.Overload f.cf_meta then raise Not_found;
-						let f2 = Codegen.find_field gen.gcon c f in
-						if f2 == f then raise Not_found;
-						c.cl_fields <- PMap.remove f.cf_name c.cl_fields;
-						false;
-					with Not_found ->
-						true
-				) c.cl_ordered_fields;
-				md
-			| TClassDecl({ cl_extern = false } as c) ->
-				let this = { eexpr = TConst TThis; etype = TInst(c,List.map snd c.cl_params); epos = c.cl_pos } in
-				(* look through all interfaces, and try to find a type that applies exactly *)
-				let rec loop_iface (iface:tclass) itl =
-					List.iter (fun (s,stl) -> loop_iface s (List.map (apply_params iface.cl_params itl) stl)) iface.cl_implements;
-					let real_itl = gen.greal_type_param (TClassDecl iface) itl in
-					let rec loop_f f =
-						List.iter loop_f f.cf_overloads;
-						let ftype = apply_params iface.cl_params itl f.cf_type in
-						let real_ftype = get_real_fun gen (apply_params iface.cl_params real_itl f.cf_type) in
-						replace_mono real_ftype;
-						let overloads = Overloads.get_overloads c f.cf_name in
-						try
-							let t2, f2 =
-								match overloads with
-								| (_, cf) :: _ when Meta.has Meta.Overload cf.cf_meta -> (* overloaded function *)
-									(* try to find exact function *)
-									List.find (fun (t,f2) ->
-										Overloads.same_overload_args ~get_vmtype ftype t f f2
-									) overloads
-								| _ :: _ ->
-									(match field_access gen (TInst(c, List.map snd c.cl_params)) f.cf_name with
-									| FClassField(_,_,_,f2,false,t,_) -> t,f2 (* if it's not an overload, all functions should have the same signature *)
-									| _ -> raise Not_found)
-								| [] -> raise Not_found
-							in
-							replace_mono t2;
-							(* if we find a function with the exact type of real_ftype, it means this interface has already been taken care of *)
-							if not (type_iseq (get_real_fun gen (apply_params f2.cf_params (List.map snd f.cf_params) t2)) real_ftype) then begin
-								(match f.cf_kind with | Method (MethNormal | MethInline) -> () | _ -> raise Not_found);
-								let t2 = get_real_fun gen t2 in
-								if List.length f.cf_params <> List.length f2.cf_params then raise Not_found;
-								replace_mono t2;
-								match follow (apply_params f2.cf_params (List.map snd f.cf_params) t2), follow real_ftype with
-								| TFun(a1,r1), TFun(a2,r2) when not implement_explicitly && not (type_iseq r1 r2) && Overloads.same_overload_args ~get_vmtype real_ftype t2 f f2 ->
-									(* different return types are the trickiest cases to deal with *)
-									(* check for covariant return type *)
-									let is_covariant = match follow r1, follow r2 with
-										| _, TDynamic _ -> false
-										| r1, r2 -> try
-											unify r1 r2;
-											true
-										with | Unify_error _ -> false
-									in
-									(* we only have to worry about non-covariant issues *)
-									if not is_covariant then begin
-										(* override return type and cast implemented function *)
-										let args, newr = match follow t2, follow (apply_params f.cf_params (List.map snd f2.cf_params) real_ftype) with
-											| TFun(a,_), TFun(_,r) -> a,r
-											| _ -> assert false
-										in
-										f2.cf_type <- TFun(args,newr);
-										(match f2.cf_expr with
-										| Some ({ eexpr = TFunction tf } as e) ->
-												f2.cf_expr <- Some { e with eexpr = TFunction { tf with tf_type = newr } }
-										| _ -> ())
-									end
-								| TFun(a1,r1), TFun(a2,r2) ->
-									(* just implement a function that will call the main one *)
-									let name, is_explicit = match explicit_fn_name with
-										| Some fn when not (type_iseq r1 r2) && Overloads.same_overload_args ~get_vmtype real_ftype t2 f f2 ->
-												fn iface itl f.cf_name, true
-										| _ -> f.cf_name, false
-									in
-									let p = f2.cf_pos in
-									let newf = mk_class_field name real_ftype true f.cf_pos (Method MethNormal) f.cf_params in
-									let vars = List.map (fun (n,_,t) -> alloc_var n t) a2 in
-
-									let args = List.map2 (fun v (_,_,t) -> mk_cast t (mk_local v f2.cf_pos)) vars a1 in
-									let field = { eexpr = TField(this, FInstance(c,List.map snd c.cl_params,f2)); etype = TFun(a1,r1); epos = p } in
-									let call = { eexpr = TCall(field, args); etype = r1; epos = p } in
-									(* let call = gen.gparam_func_call call field (List.map snd f.cf_params) args in *)
-									let is_void = ExtType.is_void r2 in
-
-									newf.cf_expr <- Some {
-										eexpr = TFunction({
-											tf_args = List.map (fun v -> v,None) vars;
-											tf_type = r2;
-											tf_expr = (if is_void then call else {
-												eexpr = TReturn (Some (mk_cast r2 call));
-												etype = r2;
-												epos = p
-											})
-										});
-										etype = real_ftype;
-										epos = p;
-									};
-									(try
-										let fm = PMap.find name c.cl_fields in
-										fm.cf_overloads <- newf :: fm.cf_overloads
-									with | Not_found ->
-										c.cl_fields <- PMap.add name newf c.cl_fields;
-										c.cl_ordered_fields <- newf :: c.cl_ordered_fields)
-								| _ -> assert false
-							end
-						with | Not_found -> ()
-					in
-					List.iter (fun f -> match f.cf_kind with | Var _ -> () | _ -> loop_f f) iface.cl_ordered_fields
-				in
-				List.iter (fun (iface,itl) -> loop_iface iface itl) c.cl_implements;
-				(* now go through all overrides, *)
-				let rec check_f f =
-					(* find the first declared field *)
-					let is_overload = Meta.has Meta.Overload f.cf_meta in
-					let decl = if is_overload then
-						find_first_declared_field gen c ~get_vmtype ~exact_field:f f.cf_name
-					else
-						find_first_declared_field gen c ~get_vmtype f.cf_name
-					in
-					match decl with
-					| Some(f2,actual_t,_,t,declared_cl,_,_)
-						when not (Overloads.same_overload_args ~get_vmtype actual_t (get_real_fun gen f.cf_type) f2 f) ->
-							(match f.cf_expr with
-							| Some({ eexpr = TFunction(tf) } as e) ->
-								let actual_args, _ = get_fun (get_real_fun gen actual_t) in
-								let new_args, vardecl = List.fold_left2 (fun (args,vdecl) (v,_) (_,_,t) ->
-									if not (type_iseq (gen.greal_type v.v_type) (gen.greal_type t)) then begin
-										let new_var = mk_temp gen v.v_name t in
-										(new_var,None) :: args, (v, Some(mk_cast v.v_type (mk_local new_var f.cf_pos))) :: vdecl
-									end else
-										(v,None) :: args, vdecl
-								) ([],[]) tf.tf_args actual_args in
-								let block = { eexpr = TBlock(List.map (fun (v,ve) ->
-									{
-										eexpr = TVar(v,ve);
-										etype = gen.gcon.basic.tvoid;
-										epos = tf.tf_expr.epos
-									}) vardecl);
-									etype = gen.gcon.basic.tvoid;
-									epos = tf.tf_expr.epos
-								} in
-								if Meta.has Meta.Overload f.cf_meta then begin
-									(* if it is overload, create another field with the requested type *)
-									let f3 = mk_class_field f.cf_name t f.cf_public f.cf_pos f.cf_kind f.cf_params in
-									let p = f.cf_pos in
-									let old_args, old_ret = get_fun f.cf_type in
-									let args, ret = get_fun t in
-									let tf_args = List.map (fun (n,o,t) -> alloc_var n t, None) args in
-									let f3_mk_return = if ExtType.is_void ret then (fun e -> e) else (fun e -> mk_return (mk_cast ret e)) in
-									f3.cf_expr <- Some {
-										eexpr = TFunction({
-											tf_args = List.rev new_args;
-											tf_type = ret;
-											tf_expr = Type.concat block (mk_block (f3_mk_return {
-												eexpr = TCall(
-													{
-														eexpr = TField(
-															{ eexpr = TConst TThis; etype = TInst(c, List.map snd c.cl_params); epos = p },
-															FInstance(c,List.map snd c.cl_params,f));
-														etype = f.cf_type;
-														epos = p
-													},
-													List.map2 (fun (v,_) (_,_,t) -> mk_cast t (mk_local v p)) tf_args old_args);
-												etype = old_ret;
-												epos = p
-											}))
-										});
-										etype = t;
-										epos = p;
-									};
-									(* make sure we skip cast detect - otherwise this new function will make the overload detection go crazy *)
-									f3.cf_meta <- (Meta.Custom(":skipCastDetect"), [], f3.cf_pos) :: f3.cf_meta;
-									gen.gafter_expr_filters_ended <- ((fun () ->
-										f.cf_overloads <- f3 :: f.cf_overloads;
-									) :: gen.gafter_expr_filters_ended);
-									f3
-								end else begin
-									(* if it's not overload, just cast the vars *)
-									if vardecl <> [] then
-									f.cf_expr <- Some({ e with
-										eexpr = TFunction({ tf with
-											tf_args = List.rev new_args;
-											tf_expr = Type.concat block tf.tf_expr
-										});
-									});
-									f
-								end
-							| _ -> f)
-					| _ -> f
-				in
-				if not c.cl_extern then
-					c.cl_overrides <- List.map (fun f -> check_f f) c.cl_overrides;
-				md
-			| _ -> md
-		in
-		run
-
-	let configure ?explicit_fn_name ~get_vmtype gen =
-		let delay () =
-			Hashtbl.clear gen.greal_field_types
-		in
-		gen.gafter_mod_filters_ended <- delay :: gen.gafter_mod_filters_ended;
-		let run = run ~explicit_fn_name ~get_vmtype gen in
-		let map md = Some(run md) in
-		gen.gmodule_filters#add ~name:name ~priority:(PCustom priority) map
-end;;
-
-
-(* ******************************************* *)
-(* Normalize *)
-(* ******************************************* *)
-(*
-	- Filters out enum constructor type parameters from the AST; See Issue #1796
-	- Filters out monomorphs
-	- Filters out all non-whitelisted AST metadata
-
-	dependencies:
-		No dependencies; but it still should be one of the first filters to run,
-		as it will help normalize the AST
-*)
-module Normalize =
-struct
-	let name = "normalize_type"
-	let priority = max_dep
-
-	let rec filter_param t =
-		match t with
-		| TInst({ cl_kind = KTypeParameter _ } as c,_) when Meta.has Meta.EnumConstructorParam c.cl_meta ->
-			t_dynamic
-		| TMono r ->
-			(match !r with
-			| None -> t_dynamic
-			| Some t -> filter_param t)
-		| TInst(_,[]) | TEnum(_,[]) | TType(_,[]) | TAbstract(_,[]) ->
-			t
-		| TType(t,tl) ->
-			TType(t,List.map filter_param tl)
-		| TInst(c,tl) ->
-			TInst(c,List.map filter_param tl)
-		| TEnum(e,tl) ->
-			TEnum(e,List.map filter_param tl)
-		| TAbstract({ a_path = (["haxe";"extern"],"Rest") } as a,tl) ->
-			TAbstract(a, List.map filter_param tl)
-		| TAbstract(a,tl) when not (Meta.has Meta.CoreType a.a_meta) ->
-			filter_param (Abstract.get_underlying_type a tl)
-		| TAbstract(a,tl) ->
-			TAbstract(a, List.map filter_param tl)
-		| TAnon a ->
-			TAnon {
-				a_fields = PMap.map (fun f -> { f with cf_type = filter_param f.cf_type }) a.a_fields;
-				a_status = a.a_status;
-			}
-		| TFun(args,ret) ->
-			TFun(List.map (fun (n,o,t) -> (n,o,filter_param t)) args, filter_param ret)
-		| TDynamic _ ->
-			t
-		| TLazy f ->
-			filter_param (!f())
-
-	let configure gen ~metas =
-		let rec run e =
-			match e.eexpr with
-			| TMeta (entry, e) when not (Hashtbl.mem metas entry) ->
-				run e
-			| _ ->
-				map_expr_type (fun e -> run e) filter_param (fun v -> v.v_type <- filter_param v.v_type; v) e
-		in
-		let map e = Some (run e) in
-		gen.gexpr_filters#add ~name:name ~priority:(PCustom priority) map;
-
-		let run md =
-			match md with
-			| TClassDecl cl ->
-				let rec map cf =
-					cf.cf_type <- filter_param cf.cf_type;
-					List.iter map cf.cf_overloads
-				in
-				List.iter map cl.cl_ordered_fields;
-				List.iter map cl.cl_ordered_statics;
-				Option.may map cl.cl_constructor
-			| _ ->
-				()
-		in
-		let map md = Some (run md; md) in
-		gen.gmodule_filters#add ~name:name ~priority:(PCustom priority) map
-
-end;;
-
-
-(* ******************************************* *)
-(* InterfaceMetas *)
-(* ******************************************* *)
-(*
-	Deal with metadata on interfaces by taking it off from interface, and adding a new class with `_HxMeta` suffix
-
-	dependencies:
-		Must run before InitFunction
-*)
-module InterfaceMetas =
-struct
-	let name = "interface_metas"
-	let priority = solve_deps name [ DBefore InitFunction.priority ]
-
-	let configure gen =
-		let run md =
-			match md with
-			| TClassDecl ({ cl_interface = true; cl_ordered_statics = (_ :: _) } as cl) ->
-				cl.cl_ordered_statics <- [];
-				let path = fst cl.cl_path,snd cl.cl_path ^ "_HxMeta" in
-				(match Codegen.build_metadata gen.gcon (TClassDecl cl) with
-				| Some expr ->
-					let ncls = mk_class cl.cl_module path cl.cl_pos in
-					let cf = mk_class_field "__meta__" expr.etype false expr.epos (Var { v_read = AccNormal; v_write = AccNormal }) [] in
-					cf.cf_expr <- Some expr;
-					ncls.cl_statics <- PMap.add "__meta__" cf ncls.cl_statics;
-					ncls.cl_ordered_statics <- cf :: ncls.cl_ordered_statics;
-					gen.gadd_to_module (TClassDecl(ncls)) priority;
-				| _ -> ())
-			| _ -> ()
-		in
-		let map md = run md; Some(md) in
-		gen.gmodule_filters#add ~name:name ~priority:(PCustom priority) map
-end;;

+ 43 - 0
src/generators/gencommon/abstractImplementationFix.ml

@@ -0,0 +1,43 @@
+(*
+	The Haxe Compiler
+	Copyright (C) 2005-2017  Haxe Foundation
+
+	This program is free software; you can redistribute it and/or
+	modify it under the terms of the GNU General Public License
+	as published by the Free Software Foundation; either version 2
+	of the License, or (at your option) any later version.
+
+	This program is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+
+	You should have received a copy of the GNU General Public License
+	along with this program; if not, write to the Free Software
+	Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+*)
+open Type
+open Gencommon
+
+(** add abstract type parameters to abstract implementation methods *)
+let add_abstract_params = function
+	| TClassDecl ({ cl_kind = KAbstractImpl a } as c) ->
+		List.iter (
+			function
+			| ({ cf_name = "_new" } as cf) ->
+				cf.cf_params <- cf.cf_params @ a.a_params
+			| cf when Meta.has Meta.Impl cf.cf_meta ->
+				(match cf.cf_expr with
+				| Some({ eexpr = TFunction({ tf_args = (v, _) :: _ }) }) when Meta.has Meta.This v.v_meta ->
+					cf.cf_params <- cf.cf_params @ a.a_params
+				| _ -> ())
+			| _ -> ()
+		) c.cl_ordered_statics
+	| _ -> ()
+
+let name = "abstract_implementation_fix"
+let priority = solve_deps name []
+
+let configure gen =
+	let run md = (add_abstract_params md; md) in
+	gen.gmodule_filters#add name (PCustom priority) run

+ 51 - 0
src/generators/gencommon/arrayDeclSynf.ml

@@ -0,0 +1,51 @@
+(*
+	The Haxe Compiler
+	Copyright (C) 2005-2017  Haxe Foundation
+
+	This program is free software; you can redistribute it and/or
+	modify it under the terms of the GNU General Public License
+	as published by the Free Software Foundation; either version 2
+	of the License, or (at your option) any later version.
+
+	This program is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+
+	You should have received a copy of the GNU General Public License
+	along with this program; if not, write to the Free Software
+	Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+*)
+open Type
+open Gencommon
+
+(*
+	A syntax filter that will change array declarations to the actual native array declarations plus
+	the haxe array initialization
+
+	dependencies:
+		Must run after ObjectDeclMap since it can add TArrayDecl expressions
+*)
+let init (native_array_cl : tclass) (change_type_params : module_type -> t list -> t list) =
+	let rec run e =
+		match e.eexpr with
+		| TArrayDecl el ->
+			let cl, params = match follow e.etype with
+				| TInst(({ cl_path = ([], "Array") } as cl), ( _ :: _  as params)) -> cl, params
+				| TInst(({ cl_path = ([], "Array") } as cl), []) -> cl, [t_dynamic]
+				| _ -> assert false
+			in
+			let params = change_type_params (TClassDecl cl) params in
+			let e_inner_decl = mk (TArrayDecl (List.map run el)) (TInst (native_array_cl, params)) e.epos in
+			mk (TNew (cl, params, [e_inner_decl])) e.etype e.epos
+		| _ ->
+			Type.map_expr run e
+	in
+	run
+
+let name = "array_decl_synf"
+let priority = solve_deps name [DAfter ObjectDeclMap.priority]
+
+let configure gen native_array_cl change_type_params =
+	let run = init native_array_cl change_type_params in
+	gen.gsyntax_filters#add name (PCustom priority) run

+ 1240 - 0
src/generators/gencommon/castDetect.ml

@@ -0,0 +1,1240 @@
+(*
+	The Haxe Compiler
+	Copyright (C) 2005-2017  Haxe Foundation
+
+	This program is free software; you can redistribute it and/or
+	modify it under the terms of the GNU General Public License
+	as published by the Free Software Foundation; either version 2
+	of the License, or (at your option) any later version.
+
+	This program is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+
+	You should have received a copy of the GNU General Public License
+	along with this program; if not, write to the Free Software
+	Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+*)
+open Option
+open Common
+open Ast
+open Globals
+open Type
+open Codegen
+open Gencommon
+
+(* ******************************************* *)
+(* Casts detection v2 *)
+(* ******************************************* *)
+(*
+	Will detect implicit casts and add TCast for them. Since everything is already followed by follow_all, typedefs are considered a new type altogether
+
+	Types shouldn't be cast if:
+		* When an instance is being coerced to a superclass or to an implemented interface
+		* When anything is being coerced to Dynamic
+
+	edit:
+		As a matter of performance, we will also run the type parameters casts in here. Otherwise the exact same computation would have to be performed twice,
+		with maybe even some loss of information
+
+		* TAnon / TDynamic will call
+		* Type parameter handling will be abstracted
+
+	dependencies:
+		Must run before ExpressionUnwrap
+
+*)
+let name = "cast_detect"
+let priority = solve_deps name [DBefore RealTypeParams.priority; DBefore ExpressionUnwrap.priority]
+
+(* ******************************************* *)
+(* ReturnCast *)
+(* ******************************************* *)
+(*
+	Cast detection for return types can't be done at CastDetect time, since we need an
+	unwrapped expression to make sure we catch all return cast detections. So this module
+	is specifically to deal with that, and is configured automatically by CastDetect
+*)
+module ReturnCast =
+struct
+	let name = "return_cast"
+	let priority = solve_deps name [DAfter priority; DAfter ExpressionUnwrap.priority]
+
+	let default_implementation gen =
+		let rec extract_expr e = match e.eexpr with
+			| TParenthesis e
+			| TMeta (_,e)
+			| TCast(e,_) -> extract_expr e
+			| _ -> e
+		in
+		let current_ret_type = ref None in
+		let handle e tto tfrom = gen.ghandle_cast (gen.greal_type tto) (gen.greal_type tfrom) e in
+		let in_value = ref false in
+
+		let rec run e =
+			let was_in_value = !in_value in
+			in_value := true;
+			match e.eexpr with
+			| TReturn (eopt) ->
+				(* a return must be inside a function *)
+				let ret_type = match !current_ret_type with | Some(s) -> s | None -> gen.gcon.error "Invalid return outside function declaration." e.epos; assert false in
+				(match eopt with
+				| None when not (ExtType.is_void ret_type) ->
+					mk_return (null ret_type e.epos)
+				| None -> e
+				| Some eret ->
+					mk_return (handle (run eret) ret_type eret.etype))
+			| TFunction(tfunc) ->
+				let last_ret = !current_ret_type in
+				current_ret_type := Some(tfunc.tf_type);
+				let ret = Type.map_expr run e in
+				current_ret_type := last_ret;
+				ret
+			| TBlock el ->
+				{ e with eexpr = TBlock ( List.map (fun e -> in_value := false; run e) el ) }
+			| TBinop ( (Ast.OpAssign as op),e1,e2)
+			| TBinop ( (Ast.OpAssignOp _ as op),e1,e2) when was_in_value ->
+				let e1 = extract_expr (run e1) in
+				let r = { e with eexpr = TBinop(op, e1, handle (run e2) e1.etype e2.etype); etype = e1.etype } in
+				handle r e.etype e1.etype
+			| TBinop ( (Ast.OpAssign as op),({ eexpr = TField(tf, f) } as e1), e2 )
+			| TBinop ( (Ast.OpAssignOp _ as op),({ eexpr = TField(tf, f) } as e1), e2 ) ->
+				(match field_access_esp gen (gen.greal_type tf.etype) (f) with
+					| FClassField(cl,params,_,_,is_static,actual_t,_) ->
+						let actual_t = if is_static then actual_t else apply_params cl.cl_params params actual_t in
+						let e1 = extract_expr (run e1) in
+						{ e with eexpr = TBinop(op, e1, handle (run e2) actual_t e2.etype); etype = e1.etype }
+					| _ ->
+						let e1 = extract_expr (run e1) in
+						{ e with eexpr = TBinop(op, e1, handle (run e2) e1.etype e2.etype); etype = e1.etype }
+				)
+			| TBinop ( (Ast.OpAssign as op),e1,e2)
+			| TBinop ( (Ast.OpAssignOp _ as op),e1,e2) ->
+				let e1 = extract_expr (run e1) in
+				{ e with eexpr = TBinop(op, e1, handle (run e2) e1.etype e2.etype); etype = e1.etype }
+			| _ -> Type.map_expr run e
+		in
+		run
+
+	let configure gen =
+		let map = default_implementation gen in
+		gen.gsyntax_filters#add name (PCustom priority) map
+end;;
+
+(*
+	Since this function is applied under native-context only, the type paraters will already be changed
+*)
+let map_cls gen also_implements fn super =
+	let rec loop c tl =
+		if c == super then
+			fn c tl
+		else
+			(match c.cl_super with
+				| None -> false
+				| Some (cs,tls) ->
+					let tls = gen.greal_type_param (TClassDecl cs) tls in
+					loop cs (List.map (apply_params c.cl_params tl) tls)
+			)
+			||
+			(if also_implements then
+				List.exists (fun (cs,tls) -> loop cs (List.map (apply_params c.cl_params tl) tls)) c.cl_implements
+			else
+				false)
+	in
+	loop
+
+let follow_dyn t = match follow t with
+	| TMono _ | TLazy _ -> t_dynamic
+	| t -> t
+
+(*
+	this has a slight change from the type.ml version, in which it doesn't
+	change a TMono into the other parameter
+*)
+let rec type_eq gen param a b =
+	if a == b then
+		()
+	else match follow_dyn (gen.greal_type a) , follow_dyn (gen.greal_type b) with
+	| TEnum (e1,tl1) , TEnum (e2,tl2) ->
+		if e1 != e2 && not (param = EqCoreType && e1.e_path = e2.e_path) then Type.error [cannot_unify a b];
+		List.iter2 (type_eq gen param) tl1 tl2
+	| TAbstract (a1,tl1) , TAbstract (a2,tl2) ->
+		if a1 != a2 && not (param = EqCoreType && a1.a_path = a2.a_path) then Type.error [cannot_unify a b];
+		List.iter2 (type_eq gen param) tl1 tl2
+	| TInst (c1,tl1) , TInst (c2,tl2) ->
+		if c1 != c2 && not (param = EqCoreType && c1.cl_path = c2.cl_path) && (match c1.cl_kind, c2.cl_kind with KExpr _, KExpr _ -> false | _ -> true) then Type.error [cannot_unify a b];
+		List.iter2 (type_eq gen param) tl1 tl2
+	| TFun (l1,r1) , TFun (l2,r2) when List.length l1 = List.length l2 ->
+		(try
+			type_eq gen param r1 r2;
+			List.iter2 (fun (n,o1,t1) (_,o2,t2) ->
+				if o1 <> o2 then Type.error [Not_matching_optional n];
+				type_eq gen param t1 t2
+			) l1 l2
+		with
+			Unify_error l -> Type.error (cannot_unify a b :: l))
+	| TDynamic a , TDynamic b ->
+		type_eq gen param a b
+	| TAnon a1, TAnon a2 ->
+		(try
+			PMap.iter (fun n f1 ->
+				try
+					let f2 = PMap.find n a2.a_fields in
+					if f1.cf_kind <> f2.cf_kind && (param = EqStrict || param = EqCoreType || not (unify_kind f1.cf_kind f2.cf_kind)) then Type.error [invalid_kind n f1.cf_kind f2.cf_kind];
+					try
+						type_eq gen param f1.cf_type f2.cf_type
+					with
+						Unify_error l -> Type.error (invalid_field n :: l)
+				with
+					Not_found ->
+						if is_closed a2 then Type.error [has_no_field b n];
+						if not (link (ref None) b f1.cf_type) then Type.error [cannot_unify a b];
+						a2.a_fields <- PMap.add n f1 a2.a_fields
+			) a1.a_fields;
+			PMap.iter (fun n f2 ->
+				if not (PMap.mem n a1.a_fields) then begin
+					if is_closed a1 then Type.error [has_no_field a n];
+					if not (link (ref None) a f2.cf_type) then Type.error [cannot_unify a b];
+					a1.a_fields <- PMap.add n f2 a1.a_fields
+				end;
+			) a2.a_fields;
+		with
+			Unify_error l -> Type.error (cannot_unify a b :: l))
+	| _ , _ ->
+		if b == t_dynamic && (param = EqRightDynamic || param = EqBothDynamic) then
+			()
+		else if a == t_dynamic && param = EqBothDynamic then
+			()
+		else
+			Type.error [cannot_unify a b]
+
+let type_iseq gen a b =
+	try
+		type_eq gen EqStrict a b;
+		true
+	with
+		Unify_error _ -> false
+
+(* will return true if both arguments are compatible. If it's not the case, a runtime error is very likely *)
+let is_cl_related gen cl tl super superl =
+	let is_cl_related cl tl super superl = map_cls gen (match cl.cl_kind,super.cl_kind with KTypeParameter _, _ | _,KTypeParameter _ -> false | _ -> true) (fun _ _ -> true) super cl tl in
+	is_cl_related cl tl super superl || is_cl_related super superl cl tl
+
+let is_exactly_basic gen t1 t2 =
+	match gen.gfollow#run_f t1, gen.gfollow#run_f t2 with
+		| TAbstract(a1, []), TAbstract(a2, []) ->
+			a1 == a2 && Common.defined gen.gcon Define.FastCast
+		| TInst(c1, []), TInst(c2, []) ->
+			c1 == c2 && Common.defined gen.gcon Define.FastCast
+		| TEnum(e1, []), TEnum(e2, []) ->
+			e1 == e2 && Common.defined gen.gcon Define.FastCast
+		| _ ->
+			false
+
+let rec is_unsafe_cast gen to_t from_t =
+	match (follow to_t, follow from_t) with
+		| TInst(cl_to, to_params), TInst(cl_from, from_params) ->
+			not (is_cl_related gen cl_from from_params cl_to to_params)
+		| TEnum(e_to, _), TEnum(e_from, _) ->
+			e_to.e_path <> e_from.e_path
+		| TFun _, TFun _ ->
+			(* functions are never unsafe cast by default. This behavior might be changed *)
+			(* with a later AST pass which will run through TFun to TFun casts *)
+			false
+		| TMono _, _
+		| _, TMono _
+		| TDynamic _, _
+		| _, TDynamic _ ->
+			false
+		| TAnon _, _
+		| _, TAnon _ ->
+			(* anonymous are never unsafe also. *)
+			(* Though they will generate a cast, so if this cast is unneeded it's better to avoid them by tweaking gen.greal_type *)
+			false
+		| TAbstract _, _
+		| _, TAbstract _ ->
+			(try
+				unify from_t to_t;
+				false
+			with | Unify_error _ ->
+				try
+					unify to_t from_t; (* still not unsafe *)
+					false
+				with | Unify_error _ ->
+					true)
+		| _ -> true
+
+let unifies tfrom tto = try
+	unify tfrom tto;
+	true
+with | _ ->
+	false
+
+let do_unsafe_cast gen from_t to_t e	=
+	let t_path t =
+		match t with
+			| TInst(cl, _) -> cl.cl_path
+			| TEnum(e, _) -> e.e_path
+			| TType(t, _) -> t.t_path
+			| TAbstract(a, _) -> a.a_path
+			| TDynamic _ -> ([], "Dynamic")
+			| _ -> raise Not_found
+	in
+	match gen.gfollow#run_f from_t, gen.gfollow#run_f to_t with
+	| TInst({ cl_kind = KTypeParameter tl },_), t2 when List.exists (fun t -> unifies t t2) tl ->
+		mk_cast to_t (mk_cast t_dynamic e)
+	| from_t, to_t when gen.gspecial_needs_cast to_t from_t ->
+		mk_cast to_t e
+	| _ ->
+		let do_default () =
+			gen.gon_unsafe_cast to_t e.etype e.epos;
+			mk_cast to_t (mk_cast t_dynamic e)
+		in
+		(* TODO: there really should be a better way to write that *)
+		try
+			if (Hashtbl.find gen.gsupported_conversions (t_path from_t)) from_t to_t then
+				mk_cast to_t e
+			else
+				do_default()
+		with
+			| Not_found ->
+				try
+					if (Hashtbl.find gen.gsupported_conversions (t_path to_t)) from_t to_t then
+						mk_cast to_t e
+					else
+						do_default()
+				with
+					| Not_found -> do_default()
+
+(* ****************************** *)
+(* cast handler *)
+(* decides if a cast should be emitted, given a from and a to type *)
+(*
+	this function is like a mini unify, without e.g. subtyping, which makes sense
+	at the backend level, since most probably Anons and TInst will have a different representation there
+*)
+let rec handle_cast gen e real_to_t real_from_t =
+	let do_unsafe_cast () = do_unsafe_cast gen real_from_t real_to_t { e with etype = real_from_t } in
+	let to_t, from_t = real_to_t, real_from_t in
+
+	let mk_cast fast t e =
+		match e.eexpr with
+			(* TThrow is always typed as Dynamic, we just need to type it accordingly *)
+			| TThrow _ -> { e with etype = t }
+			| _ -> if fast then mk_castfast t e else mk_cast t e
+	in
+
+	let e = { e with etype = real_from_t } in
+	if try fast_eq real_to_t real_from_t with Invalid_argument _ -> false then e else
+	match real_to_t, real_from_t with
+		(* string is the only type that can be implicitly converted from any other *)
+		| TInst( { cl_path = ([], "String") }, []), TInst( { cl_path = ([], "String") }, [] ) ->
+			mk_cast true to_t e
+		| TInst( { cl_path = ([], "String") }, []), _ ->
+			mk_cast false to_t e
+		| TInst( ({ cl_path = (["cs"|"java"], "NativeArray") } as c_array), [tp_to] ), TInst({ cl_path = (["cs"|"java"], "NativeArray") }, [tp_from]) when not (type_iseq gen (gen.greal_type tp_to) (gen.greal_type tp_from)) ->
+				(* when running e.g. var nativeArray:NativeArray<Dynamic> = @:privateAccess someIntMap.vals, we end up with a bad cast because of the type parameters differences *)
+				(* se clean these kinds of casts *)
+				let rec clean_cast e = match e.eexpr with
+					| TCast(expr,_) -> (match gen.greal_type e.etype with
+						| TInst({ cl_path = (["cs"|"java"],"NativeArray") }, _) ->
+							clean_cast expr
+						| _ ->
+							e)
+					| TParenthesis(e) | TMeta(_,e) -> clean_cast e
+					| _ -> e
+				in
+			(* see #5751 . NativeArray is special because of its ties to Array. We could potentially deal with this for all *)
+			(* TNew expressions, but it's not that simple, since we don't want to retype the whole expression list with the *)
+			(* updated type. *)
+			(match e.eexpr with
+				| TNew(c,_,el) when c == c_array ->
+					mk_cast false (TInst(c_array,[tp_to])) { e with eexpr = TNew(c, [tp_to], el); etype = TInst(c_array,[tp_to]) }
+				| _ ->
+					try
+						type_eq gen EqRightDynamic tp_from tp_to;
+						e
+					with | Unify_error _ ->
+						mk_cast false to_t (clean_cast e))
+
+		| TInst(cl_to, params_to), TInst(cl_from, params_from) ->
+			let ret = ref None in
+			(*
+				this is a little confusing:
+				we are here mapping classes until we have the same to and from classes, applying the type parameters in each step, so we can
+				compare the type parameters;
+
+				If a class is found - meaning that the cl_from can be converted without a cast into cl_to,
+				we still need to check their type parameters.
+			*)
+			ignore (map_cls gen (match cl_from.cl_kind,cl_to.cl_kind with KTypeParameter _, _ | _,KTypeParameter _ -> false | _ -> true) (fun _ tl ->
+				try
+					(* type found, checking type parameters *)
+					List.iter2 (type_eq gen EqStrict) tl params_to;
+					ret := Some e;
+					true
+				with | Unify_error _ ->
+					(* type parameters need casting *)
+					if gen.ghas_tparam_cast_handler then begin
+						(*
+							if we are already handling type parameter casts on other part of code (e.g. RealTypeParameters),
+							we'll just make a cast to indicate that this place needs type parameter-involved casting
+						*)
+						ret := Some (mk_cast true to_t e);
+						true
+					end else
+						(*
+							if not, we're going to check if we only need a simple cast,
+							or if we need to first cast into the dynamic version of it
+						*)
+						try
+							List.iter2 (type_eq gen EqRightDynamic) tl params_to;
+							ret := Some (mk_cast true to_t e);
+							true
+						with | Unify_error _ ->
+							ret := Some (mk_cast true to_t (mk_cast true (TInst(cl_to, List.map (fun _ -> t_dynamic) params_to)) e));
+							true
+			) cl_to cl_from params_from);
+			if is_some !ret then
+				get !ret
+			else if is_cl_related gen cl_from params_from cl_to params_to then
+				mk_cast true to_t e
+			else
+				(* potential unsafe cast *)
+				(do_unsafe_cast ())
+		| TMono _, TMono _
+		| TMono _, TDynamic _
+		| TDynamic _, TDynamic _
+		| TDynamic _, TMono _ ->
+			e
+		| TMono _, _
+		| TDynamic _, _
+		| TAnon _, _ when gen.gneeds_box real_from_t ->
+			mk_cast false to_t e
+		| TMono _, _
+		| TDynamic _, _ -> e
+		| _, TMono _
+		| _, TDynamic _ -> mk_cast false to_t e
+		| TAnon (a_to), TAnon (a_from) ->
+			if a_to == a_from then
+				e
+			else if type_iseq gen to_t from_t then (* FIXME apply unify correctly *)
+				e
+			else
+				mk_cast true to_t e
+		| _, TAnon(anon) -> (try
+			let p2 = match !(anon.a_status) with
+			| Statics c -> TInst(c,List.map (fun _ -> t_dynamic) c.cl_params)
+			| EnumStatics e -> TEnum(e, List.map (fun _ -> t_dynamic) e.e_params)
+			| AbstractStatics a -> TAbstract(a, List.map (fun _ -> t_dynamic) a.a_params)
+			| _ -> raise Not_found
+			in
+			let tclass = match get_type gen ([],"Class") with
+			| TAbstractDecl(a) -> a
+			| _ -> assert false in
+			handle_cast gen e real_to_t (gen.greal_type (TAbstract(tclass, [p2])))
+		with | Not_found ->
+			mk_cast false to_t e)
+		| TAbstract (a_to, _), TAbstract(a_from, _) when a_to == a_from ->
+			e
+		| TAbstract _, TInst({ cl_kind = KTypeParameter _ }, _)
+		| TInst({ cl_kind = KTypeParameter _ }, _), TAbstract _ ->
+			do_unsafe_cast()
+		| TAbstract _, _
+		| _, TAbstract _ ->
+			(try
+				unify from_t to_t;
+				mk_cast true to_t e
+			with | Unify_error _ ->
+				try
+					unify to_t from_t;
+					mk_cast true to_t e
+				with | Unify_error _ ->
+					do_unsafe_cast())
+		| TEnum(e_to, []), TEnum(e_from, []) ->
+			if e_to == e_from then
+				e
+			else
+				(* potential unsafe cast *)
+				(do_unsafe_cast ())
+		| TEnum(e_to, params_to), TEnum(e_from, params_from) when e_to.e_path = e_from.e_path ->
+			(try
+					List.iter2 (type_eq gen (if gen.gallow_tp_dynamic_conversion then EqRightDynamic else EqStrict)) params_from params_to;
+					e
+				with
+					| Unify_error _ -> do_unsafe_cast ()
+			)
+		| TEnum(en, params_to), TInst(cl, params_from)
+			| TInst(cl, params_to), TEnum(en, params_from) ->
+				(* this is here for max compatibility with EnumsToClass module *)
+			if en.e_path = cl.cl_path && Meta.has Meta.Class en.e_meta then begin
+				(try
+					List.iter2 (type_eq gen (if gen.gallow_tp_dynamic_conversion then EqRightDynamic else EqStrict)) params_from params_to;
+					e
+				with
+					| Invalid_argument _ ->
+						(*
+							this is a hack for RealTypeParams. Since there is no way at this stage to know if the class is the actual
+							EnumsToClass derived from the enum, we need to imply from possible ArgumentErrors (because of RealTypeParams interfaces),
+							that they would only happen if they were a RealTypeParams created interface
+						*)
+						e
+					| Unify_error _ -> do_unsafe_cast ()
+				)
+			end else
+				do_unsafe_cast ()
+		| TType(t_to, params_to), TType(t_from, params_from) when t_to == t_from ->
+			if gen.gspecial_needs_cast real_to_t real_from_t then
+				(try
+					List.iter2 (type_eq gen (if gen.gallow_tp_dynamic_conversion then EqRightDynamic else EqStrict)) params_from params_to;
+					e
+				with
+					| Unify_error _ -> do_unsafe_cast ()
+				)
+			else
+				e
+		| TType(t_to, _), TType(t_from,_) ->
+			if gen.gspecial_needs_cast real_to_t real_from_t then
+				mk_cast false to_t e
+			else
+				e
+		| TType _, _ when gen.gspecial_needs_cast real_to_t real_from_t ->
+			mk_cast false to_t e
+		| _, TType _ when gen.gspecial_needs_cast real_to_t real_from_t ->
+			mk_cast false to_t e
+		(*| TType(t_to, _), TType(t_from, _) ->
+			if t_to.t_path = t_from.t_path then
+				e
+			else if is_unsafe_cast gen real_to_t real_from_t then (* is_unsafe_cast will already follow both *)
+				(do_unsafe_cast ())
+			else
+				mk_cast to_t e*)
+		| TType _, _
+		| _, TType _ ->
+			if is_unsafe_cast gen real_to_t real_from_t then (* is_unsafe_cast will already follow both *)
+				(do_unsafe_cast ())
+			else
+				mk_cast false to_t e
+		| TAnon anon, _ ->
+			if PMap.is_empty anon.a_fields then
+				e
+			else
+				mk_cast true to_t e
+		| TFun(args, ret), TFun(args2, ret2) ->
+			let get_args = List.map (fun (_,_,t) -> t) in
+			(try List.iter2 (type_eq gen (EqBothDynamic)) (ret :: get_args args) (ret2 :: get_args args2); e with | Unify_error _ | Invalid_argument _ -> mk_cast true to_t e)
+		| _, _ ->
+			do_unsafe_cast ()
+
+(* end of cast handler *)
+(* ******************* *)
+
+let is_static_overload c name =
+	match c.cl_super with
+	| None -> false
+	| Some (sup,_) ->
+		let rec loop c =
+			(PMap.mem name c.cl_statics) || (match c.cl_super with
+				| None -> false
+				| Some (sup,_) -> loop sup)
+		in
+		loop sup
+
+(* this is a workaround for issue #1743, as FInstance() is returning the incorrect classfield *)
+let rec clean_t t = match follow t with
+	| TAbstract(a,tl) when not (Meta.has Meta.CoreType a.a_meta) ->
+		clean_t (Abstract.get_underlying_type a tl)
+	| t -> t
+
+let select_overload gen applied_f overloads types params =
+	let rec check_arg arglist elist =
+		match arglist, elist with
+			| [], [] -> true (* it is valid *)
+			| (_,_,TAbstract({ a_path = (["haxe";"extern"],"Rest") }, [t])) :: [], elist ->
+				List.for_all (fun (_,_,et) -> Type.type_iseq (clean_t et) (clean_t t)) elist
+			| (_,_,t) :: arglist, (_,_,et) :: elist when Type.type_iseq (clean_t et) (clean_t t) ->
+				check_arg arglist elist
+			| _ -> false
+	in
+	match follow applied_f with
+	| TFun _ ->
+		replace_mono applied_f;
+		let args, _ = get_fun applied_f in
+		let elist = List.rev args in
+		let rec check_overload overloads =
+			match overloads with
+			| (t, cf) :: overloads ->
+					let cft = apply_params types params t in
+					let cft = monomorphs cf.cf_params cft in
+					let args, _ = get_fun cft in
+					if check_arg (List.rev args) elist then
+						cf,t,false
+					else if overloads = [] then
+						cf,t,true (* no compatible overload was found *)
+					else
+						check_overload overloads
+			| [] -> assert false
+		in
+		check_overload overloads
+	| _ -> match overloads with  (* issue #1742 *)
+	| (t,cf) :: [] -> cf,t,true
+	| (t,cf) :: _ -> cf,t,false
+	| _ -> assert false
+
+let choose_ctor gen cl tparams etl maybe_empty_t p =
+	let ctor, sup, stl = OverloadingConstructor.cur_ctor cl tparams in
+	(* get returned stl, with Dynamic as t_empty *)
+	let rec get_changed_stl c tl =
+		if c == sup then
+			tl
+		else match c.cl_super with
+		| None -> stl
+		| Some(sup,stl) -> get_changed_stl sup (List.map (apply_params c.cl_params tl) stl)
+	in
+	let ret_tparams = List.map (fun t -> match follow t with
+		| TDynamic _ | TMono _ -> t_empty
+		| _ -> t) tparams
+	in
+	let ret_stl = get_changed_stl cl ret_tparams in
+	let ctors = ctor :: ctor.cf_overloads in
+	List.iter replace_mono etl;
+	(* first filter out or select outright maybe_empty *)
+	let ctors, is_overload = match etl, maybe_empty_t with
+		| [t], Some empty_t ->
+			let count = ref 0 in
+			let is_empty_call = Type.type_iseq t empty_t in
+			let ret = List.filter (fun cf -> match follow cf.cf_type with
+				| TFun([_,_,t],_) ->
+					replace_mono t; incr count; is_empty_call = (Type.type_iseq t empty_t)
+				| _ -> false) ctors
+			in
+			ret, !count > 1
+		| _ ->
+			let len = List.length etl in
+			let ret = List.filter (fun cf -> List.length (fst (get_fun cf.cf_type)) = len) ctors in
+			ret, (match ret with | _ :: [] -> false | _ -> true)
+	in
+	let rec check_arg arglist elist =
+		match arglist, elist with
+		| [], [] -> true
+		| (_,_,t) :: arglist, et :: elist -> (try
+			let t = run_follow gen t in
+			unify et t;
+			check_arg arglist elist
+		with | Unify_error el ->
+			(* List.iter (fun el -> gen.gcon.warning (Typecore.unify_error_msg (print_context()) el) p) el; *)
+			false)
+		| _ ->
+			false
+	in
+	let rec check_cf cf =
+		let t = apply_params sup.cl_params stl cf.cf_type in
+		replace_mono t;
+		let args, _ = get_fun t in
+		check_arg args etl
+	in
+	match is_overload, ctors with
+		| false, [c] ->
+			false, c, sup, ret_stl
+		| _ ->
+			is_overload, List.find check_cf ctors, sup, ret_stl
+
+let change_rest tfun elist =
+	let rec loop acc arglist elist = match arglist, elist with
+		| (_,_,TAbstract({ a_path = (["haxe";"extern"],"Rest") },[t])) :: [], elist ->
+			List.rev (List.map (fun _ -> "rest",false,t) elist @ acc)
+		| (n,o,t) :: arglist, _ :: elist ->
+			loop ((n,o,t) :: acc) arglist elist
+		| _, _ ->
+			List.rev acc
+	in
+	let args,ret = get_fun tfun in
+	TFun(loop [] args elist, ret)
+
+let fastcast_if_needed gen expr real_to_t real_from_t =
+	if Common.defined gen.gcon Define.FastCast then begin
+		if type_iseq gen real_to_t real_from_t then
+			{ expr with etype = real_to_t }
+		else
+			mk_castfast real_to_t { expr with etype=real_from_t }
+	end else
+		handle_cast gen expr real_to_t real_from_t
+
+(*
+	Type parameter handling
+	It will detect if/what type parameters were used, and call the cast handler
+	It will handle both TCall(TField) and TCall by receiving a texpr option field: e
+	Also it will transform the type parameters with greal_type_param and make
+
+	handle_impossible_tparam - should cases where the type parameter is impossible to be determined from the called parameters be Dynamic?
+	e.g. static function test<T>():T {}
+*)
+
+(* match e.eexpr with | TCall( ({ eexpr = TField(ef, f) }) as e1, elist ) -> *)
+let handle_type_parameter gen e e1 ef ~clean_ef ~overloads_cast_to_base f elist calls_parameters_explicitly =
+	(* the ONLY way to know if this call has parameters is to analyze the calling field. *)
+	(* To make matters a little worse, on both C# and Java only in some special cases that type parameters will be used *)
+	(* Namely, when using reflection type parameters are useless, of course. This also includes anonymous types *)
+	(* this will have to be handled by gparam_func_call *)
+
+	let return_var efield =
+		match e with
+			| None ->
+				efield
+			| Some ecall ->
+				match follow efield.etype with
+					| TFun(_,ret) ->
+						(* closures will be handled by the closure handler. So we will just hint what's the expected type *)
+						(* FIXME: should closures have also its arguments cast correctly? In the current implementation I think not. TO_REVIEW *)
+						handle_cast gen { ecall with eexpr = TCall(efield, elist) } (gen.greal_type ecall.etype) ret
+					| _ ->
+						{ ecall with eexpr = TCall(efield, elist) }
+	in
+
+	(* this function will receive the original function argument, the applied function argument and the original function parameters. *)
+	(* from this info, it will infer the applied tparams for the function *)
+	let infer_params pos (original_args:((string * bool * t) list * t)) (applied_args:((string * bool * t) list * t)) (params:(string * t) list) calls_parameters_explicitly : tparams =
+		match params with
+		| [] -> []
+		| _ ->
+			let args_list args = (if not calls_parameters_explicitly then t_dynamic else snd args) :: (List.map (fun (n,o,t) -> t) (fst args)) in
+
+			let monos = List.map (fun _ -> mk_mono()) params in
+			let original = args_list (get_fun (apply_params params monos (TFun(fst original_args,snd original_args)))) in
+			let applied = args_list applied_args in
+
+			(try
+				List.iter2 (fun a o ->
+					unify a o
+					(* type_eq EqStrict a o *)
+				) applied original
+				(* unify applied original *)
+			with
+				| Unify_error el ->
+						(* List.iter (fun el -> gen.gcon.warning (Typecore.unify_error_msg (print_context()) el) pos) el; *)
+						gen.gcon.warning ("This expression may be invalid") pos
+				| Invalid_argument _ ->
+						gen.gcon.warning ("This expression may be invalid") pos
+			);
+
+			List.map (fun t ->
+				match follow t with
+					| TMono _ ->	t_empty
+					| t -> t
+			) monos
+	in
+
+	let real_type = gen.greal_type ef.etype in
+	(* this part was rewritten at roughly r6477 in order to correctly support overloads *)
+	(match field_access_esp gen real_type (f) with
+	| FClassField (cl, params, _, cf, is_static, actual_t, declared_t) when e <> None && (cf.cf_kind = Method MethNormal || cf.cf_kind = Method MethInline) ->
+			(* C# target changes params with a real_type function *)
+			let params = match follow clean_ef.etype with
+				| TInst(_,params) -> params
+				| _ -> params
+			in
+			let local_mk_cast t expr =
+				(* handle_cast gen expr t expr.etype *)
+				if is_exactly_basic gen t expr.etype then
+					expr
+				else
+					mk_castfast t expr
+			in
+
+			let ecall = get e in
+			let ef = ref ef in
+			let is_overload = cf.cf_overloads <> [] || Meta.has Meta.Overload cf.cf_meta || (is_static && is_static_overload cl (field_name f)) in
+			let cf, actual_t, error = match is_overload with
+				| false ->
+						(* since actual_t from FClassField already applies greal_type, we're using the get_overloads helper to get this info *)
+						let t = if cf.cf_params = [] then (* this if statement must be eliminated - it's a workaround for #3516 + infer params. *)
+							actual_t
+						else
+							declared_t
+						in
+						cf,t,false
+				| true ->
+				let (cf, actual_t, error), is_static = match f with
+					| FInstance(c,_,cf) | FClosure(Some (c,_),cf) ->
+						(* get from overloads *)
+						(* FIXME: this is a workaround for issue #1743 . Uncomment this code after it was solved *)
+						(* let t, cf = List.find (fun (t,cf2) -> cf == cf2) (Overloads.get_overloads cl (field_name f)) in *)
+						(* cf, t, false *)
+						select_overload gen e1.etype (Overloads.get_overloads cl (field_name f)) cl.cl_params params, false
+					| FStatic(c,f) ->
+						(* workaround for issue #1743 *)
+						(* f,f.cf_type, false *)
+						select_overload gen e1.etype ((f.cf_type,f) :: List.map (fun f -> f.cf_type,f) f.cf_overloads) [] [], true
+					| _ ->
+						gen.gcon.warning "Overloaded classfield typed as anonymous" ecall.epos;
+						(cf, actual_t, true), true
+				in
+
+				if not (is_static || error) then match find_first_declared_field gen cl ~exact_field:{ cf with cf_type = actual_t } cf.cf_name with
+				| Some(cf_orig,actual_t,_,_,declared_cl,tl,tlch) ->
+					let rec is_super e = match e.eexpr with
+						| TConst TSuper -> true
+						| TParenthesis p | TMeta(_,p) -> is_super p
+						| _ -> false
+					in
+					if declared_cl != cl && overloads_cast_to_base && not (is_super !ef) then begin
+						let pos = (!ef).epos in
+						ef := {
+							eexpr = TCall(
+								{ eexpr = TIdent "__as__"; etype = t_dynamic; epos = pos },
+								[!ef]);
+							etype = TInst(declared_cl,List.map (apply_params cl.cl_params params) tl);
+							epos = pos
+						}
+					end;
+					{ cf_orig with cf_name = cf.cf_name },actual_t,false
+				| None ->
+					gen.gcon.warning "Cannot find matching overload" ecall.epos;
+					cf, actual_t, true
+				else
+					cf,actual_t,error
+			in
+
+			(* take off Rest param *)
+			let actual_t = change_rest actual_t elist in
+			(* set the real (selected) class field *)
+			let f = match f with
+				| FInstance(c,tl,_) -> FInstance(c,tl,cf)
+				| FClosure(c,_) -> FClosure(c,cf)
+				| FStatic(c,_) -> FStatic(c,cf)
+				| f -> f
+			in
+			let error = error || (match follow actual_t with | TFun _ -> false | _ -> true) in
+			if error then (* if error, ignore arguments *)
+				if ExtType.is_void ecall.etype then
+					{ ecall with eexpr = TCall({ e1 with eexpr = TField(!ef, f) }, elist ) }
+				else
+					local_mk_cast ecall.etype { ecall with eexpr = TCall({ e1 with eexpr = TField(!ef, f) }, elist ) }
+			else begin
+				(* infer arguments *)
+				(* let called_t = TFun(List.map (fun e -> "arg",false,e.etype) elist, ecall.etype) in *)
+				let called_t = match follow e1.etype with | TFun _ -> e1.etype | _ -> TFun(List.map (fun e -> "arg",false,e.etype) elist, ecall.etype)	in (* workaround for issue #1742 *)
+				let called_t = change_rest called_t elist in
+				let fparams = infer_params ecall.epos (get_fun (apply_params cl.cl_params params actual_t)) (get_fun called_t) cf.cf_params calls_parameters_explicitly in
+				(* get what the backend actually sees *)
+				(* actual field's function *)
+				let actual_t = get_real_fun gen actual_t in
+				let real_params = gen.greal_type_param (TClassDecl cl) params in
+				let function_t = apply_params cl.cl_params real_params actual_t in
+				let real_fparams = if calls_parameters_explicitly then
+					gen.greal_type_param (TClassDecl cl) fparams
+				else
+					gen.greal_type_param (TClassDecl cl) (infer_params ecall.epos (get_fun function_t) (get_fun (get_real_fun gen called_t)) cf.cf_params calls_parameters_explicitly) in
+				let function_t = get_real_fun gen (apply_params cf.cf_params real_fparams function_t) in
+				let args_ft, ret_ft = get_fun function_t in
+				(* applied function *)
+				let applied = elist in
+				(* check types list *)
+				let new_ecall, elist = try
+					let elist = List.map2 (fun applied (_,_,funct) ->
+						match is_overload || real_fparams <> [], applied.eexpr with
+						| true, TConst TNull ->
+							mk_castfast (gen.greal_type funct) applied
+						| true, _ -> (* when not (type_iseq gen (gen.greal_type applied.etype) funct) -> *)
+							let ret = handle_cast gen applied (funct) (gen.greal_type applied.etype) in
+							(match ret.eexpr with
+							| TCast _ -> ret
+							| _ -> local_mk_cast (funct) ret)
+						| _ ->
+							handle_cast gen applied (funct) (gen.greal_type applied.etype)
+					) applied args_ft in
+					{ ecall with
+						eexpr = TCall(
+							{ e1 with eexpr = TField(!ef, f) },
+							elist);
+					}, elist
+				with Invalid_argument _ ->
+					gen.gcon.warning ("This expression may be invalid" ) ecall.epos;
+					{ ecall with eexpr = TCall({ e1 with eexpr = TField(!ef, f) }, elist) }, elist
+				in
+				let new_ecall = if fparams <> [] then gen.gparam_func_call new_ecall { e1 with eexpr = TField(!ef, f) } fparams elist else new_ecall in
+				let ret = handle_cast gen new_ecall (gen.greal_type ecall.etype) (gen.greal_type ret_ft) in
+				(match gen.gcon.platform, cf.cf_params, ret.eexpr with
+					| _, _, TCast _ -> ret
+					| Java, _ :: _, _ ->
+						(* this is a workaround for a javac openjdk issue with unused type parameters and return type inference *)
+						(* see more at issue #3123 *)
+						mk_cast (gen.greal_type ret_ft) new_ecall
+					| _ -> ret)
+			end
+	| FClassField (cl,params,_,{ cf_kind = (Method MethDynamic | Var _) },_,actual_t,_) ->
+		(* if it's a var, we will just try to apply the class parameters that have been changed with greal_type_param *)
+		let t = apply_params cl.cl_params (gen.greal_type_param (TClassDecl cl) params) (gen.greal_type actual_t) in
+		return_var (handle_cast gen { e1 with eexpr = TField(ef, f) } (gen.greal_type e1.etype) (gen.greal_type t))
+	| FClassField (cl,params,_,cf,_,actual_t,_) ->
+		return_var (handle_cast gen { e1 with eexpr = TField({ ef with etype = t_dynamic }, f) } e1.etype t_dynamic) (* force dynamic and cast back to needed type *)
+	| FEnumField (en, efield, true) ->
+		let ecall = match e with | None -> trace (field_name f); trace efield.ef_name; gen.gcon.error "This field should be called immediately" ef.epos; assert false | Some ecall -> ecall in
+		(match en.e_params with
+			(*
+			| [] ->
+				let args, ret = get_fun (efield.ef_type) in
+				let ef = { ef with eexpr = TTypeExpr( TEnumDecl en ); etype = TEnum(en, []) } in
+				handle_cast gen { ecall with eexpr = TCall({ e1 with eexpr = TField(ef, FEnum(en, efield)) }, List.map2 (fun param (_,_,t) -> handle_cast gen param (gen.greal_type t) (gen.greal_type param.etype)) elist args) } (gen.greal_type ecall.etype) (gen.greal_type ret)
+		*)
+			| _ ->
+				let pt = match e with | None -> real_type | Some _ -> snd (get_fun e1.etype) in
+				let _params = match follow pt with | TEnum(_, p) -> p | _ -> gen.gcon.warning (debug_expr e1) e1.epos; assert false in
+				let args, ret = get_fun efield.ef_type in
+				let actual_t = TFun(List.map (fun (n,o,t) -> (n,o,gen.greal_type t)) args, gen.greal_type ret) in
+				(*
+					because of differences on how <Dynamic> is handled on the platforms, this is a hack to be able to
+					correctly use class field type parameters with RealTypeParams
+				*)
+				let cf_params = List.map (fun t -> match follow t with | TDynamic _ -> t_empty | _ -> t) _params in
+				let t = apply_params en.e_params (gen.greal_type_param (TEnumDecl en) cf_params) actual_t in
+				let t = apply_params efield.ef_params (List.map (fun _ -> t_dynamic) efield.ef_params) t in
+
+				let args, ret = get_fun t in
+
+				let elist = List.map2 (fun param (_,_,t) -> handle_cast gen (param) (gen.greal_type t) (gen.greal_type param.etype)) elist args in
+				let e1 = { e1 with eexpr = TField({ ef with eexpr = TTypeExpr( TEnumDecl en ); etype = TEnum(en, _params) }, FEnum(en, efield) ) } in
+				let new_ecall = gen.gparam_func_call ecall e1 _params elist in
+
+				handle_cast gen new_ecall (gen.greal_type ecall.etype) (gen.greal_type ret)
+		)
+	| FEnumField _ when is_some e -> assert false
+	| FEnumField (en,efield,_) ->
+			return_var { e1 with eexpr = TField({ ef with eexpr = TTypeExpr( TEnumDecl en ); },FEnum(en,efield)) }
+	(* no target by date will uses this.so this code may not be correct at all *)
+	| FAnonField cf ->
+		let t = gen.greal_type cf.cf_type in
+		return_var (handle_cast gen { e1 with eexpr = TField(ef, f) } (gen.greal_type e1.etype) t)
+	| FNotFound
+	| FDynamicField _ ->
+		if is_some e then
+			return_var { e1 with eexpr = TField(ef, f) }
+		else
+			return_var (handle_cast gen { e1 with eexpr = TField({ ef with etype = t_dynamic }, f) } e1.etype t_dynamic) (* force dynamic and cast back to needed type *)
+	)
+
+(* end of type parameter handling *)
+(* ****************************** *)
+
+(** overloads_cast_to_base argument will cast overloaded function types to the class that declared it. **)
+(**			This is necessary for C#, and if true, will require the target to implement __as__, as a `quicker` form of casting **)
+let configure gen ?(overloads_cast_to_base = false) maybe_empty_t calls_parameters_explicitly =
+	let handle e t1 t2 = handle_cast gen e (gen.greal_type t1) (gen.greal_type t2) in
+
+	let in_value = ref false in
+
+	let rec clean_cast e = match e.eexpr with
+		| TCast(e,_) -> clean_cast e
+		| TParenthesis(e) | TMeta(_,e) -> clean_cast e
+		| _ -> e
+	in
+
+	let get_abstract_impl t = match t with
+		| TAbstract(a,pl) when not (Meta.has Meta.CoreType a.a_meta) ->
+			Abstract.get_underlying_type a pl
+		| t -> t
+	in
+
+	let rec is_abstract_to_struct t = match t with
+		| TAbstract(a,pl) when not (Meta.has Meta.CoreType a.a_meta) ->
+			is_abstract_to_struct (Abstract.get_underlying_type a pl)
+		| TInst(c,_) when Meta.has Meta.Struct c.cl_meta ->
+			true
+		| _ -> false
+	in
+
+	let binop_type op main_expr e1 e2 =
+		let name = platform_name gen.gcon.platform in
+		let basic = gen.gcon.basic in
+		(* If either operand is of type decimal, the other operand is converted to type decimal, or a compile-time error occurs if the other operand is of type float or double.
+			* Otherwise, if either operand is of type double, the other operand is converted to type double.
+			* Otherwise, if either operand is of type float, the other operand is converted to type float.
+			* Otherwise, if either operand is of type ulong, the other operand is converted to type ulong, or a compile-time error occurs if the other operand is of type sbyte, short, int, or long.
+			* Otherwise, if either operand is of type long, the other operand is converted to type long.
+			* Otherwise, if either operand is of type uint and the other operand is of type sbyte, short, or int, both operands are converted to type long.
+			* Otherwise, if either operand is of type uint, the other operand is converted to type uint.
+			* Otherwise, both operands are converted to type int.
+			*  *)
+		let t1, t2 = follow (run_follow gen e1.etype), follow (run_follow gen e2.etype) in
+		match t1, t2 with
+			| TAbstract(a1,[]), TAbstract(a2,[]) when a1 == a2 ->
+				{ main_expr with eexpr = TBinop(op, e1, e2); etype = e1.etype }
+			| TInst(i1,[]), TInst(i2,[]) when i1 == i2 ->
+				{ main_expr with eexpr = TBinop(op, e1, e2); etype = e1.etype }
+			| TInst({ cl_path = ([],"String") },[]), _ when op = OpAdd ->
+				{ main_expr with eexpr = TBinop(op, e1, mk_cast basic.tstring e2); etype = basic.tstring }
+			| _, TInst({ cl_path = ([],"String") },[]) when op = OpAdd ->
+				{ main_expr with eexpr = TBinop(op, mk_cast basic.tstring e1, e2); etype = basic.tstring }
+			| TAbstract({ a_path = ([], "Float") }, []), _ ->
+				{ main_expr with eexpr = TBinop(op, e1, e2); etype = e1.etype }
+			| _, TAbstract({ a_path = ([], "Float") }, []) ->
+				{ main_expr with eexpr = TBinop(op, e1, e2); etype = e2.etype }
+			| TAbstract({ a_path = ([], "Single") }, []), _ ->
+				{ main_expr with eexpr = TBinop(op, e1, e2); etype = e1.etype }
+			| _, TAbstract({ a_path = ([], "Single") }, []) ->
+				{ main_expr with eexpr = TBinop(op, e1, e2); etype = e2.etype }
+			| TAbstract({ a_path = ([pf], "UInt64") }, []), _ when pf = name ->
+				{ main_expr with eexpr = TBinop(op, e1, e2); etype = e1.etype }
+			| _, TAbstract({ a_path = ([pf], "UInt64") }, []) when pf = name ->
+				{ main_expr with eexpr = TBinop(op, e1, e2); etype = e2.etype }
+			| TAbstract({ a_path = ([pf], "Int64") }, []), _ when pf = name ->
+				{ main_expr with eexpr = TBinop(op, e1, e2); etype = e1.etype }
+			| _, TAbstract({ a_path = ([pf], "Int64") }, []) when pf = name ->
+				{ main_expr with eexpr = TBinop(op, e1, e2); etype = e2.etype }
+			| TAbstract({ a_path = ([], "UInt") }, []), tother when like_int tother ->
+				let ti64 = mt_to_t_dyn ( get_type gen ([name], "Int64") ) in
+				let ret = { main_expr with eexpr = TBinop(op, e1, e2); etype = ti64 } in
+				if op <> OpDiv then
+					mk_cast t1 ret
+				else
+					ret
+			| tother, TAbstract({ a_path = ([], "UInt") }, []) when like_int tother ->
+				let ti64 = mt_to_t_dyn ( get_type gen ([name], "Int64") ) in
+				let ret = { main_expr with eexpr = TBinop(op, e1, e2); etype = ti64 } in
+				if op <> OpDiv then
+					mk_cast t2 ret
+				else
+					ret
+			| TAbstract({ a_path = ([], "UInt") }, []), _ ->
+				{ main_expr with eexpr = TBinop(op, e1, e2); etype = e1.etype }
+			| _, TAbstract({ a_path = ([], "UInt") }, []) ->
+				{ main_expr with eexpr = TBinop(op, e1, e2); etype = e2.etype }
+			| TAbstract(a1,[]), TAbstract(a2,[]) ->
+				{ main_expr with eexpr = TBinop(op, e1, e2); etype = basic.tint }
+			| _ ->
+				{ main_expr with eexpr = TBinop(op, e1, e2) }
+	in
+	let binop_type = if Common.defined gen.gcon Define.FastCast then
+		binop_type
+	else
+		fun op main_expr e1 e2 -> { main_expr with eexpr = TBinop(op, e1, e2) }
+	in
+
+	let rec run ?(just_type = false) e =
+		let handle = if not just_type then handle else fun e t1 t2 -> { e with etype = gen.greal_type t2 } in
+		let was_in_value = !in_value in
+		in_value := true;
+		match e.eexpr with
+			| TConst ( TInt _ | TFloat _ | TBool _ as const ) ->
+				(* take off any Null<> that it may have *)
+				let t = follow (run_follow gen e.etype) in
+				(* do not allow constants typed as Single - need to cast them *)
+				let real_t = match const with
+					| TInt _ -> gen.gcon.basic.tint
+					| TFloat _ -> gen.gcon.basic.tfloat
+					| TBool _ -> gen.gcon.basic.tbool
+					| _ -> assert false
+				in
+				handle e t real_t
+			| TCast( { eexpr = TConst TNull }, _ ) ->
+				{ e with eexpr = TConst TNull }
+			| TCast( { eexpr = TCall( { eexpr = TIdent "__delegate__" } as local, [del] ) } as e2, _) ->
+				{ e with eexpr = TCast({ e2 with eexpr = TCall(local, [Type.map_expr run del]) }, None) }
+
+			| TBinop ( (Ast.OpAssign | Ast.OpAssignOp _ as op), e1, e2 ) ->
+				let e1 = run ~just_type:true e1 in
+				let e2 = handle (run e2) e1.etype e2.etype in
+				{ e with eexpr = TBinop(op, clean_cast e1, e2) }
+			| TBinop ( (Ast.OpShl | Ast.OpShr | Ast.OpUShr as op), e1, e2 ) ->
+				let e1 = run e1 in
+				let e2 = handle (run e2) (gen.gcon.basic.tint) e2.etype in
+				let rett = binop_type op e e1 e2 in
+				{ e with eexpr = TBinop(op, e1, e2); etype = rett.etype }
+			| TBinop( (OpAdd | OpMult | OpDiv | OpSub | OpAnd | OpOr | OpXor | OpMod) as op, e1, e2 ) ->
+				binop_type op e (run e1) (run e2)
+			| TBinop( (OpEq | OpNotEq | OpGt | OpGte | OpLt | OpLte | OpBoolAnd | OpBoolOr) as op, e1, e2 ) ->
+				handle { e with eexpr = TBinop(op, run e1, run e2) } e.etype gen.gcon.basic.tbool
+			| TField(ef, f) ->
+				handle_type_parameter gen None e (run ef) ~clean_ef:ef ~overloads_cast_to_base:overloads_cast_to_base f [] calls_parameters_explicitly
+			| TArrayDecl el ->
+				let et = e.etype in
+				let base_type = match follow et with
+					| TInst({ cl_path = ([], "Array") } as cl, bt) -> gen.greal_type_param (TClassDecl cl) bt
+					| _ ->
+						gen.gcon.warning (debug_type et) e.epos;
+						(match gen.gcurrent_class with
+							| Some cl -> print_endline (s_type_path cl.cl_path)
+							| _ -> ());
+						assert false
+				in
+				let base_type = List.hd base_type in
+				{ e with eexpr = TArrayDecl( List.map (fun e -> handle (run e) base_type e.etype) el ); etype = et }
+			| TCall ({ eexpr = TIdent "__array__" } as arr_local, el) ->
+				let et = e.etype in
+				let base_type = match follow et with
+					| TInst(cl, bt) -> gen.greal_type_param (TClassDecl cl) bt
+					| _ -> assert false
+				in
+				let base_type = List.hd base_type in
+				{ e with eexpr = TCall(arr_local, List.map (fun e -> handle (run e) base_type e.etype) el ); etype = et }
+			| TCall( ({ eexpr = TIdent s } as local), params ) when String.get s 0 = '_' && String.get s 1 = '_' && Hashtbl.mem gen.gspecial_vars s ->
+				{ e with eexpr = TCall(local, List.map (fun e -> (match e.eexpr with TBlock _ -> in_value := false | _ -> ()); run e) params) }
+			| TCall( ({ eexpr = TField(ef, f) }) as e1, elist ) ->
+				handle_type_parameter gen (Some e) (e1) (run ef) ~clean_ef:ef ~overloads_cast_to_base:overloads_cast_to_base f (List.map run elist) calls_parameters_explicitly
+
+			| TCall( { eexpr = TConst TSuper } as ef, eparams ) ->
+				let cl, tparams = match follow ef.etype with
+				| TInst(cl,p) ->
+					cl,p
+				| _ -> assert false in
+				(try
+					let is_overload, cf, sup, stl = choose_ctor gen cl tparams (List.map (fun e -> e.etype) eparams) maybe_empty_t e.epos in
+					let handle e t1 t2 =
+						if is_overload then
+							let ret = handle e t1 t2 in
+							match ret.eexpr with
+							| TCast _ -> ret
+							| _ -> mk_cast (gen.greal_type t1) e
+						else
+							handle e t1 t2
+					in
+					let stl = gen.greal_type_param (TClassDecl sup) stl in
+					let args, _ = get_fun (apply_params sup.cl_params stl cf.cf_type) in
+					let eparams = List.map2 (fun e (_,_,t) ->
+						handle (run e) t e.etype
+					) eparams args in
+					{ e with eexpr = TCall(ef, eparams) }
+				with | Not_found ->
+					gen.gcon.warning "No overload found for this constructor call" e.epos;
+					{ e with eexpr = TCall(ef, List.map run eparams) })
+			| TCall (ef, eparams) ->
+				(match ef.etype with
+					| TFun(p, ret) ->
+						handle ({ e with eexpr = TCall(run ef, List.map2 (fun param (_,_,t) -> handle (run param) t param.etype) eparams p) }) e.etype ret
+					| _ -> Type.map_expr run e
+				)
+			| TNew ({ cl_kind = KTypeParameter _ }, _, _) ->
+				Type.map_expr run e
+			| TNew (cl, tparams, eparams) -> (try
+				let is_overload, cf, sup, stl = choose_ctor gen cl tparams (List.map (fun e -> e.etype) eparams) maybe_empty_t e.epos in
+				let handle e t1 t2 =
+					if is_overload then
+						let ret = handle e t1 t2 in
+						match ret.eexpr with
+						| TCast _ -> ret
+						| _ -> mk_cast (gen.greal_type t1) e
+					else
+						handle e t1 t2
+				in
+				let stl = gen.greal_type_param (TClassDecl sup) stl in
+				let args, _ = get_fun (apply_params sup.cl_params stl cf.cf_type) in
+				let eparams = List.map2 (fun e (_,_,t) ->
+					handle (run e) t e.etype
+				) eparams args in
+				{ e with eexpr = TNew(cl, tparams, eparams) }
+			with | Not_found ->
+				gen.gcon.warning "No overload found for this constructor call" e.epos;
+				{ e with eexpr = TNew(cl, tparams, List.map run eparams) })
+			| TArray(arr, idx) ->
+				let arr_etype = match follow arr.etype with
+					| (TInst _ as t) -> t
+					| TAbstract (a, pl) when not (Meta.has Meta.CoreType a.a_meta) ->
+						follow (Abstract.get_underlying_type a pl)
+					| t -> t
+				in
+				let idx = run idx in
+				let idx = match gen.greal_type idx.etype with
+					| TAbstract({ a_path = [],"Int" },_) -> idx
+					| _ -> match handle idx gen.gcon.basic.tint (gen.greal_type idx.etype) with
+						| ({ eexpr = TCast _ } as idx) -> idx
+						| idx -> mk_cast gen.gcon.basic.tint idx
+				in
+				let e = { e with eexpr = TArray(run arr, idx) } in
+				(* get underlying class (if it's a class *)
+				(match arr_etype with
+					| TInst(cl, params) ->
+						(* see if it implements ArrayAccess *)
+						(match cl.cl_array_access with
+							| None -> e
+							| Some t ->
+								(* if it does, apply current parameters (and change them) *)
+								(* let real_t = apply_params_internal (List.map (gen.greal_type_param (TClassDecl cl))) cl params t in *)
+								let param = apply_params cl.cl_params (gen.greal_type_param (TClassDecl cl) params) t in
+								let real_t = apply_params cl.cl_params params param in
+								(* see if it needs a cast *)
+
+								fastcast_if_needed gen e (gen.greal_type e.etype) (gen.greal_type real_t)
+								(* handle (e) (gen.greal_type e.etype) (gen.greal_type real_t) *)
+						)
+					| _ -> Type.map_expr run e)
+			| TVar (v, eopt) ->
+				{ e with eexpr = TVar (v, match eopt with
+						| None -> eopt
+						| Some e -> Some( handle (run e) v.v_type e.etype ))
+				}
+			(* FIXME deal with in_value when using other statements that may not have a TBlock wrapped on them *)
+			| TIf (econd, ethen, Some(eelse)) when was_in_value ->
+				{ e with eexpr = TIf (handle (run econd) gen.gcon.basic.tbool econd.etype, handle (run ethen) e.etype ethen.etype, Some( handle (run eelse) e.etype eelse.etype ) ) }
+			| TIf (econd, ethen, eelse) ->
+				{ e with eexpr = TIf (handle (run econd) gen.gcon.basic.tbool econd.etype, (in_value := false; run (mk_block ethen)), Option.map (fun e -> in_value := false; run (mk_block e)) eelse) }
+			| TWhile (econd, e1, flag) ->
+				{ e with eexpr = TWhile (handle (run econd) gen.gcon.basic.tbool econd.etype, (in_value := false; run (mk_block e1)), flag) }
+			| TSwitch (cond, el_e_l, edef) ->
+				{ e with eexpr = TSwitch(run cond, List.map (fun (el,e) -> (List.map run el, (in_value := false; run (mk_block e)))) el_e_l, Option.map (fun e -> in_value := false; run (mk_block e)) edef) }
+			| TFor (v,cond,e1) ->
+				{ e with eexpr = TFor(v, run cond, (in_value := false; run (mk_block e1))) }
+			| TTry (e, ve_l) ->
+				{ e with eexpr = TTry((in_value := false; run (mk_block e)), List.map (fun (v,e) -> in_value := false; (v, run (mk_block e))) ve_l) }
+			| TBlock el ->
+				let i = ref 0 in
+				let len = List.length el in
+				{ e with eexpr = TBlock ( List.map (fun e ->
+					incr i;
+					if !i <> len || not was_in_value then
+						in_value := false;
+					run e
+				) el ) }
+			| TCast (expr, md) when ExtType.is_void (follow e.etype) ->
+				run expr
+			| TCast (expr, md) ->
+				let rec get_null e =
+					match e.eexpr with
+					| TConst TNull -> Some e
+					| TParenthesis e | TMeta(_,e) -> get_null e
+					| _ -> None
+				in
+
+				(match get_null expr with
+				| Some enull ->
+						if gen.gcon.platform = Cs then
+							{ enull with etype = gen.greal_type e.etype }
+						else
+							mk_cast (gen.greal_type e.etype) enull
+				| _ when is_abstract_to_struct expr.etype && type_iseq gen e.etype (get_abstract_impl expr.etype) ->
+					run { expr with etype = expr.etype }
+				| _ when is_exactly_basic gen expr.etype e.etype ->
+					run { expr with etype = expr.etype }
+				| _ ->
+					match gen.greal_type e.etype, gen.greal_type expr.etype with
+						| (TInst(c,tl) as tinst1), TAbstract({ a_path = ["cs"],"Pointer" }, [tinst2]) when type_iseq gen tinst1 (gen.greal_type tinst2) ->
+							run expr
+						| _ ->
+							let expr = run expr in
+							let last_unsafe = gen.gon_unsafe_cast in
+							gen.gon_unsafe_cast <- (fun t t2 pos -> ());
+							let ret = handle expr e.etype expr.etype in
+							gen.gon_unsafe_cast <- last_unsafe;
+							match ret.eexpr with
+								| TCast _ -> { ret with etype = gen.greal_type e.etype }
+								| _ -> { e with eexpr = TCast(ret,md); etype = gen.greal_type e.etype }
+				)
+			(*| TCast _ ->
+				(* if there is already a cast, we should skip this cast check *)
+				Type.map_expr run e*)
+			| TFunction f ->
+				in_value := false;
+				Type.map_expr run e
+
+			| _ -> Type.map_expr run e
+	in
+	gen.ghandle_cast <- (fun tto tfrom expr -> handle_cast gen expr (gen.greal_type tto) (gen.greal_type tfrom));
+	let map e =
+		match gen.gcurrent_classfield with
+		| Some cf when Meta.has (Meta.Custom ":skipCastDetect") cf.cf_meta ->
+			e
+		| _ ->
+			run e
+	in
+	gen.gsyntax_filters#add name (PCustom priority) map;
+	ReturnCast.configure gen

+ 57 - 0
src/generators/gencommon/classInstance.ml

@@ -0,0 +1,57 @@
+(*
+	The Haxe Compiler
+	Copyright (C) 2005-2017  Haxe Foundation
+
+	This program is free software; you can redistribute it and/or
+	modify it under the terms of the GNU General Public License
+	as published by the Free Software Foundation; either version 2
+	of the License, or (at your option) any later version.
+
+	This program is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+
+	You should have received a copy of the GNU General Public License
+	along with this program; if not, write to the Free Software
+	Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+*)
+open Type
+open Gencommon
+
+(*
+	When we pass a class as an object, in some languages we will need a special construct to be able to
+	access its statics as if they were normal object fields. On C# and Java the way found to do that is
+	by handling statics reflection also by a normal instance. This also happens in hxcpp and neko, so I
+	guess it's a valid practice.
+
+	So if we want to handle the reflection of the static MyClass, here's roughly how it will be done:
+
+	var x = MyClass;
+	gets converted into
+	var x = typeof(MyClass);
+*)
+let add_typeof =
+	let rec run e =
+		match e.eexpr with
+		| TCall (({ eexpr = TIdent ("__is__" | "__as__" | "__typeof__") } as elocal), args) ->
+			let args = List.map (fun e -> match e.eexpr with TTypeExpr _ -> e | _ -> run e) args in
+			{ e with eexpr = TCall (elocal, args) }
+		| TField ({ eexpr = TTypeExpr _ }, _) ->
+			e
+		| TField (ef, f) ->
+			(match anon_class ef.etype with
+			| None -> Type.map_expr run e
+			| Some t -> { e with eexpr = TField ({ ef with eexpr = TTypeExpr t }, f)})
+		| TTypeExpr _ ->
+			{ e with eexpr = TCall (mk (TIdent "__typeof__") t_dynamic e.epos, [e]) }
+		| _ ->
+			Type.map_expr run e
+	in
+	run
+
+let name = "class_instance"
+let priority = solve_deps name []
+
+let configure gen =
+	gen.gsyntax_filters#add name (PCustom priority) add_typeof

+ 1131 - 0
src/generators/gencommon/closuresToClass.ml

@@ -0,0 +1,1131 @@
+(*
+	The Haxe Compiler
+	Copyright (C) 2005-2017  Haxe Foundation
+
+	This program is free software; you can redistribute it and/or
+	modify it under the terms of the GNU General Public License
+	as published by the Free Software Foundation; either version 2
+	of the License, or (at your option) any later version.
+
+	This program is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+
+	You should have received a copy of the GNU General Public License
+	along with this program; if not, write to the Free Software
+	Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+*)
+open Option
+open Common
+open Globals
+open Codegen
+open Ast
+open Type
+open Gencommon
+
+(* ******************************************* *)
+(* Closures To Class *)
+(* ******************************************* *)
+(*
+
+	This is a very important filter. It will take all anonymous functions from the AST, will search for all captured variables, and will create a class
+	that implements an abstract interface for calling functions. This is very important for targets that don't support anonymous functions to work correctly.
+	Also it is possible to implement some strategies to avoid value type boxing, such as NaN tagging or double/object arguments. All this will be abstracted away
+	from this interface.
+
+
+	dependencies:
+		must run after dynamic field access, because of conflicting ways to deal with invokeField
+		(module filter) must run after OverloadingConstructor so we can also change the dynamic function expressions
+
+		uses TArray expressions for array. TODO see interaction
+		uses TThrow expressions.
+*)
+let name = "closures_to_class"
+let priority = solve_deps name [ DAfter DynamicFieldAccess.priority ]
+
+type closures_ctx = {
+	func_class : tclass;
+
+	(*
+		this is what will actually turn the function into class field.
+		The standard implementation by default will already take care of creating the class, and setting the captured variables.
+
+		It will also return the super arguments to be called
+	*)
+	closure_to_classfield : tfunc->t->pos->tclass_field * (texpr list);
+
+	(*
+		when a dynamic function call is made, we need to convert it as if it were calling the dynamic function interface.
+
+		TCall expr -> new TCall expr
+	*)
+	dynamic_fun_call : texpr->texpr;
+
+	(*
+		Provide a toolchain so we can easily create classes that extend Function and add more functionality on top of it.
+
+		arguments:
+			tclass -> subject (so we know the type of this)
+			( int -> (int->t->tconstant option->texpr) -> ( (tvar * tconstant option) list * texpr) )
+				int -> current arity of the function whose member will be mapped; -1 for dynamic function. It is guaranteed that dynamic function will be called last
+				t -> the return type of the function
+				(int->t->tconstant option->texpr) -> api to get exprs that unwrap arguments correctly
+					int -> argument wanted to unwrap
+					t -> solicited type
+					tconstant option -> map to this default value if null
+					returns a texpr that tells how the default
+				should return a list with additional arguments (only works if is_function_base = true)
+				and the underlying function expression
+	*)
+	map_base_classfields : tclass->( int -> t -> (tvar list) -> (int->t->tconstant option->texpr) -> texpr )->tclass_field list;
+}
+
+type map_info = {
+	in_unsafe : bool;
+	in_unused : bool;
+}
+
+let null_map_info = { in_unsafe = false; in_unused = false; }
+
+(*
+	the default implementation will take 3 transformation functions:
+		* one that will transform closures that are not called immediately (instance.myFunc).
+			normally on this case it's best to have a runtime handler that will take the instance, the function and call its invokeField when invoked
+		* one that will actually handle the anonymous functions themselves.
+		* one that will transform calling a dynamic function. So for example, dynFunc(arg1, arg2) might turn into dynFunc.apply2(arg1, arg2);
+		( suspended ) * an option to match papplied functions
+		* handling parameterized anonymous function declaration (optional - tparam_anon_decl and tparam_anon_acc)
+*)
+
+let rec cleanup_delegate e = match e.eexpr with
+	| TParenthesis e | TMeta(_,e)
+	| TCast(e,_) -> cleanup_delegate e
+	| _ -> e
+
+let funct gen t = match follow (run_follow gen t) with
+	| TFun(args,ret) -> args,ret
+	| _ -> raise Not_found
+
+let mk_conversion_fun gen e =
+	let args, ret = funct gen e.etype in
+	let tf_args = List.map (fun (n,o,t) -> alloc_var n t,None) args in
+	let block, local = match e.eexpr with
+		| TLocal v ->
+			v.v_capture <- true;
+			[],e
+		| _ ->
+			let tmp = mk_temp "delegate_conv" e.etype in
+			tmp.v_capture <- true;
+			[{ eexpr = TVar(tmp,Some e); etype = gen.gcon.basic.tvoid; epos = e.epos }], mk_local tmp e.epos
+	in
+	let body = {
+		eexpr = TCall(local, List.map (fun (v,_) -> mk_local v e.epos) tf_args);
+		etype = ret;
+		epos = e.epos;
+	} in
+	let body = if not (ExtType.is_void ret) then
+		mk_return body
+	else
+		body
+	in
+	let body = {
+		eexpr = TBlock([body]);
+		etype = body.etype;
+		epos = body.epos;
+	} in
+	block, {
+		tf_args = tf_args;
+		tf_expr = body;
+		tf_type = ret;
+	}
+
+let traverse gen ?tparam_anon_decl ?tparam_anon_acc (handle_anon_func:texpr->tfunc->map_info->t option->texpr) (dynamic_func_call:texpr->texpr) e =
+	let info = ref null_map_info in
+	let rec run e =
+		match e.eexpr with
+			| TCast({ eexpr = TCall({ eexpr = TIdent "__delegate__" } as local, [del] ) } as e2, _) ->
+				let e2 = { e2 with etype = e.etype } in
+				let replace_delegate ex =
+					{ e with eexpr = TCast({ e2 with eexpr = TCall(local, [ex]) }, None) }
+				in
+				(* found a delegate; let's see if it's a closure or not *)
+				let clean = cleanup_delegate del in
+				(match clean.eexpr with
+					| TField( ef, (FClosure _ as f)) | TField( ef, (FStatic _ as f)) ->
+						(* a closure; let's leave this unchanged for FilterClosures to handle it *)
+						replace_delegate { clean with eexpr = TField( run ef, f ) }
+					| TFunction tf ->
+						(* handle like we'd handle a normal function, but create an unchanged closure field for it *)
+						let ret = handle_anon_func clean { tf with tf_expr = run tf.tf_expr } !info (Some e.etype) in
+						replace_delegate ret
+					| _ -> try
+						let block, tf = mk_conversion_fun gen del in
+						let block = List.map run block in
+						let tf = { tf with tf_expr = run tf.tf_expr } in
+						let ret = handle_anon_func { clean with eexpr = TFunction(tf) } { tf with tf_expr = run tf.tf_expr } !info (Some e.etype) in
+						let ret = replace_delegate ret in
+						if block = [] then
+							ret
+						else
+							{ ret with eexpr = TBlock(block @ [ret]) }
+					with Not_found ->
+						gen.gcon.error "This delegate construct is unsupported" e.epos;
+						replace_delegate (run clean))
+
+			| TCall(({ eexpr = TIdent "__unsafe__" } as local), [arg]) ->
+				let old = !info in
+				info := { !info with in_unsafe = true };
+				let arg2 = run arg in
+				info := old;
+				{ e with eexpr = TCall(local,[arg2]) }
+			(* parameterized functions handling *)
+			| TVar(vv, ve) -> (match tparam_anon_decl with
+				| None -> Type.map_expr run e
+				| Some tparam_anon_decl ->
+					(match (vv, ve) with
+						| ({ v_extra = Some( _ :: _, _) } as v), Some ({ eexpr = TFunction tf } as f)
+						| ({ v_extra = Some( _ :: _, _) } as v), Some { eexpr = TArrayDecl([{ eexpr = TFunction tf } as f]) | TCall({ eexpr = TIdent "__array__" }, [{ eexpr = TFunction tf } as f]) } -> (* captured transformation *)
+							ignore(tparam_anon_decl v f { tf with tf_expr = run tf.tf_expr });
+							{ e with eexpr = TBlock([]) }
+						| _ ->
+							Type.map_expr run { e with eexpr = TVar(vv, ve) })
+					)
+			| TLocal ({ v_extra = Some( _ :: _, _) } as v)
+			| TArray ({ eexpr = TLocal ({ v_extra = Some( _ :: _, _) } as v) }, _) -> (* captured transformation *)
+				(match tparam_anon_acc with
+				| None -> Type.map_expr run e
+				| Some tparam_anon_acc -> tparam_anon_acc v e)
+			| TCall( { eexpr = TField(_, FEnum _) }, _ ) ->
+				Type.map_expr run e
+			(* if a TClosure is being call immediately, there's no need to convert it to a TClosure *)
+			| TCall(( { eexpr = TField(ecl,f) } as e1), params) ->
+				(* check to see if called field is known and if it is a MethNormal (only MethNormal fields can be called directly) *)
+				(* let name = field_name f in *)
+				(match field_access_esp gen (gen.greal_type ecl.etype) f with
+					| FClassField(_,_,_,cf,_,_,_) ->
+						(match cf.cf_kind with
+							| Method MethNormal
+							| Method MethInline ->
+								{ e with eexpr = TCall({ e1 with eexpr = TField(run ecl, f) }, List.map run params) }
+							| _ ->
+								match gen.gfollow#run_f e1.etype with
+									| TFun _ ->
+										dynamic_func_call { e with eexpr = TCall(run e1, List.map run params) }
+									| _ ->
+										let i = ref 0 in
+										let t = TFun(List.map (fun e -> incr i; "arg" ^ (string_of_int !i), false, e.etype) params, e.etype) in
+										dynamic_func_call { e with eexpr = TCall( mk_castfast t (run e1), List.map run params ) }
+						)
+					(* | FNotFound ->
+						{ e with eexpr = TCall({ e1 with eexpr = TField(run ecl, f) }, List.map run params) }
+							(* expressions by now may have generated invalid expressions *) *)
+					| _ ->
+						match gen.gfollow#run_f e1.etype with
+							| TFun _ ->
+								dynamic_func_call { e with eexpr = TCall(run e1, List.map run params) }
+							| _ ->
+								let i = ref 0 in
+								let t = TFun(List.map (fun e -> incr i; "arg" ^ (string_of_int !i), false, e.etype) params, e.etype) in
+								dynamic_func_call { e with eexpr = TCall( mk_castfast t (run e1), List.map run params ) }
+				)
+			| TFunction tf ->
+				handle_anon_func e { tf with tf_expr = run tf.tf_expr } !info None
+			| TCall({ eexpr = TConst(TSuper) }, _) ->
+				Type.map_expr run e
+			| TCall({ eexpr = TIdent s }, args) when String.get s 0 = '_' && Hashtbl.mem gen.gspecial_vars s ->
+				Type.map_expr run e
+			| TCall(tc,params) ->
+				let i = ref 0 in
+				let may_cast = match gen.gfollow#run_f tc.etype with
+					| TFun _ -> fun e -> e
+					| _ ->
+						let t = TFun(List.map (fun e ->
+								incr i;
+								("p" ^ (string_of_int !i), false, e.etype)
+							) params, e.etype)
+						in
+						fun e -> mk_castfast t e
+				in
+				dynamic_func_call { e with eexpr = TCall(run (may_cast tc), List.map run params) }
+			| _ -> Type.map_expr run e
+	in
+
+	(match e.eexpr with
+		| TFunction(tf) -> Type.map_expr run e
+		| _ -> run e)
+
+let rec get_type_params acc t =
+	match t with
+		| TInst(( { cl_kind = KTypeParameter _ } as cl), []) ->
+			if List.memq cl acc then acc else cl :: acc
+		| TFun (params,tret) ->
+			List.fold_left get_type_params acc ( tret :: List.map (fun (_,_,t) -> t) params )
+		| TDynamic t ->
+			(match t with | TDynamic _ -> acc | _ -> get_type_params acc t)
+		| TAbstract (a, pl) when not (Meta.has Meta.CoreType a.a_meta) ->
+				get_type_params acc ( Abstract.get_underlying_type a pl)
+		| TAnon a ->
+			PMap.fold (fun cf acc ->
+				let params = List.map (fun (_,t) -> match follow t with
+					| TInst(c,_) -> c
+					| _ -> assert false) cf.cf_params
+				in
+				List.filter (fun t -> not (List.memq t params)) (get_type_params acc cf.cf_type)
+			) a.a_fields acc
+		| TType(_, [])
+		| TAbstract (_, [])
+		| TInst(_, [])
+		| TEnum(_, []) ->
+			acc
+		| TType(_, params)
+		| TAbstract(_, params)
+		| TEnum(_, params)
+		| TInst(_, params) ->
+			List.fold_left get_type_params acc params
+		| TMono r -> (match !r with
+			| Some t -> get_type_params acc t
+			| None -> acc)
+		| _ -> get_type_params acc (follow_once t)
+
+let get_captured expr =
+	let ret = Hashtbl.create 1 in
+	let ignored = Hashtbl.create 0 in
+
+	let params = ref [] in
+	let check_params t = params := get_type_params !params t in
+	let rec traverse expr =
+		match expr.eexpr with
+			| TFor (v, _, _) ->
+				Hashtbl.add ignored v.v_id v;
+				check_params v.v_type;
+				Type.iter traverse expr
+			| TFunction(tf) ->
+				List.iter (fun (v,_) -> Hashtbl.add ignored v.v_id v) tf.tf_args;
+				(match follow expr.etype with
+					| TFun(args,ret) ->
+						List.iter (fun (_,_,t) ->
+							check_params t
+						) args;
+						check_params ret
+					| _ -> ());
+				Type.iter traverse expr
+			| TVar (v, opt) ->
+				(match v.v_extra with
+					| Some(_ :: _, _) -> ()
+					| _ ->
+						check_params v.v_type);
+				Hashtbl.add ignored v.v_id v;
+				ignore(Option.map traverse opt)
+			| TLocal { v_extra = Some( (_ :: _ ),_) } ->
+				()
+			| TLocal(( { v_capture = true } ) as v) ->
+				(if not (Hashtbl.mem ignored v.v_id || Hashtbl.mem ret v.v_id) then begin check_params v.v_type; Hashtbl.replace ret v.v_id expr end);
+			| _ -> Type.iter traverse expr
+	in traverse expr;
+	ret, !params
+
+(*
+	OPTIMIZEME:
+
+	Take off from Codegen the code that wraps captured variables,
+
+	traverse through all variables, looking for their use (just like local_usage)
+	three possible outcomes for captured variables:
+		- become a function member variable <- best performance.
+			Will not work on functions that can be created more than once (functions inside a loop or functions inside functions)
+			The function will have to be created on top of the block, so its variables can be filled in instead of being declared
+		- single-element array - the most compatible way, though also creates a slight overhead.
+	- we'll have some labels for captured variables:
+		- used in loop
+*)
+
+(*
+	The default implementation will impose a naming convention:
+		invoke(arity)_(o for returning object/d for returning double) when arity < max_arity
+		invoke_dynamic_(o/d) when arity > max_arity
+
+	This means that it also imposes that the dynamic function return types may only be Dynamic or Float, and all other basic types must be converted to/from it.
+*)
+let configure gen ft =
+
+	let handle_anon_func fexpr tfunc mapinfo delegate_type : texpr * (tclass * texpr list) =
+		let fexpr = match fexpr.eexpr with
+			| TFunction(_) ->
+				{ fexpr with eexpr = TFunction(tfunc) }
+			| _ ->
+				gen.gcon.error "Function expected" fexpr.epos;
+				fexpr
+		in
+		let in_unsafe = mapinfo.in_unsafe || match gen.gcurrent_class, gen.gcurrent_classfield with
+			| Some c, _ when Meta.has Meta.Unsafe c.cl_meta -> true
+			| _, Some cf when Meta.has Meta.Unsafe cf.cf_meta -> true
+			| _ -> false
+		in
+		(* get all captured variables it uses *)
+		let captured_ht, tparams = get_captured fexpr in
+		let captured = Hashtbl.fold (fun _ e acc -> e :: acc) captured_ht [] in
+		let captured = List.sort (fun e1 e2 -> match e1, e2 with
+			| { eexpr = TLocal v1 }, { eexpr = TLocal v2 } ->
+				compare v1.v_name v2.v_name
+			| _ -> assert false) captured
+		in
+
+		(*let cltypes = List.map (fun cl -> (snd cl.cl_path, TInst(map_param cl, []) )) tparams in*)
+		let cltypes = List.map (fun cl -> (snd cl.cl_path, TInst(cl, []) )) tparams in
+
+		(* create a new class that extends abstract function class, with a ctor implementation that will setup all captured variables *)
+		let cfield = match gen.gcurrent_classfield with
+			| None -> "Anon"
+			| Some cf -> cf.cf_name
+		in
+		let cur_line = Lexer.get_error_line fexpr.epos in
+		let path = (fst gen.gcurrent_path, Printf.sprintf "%s_%s_%d__Fun" (snd gen.gcurrent_path) cfield cur_line) in
+		let cls = mk_class (get gen.gcurrent_class).cl_module path tfunc.tf_expr.epos in
+		if in_unsafe then cls.cl_meta <- (Meta.Unsafe,[],null_pos) :: cls.cl_meta;
+
+		if Common.defined gen.gcon Define.EraseGenerics then begin
+			cls.cl_meta <- (Meta.HaxeGeneric,[],null_pos) :: cls.cl_meta
+		end;
+		cls.cl_module <- (get gen.gcurrent_class).cl_module;
+		cls.cl_params <- cltypes;
+
+		let mk_this v pos =
+			{
+				(mk_field_access gen { eexpr = TConst TThis; etype = TInst(cls, List.map snd cls.cl_params); epos = pos } v.v_name pos)
+				with etype = v.v_type
+			}
+		in
+
+		let mk_this_assign v pos =
+		{
+			eexpr = TBinop(OpAssign, mk_this v pos, { eexpr = TLocal(v); etype = v.v_type; epos = pos });
+			etype = v.v_type;
+			epos = pos
+		} in
+
+		(* mk_class_field name t public pos kind params *)
+		let ctor_args, ctor_sig, ctor_exprs = List.fold_left (fun (ctor_args, ctor_sig, ctor_exprs) lexpr ->
+			match lexpr.eexpr with
+				| TLocal(v) ->
+					let cf = mk_class_field v.v_name v.v_type false lexpr.epos (Var({ v_read = AccNormal; v_write = AccNormal; })) [] in
+					cls.cl_fields <- PMap.add v.v_name cf cls.cl_fields;
+					cls.cl_ordered_fields <- cf :: cls.cl_ordered_fields;
+
+					let ctor_v = alloc_var v.v_name v.v_type in
+					((ctor_v, None) :: ctor_args, (v.v_name, false, v.v_type) :: ctor_sig, (mk_this_assign v cls.cl_pos) :: ctor_exprs)
+				| _ -> assert false
+		) ([],[],[]) captured in
+
+		(* change all captured variables to this.capturedVariable *)
+		let rec change_captured e =
+			match e.eexpr with
+				| TLocal( ({ v_capture = true }) as v ) when Hashtbl.mem captured_ht v.v_id ->
+					mk_this v e.epos
+				| _ -> Type.map_expr change_captured e
+		in
+		let func_expr = change_captured tfunc.tf_expr in
+
+		let invokecf, invoke_field, super_args = match delegate_type with
+			| None -> (* no delegate *)
+				let ifield, sa = ft.closure_to_classfield { tfunc with tf_expr = func_expr } fexpr.etype fexpr.epos in
+				ifield,ifield,sa
+			| Some _ ->
+				let pos = cls.cl_pos in
+				let cf = mk_class_field "Delegate" (TFun(fun_args tfunc.tf_args, tfunc.tf_type)) true pos (Method MethNormal) [] in
+				cf.cf_expr <- Some { fexpr with eexpr = TFunction { tfunc with tf_expr = func_expr }; };
+				cf.cf_meta <- (Meta.Final,[],pos) :: cf.cf_meta;
+				cls.cl_ordered_fields <- cf :: cls.cl_ordered_fields;
+				cls.cl_fields <- PMap.add cf.cf_name cf cls.cl_fields;
+				(* invoke function body: call Delegate function *)
+				let ibody = {
+					eexpr = TCall({
+						eexpr = TField({
+							eexpr = TConst TThis;
+							etype = TInst(cls, List.map snd cls.cl_params);
+							epos = pos;
+						}, FInstance(cls, List.map snd cls.cl_params, cf));
+						etype = cf.cf_type;
+						epos = pos;
+					}, List.map (fun (v,_) -> mk_local v pos) tfunc.tf_args);
+					etype = tfunc.tf_type;
+					epos = pos
+				} in
+				let ibody = if not (ExtType.is_void tfunc.tf_type) then
+					mk_return ibody
+				else
+					ibody
+				in
+				let ifield, sa = ft.closure_to_classfield { tfunc with tf_expr = ibody } fexpr.etype fexpr.epos in
+				cf,ifield,sa
+		in
+
+		(* create the constructor *)
+		(* todo properly abstract how type var is set *)
+
+		cls.cl_super <- Some(ft.func_class, []);
+		let pos = cls.cl_pos in
+		let super_call =
+		{
+			eexpr = TCall({ eexpr = TConst(TSuper); etype = TInst(ft.func_class,[]); epos = pos }, super_args);
+			etype = gen.gcon.basic.tvoid;
+			epos = pos;
+		} in
+
+		let ctor_type = (TFun(ctor_sig, gen.gcon.basic.tvoid)) in
+		let ctor = mk_class_field "new" ctor_type true cls.cl_pos (Method(MethNormal)) [] in
+		ctor.cf_expr <- Some(
+		{
+			eexpr = TFunction(
+			{
+				tf_args = ctor_args;
+				tf_type = gen.gcon.basic.tvoid;
+				tf_expr = { eexpr = TBlock(super_call :: ctor_exprs); etype = gen.gcon.basic.tvoid; epos = cls.cl_pos }
+			});
+			etype = ctor_type;
+			epos = cls.cl_pos;
+		});
+		cls.cl_constructor <- Some(ctor);
+
+		(* add invoke function to the class *)
+		cls.cl_ordered_fields <- invoke_field :: cls.cl_ordered_fields;
+		cls.cl_fields <- PMap.add invoke_field.cf_name invoke_field cls.cl_fields;
+		cls.cl_overrides <- invoke_field :: cls.cl_overrides;
+
+		gen.gadd_to_module (TClassDecl cls) priority;
+
+		(* if there are no captured variables, we can create a cache so subsequent calls don't need to create a new function *)
+		let expr, clscapt =
+			match captured, tparams with
+			| [], [] ->
+				let cache_var = mk_internal_name "hx" "current" in
+				let cache_cf = mk_class_field cache_var (TInst(cls,[])) false func_expr.epos (Var({ v_read = AccNormal; v_write = AccNormal })) [] in
+				cls.cl_ordered_statics <- cache_cf :: cls.cl_ordered_statics;
+				cls.cl_statics <- PMap.add cache_var cache_cf cls.cl_statics;
+
+				(* if (FuncClass.hx_current != null) FuncClass.hx_current; else (FuncClass.hx_current = new FuncClass()); *)
+
+				(* let mk_static_field_access cl field fieldt pos = *)
+				let hx_current = mk_static_field_access cls cache_var (TInst(cls,[])) func_expr.epos in
+
+				let pos = func_expr.epos in
+				{ fexpr with
+					etype = hx_current.etype;
+					eexpr = TIf(
+						{
+							eexpr = TBinop(OpNotEq, hx_current, null (TInst(cls,[])) pos);
+							etype = gen.gcon.basic.tbool;
+							epos = pos;
+						},
+						hx_current,
+						Some(
+						{
+							eexpr = TBinop(OpAssign, hx_current, { fexpr with eexpr = TNew(cls, [], captured) });
+							etype = (TInst(cls,[]));
+							epos = pos;
+						}))
+				}, (cls,captured)
+			| _ ->
+				(* change the expression so it will be a new "added class" ( captured variables arguments ) *)
+				{ fexpr with eexpr = TNew(cls, List.map (fun cl -> TInst(cl,[])) tparams, List.rev captured) }, (cls,captured)
+		in
+		match delegate_type with
+		| None ->
+			expr,clscapt
+		| Some _ ->
+			{
+				eexpr = TField(expr, FClosure(Some (cls,[]),invokecf)); (* TODO: FClosure change *)
+				etype = invokecf.cf_type;
+				epos = cls.cl_pos
+			}, clscapt
+	in
+
+	let tvar_to_cdecl = Hashtbl.create 0 in
+
+	let run = traverse
+		gen
+		~tparam_anon_decl:(fun v e fn ->
+			let _, info = handle_anon_func e fn null_map_info None in
+			Hashtbl.add tvar_to_cdecl v.v_id info
+		)
+		~tparam_anon_acc:(fun v e -> try
+			let cls, captured = Hashtbl.find tvar_to_cdecl v.v_id in
+			let captured = List.sort (fun e1 e2 -> match e1, e2 with
+				| { eexpr = TLocal v1 }, { eexpr = TLocal v2 } ->
+					compare v1.v_name v2.v_name
+				| _ -> assert false) captured
+			in
+			let types = match v.v_extra with
+				| Some(t,_) -> t
+				| _ -> assert false
+			in
+			let monos = List.map (fun _ -> mk_mono()) types in
+			let vt = match follow v.v_type with
+				| TInst(_, [v]) -> v
+				| v -> v
+			in
+			let et = match follow e.etype with
+				| TInst(_, [v]) -> v
+				| v -> v
+			in
+			let original = apply_params types monos vt in
+			unify et original;
+
+			let monos = List.map (fun t -> apply_params types (List.map (fun _ -> t_dynamic) types) t) monos in
+
+			let same_cl t1 t2 = match follow t1, follow t2 with
+				| TInst(c,_), TInst(c2,_) -> c == c2
+				| _ -> false
+			in
+			let passoc = List.map2 (fun (_,t) m -> t,m) types monos in
+			let cltparams = List.map (fun (_,t) ->
+				try
+					snd (List.find (fun (t2,_) -> same_cl t t2) passoc)
+				with | Not_found -> t) cls.cl_params
+			in
+			{ e with eexpr = TNew(cls, cltparams, List.rev captured) }
+		with
+			| Not_found ->
+			gen.gcon.warning "This expression may be invalid" e.epos;
+			e
+			| Unify_error el ->
+				List.iter (fun el -> gen.gcon.warning (Error.unify_error_msg (print_context()) el) e.epos) el;
+			gen.gcon.warning "This expression may be invalid" e.epos;
+			e
+		)
+		(* (handle_anon_func:texpr->tfunc->texpr) (dynamic_func_call:texpr->texpr->texpr list->texpr) *)
+		(fun e f info delegate_type -> fst (handle_anon_func e f info delegate_type))
+		ft.dynamic_fun_call
+		(* (dynamic_func_call:texpr->texpr->texpr list->texpr) *)
+	in
+	gen.gexpr_filters#add name (PCustom priority) run
+
+
+(*
+	this submodule will provide the default implementation for the C# and Java targets.
+
+	it will have two return types: double and dynamic, and
+*)
+module DoubleAndDynamicClosureImpl =
+struct
+	let get_ctx gen parent_func_class max_arity mk_arg_exception (* e.g. new haxe.lang.ClassClosure *) =
+		let basic = gen.gcon.basic in
+
+		let func_args_i i =
+			let rec loop i (acc) =
+				if i = 0 then (acc) else begin
+					let vfloat = alloc_var (mk_internal_name "fn" ("float" ^ string_of_int i)) basic.tfloat in
+					let vdyn = alloc_var (mk_internal_name "fn" ("dyn" ^ string_of_int i)) t_dynamic in
+
+					loop (i - 1) ((vfloat, None) :: (vdyn, None) :: acc)
+				end
+			in
+			loop i []
+		in
+
+		let args_real_to_func args =
+			let arity = List.length args in
+			if arity >= max_arity then
+				[ alloc_var (mk_internal_name "fn" "dynargs") (gen.gclasses.nativearray t_dynamic), None ]
+			else func_args_i arity
+		in
+
+		let func_sig_i i =
+			let rec loop i acc =
+				if i = 0 then acc else begin
+					let vfloat = mk_internal_name "fn" ("float" ^ string_of_int i) in
+					let vdyn = mk_internal_name "fn" ("dyn" ^ string_of_int i) in
+
+					loop (i - 1) ( (vfloat,false,basic.tfloat) :: (vdyn,false,t_dynamic) :: acc )
+				end
+			in
+			loop i []
+		in
+
+		let args_real_to_func_sig args =
+			let arity = List.length args in
+			if arity >= max_arity then
+				[mk_internal_name "fn" "dynargs", false, gen.gclasses.nativearray t_dynamic]
+			else begin
+				func_sig_i arity
+			end
+		in
+
+		let rettype_real_to_func t = match run_follow gen t with
+			| TAbstract({ a_path = [],"Null" }, _) ->
+				0,t_dynamic
+			| _ when like_float t && not (like_i64 t) ->
+				(1, basic.tfloat)
+			| _ ->
+				(0, t_dynamic)
+		in
+
+		let args_real_to_func_call el (pos:pos) =
+			if List.length el >= max_arity then
+				[mk_nativearray_decl gen t_dynamic el pos]
+			else begin
+				List.fold_left (fun acc e ->
+					if like_float (gen.greal_type e.etype) && not (like_i64 (gen.greal_type e.etype)) then
+						( e :: undefined e.epos :: acc )
+					else
+						( null basic.tfloat e.epos :: e :: acc )
+				) ([]) (List.rev el)
+			end
+		in
+
+		let get_args_func args changed_args pos =
+			let arity = List.length args in
+			let mk_const const elocal t =
+				match const with
+				| None ->
+					mk_cast t elocal
+				| Some const ->
+					{ eexpr = TIf(
+						{ elocal with eexpr = TBinop(Ast.OpEq, elocal, null elocal.etype elocal.epos); etype = basic.tbool },
+						{ elocal with eexpr = TConst(const); etype = const_type basic const t },
+						Some ( mk_cast t elocal )
+					); etype = t; epos = elocal.epos }
+			in
+
+			if arity >= max_arity then begin
+				let varray = match changed_args with | [v,_] -> v | _ -> assert false in
+				let varray_local = mk_local varray pos in
+				let mk_varray i = { eexpr = TArray(varray_local, ExprBuilder.make_int gen.gcon i pos); etype = t_dynamic; epos = pos } in
+				let el =
+					snd (List.fold_left (fun (count,acc) (v,const) ->
+						(count + 1, (mk (TVar(v, Some(mk_const const (mk_varray count) v.v_type))) basic.tvoid pos) :: acc)
+					) (0, []) args)
+				in
+				List.rev el
+			end else begin
+				let _, dyn_args, float_args = List.fold_left (fun (count,fargs, dargs) arg ->
+					if count land 1 = 0 then
+						(count + 1, fargs, arg :: dargs)
+					else
+						(count + 1, arg :: fargs, dargs)
+				) (1,[],[]) (List.rev changed_args) in
+
+				let rec loop acc args fargs dargs =
+					match args, fargs, dargs with
+						| [], [], [] -> acc
+						| (v,const) :: args, (vf,_) :: fargs, (vd,_) :: dargs ->
+							let acc = { eexpr = TVar(v, Some(
+								{
+									eexpr = TIf(
+										{ eexpr = TBinop(Ast.OpEq, mk_local vd pos, undefined pos); etype = basic.tbool; epos = pos },
+										mk_cast v.v_type (mk_local vf pos),
+										Some ( mk_const const (mk_local vd pos) v.v_type )
+									);
+									etype = v.v_type;
+									epos = pos
+								} )); etype = basic.tvoid; epos = pos } :: acc in
+							loop acc args fargs dargs
+						| _ -> assert false
+				in
+
+				loop [] args float_args dyn_args
+			end
+		in
+
+		let closure_to_classfield tfunc old_sig pos =
+			(* change function signature *)
+			let old_args = tfunc.tf_args in
+			let changed_args = args_real_to_func old_args in
+
+			(*
+				FIXME properly handle int64 cases, which will break here (because of inference to int)
+				UPDATE: the fix will be that Int64 won't be a typedef to Float/Int
+			*)
+			let changed_sig, arity, type_number, changed_sig_ret, is_void, is_dynamic_func = match follow old_sig with
+				| TFun(_sig, ret) ->
+					let type_n, ret_t = rettype_real_to_func ret in
+					let arity = List.length _sig in
+					let is_dynamic_func = arity >= max_arity in
+					let ret_t = if is_dynamic_func then t_dynamic else ret_t in
+
+					(TFun(args_real_to_func_sig _sig, ret_t), arity, type_n, ret_t, ExtType.is_void ret, is_dynamic_func)
+				| _ -> (print_endline (s_type (print_context()) (follow old_sig) )); assert false
+			in
+
+			let tf_expr = if is_void then begin
+				let rec map e =
+					match e.eexpr with
+						| TReturn None ->
+							mk_return (null t_dynamic e.epos)
+						| _ -> Type.map_expr map e
+				in
+				let e = mk_block (map tfunc.tf_expr) in
+				match e.eexpr with
+					| TBlock bl -> { e with eexpr = TBlock (bl @ [mk_return (null t_dynamic e.epos)]) }
+					| _ -> assert false
+			end else tfunc.tf_expr in
+
+			let changed_sig_ret = if is_dynamic_func then t_dynamic else changed_sig_ret in
+
+			(* get real arguments on top of function body *)
+			let get_args = get_args_func tfunc.tf_args changed_args pos in
+			(*
+				FIXME HACK: in order to be able to run the filters that have already ran for this piece of code,
+				we will cheat and run it as if it was the whole code
+				We could just make ClosuresToClass run before TArrayTransform, but we cannot because of the
+				dependency between ClosuresToClass (after DynamicFieldAccess, and before TArrayTransform)
+
+				maybe a way to solve this would be to add an "until" field to run_from
+			*)
+			let real_get_args = gen.gexpr_filters#run (mk (TBlock get_args) basic.tvoid pos) in
+
+			let func_expr = Type.concat real_get_args tf_expr in
+
+			(* set invoke function *)
+			(* todo properly abstract how naming for invoke is made *)
+			let invoke_name = if is_dynamic_func then "invokeDynamic" else ("invoke" ^ (string_of_int arity) ^ (if type_number = 0 then "_o" else "_f")) in
+			let invoke_name = mk_internal_name "hx" invoke_name in
+			let invoke_field = mk_class_field invoke_name changed_sig false func_expr.epos (Method(MethNormal)) [] in
+			let invoke_fun = {
+				eexpr = TFunction {
+					tf_args = changed_args;
+					tf_type = changed_sig_ret;
+					tf_expr = func_expr;
+				};
+				etype = changed_sig;
+				epos = func_expr.epos;
+			} in
+			invoke_field.cf_expr <- Some invoke_fun;
+
+			invoke_field, [
+				ExprBuilder.make_int gen.gcon arity pos;
+				ExprBuilder.make_int gen.gcon type_number pos;
+			]
+		in
+
+		let dynamic_fun_call call_expr =
+			let tc, params = match call_expr.eexpr with
+				| TCall(tc, params) -> tc, params
+				| _ -> assert false
+			in
+			let ct = gen.greal_type call_expr.etype in
+			let postfix, ret_t =
+				if like_float ct && not (like_i64 ct) then
+						"_f", gen.gcon.basic.tfloat
+				else
+					"_o", t_dynamic
+			in
+			let params_len = List.length params in
+			let ret_t = if params_len >= max_arity then t_dynamic else ret_t in
+
+			let invoke_fun = if params_len >= max_arity then "invokeDynamic" else "invoke" ^ (string_of_int params_len) ^ postfix in
+			let invoke_fun = mk_internal_name "hx" invoke_fun in
+			let fun_t = match follow tc.etype with
+				| TFun(_sig, _) ->
+					TFun(args_real_to_func_sig _sig, ret_t)
+				| _ ->
+					let i = ref 0 in
+					let _sig = List.map (fun p -> let name = "arg" ^ (string_of_int !i) in incr i; (name,false,p.etype) ) params in
+					TFun(args_real_to_func_sig _sig, ret_t)
+			in
+
+			let may_cast = match follow call_expr.etype with
+				| TAbstract ({ a_path = ([], "Void") },[]) -> (fun e -> e)
+				| _ -> mk_cast call_expr.etype
+			in
+
+			may_cast
+			{
+				eexpr = TCall(
+					{ (mk_field_access gen { tc with etype = gen.greal_type tc.etype } invoke_fun tc.epos) with etype = fun_t },
+					args_real_to_func_call params call_expr.epos
+				);
+				etype = ret_t;
+				epos = call_expr.epos
+			}
+		in
+
+		let iname i is_float =
+			let postfix = if is_float then "_f" else "_o" in
+			mk_internal_name "hx" ("invoke" ^ string_of_int i) ^ postfix
+		in
+
+		let map_base_classfields cl map_fn =
+			let pos = cl.cl_pos in
+			let this_t = TInst(cl,List.map snd cl.cl_params) in
+			let this = { eexpr = TConst(TThis); etype = this_t; epos = pos } in
+			let mk_this field t = { (mk_field_access gen this field pos) with etype = t } in
+
+			let mk_invoke_i i is_float =
+				let cf = mk_class_field (iname i is_float) (TFun(func_sig_i i, if is_float then basic.tfloat else t_dynamic)) false pos (Method MethNormal) [] in
+				cf
+			in
+
+			let type_name = mk_internal_name "fn" "type" in
+			let dynamic_arg = alloc_var (mk_internal_name "fn" "dynargs") (gen.gclasses.nativearray t_dynamic) in
+
+			let mk_invoke_complete_i i is_float =
+
+				(* let arity = i in *)
+				let args = func_args_i i in
+
+				(* api fn *)
+
+				(* only cast if needed *)
+				let mk_cast tto efrom = gen.ghandle_cast (gen.greal_type tto) (gen.greal_type efrom.etype) efrom in
+				let api i t const =
+					let vf, _ = List.nth args (i * 2) in
+					let vo, _ = List.nth args (i * 2 + 1) in
+
+					let needs_cast, is_float = match t, like_float t && not (like_i64 t) with
+						| TAbstract({ a_path = ([], "Float") },[]), _ -> false, true
+						| _, true -> true, true
+						| _ -> false,false
+					in
+
+					let olocal = mk_local vo pos in
+					let flocal = mk_local vf pos in
+
+					let get_from_obj e = match const with
+						| None -> mk_cast t e
+						| Some tc ->
+							{
+								eexpr = TIf(
+									{ eexpr = TBinop(Ast.OpEq, olocal, null t_dynamic pos); etype = basic.tbool; epos = pos } ,
+									{ eexpr = TConst(tc); etype = t; epos = pos },
+									Some (mk_cast t e)
+								);
+								etype = t;
+								epos = pos;
+							}
+					in
+
+					{
+						eexpr = TIf(
+							{ eexpr = TBinop(Ast.OpEq, olocal, undefined pos); etype = basic.tbool; epos = pos },
+							(if needs_cast then mk_cast t flocal else flocal),
+							Some ( get_from_obj olocal )
+						);
+						etype = t;
+						epos = pos
+					}
+				in
+				(* end of api fn *)
+
+				let ret = if is_float then basic.tfloat else t_dynamic in
+
+				let fn_expr = map_fn i ret (List.map fst args) api in
+
+				let t = TFun(fun_args args, ret) in
+
+				let tfunction =
+					{
+						eexpr = TFunction({
+							tf_args = args;
+							tf_type = ret;
+							tf_expr =
+							mk_block fn_expr
+						});
+						etype = t;
+						epos = pos;
+					}
+				in
+
+				let cf = mk_invoke_i i is_float in
+				cf.cf_expr <- Some tfunction;
+				cf
+			in
+
+			let rec loop i cfs =
+				if i < 0 then cfs else begin
+					(*let mk_invoke_complete_i i is_float =*)
+					(mk_invoke_complete_i i false) :: (mk_invoke_complete_i i true) :: (loop (i-1) cfs)
+				end
+			in
+
+			let cfs = loop max_arity [] in
+
+			let switch =
+				let api i t const =
+					match i with
+						| -1 ->
+							mk_local dynamic_arg pos
+						| _ ->
+							mk_cast t {
+								eexpr = TArray(
+									mk_local dynamic_arg pos,
+									{ eexpr = TConst(TInt(Int32.of_int i)); etype = basic.tint; epos = pos });
+								etype = t;
+								epos = pos;
+							}
+				in
+				map_fn (-1) t_dynamic [dynamic_arg] api
+			in
+
+			let args = [dynamic_arg, None] in
+			let dyn_t = TFun(fun_args args, t_dynamic) in
+			let dyn_cf = mk_class_field (mk_internal_name "hx" "invokeDynamic") dyn_t false pos (Method MethNormal) [] in
+
+			dyn_cf.cf_expr <- Some {
+				eexpr = TFunction {
+					tf_args = args;
+					tf_type = t_dynamic;
+					tf_expr = mk_block switch
+				};
+				etype = dyn_t;
+				epos = pos;
+			};
+
+			let additional_cfs = begin
+				let new_t = TFun(["arity", false, basic.tint; "type", false, basic.tint],basic.tvoid) in
+				let new_cf = mk_class_field "new" (new_t) true pos (Method MethNormal) [] in
+				let v_arity, v_type = alloc_var "arity" basic.tint, alloc_var "type" basic.tint in
+				let mk_assign v field = mk (TBinop (OpAssign, mk_this field v.v_type, mk_local v pos)) v.v_type pos in
+
+				let arity_name = mk_internal_name "hx" "arity" in
+				new_cf.cf_expr <- Some {
+					eexpr = TFunction({
+						tf_args = [v_arity, None; v_type, None];
+						tf_type = basic.tvoid;
+						tf_expr =
+						{
+							eexpr = TBlock([
+								mk_assign v_type type_name;
+								mk_assign v_arity arity_name
+							]);
+							etype = basic.tvoid;
+							epos = pos;
+						}
+					});
+					etype = new_t;
+					epos = pos;
+				};
+
+				[
+					new_cf;
+					mk_class_field type_name basic.tint true pos (Var { v_read = AccNormal; v_write = AccNormal }) [];
+					mk_class_field arity_name basic.tint true pos (Var { v_read = AccNormal; v_write = AccNormal }) [];
+				]
+			end in
+
+			dyn_cf :: (additional_cfs @ cfs)
+		in
+
+		begin
+			(*
+				setup fields for the abstract implementation of the Function class
+
+				new(arity, type)
+				{
+					this.arity = arity;
+					this.type = type;
+				}
+
+				hx::invokeX_f|o (where X is from 0 to max_arity) (args)
+				{
+					if (this.type == 0|1) return invokeX_o|f(args); else throw "Invalid number of arguments."
+				}
+
+				hx::invokeDynamic, which will work in the same way
+			*)
+			let cl = parent_func_class in
+			let pos = cl.cl_pos in
+
+			let rec mk_dyn_call arity api =
+				let zero = ExprBuilder.make_float gen.gcon "0.0" pos in
+				let rec loop i acc =
+					if i = 0 then
+						acc
+					else begin
+						let arr = api (i - 1) t_dynamic None in
+						loop (i - 1) (zero :: arr :: acc)
+					end
+				in
+				loop arity []
+			in
+
+			let this = mk (TConst TThis) (TInst (cl, List.map snd cl.cl_params)) pos in
+			let mk_this field t = { (mk_field_access gen this field pos) with etype = t } in
+
+			let mk_invoke_switch i api =
+				let t = TFun (func_sig_i i, t_dynamic) in
+				(* case i: return this.invokeX_o(0, 0, 0, 0, 0, ... arg[0], args[1]....); *)
+				[ExprBuilder.make_int gen.gcon i pos], mk_return (mk (TCall(mk_this (iname i false) t, mk_dyn_call i api)) t_dynamic pos)
+			in
+			let rec loop_cases api arity acc =
+				if arity < 0 then
+					acc
+				else
+					loop_cases api (arity - 1) (mk_invoke_switch arity api :: acc)
+			in
+
+			let type_name = mk_internal_name "fn" "type" in
+			let mk_expr i is_float vars =
+				let call_expr =
+					let call_t = TFun(List.map (fun v -> (v.v_name, false, v.v_type)) vars, if is_float then t_dynamic else basic.tfloat) in
+					{
+						eexpr = TCall(mk_this (iname i (not is_float)) call_t, List.map (fun v -> mk_local v pos) vars);
+						etype = if is_float then t_dynamic else basic.tfloat;
+						epos = pos
+					}
+				in
+				{
+					eexpr = TIf(
+						mk (TBinop (Ast.OpNotEq, mk_this type_name basic.tint, (ExprBuilder.make_int gen.gcon (if is_float then 0 else 1) pos))) basic.tbool pos,
+						ExprBuilder.make_throw (mk_arg_exception "Wrong number of arguments" pos) pos,
+						Some (mk_return call_expr)
+					);
+					etype = t_dynamic;
+					epos = pos;
+				}
+			in
+
+			let arities_processed = Hashtbl.create 10 in
+			let max_arity = ref 0 in
+
+			let map_fn cur_arity fun_ret_type vars (api:int->t->tconstant option->texpr) =
+				let is_float = like_float fun_ret_type && not (like_i64 fun_ret_type) in
+				match cur_arity with
+				| -1 ->
+					let dynargs = api (-1) t_dynamic None in
+
+					(* (dynargs == null) ? 0 : dynargs.length *)
+					let switch_cond = {
+						eexpr = TIf(
+							mk (TBinop (OpEq, dynargs, null dynargs.etype pos)) basic.tbool pos,
+							mk (TConst (TInt Int32.zero)) basic.tint pos,
+							Some (gen.gclasses.nativearray_len dynargs pos));
+						etype = basic.tint;
+						epos = pos;
+					} in
+
+					{
+						eexpr = TSwitch(
+							switch_cond,
+							loop_cases api !max_arity [],
+							Some(ExprBuilder.make_throw (mk_arg_exception "Too many arguments" pos) pos));
+						etype = basic.tvoid;
+						epos = pos;
+					}
+				| _ ->
+					if not (Hashtbl.mem arities_processed cur_arity) then begin
+						Hashtbl.add arities_processed cur_arity true;
+						if cur_arity > !max_arity then max_arity := cur_arity
+					end;
+
+					mk_expr cur_arity is_float vars
+			in
+
+			let cfs = map_base_classfields cl map_fn in
+			List.iter (fun cf ->
+				if cf.cf_name = "new" then
+					parent_func_class.cl_constructor <- Some cf
+				else
+					parent_func_class.cl_fields <- PMap.add cf.cf_name cf parent_func_class.cl_fields
+			) cfs;
+			parent_func_class.cl_ordered_fields <- (List.filter (fun cf -> cf.cf_name <> "new") cfs) @ parent_func_class.cl_ordered_fields
+		end;
+
+		{
+			func_class = parent_func_class;
+			closure_to_classfield = closure_to_classfield;
+			dynamic_fun_call = dynamic_fun_call;
+			map_base_classfields = map_base_classfields;
+		}
+end;;

+ 132 - 0
src/generators/gencommon/dynamicFieldAccess.ml

@@ -0,0 +1,132 @@
+(*
+	The Haxe Compiler
+	Copyright (C) 2005-2017  Haxe Foundation
+
+	This program is free software; you can redistribute it and/or
+	modify it under the terms of the GNU General Public License
+	as published by the Free Software Foundation; either version 2
+	of the License, or (at your option) any later version.
+
+	This program is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+
+	You should have received a copy of the GNU General Public License
+	along with this program; if not, write to the Free Software
+	Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+*)
+open Option
+open Ast
+open Type
+open Gencommon
+
+(*
+	This module will filter every dynamic field access in haxe.
+
+	On platforms that do not support dynamic access, it is with this that you should
+	replace dynamic calls with x.field / Reflect.setField calls, and guess what -
+	this is the default implemenation!
+	Actually there is a problem with Reflect.setField because it returns void, which is a bad thing for us,
+	so even in the default implementation, the function call should be specified to a Reflect.setField version that returns
+	the value that was set
+
+	(TODO: should it be separated?)
+	As a plus, the default implementation adds something that doesn't hurt anybody, it looks for
+	TAnon with Statics / EnumStatics field accesses and transforms them into real static calls.
+	This means it will take this
+
+	var m = Math;
+	for (i in 0...1000) m.cos(10);
+
+	which is an optimization in dynamic platforms, but performs horribly on strongly typed platforms
+	and transform into:
+
+	var m = Math;
+	for (i in 0...1000) Math.cos(10);
+
+	depends on:
+		(ok) must run AFTER Binop/Unop handler - so Unops / Binops are already unrolled
+*)
+let name = "dynamic_field_access"
+let priority = solve_deps name [DAfter DynamicOperators.priority]
+
+(*
+	is_dynamic (expr) (field_access_expr) (field) : a function that indicates if the field access should be changed
+	change_expr (expr) (field_access_expr) (field) (setting expr) (is_unsafe) : changes the expression
+	call_expr (expr) (field_access_expr) (field) (call_params) : changes a call expression
+*)
+let configure gen (is_dynamic:texpr->Type.tfield_access->bool) (change_expr:texpr->texpr->string->texpr option->bool->texpr) (call_expr:texpr->texpr->string->texpr list->texpr) =
+	let is_nondynamic_tparam fexpr f = match follow fexpr.etype with
+		| TInst({ cl_kind = KTypeParameter(tl) }, _) ->
+			List.exists (fun t -> not (is_dynamic { fexpr with etype = t } f)) tl
+		| _ -> false
+	in
+
+	let rec run e =
+		match e.eexpr with
+		(* class types *)
+		| TField(fexpr, f) when is_nondynamic_tparam fexpr f ->
+			(match follow fexpr.etype with
+				| TInst({ cl_kind = KTypeParameter(tl) }, _) ->
+					let t = List.find (fun t -> not (is_dynamic { fexpr with etype = t } f)) tl in
+					{ e with eexpr = TField(mk_cast t (run fexpr), f) }
+				| _ -> assert false)
+
+		| TField(fexpr, f) when is_some (anon_class fexpr.etype) ->
+			let decl = get (anon_class fexpr.etype) in
+			let name = field_name f in
+			(try
+				match decl with
+				| TClassDecl cl ->
+					let cf = PMap.find name cl.cl_statics in
+					{ e with eexpr = TField ({ fexpr with eexpr = TTypeExpr decl }, FStatic (cl, cf)) }
+				| TEnumDecl en ->
+					let ef = PMap.find name en.e_constrs in
+					{ e with eexpr = TField ({ fexpr with eexpr = TTypeExpr decl }, FEnum (en, ef)) }
+				| TAbstractDecl _ (* abstracts don't have TFields *)
+				| TTypeDecl _ -> (* anon_class doesn't return TTypeDecl *)
+					assert false
+			with Not_found ->
+				match f with
+				| FStatic (cl, cf) when Meta.has Meta.Extern cf.cf_meta ->
+					{ e with eexpr = TField ({ fexpr with eexpr = TTypeExpr decl }, FStatic (cl, cf)) }
+				| _ ->
+					change_expr e { fexpr with eexpr = TTypeExpr decl } (field_name f) None true)
+
+		| TField (fexpr, f) when is_dynamic fexpr f ->
+			change_expr e (run fexpr) (field_name f) None true
+
+		| TCall ({ eexpr = TField (_, FStatic({ cl_path = ([], "Reflect") }, { cf_name = "field" })) }, [obj; { eexpr = TConst (TString field) }]) ->
+			let t = match gen.greal_type obj.etype with
+			| TDynamic _ | TAnon _ | TMono _ -> t_dynamic
+			| t -> t
+			in
+			change_expr (mk_field_access gen { obj with etype = t } field obj.epos) (run obj) field None false
+
+		| TCall ({ eexpr = TField (_, FStatic({ cl_path = ([], "Reflect") }, { cf_name = "setField" } )) }, [obj; { eexpr = TConst(TString field) }; evalue]) ->
+			change_expr (mk_field_access gen obj field obj.epos) (run obj) field (Some (run evalue)) false
+
+		| TBinop (OpAssign, { eexpr = TField(fexpr, f) }, evalue) when is_dynamic fexpr f ->
+			change_expr e (run fexpr) (field_name f) (Some (run evalue)) true
+
+		| TBinop (OpAssign, { eexpr = TField(fexpr, f) }, evalue) ->
+			(match field_access_esp gen fexpr.etype f with
+			| FClassField(_,_,_,cf,false,t,_) when (try PMap.find cf.cf_name gen.gbase_class_fields == cf with Not_found -> false) ->
+				change_expr e (run fexpr) (field_name f) (Some (run evalue)) true
+			| _ ->
+				Type.map_expr run e)
+
+		| TBinop (OpAssignOp _, { eexpr = TField (fexpr, f) }, _) when is_dynamic fexpr f ->
+			assert false (* this case shouldn't happen *)
+		| TUnop (Increment, _, { eexpr = TField (({ eexpr = TLocal _ } as fexpr), f)})
+		| TUnop (Decrement, _, { eexpr = TField (({ eexpr = TLocal _ } as fexpr), f)}) when is_dynamic fexpr f ->
+			assert false (* this case shouldn't happen *)
+
+		| TCall ({ eexpr = TField (fexpr, f) }, params) when is_dynamic fexpr f && (not (is_nondynamic_tparam fexpr f)) ->
+			call_expr e (run fexpr) (field_name f) (List.map run params)
+
+		| _ ->
+			Type.map_expr run e
+	in
+	gen.gexpr_filters#add name (PCustom priority) run

+ 181 - 0
src/generators/gencommon/dynamicOperators.ml

@@ -0,0 +1,181 @@
+(*
+	The Haxe Compiler
+	Copyright (C) 2005-2017  Haxe Foundation
+
+	This program is free software; you can redistribute it and/or
+	modify it under the terms of the GNU General Public License
+	as published by the Free Software Foundation; either version 2
+	of the License, or (at your option) any later version.
+
+	This program is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+
+	You should have received a copy of the GNU General Public License
+	along with this program; if not, write to the Free Software
+	Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+*)
+open Common
+open Ast
+open Type
+open Codegen
+open Gencommon
+
+(* ******************************************* *)
+(* Dynamic Binop/Unop handler *)
+(* ******************************************* *)
+(*
+	On some languages there is limited support for operations on
+	dynamic variables, so those operations must be changed.
+
+	There are 5 types of binary operators:
+		1 - can take any variable and returns a bool (== and !=)
+		2 - can take either a string, or a number and returns either a bool or the underlying type ( >, < for bool and + for returning its type)
+		3 - take numbers and return a number ( *, /, ...)
+		4 - take ints and return an int (bit manipulation)
+		5 - take a bool and returns a bool ( &&, || ...)
+
+	On the default implementation, type 1 and the plus function will be handled with a function call;
+	Type 2 will be handled with the parameter "compare_handler", which will do something like Reflect.compare(x1, x2);
+	Types 3, 4 and 5 will perform a cast to double, int and bool, which will then be handled normally by the platform
+
+	Unary operators are the most difficult to handle correctly.
+	With unary operators, there are 2 types:
+
+		1 - can take a number, changes and returns the result (++, --, ~)
+		2 - can take a number (-) or bool (!), and returns the result
+
+	The first case is much trickier, because it doesn't seem a good idea to change any variable to double just because it is dynamic,
+	but this is how we will handle right now.
+	something like that:
+
+	var x:Dynamic = 10;
+	x++;
+
+	will be:
+	object x = 10;
+	x = ((IConvertible)x).ToDouble(null) + 1;
+
+	depends on:
+		(syntax) must run before expression/statment normalization because it may generate complex expressions
+		must run before OverloadingConstructor due to later priority conflicts. Since ExpressionUnwrap is only
+		defined afterwards, we will set this value with absolute values
+*)
+let init com handle_strings (should_change:texpr->bool) (equals_handler:texpr->texpr->texpr) (dyn_plus_handler:texpr->texpr->texpr->texpr) (compare_handler:Ast.binop->texpr->texpr->texpr->texpr) =
+	let get_etype_one e =
+		if like_int e.etype then
+			ExprBuilder.make_int com 1 e.epos
+		else
+			ExprBuilder.make_float com "1.0" e.epos
+	in
+	let rec run e =
+		match e.eexpr with
+		| TBinop (OpAssignOp op, e1, e2) when should_change e -> (* e1 will never contain another TBinop *)
+			(match e1.eexpr with
+			| TLocal _ ->
+				mk_paren { e with eexpr = TBinop(OpAssign, e1, run { e with eexpr = TBinop(op, e1, e2) }) }
+			| TField _ | TArray _ ->
+				let eleft, rest =
+					match e1.eexpr with
+					| TField(ef, f) ->
+						let v = mk_temp "dynop" ef.etype in
+						{ e1 with eexpr = TField (mk_local v ef.epos, f) }, [mk (TVar (v, Some (run ef))) com.basic.tvoid ef.epos]
+					| TArray(e1a, e2a) ->
+						let v = mk_temp "dynop" e1a.etype in
+						let v2 = mk_temp "dynopi" e2a.etype in
+						{ e1 with eexpr = TArray(mk_local v e1a.epos, mk_local v2 e2a.epos) }, [
+							(mk (TVar (v, Some (run e1a))) com.basic.tvoid e1.epos);
+							(mk (TVar (v2, Some (run e2a))) com.basic.tvoid e1.epos)
+						]
+					| _ -> assert false
+				in
+				{ e with eexpr = TBlock (rest @ [{ e with eexpr = TBinop (OpAssign, eleft, run { e with eexpr = TBinop (op, eleft, e2) }) }]) }
+			| _ ->
+				assert false)
+
+		| TBinop (OpAssign, e1, e2)
+		| TBinop (OpInterval, e1, e2) ->
+			Type.map_expr run e
+
+		| TBinop (op, e1, e2) when should_change e ->
+			(match op with
+			| OpEq -> (* type 1 *)
+				equals_handler (run e1) (run e2)
+			| OpNotEq -> (* != -> !equals() *)
+				mk_parent (mk (TUnop (Not, Prefix, (equals_handler (run e1) (run e2)))) com.basic.tbool e.epos)
+			| OpAdd  ->
+				if handle_strings && (is_string e.etype || is_string e1.etype || is_string e2.etype) then
+					{ e with eexpr = TBinop (op, mk_cast com.basic.tstring (run e1), mk_cast com.basic.tstring (run e2)) }
+				else
+					dyn_plus_handler e (run e1) (run e2)
+			| OpGt | OpGte | OpLt | OpLte  -> (* type 2 *)
+				compare_handler op e (run e1) (run e2)
+			| OpMult | OpDiv | OpSub | OpMod -> (* always cast everything to double *)
+				let etype = (get_etype_one e).etype in
+				{ e with eexpr = TBinop (op, mk_cast etype (run e1), mk_cast etype (run e2)) }
+			| OpBoolAnd | OpBoolOr ->
+				{ e with eexpr = TBinop (op, mk_cast com.basic.tbool (run e1), mk_cast com.basic.tbool (run e2)) }
+			| OpAnd | OpOr | OpXor | OpShl | OpShr | OpUShr ->
+				{ e with eexpr = TBinop (op, mk_cast com.basic.tint (run e1), mk_cast com.basic.tint (run e2)) }
+			| OpAssign | OpAssignOp _ | OpInterval | OpArrow | OpIn ->
+				assert false)
+
+		| TUnop (Increment as op, flag, e1)
+		| TUnop (Decrement as op, flag, e1) when should_change e ->
+			(*
+				some naming definitions:
+				* ret => the returning variable
+				* _g => the get body
+				* getvar => the get variable expr
+
+				This will work like this:
+					- if e1 is a TField, set _g = get body, getvar = (get body).varname
+					- if Prefix, return getvar = getvar + 1.0
+					- if Postfix, set ret = getvar; getvar = getvar + 1.0; ret;
+			*)
+			let one = get_etype_one e in
+			let etype = one.etype in
+			let op = (match op with Increment -> OpAdd | Decrement -> OpSub | _ -> assert false) in
+
+			let block =
+				let vars, getvar =
+					match e1.eexpr with
+					| TField (fexpr, field) ->
+						let tmp = mk_temp "getvar" fexpr.etype in
+						let var = mk (TVar (tmp, Some (run fexpr))) com.basic.tvoid e.epos in
+						([var], mk (TField (ExprBuilder.make_local tmp fexpr.epos, field)) etype e1.epos)
+					| _ ->
+						([], e1)
+				in
+				match flag with
+				| Prefix ->
+					vars @ [
+						mk_cast etype { e with eexpr = TBinop(OpAssign, getvar, Codegen.binop op (mk_cast etype getvar) one etype e.epos); etype = getvar.etype }
+					]
+				| Postfix ->
+					let ret = mk_temp "ret" etype in
+					let retlocal = ExprBuilder.make_local ret e.epos in
+					vars @ [
+						mk (TVar (ret, Some (mk_cast etype getvar))) com.basic.tvoid e.epos;
+						{ e with eexpr = TBinop (OpAssign, getvar, Codegen.binop op retlocal one getvar.etype e.epos) };
+						retlocal
+					]
+			in
+			mk (TBlock block) etype e.epos
+
+	| TUnop (op, flag, e1) when should_change e ->
+		let etype = match op with Not -> com.basic.tbool | _ -> com.basic.tint in
+		mk_parent (mk (TUnop (op, flag, mk_cast etype (run e1))) etype e.epos)
+
+	| _ ->
+		Type.map_expr run e
+	in
+	run
+
+let name = "dyn_ops"
+let priority = 0.0
+
+let configure gen ~handle_strings should_change equals_handler dyn_plus_handler compare_handler =
+	let run = init gen.gcon handle_strings should_change equals_handler dyn_plus_handler compare_handler in
+	gen.gexpr_filters#add name (PCustom priority) run

+ 301 - 0
src/generators/gencommon/enumToClass.ml

@@ -0,0 +1,301 @@
+(*
+	The Haxe Compiler
+	Copyright (C) 2005-2017  Haxe Foundation
+
+	This program is free software; you can redistribute it and/or
+	modify it under the terms of the GNU General Public License
+	as published by the Free Software Foundation; either version 2
+	of the License, or (at your option) any later version.
+
+	This program is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+
+	You should have received a copy of the GNU General Public License
+	along with this program; if not, write to the Free Software
+	Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+*)
+open Common
+open Globals
+open Ast
+open Type
+open Codegen
+open Gencommon
+
+(* ******************************************* *)
+(* EnumToClass *)
+(* ******************************************* *)
+(*
+	For languages that don't support parameterized enums and/or metadata in enums, we need to transform
+	enums into normal classes. This is done at the first module pass by creating new classes with the same
+	path inside the modules, and removing the actual enum module by setting it as en extern.
+
+	* The target must create its own strategy to deal with reflection. As it is right now, we will have a base class
+	which the class will extend, create @:$IsEnum metadata for the class, and create @:alias() metadatas for the fields,
+	with their tag order (as a string) as their alias. If you are using ReflectionCFs, then you don't have to worry
+	about that, as it's already generating all information needed by the haxe runtime.
+	so they can be
+*)
+let name = "enum_to_class"
+let priority = solve_deps name []
+
+type t = {
+	ec_tbl : (path, tclass) Hashtbl.t;
+}
+
+let new_t () = {
+	ec_tbl = Hashtbl.create 10
+}
+
+(* ******************************************* *)
+(* EnumToClassModf *)
+(* ******************************************* *)
+(*
+	The actual Module Filter that will transform the enum into a class
+
+	dependencies:
+		Should run before ReflectionCFs, in order to enable proper reflection access.
+		Should run before RealTypeParams.RealTypeParamsModf, since generic enums must be first converted to generic classes
+		It needs that the target platform implements __array__() as a shortcut to declare haxe.ds.Vector
+*)
+module EnumToClassModf =
+struct
+	let name = "enum_to_class_mod"
+	let priority = solve_deps name [DBefore ReflectionCFs.priority; DBefore RealTypeParams.RealTypeParamsModf.priority]
+
+	let pmap_exists fn pmap = try PMap.iter (fun a b -> if fn a b then raise Exit) pmap; false with | Exit -> true
+
+	let has_any_meta en =
+		let has_meta meta = List.exists (fun (m,_,_) -> match m with Meta.Custom _ -> true | _ -> false) meta in
+		has_meta en.e_meta || pmap_exists (fun _ ef -> has_meta ef.ef_meta) en.e_constrs
+
+	let convert gen t base_class base_param_class en =
+		let handle_type_params = false in (* TODO: look into this *)
+		let basic = gen.gcon.basic in
+		let pos = en.e_pos in
+
+		(* create the class *)
+		let cl = mk_class en.e_module en.e_path pos in
+		Hashtbl.add t.ec_tbl en.e_path cl;
+
+		(match Codegen.build_metadata gen.gcon (TEnumDecl en) with
+			| Some expr ->
+				let cf = mk_class_field "__meta__" expr.etype false expr.epos (Var { v_read = AccNormal; v_write = AccNormal }) [] in
+				cf.cf_expr <- Some expr;
+				cl.cl_statics <- PMap.add "__meta__" cf cl.cl_statics;
+				cl.cl_ordered_statics <- cf :: cl.cl_ordered_statics
+			| _ -> ()
+		);
+
+		let super, has_params = if Meta.has Meta.FlatEnum en.e_meta then base_class, false else base_param_class, true in
+
+		cl.cl_super <- Some(super,[]);
+		cl.cl_extern <- en.e_extern;
+		en.e_meta <- (Meta.Class, [], pos) :: en.e_meta;
+		cl.cl_module <- en.e_module;
+		cl.cl_meta <- ( Meta.Enum, [], pos ) :: cl.cl_meta;
+
+		(match gen.gcon.platform with
+			| Cs when Common.defined gen.gcon Define.CoreApiSerialize ->
+				cl.cl_meta <- ( Meta.Meta, [ (EField( (EConst (Ident "System"), null_pos ), "Serializable" ), null_pos) ], null_pos ) :: cl.cl_meta
+			| _ -> ());
+		let c_types =
+			if handle_type_params then
+				List.map (fun (s,t) -> (s, TInst (map_param (get_cl_t t), []))) en.e_params
+			else
+				[]
+		in
+
+		cl.cl_params <- c_types;
+
+		let i = ref 0 in
+		let cfs = List.map (fun name ->
+			let ef = PMap.find name en.e_constrs in
+			let pos = ef.ef_pos in
+			let old_i = !i in
+			incr i;
+
+			let cf = match follow ef.ef_type with
+				| TFun(params,ret) ->
+					let dup_types =
+						if handle_type_params then
+							List.map (fun (s,t) -> (s, TInst (map_param (get_cl_t t), []))) en.e_params
+						else
+							[]
+					in
+
+					let ef_type =
+						let fn, types = if handle_type_params then snd, dup_types else (fun _ -> t_dynamic), en.e_params in
+						let t = apply_params en.e_params (List.map fn types) ef.ef_type in
+						apply_params ef.ef_params (List.map fn ef.ef_params) t
+					in
+
+					let params, ret = get_fun ef_type in
+					let cf_params = if handle_type_params then dup_types @ ef.ef_params else [] in
+
+					let cf = mk_class_field name ef_type true pos (Method MethNormal) cf_params in
+					cf.cf_meta <- [];
+
+					let tf_args = List.map (fun (name,opt,t) ->  (alloc_var name t, if opt then Some TNull else None) ) params in
+					let arr_decl = mk_nativearray_decl gen t_dynamic (List.map (fun (v,_) -> mk_local v pos) tf_args) pos in
+					let expr = {
+						eexpr = TFunction({
+							tf_args = tf_args;
+							tf_type = ret;
+							tf_expr = mk_block ( mk_return { eexpr = TNew(cl,List.map snd dup_types, [ExprBuilder.make_int gen.gcon old_i pos; arr_decl] ); etype = TInst(cl, List.map snd dup_types); epos = pos } );
+						});
+						etype = ef_type;
+						epos = pos
+					} in
+					cf.cf_expr <- Some expr;
+					cf
+				| _ ->
+					let actual_t = match follow ef.ef_type with
+						| TEnum(e, p) -> TEnum(e, List.map (fun _ -> t_dynamic) p)
+						| _ -> assert false
+					in
+					let cf = mk_class_field name actual_t true pos (Var { v_read = AccNormal; v_write = AccNever }) [] in
+					let args = if has_params then
+						[ExprBuilder.make_int gen.gcon old_i pos; null (gen.gclasses.nativearray t_dynamic) pos]
+					else
+						[ExprBuilder.make_int gen.gcon old_i pos]
+					in
+					cf.cf_meta <- [Meta.ReadOnly,[],pos];
+					cf.cf_expr <- Some {
+						eexpr = TNew(cl, List.map (fun _ -> t_empty) cl.cl_params, args);
+						etype = TInst(cl, List.map (fun _ -> t_empty) cl.cl_params);
+						epos = pos;
+					};
+					cf
+			in
+			cl.cl_statics <- PMap.add cf.cf_name cf cl.cl_statics;
+			cf
+		) en.e_names in
+		let constructs_cf = mk_class_field "__hx_constructs" (gen.gclasses.nativearray basic.tstring) true pos (Var { v_read = AccNormal; v_write = AccNever }) [] in
+		constructs_cf.cf_meta <- [Meta.ReadOnly,[],pos];
+		constructs_cf.cf_expr <- Some (mk_nativearray_decl gen basic.tstring (List.map (fun s -> { eexpr = TConst(TString s); etype = basic.tstring; epos = pos }) en.e_names) pos);
+
+		cl.cl_ordered_statics <- constructs_cf :: cfs @ cl.cl_ordered_statics ;
+		cl.cl_statics <- PMap.add "__hx_constructs" constructs_cf cl.cl_statics;
+
+		let getTag_cf_type = tfun [] basic.tstring in
+		let getTag_cf = mk_class_field "getTag" getTag_cf_type true pos (Method MethNormal) [] in
+		getTag_cf.cf_meta <- [(Meta.Final, [], pos)];
+		getTag_cf.cf_expr <- Some {
+			eexpr = TFunction {
+				tf_args = [];
+				tf_type = basic.tstring;
+				tf_expr = mk_return (
+					let e_constructs = mk_static_field_access_infer cl "__hx_constructs" pos [] in
+					let e_this = mk (TConst TThis) (TInst (cl,[])) pos in
+					let e_index = mk_field_access gen e_this "index" pos in
+					{
+						eexpr = TArray(e_constructs,e_index);
+						etype = basic.tstring;
+						epos = pos;
+					}
+				)
+			};
+			etype = getTag_cf_type;
+			epos = pos;
+		};
+
+		cl.cl_ordered_fields <- getTag_cf :: cl.cl_ordered_fields ;
+		cl.cl_fields <- PMap.add "getTag" getTag_cf cl.cl_fields;
+		cl.cl_overrides <- getTag_cf :: cl.cl_overrides;
+		cl.cl_meta <- (Meta.NativeGen,[],cl.cl_pos) :: cl.cl_meta;
+		gen.gadd_to_module (TClassDecl cl) (max_dep);
+
+		TEnumDecl en
+
+	(*
+		traverse
+			gen - gen context
+			convert_all : bool - should we convert all enums? If set, convert_if_has_meta will be ignored.
+			convert_if_has_meta : bool - should we convert only if it has meta?
+			enum_base_class : tclass - the enum base class.
+			should_be_hxgen : bool - should the created enum be hxgen?
+	*)
+	let configure gen t convert_all convert_if_has_meta enum_base_class param_enum_class =
+		let convert e = convert gen t enum_base_class param_enum_class e in
+		let run md =
+			match md with
+			| TEnumDecl e when is_hxgen md ->
+				if convert_all then
+					convert e
+				else if convert_if_has_meta && has_any_meta e then
+					convert e
+				else if not (Meta.has Meta.FlatEnum e.e_meta) then
+					convert e
+				else begin
+					(* take off the :hxgen meta from it, if there's any *)
+					e.e_meta <- List.filter (fun (n,_,_) -> not (n = Meta.HxGen)) e.e_meta;
+					md
+				end
+			| _ ->
+				md
+		in
+		gen.gmodule_filters#add name (PCustom priority) run
+end;;
+
+(* ******************************************* *)
+(* EnumToClassExprf *)
+(* ******************************************* *)
+(*
+	Enum to class Expression Filter
+
+	dependencies:
+		Should run before TArrayTransform, since it generates array access expressions
+*)
+module EnumToClassExprf =
+struct
+	let name = "enum_to_class_exprf"
+	let priority = solve_deps name [DBefore TArrayTransform.priority]
+
+	let configure gen t mk_enum_index_call =
+		let rec run e =
+			let get_converted_enum_type et =
+				let en, eparams = match follow (gen.gfollow#run_f et) with
+					| TEnum(en,p) -> en, p
+					| _ -> raise Not_found
+				in
+				let cl = Hashtbl.find t.ec_tbl en.e_path in
+				TInst(cl, eparams)
+			in
+
+			match e.eexpr with
+			| TEnumIndex f ->
+				let f = run f in
+				(try
+					mk_field_access gen {f with etype = get_converted_enum_type f.etype} "index" e.epos
+				with Not_found ->
+					mk_enum_index_call f e.epos)
+			| TCall (({eexpr = TField(_, FStatic({cl_path=[],"Type"},{cf_name="enumIndex"}))} as left), [f]) ->
+				let f = run f in
+				(try
+					mk_field_access gen {f with etype = get_converted_enum_type f.etype} "index" e.epos
+				with Not_found ->
+					{ e with eexpr = TCall(left, [f]) })
+			| TEnumParameter(f, _,i) ->
+				let f = run f in
+				(* check if en was converted to class *)
+				(* if it was, switch on tag field and change cond type *)
+				let f = try
+					{ f with etype = get_converted_enum_type f.etype }
+				with Not_found ->
+					f
+				in
+				let cond_array = { (mk_field_access gen f "params" f.epos) with etype = gen.gclasses.nativearray t_dynamic } in
+				Codegen.index gen.gcon cond_array i e.etype e.epos
+			| _ ->
+				Type.map_expr run e
+		in
+		gen.gexpr_filters#add name (PCustom priority) run
+
+end;;
+
+let configure gen convert_all convert_if_has_meta enum_base_class param_enum_class mk_enum_index_call =
+	let t = new_t () in
+	EnumToClassModf.configure gen t convert_all convert_if_has_meta enum_base_class param_enum_class;
+	EnumToClassExprf.configure gen t mk_enum_index_call

+ 399 - 0
src/generators/gencommon/enumToClass2.ml

@@ -0,0 +1,399 @@
+(*
+	The Haxe Compiler
+	Copyright (C) 2005-2017  Haxe Foundation
+
+	This program is free software; you can redistribute it and/or
+	modify it under the terms of the GNU General Public License
+	as published by the Free Software Foundation; either version 2
+	of the License, or (at your option) any later version.
+
+	This program is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+
+	You should have received a copy of the GNU General Public License
+	along with this program; if not, write to the Free Software
+	Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+*)
+open Common
+open Ast
+open Codegen
+open Codegen.ExprBuilder
+open Type
+open Gencommon
+
+let add_static c cf =
+	c.cl_statics <- PMap.add cf.cf_name cf c.cl_statics;
+	c.cl_ordered_statics <- cf :: c.cl_ordered_statics
+
+let add_field c cf override =
+	c.cl_fields <- PMap.add cf.cf_name cf c.cl_fields;
+	c.cl_ordered_fields <- cf :: c.cl_ordered_fields;
+	if override then c.cl_overrides <- cf :: c.cl_overrides
+
+let add_meta com en cl_enum =
+	Option.may (fun expr ->
+		let cf_meta = mk_field "__meta__" expr.etype expr.epos expr.epos in
+		cf_meta.cf_expr <- Some expr;
+		add_static cl_enum cf_meta;
+	) (Codegen.build_metadata com (TEnumDecl en));
+
+type enclasses = {
+	base : tclass;
+	ctors : (string, tclass) PMap.t;
+}
+
+module EnumToClass2Modf = struct
+	let name = "enum_to_class2_mod"
+	let priority = solve_deps name [DBefore ReflectionCFs.priority; DBefore RealTypeParams.RealTypeParamsModf.priority]
+
+	let convert gen ec_tbl base_class en =
+		let pos = en.e_pos in
+
+		(* create the class *)
+		let cl_enum = mk_class en.e_module en.e_path pos in
+		cl_enum.cl_super <- Some (base_class,[]);
+		cl_enum.cl_extern <- en.e_extern;
+		cl_enum.cl_meta <- [(Meta.Enum,[],pos); (Meta.NativeGen,[],pos)] @ cl_enum.cl_meta;
+
+		(* mark the enum that it's generated as a class *)
+		en.e_meta <- (Meta.Class,[],pos) :: en.e_meta;
+
+		(* add metadata *)
+		add_meta gen.gcon en cl_enum;
+
+		let basic = gen.gcon.basic in
+		let mk_array_decl t el p = mk_nativearray_decl gen t el p in
+
+		(* add constructs field (for reflection) *)
+		if has_feature gen.gcon "Type.getEnumConstructs" then begin
+			let e_constructs = mk_array_decl basic.tstring (List.map (fun s -> make_string gen.gcon s pos) en.e_names) pos in
+			let cf_constructs = mk_field "__hx_constructs" e_constructs.etype pos pos in
+			cf_constructs.cf_kind <- Var { v_read = AccNormal; v_write = AccNever };
+			cf_constructs.cf_meta <- (Meta.ReadOnly,[],pos) :: (Meta.Protected,[],pos) :: cf_constructs.cf_meta;
+			cf_constructs.cf_expr <- Some e_constructs;
+			add_static cl_enum cf_constructs
+		end;
+
+		(* add the class to the module *)
+		gen.gadd_to_module (TClassDecl cl_enum) max_dep;
+
+		let eparamsToString = mk_static_field_access_infer base_class "paramsToString" pos [] in
+		let eparamsGetHashCode = mk_static_field_access_infer base_class "paramsGetHashCode" pos [] in
+
+		let e_pack, e_name = en.e_path in
+		let cl_enum_t = TInst (cl_enum, []) in
+		let cf_getTag_t = tfun [] basic.tstring in
+		let cf_getParams_ret = basic.tarray basic.tstring in
+		let cf_getParams_t = tfun [] cf_getParams_ret in
+		let static_ctors = ref [] in
+		let ctors_map = ref PMap.empty in
+		let add_ctor name index =
+			let ef = PMap.find name en.e_constrs in
+			let pos = ef.ef_pos in
+
+			let cl_ctor = mk_class en.e_module (e_pack, e_name ^ "_" ^ name) pos in
+			cl_ctor.cl_super <- Some (cl_enum, []);
+			cl_ctor.cl_meta <- [
+				(Meta.Enum,[],pos);
+				(Meta.NativeGen,[],pos);
+				(Meta.Final,[],pos);
+			] @ cl_ctor.cl_meta;
+			ctors_map := PMap.add name cl_ctor !ctors_map;
+
+			gen.gadd_to_module (TClassDecl cl_ctor) max_dep;
+
+			let esuper = mk (TConst TSuper) cl_enum_t pos in
+			let etag = make_string gen.gcon name pos in
+			let efields = ref [] in
+			(match follow ef.ef_type with
+				| TFun(_, _) ->
+					(* erase type params *)
+					let ef_type =
+						let t = apply_params en.e_params (List.map (fun _ -> t_dynamic) en.e_params) ef.ef_type in
+						apply_params ef.ef_params (List.map (fun _ -> t_dynamic) ef.ef_params) t
+					in
+					let params, ret = get_fun ef_type in
+
+					let cl_ctor_t = TInst (cl_ctor,[]) in
+					let other_en_v = alloc_var "en" cl_ctor_t in
+					let other_en_local = mk_local other_en_v pos in
+					let enumeq = mk_static_field_access_infer (get_cl (get_type gen ([],"Type"))) "enumEq" pos [t_dynamic] in
+					let refeq = mk_static_field_access_infer (get_cl (get_type gen (["System"],"Object"))) "ReferenceEquals" pos [] in
+
+					let param_equal_checks = ref [] in
+					let ctor_block = ref [] in
+					let ctor_args = ref [] in
+					let static_ctor_args = ref [] in
+					let ethis = mk (TConst TThis) cl_ctor_t pos in
+					List.iter (fun (n,_,t) ->
+						(* create a field for enum argument *)
+						let cf_param = mk_field n t pos pos in
+						cf_param.cf_kind <- Var { v_read = AccNormal; v_write = AccNever };
+						cf_param.cf_meta <- (Meta.ReadOnly,[],pos) :: cf_param.cf_meta;
+						add_field cl_ctor cf_param false;
+
+						(* add static constructor method argument *)
+						static_ctor_args := (alloc_var n t, None) :: !static_ctor_args;
+
+						(* generate argument field access *)
+						let efield = mk (TField (ethis, FInstance (cl_ctor, [], cf_param))) t pos in
+						efields := efield :: !efields;
+
+						(* add constructor argument *)
+						let ctor_arg_v = alloc_var n t in
+						ctor_args := (ctor_arg_v, None) :: !ctor_args;
+
+						(* generate assignment for the constructor *)
+						let assign = Codegen.binop OpAssign efield (mk_local ctor_arg_v pos) t pos in
+						ctor_block := assign :: !ctor_block;
+
+						(* generate an enumEq check for the Equals method (TODO: extract this) *)
+						let eotherfield = mk (TField (other_en_local, FInstance (cl_ctor, [], cf_param))) t pos in
+						let e_enumeq_check = mk (TCall (enumeq, [efield; eotherfield])) basic.tbool pos in
+						let e_param_check =
+							mk (TIf (mk (TUnop (Not, Prefix, e_enumeq_check)) basic.tbool pos,
+							         mk_return (make_bool gen.gcon false pos),
+							         None)
+							) basic.tvoid pos in
+						param_equal_checks := e_param_check :: !param_equal_checks;
+					) (List.rev params);
+
+					ctor_block := (mk (TCall(esuper,[make_int gen.gcon index pos])) basic.tvoid pos) :: !ctor_block;
+
+					let cf_ctor_t = TFun (params, basic.tvoid) in
+					let cf_ctor = mk_class_field "new" cf_ctor_t true pos (Method MethNormal) [] in
+					cf_ctor.cf_expr <- Some {
+						eexpr = TFunction {
+							tf_args = !ctor_args;
+							tf_type = basic.tvoid;
+							tf_expr = mk (TBlock !ctor_block) basic.tvoid pos;
+						};
+						etype = cf_ctor_t;
+						epos = pos;
+					};
+					cl_ctor.cl_constructor <- Some cf_ctor;
+
+					let cf_toString_t = TFun ([],basic.tstring) in
+					let cf_toString = mk_class_field "toString" cf_toString_t true pos (Method MethNormal) [] in
+
+					let etoString_args = mk_array_decl t_dynamic !efields pos in
+					cf_toString.cf_expr <- Some {
+						eexpr = TFunction {
+							tf_args = [];
+							tf_type = basic.tstring;
+							tf_expr = mk_block (mk_return (
+								mk (TCall (eparamsToString, [etag; etoString_args])) basic.tstring pos
+							));
+						};
+						etype = cf_toString_t;
+						epos = pos;
+					};
+					add_field cl_ctor cf_toString true;
+
+					let cf_static_ctor = mk_class_field name ef_type true pos (Method MethNormal) [] in
+					cf_static_ctor.cf_expr <- Some {
+						eexpr = TFunction {
+							tf_args = !static_ctor_args;
+							tf_type = ef_type;
+							tf_expr = mk_block (mk_return {eexpr = TNew(cl_ctor,[], (List.map (fun (v,_) -> mk_local v pos) !static_ctor_args)); etype = ef_type; epos = pos});
+						};
+						etype = ef_type;
+						epos = pos;
+					};
+					static_ctors := cf_static_ctor :: !static_ctors;
+
+					(* add Equals field *)
+					begin
+						let other_v = alloc_var "other" t_dynamic in
+						let eother_local = mk_local other_v pos in
+						let eas = mk (TIdent "__as__") t_dynamic pos in
+						let ecast = mk (TCall(eas,[eother_local])) cl_ctor_t pos in
+
+						let equals_exprs = ref (List.rev [
+							mk (TIf (
+								mk (TCall(refeq,[ethis;eother_local])) basic.tbool pos,
+								mk_return (make_bool gen.gcon true pos),
+								None
+							)) basic.tvoid pos;
+							mk (TVar(other_en_v, Some ecast)) basic.tvoid pos;
+							mk (TIf(
+								mk (TBinop(OpEq,other_en_local,make_null cl_ctor_t pos)) basic.tbool pos,
+								mk_return (make_bool gen.gcon false pos),
+								None
+							)) basic.tvoid pos;
+						]) in
+						equals_exprs := (List.rev !param_equal_checks) @ !equals_exprs;
+						equals_exprs := mk_return (make_bool gen.gcon true pos) :: !equals_exprs;
+
+						let cf_Equals_t = TFun([("other",false,t_dynamic)],basic.tbool) in
+						let cf_Equals = mk_class_field "Equals" cf_Equals_t true pos (Method MethNormal) [] in
+						cf_Equals.cf_expr <- Some {
+							eexpr = TFunction {
+								tf_args = [(other_v,None)];
+								tf_type = basic.tbool;
+								tf_expr = mk (TBlock (List.rev !equals_exprs)) basic.tvoid pos;
+							};
+							etype = cf_Equals_t;
+							epos = pos;
+						};
+						add_field cl_ctor cf_Equals true;
+					end;
+
+					(* add GetHashCode field *)
+					begin
+						let cf_GetHashCode_t = TFun([],basic.tint) in
+						let cf_GetHashCode = mk_class_field "GetHashCode" cf_GetHashCode_t true pos (Method MethNormal) [] in
+						cf_GetHashCode.cf_expr <- Some {
+							eexpr = TFunction {
+								tf_args = [];
+								tf_type = basic.tint;
+								tf_expr = mk_block (mk_return (
+									mk (TCall(eparamsGetHashCode, [make_int gen.gcon index pos;etoString_args])) basic.tint pos
+								));
+							};
+							etype = cf_GetHashCode_t;
+							epos = pos;
+						};
+						add_field cl_ctor cf_GetHashCode true;
+					end
+
+				| _ ->
+					let cf_ctor_t = TFun([], basic.tvoid) in
+					let cf_ctor = mk_class_field "new" cf_ctor_t true pos (Method MethNormal) [] in
+					cf_ctor.cf_expr <- Some {
+						eexpr = TFunction {
+							tf_args = [];
+							tf_type = basic.tvoid;
+							tf_expr = mk (TBlock [mk (TCall(esuper,[make_int gen.gcon index pos])) basic.tvoid pos]) basic.tvoid pos;
+						};
+						etype = cf_ctor_t;
+						epos = pos;
+					};
+					cl_ctor.cl_constructor <- Some cf_ctor;
+
+					let cf_static_inst = mk_class_field name cl_enum_t true pos (Var { v_read = AccNormal; v_write = AccNever }) [] in
+					cf_static_inst.cf_meta <- [Meta.ReadOnly,[],pos];
+					cf_static_inst.cf_expr <- Some {
+						eexpr = TNew(cl_ctor, [], []);
+						etype = cl_enum_t;
+						epos = pos;
+					};
+
+					static_ctors := cf_static_inst :: !static_ctors;
+			);
+
+			let cf_getTag = mk_class_field "getTag" cf_getTag_t true pos (Method MethNormal) [] in
+			cf_getTag.cf_expr <- Some {
+				eexpr = TFunction {
+					tf_args = [];
+					tf_type = basic.tstring;
+					tf_expr = mk_block (mk_return etag);
+				};
+				etype = cf_getTag_t;
+				epos = pos;
+			};
+			add_field cl_ctor cf_getTag true;
+
+			if !efields <> [] then begin
+				let cf_getParams = mk_class_field "getParams" cf_getParams_t true pos (Method MethNormal) [] in
+				cf_getParams.cf_expr <- Some {
+					eexpr = TFunction {
+						tf_args = [];
+						tf_type = cf_getParams_ret;
+						tf_expr = mk_block (mk_return (mk (TArrayDecl !efields) cf_getParams_ret pos));
+					};
+					etype = cf_getParams_t;
+					epos = pos;
+				};
+				add_field cl_ctor cf_getParams true
+			end
+		in
+
+
+		(* generate constructor subclasses and add static functions to create them *)
+		let i = ref 0 in
+		List.iter (fun name -> add_ctor name !i; incr i) en.e_names;
+
+		List.iter (add_static cl_enum) !static_ctors;
+
+		Hashtbl.add ec_tbl en.e_path {
+			base = cl_enum;
+			ctors = !ctors_map;
+		};
+
+		TEnumDecl en
+
+	let configure gen t enum_base_class =
+		let run md = match md with
+			| TEnumDecl e when is_hxgen md ->
+				convert gen t enum_base_class e
+			| _ ->
+				md
+		in
+		gen.gmodule_filters#add name (PCustom priority) run
+end;;
+
+
+module EnumToClass2Exprf = struct
+	let init com ec_tbl mk_enum_index_call =
+		let rec run e =
+			let get_converted_enum_classes et =
+				let en = match follow et with
+					| TEnum (en,_) -> en
+					| _ -> raise Not_found
+				in
+				Hashtbl.find ec_tbl en.e_path
+			in
+			let mk_converted_enum_index_access f =
+				let cl = (get_converted_enum_classes f.etype).base in
+				let e_enum = { f with etype = TInst (cl, []) } in
+				Codegen.field e_enum "_hx_index" com.basic.tint e.epos
+			in
+			match e.eexpr with
+			| TEnumIndex f ->
+				let f = run f in
+				(try
+					mk_converted_enum_index_access f
+				with Not_found ->
+					mk_enum_index_call f e.epos)
+			| TCall ({ eexpr = TField (_, FStatic ({ cl_path = ([], "Type") }, { cf_name = "enumIndex" })) } as left, [f]) ->
+				let f = run f in
+				(try
+					mk_converted_enum_index_access f
+				with Not_found ->
+					{ e with eexpr = TCall(left, [f]) })
+			| TEnumParameter(f, ef, i) ->
+				let f = run f in
+				(* check if en was converted to class *)
+				(* if it was, switch on tag field and change cond type *)
+				let classes = get_converted_enum_classes f.etype in
+				let cl_enum = classes.base in
+				let f = { f with etype = TInst(cl_enum, []) } in
+
+				let cl_ctor = PMap.find ef.ef_name classes.ctors in
+				let ecast = mk (TCall (mk (TIdent "__as__") t_dynamic f.epos, [f])) (TInst (cl_ctor, [])) f.epos in
+
+				(match ef.ef_type with
+				| TFun (params, _) ->
+					let fname, _, _ = List.nth params i in
+					Codegen.field ecast fname e.etype e.epos
+				| _ -> assert false)
+			| _ ->
+				Type.map_expr run e
+		in
+		run
+
+	let name = "enum_to_class2_exprf"
+	let priority = solve_deps name []
+
+	let configure gen ec_tbl mk_enum_index_call =
+		let run = init gen.gcon ec_tbl mk_enum_index_call in
+		gen.gexpr_filters#add name (PCustom priority) run
+end;;
+
+let configure gen enum_base_class mk_enum_index_call =
+	let ec_tbl = Hashtbl.create 10 in
+	EnumToClass2Modf.configure gen ec_tbl enum_base_class;
+	EnumToClass2Exprf.configure gen ec_tbl mk_enum_index_call;

+ 638 - 0
src/generators/gencommon/expressionUnwrap.ml

@@ -0,0 +1,638 @@
+(*
+	The Haxe Compiler
+	Copyright (C) 2005-2017  Haxe Foundation
+
+	This program is free software; you can redistribute it and/or
+	modify it under the terms of the GNU General Public License
+	as published by the Free Software Foundation; either version 2
+	of the License, or (at your option) any later version.
+
+	This program is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+
+	You should have received a copy of the GNU General Public License
+	along with this program; if not, write to the Free Software
+	Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+*)
+open Common
+open Ast
+open Type
+open Gencommon
+
+(*
+	This is the most important module for source-code based targets. It will follow a convention of what's an expression and what's a statement,
+	and will unwrap statements where expressions are expected, and vice-versa.
+
+	It should be one of the first syntax filters to be applied. As a consequence, it's applied after all filters that add code to the AST, and by being
+	the first of the syntax filters, it will also have the AST retain most of the meaning of normal Haxe code. So it's easier to detect cases which are
+	side-effects free, for example
+
+	Any target can make use of this, but there is one requirement: The target must accept null to be set to any kind of variable. For example,
+	var i:Int = null; must be accepted. The best way to deal with this is to (like it's done in C#) make null equal to "default(Type)"
+
+	dependencies:
+		While it's best for Expression Unwrap to delay its execution as much as possible, since theoretically any
+		filter can return an expression that needs to be unwrapped, it is also desirable for ExpresionUnwrap to have
+		the AST as close as possible as Haxe's, so it can make some correct predictions (for example, so it can
+		more accurately know what can be side-effects-free and what can't).
+		This way, it will run slightly after the Normal priority, so if you don't say that a syntax filter must run
+		before Expression Unwrap, it will run after it.
+
+	TODO : While statement must become do / while, with the actual block inside an if for the condition, and else for 'break'
+*)
+
+(* priority: first syntax filter *)
+let priority = -10.0
+
+(*
+	We always need to rely on Blocks to be able to unwrap expressions correctly.
+	So the the standard traverse will always be based on blocks.
+	Normal block statements, like for(), while(), if(), ... will be mk_block'ed so there is always a block inside of them.
+
+		At the block level, we'll define an "add_statement" function, which will allow the current expression to
+		add statements to the block. This statement may or may not contain statements as expressions, so the texpr will be evaluated recursively before being added.
+
+		- traverse will always evaluate TBlocks
+		- for each texpr in a TBlock list,
+			check shallow type
+				if type is Statement or Both when it has problematic expression (var problematic_expr = count_problematic_expressions),
+					if we can eagerly call unwrap_statement on the whole expression (try_call_unwrap_statement), use the return expression
+					else
+						check expr_type of each underlying type (with expr_stat_map)
+							if it has ExprWithStatement or Statement,
+								call problematic_expression_unwrap in it
+								problematic_expr--
+							else if problematic_expr == 0, just add the unchanged expression
+							else if NoSideEffects and doesn't have short-circuit, just add the unchanged expression
+							else call problematic_expression_unwrap in it
+				if type is Expression, check if there are statements or Both inside.
+					if there are, problematic_expression_unwrap in it
+				aftewards, use on_expr_as_statement to get it
+
+	helpers:
+		try_call_unwrap_statement: (returns texpr option)
+			if underlying statement is TBinop(OpAssign/OpAssignOp), or TVar, with the right side being a Statement or a short circuit op, we can call apply_assign.
+
+		apply_assign:
+			if is TVar, first declare the tvar with default expression = null;
+			will receive the left and right side of the assignment; right-side must be Statement
+			see if right side is a short-circuit operation, call short_circuit_op_unwrap
+			else see eexpr of the right side
+				if it's void, just add the statement with add_statement, and set the right side as null;
+				if not, it will have a block inside. set the left side = to the last expression on each block inside. add_statement for it.
+
+		short_circuit_op_unwrap: x() && (1 + {var x = 0; x + 1;} == 2) && z()
+			-> var x = x();
+					var y = false;
+					var z = false;
+					if (x) //for &&, neg for ||
+					{
+					var temp = null;
+					{
+						var x = 0;
+						temp = x + 1;
+					}
+
+					y = (1 + temp) == 2;
+					if (y)
+					{
+						z = z();
+					}
+					}
+			expects to receive a texpr with TBinop(OpBoolAnd/OpBoolOr)
+			will traverse the AST while there is a TBinop(OpBoolAnd/OpBoolOr) as a right-side expr, and declare new temp vars in the	for each found.
+			will collect the return value, a mapped expr with all exprs as TLocal of the temp vars created
+
+
+		problematic_expression_unwrap:
+			check expr_kind:
+				if it is NoSideEffects and not short-circuit, leave it there
+				if it is ExprWithStatement and not short-circuit, call Type.map_expr problematic_expression_unwrap
+				if it is Statement or Expression or short-circuit expr, call add_assign for this expression
+
+		add_assign:
+			see if the type is void. If it is, just add_statement the expression argument, and return a null value
+			else create a new variable, set TVar with Some() with the expression argument, add TVar with add_statement, and return the TLocal of this expression.
+
+		map_problematic_expr:
+			call expr_stat_map on statement with problematic_expression_unwrap
+
+	types:
+		type shallow_expr_type = | Statement | Expression | Both (* shallow expression classification. Both means that they can be either Statements as Expressions *)
+
+		type expr_kind = | NormalExpr | ExprNoSideEffects (* -> short-circuit is considered side-effects *) | ExprWithStatement | Statement
+			evaluates an expression (as in not a statement) type. If it is ExprWithStatement or Statement, it means it contains errors
+
+	functions:
+		shallow_expr_type (expr:texpr) : shallow_expr_type
+
+		expr_kind (expr:texpr) : expr_kind
+			deeply evaluates an expression type
+
+		expr_stat_map (fn:texpr->texpr) (expr:texpr) : texpr
+			it will traverse the AST looking for places where an expression is expected, and map the value according to fn
+
+		aggregate_expr_type (is_side_effects_free:bool) (children:expr_type list) : expr_type
+			helper function to deal with expr_type aggregation (e.g. an Expression + a Statement as a children, is a ExprWithStatement)
+
+		check_statement_in_expression (expr:texpr) : texpr option :
+			will check
+
+*)
+
+type shallow_expr_type = | Statement | Expression of texpr | Both of texpr (* shallow expression classification. Both means that they can be either Statements as Expressions *)
+
+type expr_kind = | KNormalExpr | KNoSideEffects (* -> short-circuit is considered side-effects *) | KExprWithStatement | KStatement
+
+let rec no_paren e =
+	match e.eexpr with
+		| TParenthesis e -> no_paren e
+		| _ -> e
+
+(* must be called in a statement. Will execute fn whenever an expression (not statement) is expected *)
+let rec expr_stat_map fn (expr:texpr) =
+	match (no_paren expr).eexpr with
+		| TBinop ( (OpAssign as op), left_e, right_e )
+		| TBinop ( (OpAssignOp _ as op), left_e, right_e ) ->
+			{ expr with eexpr = TBinop(op, fn left_e, fn right_e) }
+		| TParenthesis _ -> assert false
+		| TCall(left_e, params) ->
+			{ expr with eexpr = TCall(fn left_e, List.map fn params) }
+		| TNew(cl, tparams, params) ->
+			{ expr with eexpr = TNew(cl, tparams, List.map fn params) }
+		| TVar(v,eopt) ->
+			{ expr with eexpr = TVar(v, Option.map fn eopt) }
+		| TFor (v,cond,block) ->
+			{ expr with eexpr = TFor(v, fn cond, block) }
+		| TIf(cond,eif,eelse) ->
+			{ expr with eexpr = TIf(fn cond, eif, eelse) }
+		| TWhile(cond, block, flag) ->
+			{ expr with eexpr = TWhile(fn cond, block, flag) }
+		| TSwitch(cond, el_block_l, default) ->
+			{ expr with eexpr = TSwitch( fn cond, List.map (fun (el,block) -> (List.map fn el, block)) el_block_l, default ) }
+		| TReturn(eopt) ->
+			{ expr with eexpr = TReturn(Option.map fn eopt) }
+		| TThrow (texpr) ->
+			{ expr with eexpr = TThrow(fn texpr) }
+		| TBreak
+		| TContinue
+		| TTry _
+		| TUnop (Increment, _, _)
+		| TUnop (Decrement, _, _) (* unop is a special case because the haxe compiler won't let us generate complex expressions with Increment/Decrement *)
+		| TBlock _ -> expr (* there is no expected expression here. Only statements *)
+		| TMeta(m,e) ->
+			{ expr with eexpr = TMeta(m,expr_stat_map fn e) }
+		| _ -> assert false (* we only expect valid statements here. other expressions aren't valid statements *)
+
+let is_expr = function | Expression _ -> true | _ -> false
+
+let aggregate_expr_type map_fn side_effects_free children =
+	let rec loop acc children =
+		match children with
+			| [] -> acc
+			| hd :: children ->
+				match acc, map_fn hd with
+					| _, KExprWithStatement
+					| _, KStatement
+					| KExprWithStatement, _
+					| KStatement, _ -> KExprWithStatement
+					| KNormalExpr, KNoSideEffects
+					| KNoSideEffects, KNormalExpr
+					| KNormalExpr, KNormalExpr -> loop KNormalExpr children
+					| KNoSideEffects, KNoSideEffects -> loop KNoSideEffects children
+	in
+	loop (if side_effects_free then KNoSideEffects else KNormalExpr) children
+
+(* statements: *)
+(* Error CS0201: Only assignment, call, increment,					 *)
+(* decrement, and new object expressions can be used as a		 *)
+(* statement (CS0201). *)
+let rec shallow_expr_type expr : shallow_expr_type =
+	match expr.eexpr with
+		| TCall _ when not (ExtType.is_void expr.etype) -> Both expr
+		| TNew _
+		| TUnop (Increment, _, _)
+		| TUnop (Decrement, _, _)
+		| TBinop (OpAssign, _, _)
+		| TBinop (OpAssignOp _, _, _) -> Both expr
+		| TIf (cond, eif, Some(eelse)) -> (match aggregate_expr_type expr_kind true [cond;eif;eelse] with
+			| KExprWithStatement -> Statement
+			| _ -> Both expr)
+		| TConst _
+		| TLocal _
+		| TIdent _
+		| TArray _
+		| TBinop _
+		| TField _
+		| TEnumParameter _
+		| TEnumIndex _
+		| TTypeExpr _
+		| TObjectDecl _
+		| TArrayDecl _
+		| TFunction _
+		| TCast _
+		| TUnop _ -> Expression (expr)
+		| TParenthesis p | TMeta(_,p) -> shallow_expr_type p
+		| TBlock ([e]) -> shallow_expr_type e
+		| TCall _
+		| TVar _
+		| TBlock _
+		| TFor _
+		| TWhile _
+		| TSwitch _
+		| TTry _
+		| TReturn _
+		| TBreak
+		| TContinue
+		| TIf _
+		| TThrow _ -> Statement
+
+and expr_kind expr =
+	match shallow_expr_type expr with
+	| Statement -> KStatement
+	| Both expr | Expression expr ->
+		let aggregate = aggregate_expr_type expr_kind in
+		match expr.eexpr with
+		| TConst _
+		| TLocal _
+		| TFunction _
+		| TTypeExpr _
+		| TIdent _ ->
+			KNoSideEffects
+		| TCall (ecall, params) ->
+			aggregate false (ecall :: params)
+		| TNew (_,_,params) ->
+			aggregate false params
+		| TUnop (Increment,_,e)
+		| TUnop (Decrement,_,e) ->
+			aggregate false [e]
+		| TUnop (_,_,e) ->
+			aggregate true [e]
+		| TBinop (OpBoolAnd, e1, e2)
+		| TBinop (OpBoolOr, e1, e2) ->	(* TODO: should OpBool never be side-effects free? *)
+			aggregate true [e1;e2]
+		| TBinop (OpAssign, e1, e2)
+		| TBinop (OpAssignOp _, e1, e2) ->
+			aggregate false [e1;e2]
+		| TBinop (_, e1, e2) ->
+			aggregate true [e1;e2]
+		| TIf (cond, eif, Some(eelse)) -> (match aggregate true [cond;eif;eelse] with
+			| KExprWithStatement -> KStatement
+			| k -> k)
+		| TArray (e1,e2) ->
+			aggregate true [e1;e2]
+		| TParenthesis e
+		| TMeta(_,e)
+		| TField (e,_) ->
+			aggregate true [e]
+		| TArrayDecl (el) ->
+			aggregate true el
+		| TObjectDecl (sel) ->
+			aggregate true (List.map snd sel)
+		| TCast (e,_) ->
+			aggregate true [e]
+		| _ -> trace (debug_expr expr); assert false (* should have been read as Statement by shallow_expr_type *)
+
+let get_kinds (statement:texpr) =
+	let kinds = ref [] in
+	ignore (expr_stat_map (fun e ->
+		kinds := (expr_kind e) :: !kinds;
+		e
+	) statement);
+	List.rev !kinds
+
+let has_problematic_expressions (kinds:expr_kind list) =
+	let rec loop kinds =
+		match kinds with
+			| [] -> false
+			| KStatement :: _
+			| KExprWithStatement :: _ -> true
+			| _ :: tl -> loop tl
+	in
+	loop kinds
+
+let count_problematic_expressions (statement:texpr) =
+	let count = ref 0 in
+	ignore (expr_stat_map (fun e ->
+		(match expr_kind e with
+			| KStatement | KExprWithStatement -> incr count
+			| _ -> ()
+		);
+		e
+	) statement);
+	!count
+
+let apply_assign_block assign_fun elist =
+	let rec assign acc elist =
+		match elist with
+			| [] -> acc
+			| last :: [] ->
+				(assign_fun last) :: acc
+			| hd :: tl ->
+				assign (hd :: acc) tl
+	in
+	List.rev (assign [] elist)
+
+let mk_get_block assign_fun e =
+	match e.eexpr with
+		| TBlock [] -> e
+		| TBlock (el) ->
+			{ e with eexpr = TBlock(apply_assign_block assign_fun el) }
+		| _ ->
+			{ e with eexpr = TBlock([ assign_fun e ]) }
+
+let add_assign add_statement expr =
+	match expr.eexpr, follow expr.etype with
+		| _, TAbstract ({ a_path = ([],"Void") },[])
+		| TThrow _, _ ->
+			add_statement expr;
+			null expr.etype expr.epos
+		| _ ->
+			let var = mk_temp "stmt" expr.etype in
+			let tvars = { expr with eexpr = TVar(var,Some(expr)) } in
+			let local = { expr with eexpr = TLocal(var) } in
+			add_statement tvars;
+			local
+
+(* requirement: right must be a statement *)
+let rec apply_assign assign_fun right =
+	match right.eexpr with
+		| TBlock el ->
+			{ right with eexpr = TBlock(apply_assign_block assign_fun el) }
+		| TSwitch (cond, elblock_l, default) ->
+			{ right with eexpr = TSwitch(cond, List.map (fun (el,block) -> (el, mk_get_block assign_fun block)) elblock_l, Option.map (mk_get_block assign_fun) default) }
+		| TTry (block, catches) ->
+			{ right with eexpr = TTry(mk_get_block assign_fun block, List.map (fun (v,block) -> (v,mk_get_block assign_fun block) ) catches) }
+		| TIf (cond,eif,eelse) ->
+			{ right with eexpr = TIf(cond, mk_get_block assign_fun eif, Option.map (mk_get_block assign_fun) eelse) }
+		| TThrow _
+		| TWhile _
+		| TFor _
+		| TReturn _
+		| TBreak
+		| TContinue -> right
+		| TParenthesis p | TMeta(_,p) ->
+			apply_assign assign_fun p
+		| TVar _ ->
+			right
+		| _ ->
+			match follow right.etype with
+				| TAbstract ({ a_path = ([], "Void") },[]) ->
+					right
+				| _ -> trace (debug_expr right); assert false (* a statement is required *)
+
+let short_circuit_op_unwrap com add_statement expr :texpr =
+	let do_not expr =
+		{ expr with eexpr = TUnop(Not, Prefix, expr) }
+	in
+
+	(* loop will always return its own TBlock, and the mapped expression *)
+	let rec loop acc expr =
+		match expr.eexpr with
+			| TBinop ( (OpBoolAnd as op), left, right) ->
+				let var = mk_temp "boolv" right.etype in
+				let tvars = { right with eexpr = TVar(var, Some( { right with eexpr = TConst(TBool false); etype = com.basic.tbool } )); etype = com.basic.tvoid } in
+				let local = { right with eexpr = TLocal(var) } in
+
+				let mapped_left, ret_acc = loop ( (local, { right with eexpr = TBinop(OpAssign, local, right) } ) :: acc) left in
+
+				add_statement tvars;
+				({ expr with eexpr = TBinop(op, mapped_left, local) }, ret_acc)
+			(* we only accept OpBoolOr when it's the first to be evaluated *)
+			| TBinop ( (OpBoolOr as op), left, right) when acc = [] ->
+				let left = match left.eexpr with
+					| TLocal _ | TConst _ -> left
+					| _ -> add_assign add_statement left
+				in
+
+				let var = mk_temp "boolv" right.etype in
+				let tvars = { right with eexpr = TVar(var, Some( { right with eexpr = TConst(TBool false); etype = com.basic.tbool } )); etype = com.basic.tvoid } in
+				let local = { right with eexpr = TLocal(var) } in
+				add_statement tvars;
+
+				({ expr with eexpr = TBinop(op, left, local) }, [ do_not left, { right with eexpr = TBinop(OpAssign, local, right) } ])
+			| _ when acc = [] -> assert false
+			| _ ->
+				let var = mk_temp "boolv" expr.etype in
+				let tvars = { expr with eexpr = TVar(var, Some( { expr with etype = com.basic.tbool } )); etype = com.basic.tvoid } in
+				let local = { expr with eexpr = TLocal(var) } in
+
+				let last_local = ref local in
+				let acc = List.map (fun (local, assign) ->
+					let l = !last_local in
+					last_local := local;
+					(l, assign)
+				) acc in
+
+				add_statement tvars;
+				(local, acc)
+	in
+
+	let mapped_expr, local_assign_list = loop [] expr in
+
+	let rec loop local_assign_list : texpr =
+		match local_assign_list with
+			| [local, assign] ->
+				{ eexpr = TIf(local, assign, None); etype = com.basic.tvoid; epos = assign.epos }
+			| (local, assign) :: tl ->
+				{ eexpr = TIf(local,
+					{
+						eexpr = TBlock ( assign :: [loop tl] );
+						etype = com.basic.tvoid;
+						epos = assign.epos;
+					},
+				None); etype = com.basic.tvoid; epos = assign.epos }
+			| [] -> assert false
+	in
+
+	add_statement (loop local_assign_list);
+	mapped_expr
+
+let twhile_with_condition_statement com add_statement twhile cond e1 flag =
+	(* when a TWhile is found with a problematic condition *)
+	let block =
+		if flag = NormalWhile then
+			{ e1 with eexpr = TIf(cond, e1, Some({ e1 with eexpr = TBreak; etype = com.basic.tvoid })) }
+		else
+			Type.concat e1 { e1 with
+				eexpr = TIf({
+					eexpr = TUnop(Not, Prefix, mk_paren cond);
+					etype = com.basic.tbool;
+					epos = cond.epos
+				}, { e1 with eexpr = TBreak; etype = com.basic.tvoid }, None);
+				etype = com.basic.tvoid
+			}
+	in
+	add_statement { twhile with
+		eexpr = TWhile(
+			{ eexpr = TConst(TBool true); etype = com.basic.tbool; epos = cond.epos },
+			block,
+			DoWhile
+		);
+	}
+
+let try_call_unwrap_statement com handle_cast problematic_expression_unwrap (add_statement:texpr->unit) (expr:texpr) : texpr option =
+	let check_left left =
+		match expr_kind left with
+			| KExprWithStatement ->
+				problematic_expression_unwrap add_statement left KExprWithStatement
+			| KStatement -> assert false (* doesn't make sense a KStatement as a left side expression *)
+			| _ -> left
+	in
+
+	let handle_assign op left right =
+		let left = check_left left in
+		Some (apply_assign (fun e -> { e with eexpr = TBinop(op, left, if ExtType.is_void left.etype then e else handle_cast left.etype e.etype e) }) right )
+	in
+
+	let handle_return e =
+		Some( apply_assign (fun e ->
+			match e.eexpr with
+				| TThrow _ -> e
+				| _ when ExtType.is_void e.etype ->
+					{ e with eexpr = TBlock([e; { e with eexpr = TReturn None }]) }
+				| _ ->
+					Codegen.mk_return e
+		) e )
+	in
+
+	let is_problematic_if right =
+		match expr_kind right with
+			| KStatement | KExprWithStatement -> true
+			| _ -> false
+	in
+
+	match expr.eexpr with
+		| TBinop((OpAssign as op),left,right)
+		| TBinop((OpAssignOp _ as op),left,right) when shallow_expr_type right = Statement ->
+			handle_assign op left right
+		| TReturn( Some right ) when shallow_expr_type right = Statement ->
+			handle_return right
+		| TBinop((OpAssign as op),left, ({ eexpr = TBinop(OpBoolAnd,_,_) } as right) )
+		| TBinop((OpAssign as op),left,({ eexpr = TBinop(OpBoolOr,_,_) } as right))
+		| TBinop((OpAssignOp _ as op),left,({ eexpr = TBinop(OpBoolAnd,_,_) } as right) )
+		| TBinop((OpAssignOp _ as op),left,({ eexpr = TBinop(OpBoolOr,_,_) } as right) ) ->
+			let right = short_circuit_op_unwrap com add_statement right in
+			Some { expr with eexpr = TBinop(op, check_left left, right) }
+		| TVar(v,Some({ eexpr = TBinop(OpBoolAnd,_,_) } as right))
+		| TVar(v,Some({ eexpr = TBinop(OpBoolOr,_,_) } as right)) ->
+			let right = short_circuit_op_unwrap com add_statement right in
+			Some { expr with eexpr = TVar(v, Some(right)) }
+		| TVar(v,Some(right)) when shallow_expr_type right = Statement ->
+			add_statement ({ expr with eexpr = TVar(v, Some(null right.etype right.epos)) });
+			handle_assign OpAssign { expr with eexpr = TLocal(v); etype = v.v_type } right
+		(* TIf handling *)
+		| TBinop((OpAssign as op),left, ({ eexpr = TIf _ } as right))
+		| TBinop((OpAssignOp _ as op),left,({ eexpr = TIf _ } as right)) when is_problematic_if right ->
+			handle_assign op left right
+		| TVar(v,Some({ eexpr = TIf _ } as right)) when is_problematic_if right ->
+			add_statement ({ expr with eexpr = TVar(v, Some(null right.etype right.epos)) });
+			handle_assign OpAssign { expr with eexpr = TLocal(v); etype = v.v_type } right
+		| TWhile(cond, e1, flag) when is_problematic_if cond ->
+			twhile_with_condition_statement com add_statement expr cond e1 flag;
+			Some (null expr.etype expr.epos)
+		| _ -> None
+
+let problematic_expression_unwrap add_statement expr e_type =
+	let rec problematic_expression_unwrap is_first expr e_type =
+		match e_type, expr.eexpr with
+			| _, TBinop(OpBoolAnd, _, _)
+			| _, TBinop(OpBoolOr, _, _) -> add_assign add_statement expr (* add_assign so try_call_unwrap_expr *)
+			| KNoSideEffects, _ -> expr
+			| KStatement, _
+			| KNormalExpr, _ -> add_assign add_statement expr
+			| KExprWithStatement, TCall _
+			| KExprWithStatement, TNew _
+			| KExprWithStatement, TBinop (OpAssign,_,_)
+			| KExprWithStatement, TBinop (OpAssignOp _,_,_)
+			| KExprWithStatement, TUnop (Increment,_,_) (* all of these may have side-effects, so they must also be add_assign'ed . is_first avoids infinite loop *)
+			| KExprWithStatement, TUnop (Decrement,_,_) when not is_first -> add_assign add_statement expr
+
+			(* bugfix: Type.map_expr doesn't guarantee the correct order of execution *)
+			| KExprWithStatement, TBinop(op,e1,e2) ->
+				let e1 = problematic_expression_unwrap false e1 (expr_kind e1) in
+				let e2 = problematic_expression_unwrap false e2 (expr_kind e2) in
+				{ expr with eexpr = TBinop(op, e1, e2) }
+			| KExprWithStatement, TArray(e1,e2) ->
+				let e1 = problematic_expression_unwrap false e1 (expr_kind e1) in
+				let e2 = problematic_expression_unwrap false e2 (expr_kind e2) in
+				{ expr with eexpr = TArray(e1, e2) }
+			(* bugfix: calls should not be transformed into closure calls *)
+			| KExprWithStatement, TCall(( { eexpr = TField (ef_left, f) } as ef ), eargs) ->
+				{ expr with eexpr = TCall(
+					{ ef with eexpr = TField(problematic_expression_unwrap false ef_left (expr_kind ef_left), f) },
+					List.map (fun e -> problematic_expression_unwrap false e (expr_kind e)) eargs)
+				}
+			| KExprWithStatement, _ -> Type.map_expr (fun e -> problematic_expression_unwrap false e (expr_kind e)) expr
+	in
+	problematic_expression_unwrap true expr e_type
+
+let configure gen =
+	let rec traverse e =
+		match e.eexpr with
+		| TBlock el ->
+			let new_block = ref [] in
+			let rec process_statement e =
+				let e = no_paren e in
+				match e.eexpr, shallow_expr_type e with
+				| TCall( { eexpr = TIdent s } as elocal, elist ), _ when String.get s 0 = '_' && Hashtbl.mem gen.gspecial_vars s ->
+					new_block := { e with eexpr = TCall( elocal, List.map (fun e ->
+						match e.eexpr with
+							| TBlock _ -> traverse e
+							| _ -> e
+					) elist ) } :: !new_block
+				| _, Statement | _, Both _ ->
+					let e = match e.eexpr with TReturn (Some ({ eexpr = TThrow _ } as ethrow)) -> ethrow | _ -> e in
+					let kinds = get_kinds e in
+					if has_problematic_expressions kinds then begin
+						match try_call_unwrap_statement gen.gcon gen.ghandle_cast problematic_expression_unwrap process_statement e with
+							| Some { eexpr = TConst(TNull) } (* no op *)
+							| Some { eexpr = TBlock [] } -> ()
+							| Some e ->
+								if has_problematic_expressions (get_kinds e) then begin
+									process_statement e
+								end else
+									new_block := (traverse e) :: !new_block
+							| None ->
+							(
+								let acc = ref kinds in
+								let new_e = expr_stat_map (fun e ->
+									match !acc with
+										| hd :: tl ->
+											acc := tl;
+											if has_problematic_expressions (hd :: tl) then begin
+												problematic_expression_unwrap process_statement e hd
+											end else
+												e
+										| [] -> assert false
+								) e in
+
+								new_block := (traverse new_e) :: !new_block
+							)
+					end else begin new_block := (traverse e) :: !new_block end
+				| _, Expression e ->
+					let e = mk (TVar (mk_temp "expr" e.etype, Some e)) gen.gcon.basic.tvoid e.epos in
+					process_statement e
+			in
+			List.iter process_statement el;
+			let block = List.rev !new_block in
+			{ e with eexpr = TBlock block }
+		| TTry (block, catches) ->
+			{ e with eexpr = TTry(traverse (mk_block block), List.map (fun (v,block) -> (v, traverse (mk_block block))) catches) }
+		| TSwitch (cond,el_e_l, default) ->
+			{ e with eexpr = TSwitch(cond, List.map (fun (el,e) -> (el, traverse (mk_block e))) el_e_l, Option.map (fun e -> traverse (mk_block e)) default) }
+		| TWhile (cond,block,flag) ->
+			{e with eexpr = TWhile(cond,traverse (mk_block block), flag) }
+		| TIf (cond, eif, eelse) ->
+			{ e with eexpr = TIf(cond, traverse (mk_block eif), Option.map (fun e -> traverse (mk_block e)) eelse) }
+		| TFor (v,it,block) ->
+			{ e with eexpr = TFor(v,it, traverse (mk_block block)) }
+		| TFunction (tfunc) ->
+			{ e with eexpr = TFunction({ tfunc with tf_expr = traverse (mk_block tfunc.tf_expr) }) }
+		| TMeta (m, e2) ->
+			{ e with eexpr = TMeta (m, traverse e2)}
+		| _ -> e (* if expression doesn't have a block, we will exit *)
+	in
+	gen.gsyntax_filters#add "expression_unwrap" (PCustom priority) traverse

+ 86 - 0
src/generators/gencommon/filterClosures.ml

@@ -0,0 +1,86 @@
+(*
+	The Haxe Compiler
+	Copyright (C) 2005-2017  Haxe Foundation
+
+	This program is free software; you can redistribute it and/or
+	modify it under the terms of the GNU General Public License
+	as published by the Free Software Foundation; either version 2
+	of the License, or (at your option) any later version.
+
+	This program is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+
+	You should have received a copy of the GNU General Public License
+	along with this program; if not, write to the Free Software
+	Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+*)
+open Type
+open Gencommon
+
+(* ******************************************* *)
+(* Closure Detection *)
+(* ******************************************* *)
+(*
+	Just a small utility filter that detects when a closure must be created.
+	On the default implementation, this means when a function field is being accessed
+	not via reflection and not to be called instantly
+
+	dependencies:
+		must run after DynamicFieldAccess, so any TAnon { Statics / EnumStatics } will be changed to the corresponding TTypeExpr
+*)
+let name = "filter_closures"
+let priority = solve_deps name [DAfter DynamicFieldAccess.priority]
+
+let configure gen (should_change:texpr->string->bool) (filter:texpr->texpr->string->bool->texpr) =
+	let rec run e =
+		match e.eexpr with
+			(*(* this is precisely the only case where we won't even ask if we should change, because it is a direct use of TClosure *)
+			| TCall ( {eexpr = TClosure(e1,s)} as clos, args ) ->
+				{ e with eexpr = TCall({ clos with eexpr = TClosure(run e1, s) }, List.map run args ) }
+			| TCall ( clos, args ) ->
+				let rec loop clos = match clos.eexpr with
+					| TClosure(e1,s) -> Some (clos, e1, s)
+					| TParenthesis p -> loop p
+					| _ -> None
+				in
+				let clos = loop clos in
+				(match clos with
+					| Some (clos, e1, s) -> { e with eexpr = TCall({ clos with eexpr = TClosure(run e1, s) }, List.map run args ) }
+					| None -> Type.map_expr run e)*)
+				| TCall({ eexpr = TIdent "__delegate__" } as local, [del]) ->
+					{ e with eexpr = TCall(local, [Type.map_expr run del]) }
+				| TCall(({ eexpr = TField(_, _) } as ef), params) ->
+					{ e with eexpr = TCall(Type.map_expr run ef, List.map run params) }
+				| TField(ef, FEnum(en, field)) ->
+						(* FIXME replace t_dynamic with actual enum Anon field *)
+						let ef = run ef in
+						(match follow field.ef_type with
+							| TFun _ when should_change ef field.ef_name ->
+								filter e ef field.ef_name true
+							| _ ->
+									{ e with eexpr = TField(ef, FEnum(en,field)) }
+						)
+				| TField(({ eexpr = TTypeExpr _ } as tf), f) ->
+					(match field_access_esp gen tf.etype (f) with
+						| FClassField(_,_,_,cf,_,_,_) ->
+							(match cf.cf_kind with
+								| Method(MethDynamic)
+								| Var _ ->
+									e
+								| _ when should_change tf cf.cf_name ->
+									filter e tf cf.cf_name true
+								| _ ->
+									e
+							)
+						| _ -> e)
+				| TField(e1, FClosure (Some _, cf)) when should_change e1 cf.cf_name ->
+					(match cf.cf_kind with
+					| Method MethDynamic | Var _ ->
+						Type.map_expr run e
+					| _ ->
+						filter e (run e1) cf.cf_name false)
+				| _ -> Type.map_expr run e
+	in
+	gen.gexpr_filters#add name (PCustom priority) run

+ 266 - 0
src/generators/gencommon/fixOverrides.ml

@@ -0,0 +1,266 @@
+(*
+	The Haxe Compiler
+	Copyright (C) 2005-2017  Haxe Foundation
+
+	This program is free software; you can redistribute it and/or
+	modify it under the terms of the GNU General Public License
+	as published by the Free Software Foundation; either version 2
+	of the License, or (at your option) any later version.
+
+	This program is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+
+	You should have received a copy of the GNU General Public License
+	along with this program; if not, write to the Free Software
+	Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+*)
+open Option
+open Common
+open Type
+open Codegen
+open Gencommon
+
+(* ******************************************* *)
+(* FixOverrides *)
+(* ******************************************* *)
+(*
+
+	Covariant return types, contravariant function arguments and applied type parameters may change
+	in a way that expected implementations / overrides aren't recognized as such.
+	This filter will fix that.
+
+	dependencies:
+		FixOverrides expects that the target platform is able to deal with overloaded functions
+		It must run after DefaultArguments, otherwise code added by the default arguments may be invalid
+
+*)
+let name = "fix_overrides"
+let priority = solve_deps name []
+
+(*
+	if the platform allows explicit interface implementation (C#),
+	specify a explicit_fn_name function (tclass->string->string)
+	Otherwise, it expects the platform to be able to handle covariant return types
+*)
+let run ~explicit_fn_name ~get_vmtype gen =
+	let implement_explicitly = is_some explicit_fn_name in
+	let run md = match md with
+		| TClassDecl ( { cl_interface = true; cl_extern = false } as c ) ->
+			(* overrides can be removed from interfaces *)
+			c.cl_ordered_fields <- List.filter (fun f ->
+				try
+					if Meta.has Meta.Overload f.cf_meta then raise Not_found;
+					let f2 = Codegen.find_field gen.gcon c f in
+					if f2 == f then raise Not_found;
+					c.cl_fields <- PMap.remove f.cf_name c.cl_fields;
+					false;
+				with Not_found ->
+					true
+			) c.cl_ordered_fields;
+			md
+		| TClassDecl({ cl_extern = false } as c) ->
+			let this = { eexpr = TConst TThis; etype = TInst(c,List.map snd c.cl_params); epos = c.cl_pos } in
+			(* look through all interfaces, and try to find a type that applies exactly *)
+			let rec loop_iface (iface:tclass) itl =
+				List.iter (fun (s,stl) -> loop_iface s (List.map (apply_params iface.cl_params itl) stl)) iface.cl_implements;
+				let real_itl = gen.greal_type_param (TClassDecl iface) itl in
+				let rec loop_f f =
+					List.iter loop_f f.cf_overloads;
+					let ftype = apply_params iface.cl_params itl f.cf_type in
+					let real_ftype = get_real_fun gen (apply_params iface.cl_params real_itl f.cf_type) in
+					replace_mono real_ftype;
+					let overloads = Overloads.get_overloads c f.cf_name in
+					try
+						let t2, f2 =
+							match overloads with
+							| (_, cf) :: _ when Meta.has Meta.Overload cf.cf_meta -> (* overloaded function *)
+								(* try to find exact function *)
+								List.find (fun (t,f2) ->
+									Overloads.same_overload_args ~get_vmtype ftype t f f2
+								) overloads
+							| _ :: _ ->
+								(match field_access gen (TInst(c, List.map snd c.cl_params)) f.cf_name with
+								| FClassField(_,_,_,f2,false,t,_) -> t,f2 (* if it's not an overload, all functions should have the same signature *)
+								| _ -> raise Not_found)
+							| [] -> raise Not_found
+						in
+						replace_mono t2;
+						(* if we find a function with the exact type of real_ftype, it means this interface has already been taken care of *)
+						if not (type_iseq (get_real_fun gen (apply_params f2.cf_params (List.map snd f.cf_params) t2)) real_ftype) then begin
+							(match f.cf_kind with | Method (MethNormal | MethInline) -> () | _ -> raise Not_found);
+							let t2 = get_real_fun gen t2 in
+							if List.length f.cf_params <> List.length f2.cf_params then raise Not_found;
+							replace_mono t2;
+							match follow (apply_params f2.cf_params (List.map snd f.cf_params) t2), follow real_ftype with
+							| TFun(a1,r1), TFun(a2,r2) when not implement_explicitly && not (type_iseq r1 r2) && Overloads.same_overload_args ~get_vmtype real_ftype t2 f f2 ->
+								(* different return types are the trickiest cases to deal with *)
+								(* check for covariant return type *)
+								let is_covariant = match follow r1, follow r2 with
+									| _, TDynamic _ -> false
+									| r1, r2 -> try
+										unify r1 r2;
+										true
+									with | Unify_error _ -> false
+								in
+								(* we only have to worry about non-covariant issues *)
+								if not is_covariant then begin
+									(* override return type and cast implemented function *)
+									let args, newr = match follow t2, follow (apply_params f.cf_params (List.map snd f2.cf_params) real_ftype) with
+										| TFun(a,_), TFun(_,r) -> a,r
+										| _ -> assert false
+									in
+									f2.cf_type <- TFun(args,newr);
+									(match f2.cf_expr with
+									| Some ({ eexpr = TFunction tf } as e) ->
+											f2.cf_expr <- Some { e with eexpr = TFunction { tf with tf_type = newr } }
+									| _ -> ())
+								end
+							| TFun(a1,r1), TFun(a2,r2) ->
+								(* just implement a function that will call the main one *)
+								let name, is_explicit = match explicit_fn_name with
+									| Some fn when not (type_iseq r1 r2) && Overloads.same_overload_args ~get_vmtype real_ftype t2 f f2 ->
+											fn iface itl f.cf_name, true
+									| _ -> f.cf_name, false
+								in
+								let p = f2.cf_pos in
+								let newf = mk_class_field name real_ftype true f.cf_pos (Method MethNormal) f.cf_params in
+								let vars = List.map (fun (n,_,t) -> alloc_var n t) a2 in
+
+								let args = List.map2 (fun v (_,_,t) -> mk_cast t (mk_local v f2.cf_pos)) vars a1 in
+								let field = { eexpr = TField(this, FInstance(c,List.map snd c.cl_params,f2)); etype = TFun(a1,r1); epos = p } in
+								let call = { eexpr = TCall(field, args); etype = r1; epos = p } in
+								(* let call = gen.gparam_func_call call field (List.map snd f.cf_params) args in *)
+								let is_void = ExtType.is_void r2 in
+
+								newf.cf_expr <- Some {
+									eexpr = TFunction({
+										tf_args = List.map (fun v -> v,None) vars;
+										tf_type = r2;
+										tf_expr = if is_void then call else (mk_return (mk_cast r2 call));
+									});
+									etype = real_ftype;
+									epos = p;
+								};
+								(try
+									let fm = PMap.find name c.cl_fields in
+									fm.cf_overloads <- newf :: fm.cf_overloads
+								with | Not_found ->
+									c.cl_fields <- PMap.add name newf c.cl_fields;
+									c.cl_ordered_fields <- newf :: c.cl_ordered_fields)
+							| _ -> assert false
+						end
+					with | Not_found -> ()
+				in
+				List.iter (fun f -> match f.cf_kind with | Var _ -> () | _ -> loop_f f) iface.cl_ordered_fields
+			in
+			List.iter (fun (iface,itl) -> loop_iface iface itl) c.cl_implements;
+			(* now go through all overrides, *)
+			let rec check_f f =
+				(* find the first declared field *)
+				let is_overload = Meta.has Meta.Overload f.cf_meta in
+				let decl = if is_overload then
+					find_first_declared_field gen c ~get_vmtype ~exact_field:f f.cf_name
+				else
+					find_first_declared_field gen c ~get_vmtype f.cf_name
+				in
+				match decl with
+				| Some(f2,actual_t,_,t,declared_cl,_,_)
+					when not (Overloads.same_overload_args ~get_vmtype actual_t (get_real_fun gen f.cf_type) f2 f) ->
+						(match f.cf_expr with
+						| Some({ eexpr = TFunction(tf) } as e) ->
+							let actual_args, _ = get_fun (get_real_fun gen actual_t) in
+							let new_args, vars_to_declare = List.fold_left2 (fun (args,vdecl) (v,_) (_,_,t) ->
+								if not (type_iseq (gen.greal_type v.v_type) (gen.greal_type t)) then begin
+									let new_var = mk_temp v.v_name t in
+									(new_var,None) :: args, (v, Some(mk_cast v.v_type (mk_local new_var f.cf_pos))) :: vdecl
+								end else
+									(v,None) :: args, vdecl
+							) ([],[]) tf.tf_args actual_args in
+							let block = { eexpr = TBlock(List.map (fun (v,ve) ->
+								{
+									eexpr = TVar(v,ve);
+									etype = gen.gcon.basic.tvoid;
+									epos = tf.tf_expr.epos
+								}) vars_to_declare);
+								etype = gen.gcon.basic.tvoid;
+								epos = tf.tf_expr.epos
+							} in
+							let has_contravariant_args = match (get_real_fun gen f.cf_type, actual_t) with
+								| TFun(current_args,_), TFun(original_args,_) ->
+										List.exists2 (fun (_,_,cur_arg) (_,_,orig_arg) -> try
+											unify orig_arg cur_arg;
+											try
+												unify cur_arg orig_arg;
+												false
+											with Unify_error _ ->
+												true
+										with Unify_error _ -> false) current_args original_args
+								| _ -> assert false
+							in
+							if (not (Meta.has Meta.Overload f.cf_meta) && has_contravariant_args) then
+								f.cf_meta <- (Meta.Overload, [], f.cf_pos) :: f.cf_meta;
+							if Meta.has Meta.Overload f.cf_meta then begin
+								(* if it is overload, create another field with the requested type *)
+								let f3 = mk_class_field f.cf_name t f.cf_public f.cf_pos f.cf_kind f.cf_params in
+								let p = f.cf_pos in
+								let old_args, old_ret = get_fun f.cf_type in
+								let args, ret = get_fun t in
+								let tf_args = List.rev new_args in
+								let f3_mk_return = if ExtType.is_void ret then (fun e -> e) else (fun e -> mk_return (mk_cast ret e)) in
+								f3.cf_expr <- Some {
+									eexpr = TFunction({
+										tf_args = tf_args;
+										tf_type = ret;
+										tf_expr = Type.concat block (mk_block (f3_mk_return {
+											eexpr = TCall(
+												{
+													eexpr = TField(
+														{ eexpr = TConst TThis; etype = TInst(c, List.map snd c.cl_params); epos = p },
+														FInstance(c,List.map snd c.cl_params,f));
+													etype = f.cf_type;
+													epos = p
+												},
+												List.map2 (fun (v,_) (_,_,t) -> mk_cast t (mk_local v p)) tf_args old_args);
+											etype = old_ret;
+											epos = p
+										}))
+									});
+									etype = t;
+									epos = p;
+								};
+								(* make sure we skip cast detect - otherwise this new function will make the overload detection go crazy *)
+								f3.cf_meta <- (Meta.Custom(":skipCastDetect"), [], f3.cf_pos) :: f3.cf_meta;
+								gen.gafter_expr_filters_ended <- ((fun () ->
+									f.cf_overloads <- f3 :: f.cf_overloads;
+								) :: gen.gafter_expr_filters_ended);
+								f3
+							end else begin
+								(* if it's not overload, just cast the vars *)
+								if vars_to_declare <> [] then
+								f.cf_expr <- Some({ e with
+									eexpr = TFunction({ tf with
+										tf_args = List.rev new_args;
+										tf_expr = Type.concat block tf.tf_expr
+									});
+								});
+								f
+							end
+						| _ -> f)
+				| _ -> f
+			in
+			if not c.cl_extern then
+				c.cl_overrides <- List.map (fun f -> check_f f) c.cl_overrides;
+			md
+		| _ -> md
+	in
+	run
+
+let configure ?explicit_fn_name ~get_vmtype gen =
+	let delay () =
+		Hashtbl.clear gen.greal_field_types
+	in
+	gen.gafter_mod_filters_ended <- delay :: gen.gafter_mod_filters_ended;
+	let run = run ~explicit_fn_name ~get_vmtype gen in
+	gen.gmodule_filters#add name (PCustom priority) run

+ 280 - 0
src/generators/gencommon/hardNullableSynf.ml

@@ -0,0 +1,280 @@
+(*
+	The Haxe Compiler
+	Copyright (C) 2005-2017  Haxe Foundation
+
+	This program is free software; you can redistribute it and/or
+	modify it under the terms of the GNU General Public License
+	as published by the Free Software Foundation; either version 2
+	of the License, or (at your option) any later version.
+
+	This program is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+
+	You should have received a copy of the GNU General Public License
+	along with this program; if not, write to the Free Software
+	Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+*)
+open Option
+open Common
+open Type
+open Gencommon
+
+(* ******************************************* *)
+(* HardNullableSynf *)
+(* ******************************************* *)
+(*
+	This module will handle Null<T> types for languages that offer a way of dealing with
+	stack-allocated structures or tuples and generics. Essentialy on those targets a Null<T>
+	will be a tuple ( 'a * bool ), where bool is whether the value is null or not.
+
+	At first (configure-time), we will modify the follow function so it can follow correctly nested Null<Null<T>>,
+	and do not follow Null<T> to its underlying type
+
+	Then we will run a syntax filter, which will look for casts to Null<T> and replace them by
+	a call to the new Null<T> creation;
+	Also casts from Null<T> to T or direct uses of Null<T> (call, field access, array access, closure)
+	will result in the actual value being accessed
+	For compatibility with the C# target, HardNullable will accept both Null<T> and haxe.lang.Null<T> types
+
+	dependencies:
+		Needs to be run after all cast detection modules
+*)
+let name = "hard_nullable"
+let priority = solve_deps name [DAfter CastDetect.ReturnCast.priority]
+
+let rec is_null_t gen t = match gen.greal_type t with
+	| TAbstract( { a_path = ([], "Null") }, [of_t])
+	| TInst( { cl_path = (["haxe";"lang"], "Null") }, [of_t]) ->
+		let rec take_off_null t =
+			match is_null_t gen t with | None -> t | Some s -> take_off_null s
+		in
+
+		Some (take_off_null of_t)
+	| TMono r -> (match !r with | Some t -> is_null_t gen t | None -> None)
+	| TLazy f -> is_null_t gen (lazy_type f)
+	| TType (t, tl) ->
+		is_null_t gen (apply_params t.t_params tl t.t_type)
+	| _ -> None
+
+let follow_addon gen t =
+	let rec strip_off_nullable t =
+		let t = gen.gfollow#run_f t in
+		match t with
+			(* haxe.lang.Null<haxe.lang.Null<>> wouldn't be a valid construct, so only follow Null<> *)
+			| TAbstract ( { a_path = ([], "Null") }, [of_t] ) -> strip_off_nullable of_t
+			| _ -> t
+	in
+
+	match t with
+		| TAbstract( ({ a_path = ([], "Null") } as tab), [of_t]) ->
+			Some( TAbstract(tab, [ strip_off_nullable of_t ]) )
+		| _ -> None
+
+let configure gen unwrap_null wrap_val null_to_dynamic has_value opeq_handler =
+	gen.gfollow#add (name ^ "_follow") PZero (follow_addon gen);
+
+	let is_null_t = is_null_t gen in
+	let is_string t = match gen.greal_type t with
+		| TInst({ cl_path=([],"String") },_) -> true
+		| _ -> false
+	in
+	let handle_unwrap to_t e =
+		let e_null_t = get (is_null_t e.etype) in
+		match gen.greal_type to_t with
+			| TDynamic _ | TMono _ | TAnon _ ->
+				(match e_null_t with
+					| TDynamic _ | TMono _ | TAnon _ ->
+						gen.ghandle_cast to_t e_null_t (unwrap_null e)
+					| _ -> null_to_dynamic e
+				)
+			| _ ->
+				gen.ghandle_cast to_t e_null_t (unwrap_null e)
+	in
+
+	let handle_wrap e t =
+		match e.eexpr with
+			| TConst(TNull) ->
+				wrap_val e t false
+			| _ ->
+				wrap_val e t true
+	in
+
+	let cur_block = ref [] in
+	let add_tmp v e p =
+		cur_block := { eexpr = TVar(v,e); etype = gen.gcon.basic.tvoid; epos = p } :: !cur_block
+	in
+	let get_local e = match e.eexpr with
+		| TLocal _ ->
+			e, e
+		| _ ->
+			let v = mk_temp "nulltmp" e.etype in
+			add_tmp v (Some (null e.etype e.epos)) e.epos;
+			let local = { e with eexpr = TLocal(v) } in
+			mk_paren { e with eexpr = TBinop(Ast.OpAssign, local, e) }, local
+	in
+	let rec run e =
+		match e.eexpr with
+			| TBlock(bl) ->
+				let lst = !cur_block in
+				cur_block := [];
+				List.iter (fun e ->
+					let e = run e in
+					cur_block := (e :: !cur_block)
+				) bl;
+				let ret = !cur_block in
+				cur_block := lst;
+				{ e with eexpr = TBlock(List.rev ret) }
+			| TCast(v, _) ->
+				let null_et = is_null_t e.etype in
+				let null_vt = is_null_t v.etype in
+				(match null_vt, null_et with
+					| Some(vt), None when is_string e.etype ->
+						let v = run v in
+						{ e with eexpr = TCast(null_to_dynamic v,None) }
+					| Some(vt), None ->
+						(match v.eexpr with
+							(* is there an unnecessary cast to Nullable? *)
+							| TCast(v2, _) ->
+								run { v with etype = e.etype }
+							| _ ->
+								handle_unwrap e.etype (run v)
+						)
+					| None, Some(et) ->
+						handle_wrap (run v) et
+					| Some(vt), Some(et) when not (type_iseq (run_follow gen vt) (run_follow gen et)) ->
+						(* check if has value and convert *)
+						let vlocal_fst, vlocal = get_local (run v) in
+						{
+							eexpr = TIf(
+								has_value vlocal_fst,
+								handle_wrap (mk_cast et (unwrap_null vlocal)) et,
+								Some( handle_wrap (null et e.epos) et ));
+							etype = e.etype;
+							epos = e.epos
+						}
+					| _ ->
+						Type.map_expr run e
+				)
+			| TField(ef, field) when is_some (is_null_t ef.etype) ->
+				let to_t = get (is_null_t ef.etype) in
+				{ e with eexpr = TField(handle_unwrap to_t (run ef), field) }
+			| TCall(ecall, params) when is_some (is_null_t ecall.etype) ->
+				let to_t = get (is_null_t ecall.etype) in
+				{ e with eexpr = TCall(handle_unwrap to_t (run ecall), List.map run params) }
+			| TArray(earray, p) when is_some (is_null_t earray.etype) ->
+				let to_t = get (is_null_t earray.etype) in
+				{ e with eexpr = TArray(handle_unwrap to_t (run earray), p) }
+			| TBinop(op, e1, e2) ->
+				let e1_t = is_null_t e1.etype in
+				let e2_t = is_null_t e2.etype in
+
+				(match op with
+					| Ast.OpAssign
+					| Ast.OpAssignOp _ ->
+						(match e1_t, e2_t with
+							| Some t1, Some t2 ->
+								(match op with
+									| Ast.OpAssign ->
+										Type.map_expr run e
+									| Ast.OpAssignOp op ->
+										(match e1.eexpr with
+											| TLocal _ ->
+												{ e with eexpr = TBinop( Ast.OpAssign, e1, handle_wrap { e with eexpr = TBinop (op, handle_unwrap t1 e1, handle_unwrap t2 (run e2) ) } t1 ) }
+											| _ ->
+												let v, e1, evars = match e1.eexpr with
+													| TField(ef, f) ->
+														let v = mk_temp "nullbinop" ef.etype in
+														v, { e1 with eexpr = TField(mk_local v ef.epos, f) }, ef
+													| _ ->
+														let v = mk_temp "nullbinop" e1.etype in
+														v, mk_local v e1.epos, e1
+												in
+												{ e with eexpr = TBlock([
+													{ eexpr = TVar(v, Some evars); etype = gen.gcon.basic.tvoid; epos = e.epos };
+													{ e with eexpr = TBinop( Ast.OpAssign, e1, handle_wrap { e with eexpr = TBinop (op, handle_unwrap t1 e1, handle_unwrap t2 (run e2) ) } t1 ) }
+												]) }
+										)
+									| _ -> assert false
+								)
+
+							| _ ->
+								Type.map_expr run e (* casts are already dealt with normal CastDetection module *)
+						)
+					| Ast.OpEq | Ast.OpNotEq ->
+						(match e1.eexpr, e2.eexpr with
+							| TConst(TNull), _ when is_some e2_t ->
+								let e = has_value (run e2) in
+								if op = Ast.OpEq then
+									{ e with eexpr = TUnop(Ast.Not, Ast.Prefix, e) }
+								else
+									e
+							| _, TConst(TNull) when is_some e1_t ->
+								let e = has_value (run e1) in
+								if op = Ast.OpEq then
+									{ e with eexpr = TUnop(Ast.Not, Ast.Prefix, e) }
+								else
+									e
+							| _ when is_some e1_t || is_some e2_t ->
+									let e1, e2 =
+										if not (is_some e1_t) then
+											run e2, handle_wrap (run e1) (get e2_t)
+										else if not (is_some e2_t) then
+											run e1, handle_wrap (run e2) (get e1_t)
+										else
+											run e1, run e2
+									in
+									let e = opeq_handler e1 e2 in
+									if op = Ast.OpEq then
+										{ e with eexpr = TUnop(Ast.Not, Ast.Prefix, e) }
+									else
+										e
+							| _ ->
+								Type.map_expr run e
+						)
+					| Ast.OpAdd when is_string e1.etype || is_string e2.etype ->
+						let e1 = if is_some e1_t then
+							null_to_dynamic (run e1)
+						else
+							run e1
+						in
+						let e2 = if is_some e2_t then
+							null_to_dynamic (run e2)
+						else
+							run e2
+						in
+						let e_t = is_null_t e.etype in
+						if is_some e_t then
+							wrap_val { eexpr = TBinop(op,e1,e2); etype = get e_t; epos = e.epos } (get e_t) true
+						else
+							{ e with eexpr = TBinop(op,e1,e2) }
+					| _ ->
+						let e1 = if is_some e1_t then
+							handle_unwrap (get e1_t) (run e1)
+						else run e1 in
+						let e2 = if is_some e2_t then
+							handle_unwrap (get e2_t) (run e2)
+						else
+							run e2 in
+
+						(* if it is Null<T>, we need to convert the result again to null *)
+						let e_t = (is_null_t e.etype) in
+						if is_some e_t then
+							wrap_val { eexpr = TBinop(op, e1, e2); etype = get e_t; epos = e.epos } (get e_t) true
+						else
+							{ e with eexpr = TBinop(op, e1, e2) }
+				)
+			(*| TUnop( (Ast.Increment as op)*)
+			| _ -> Type.map_expr run e
+	in
+	let run e = match e.eexpr with
+		| TFunction tf ->
+			run { e with eexpr = TFunction { tf with tf_expr = mk_block tf.tf_expr } }
+		| TBlock _ ->
+			run e
+		| _ -> match run (mk_block e) with
+			| { eexpr = TBlock([e]) } -> e
+			| e -> e
+	in
+	gen.gsyntax_filters#add name (PCustom priority) run

+ 235 - 0
src/generators/gencommon/initFunction.ml

@@ -0,0 +1,235 @@
+(*
+	The Haxe Compiler
+	Copyright (C) 2005-2017  Haxe Foundation
+
+	This program is free software; you can redistribute it and/or
+	modify it under the terms of the GNU General Public License
+	as published by the Free Software Foundation; either version 2
+	of the License, or (at your option) any later version.
+
+	This program is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+
+	You should have received a copy of the GNU General Public License
+	along with this program; if not, write to the Free Software
+	Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+*)
+open Common
+open Type
+open Codegen
+open Gencommon
+
+(*
+	This module will take proper care of the init function, by taking off all expressions from static vars and putting them
+	in order in the init function.
+
+	It will also initialize dynamic functions, both by putting them in the constructor and in the init function
+
+	depends on:
+		(syntax) must run before ExprStatement module
+		(ok) must run before OverloadingConstructor module so the constructor can be in the correct place
+		(syntax) must run before FunctionToClass module
+*)
+
+let ensure_simple_expr com e =
+	let rec iter e =
+		match e.eexpr with
+		| TConst _ | TLocal _ | TArray _ | TBinop _
+		| TField _ | TTypeExpr _ | TParenthesis _ | TCast _ | TMeta _
+		| TCall _ | TNew _ | TUnop _ | TIdent _ ->
+			Type.iter iter e
+		| _ ->
+			print_endline (debug_expr e);
+			com.error "Expression is too complex for a readonly variable initialization" e.epos
+	in
+	iter e
+
+let handle_override_dynfun acc e this field =
+	let v = mk_temp ("super_" ^ field) e.etype in
+	v.v_capture <- true;
+
+	let add_expr = ref None in
+
+	let rec loop e =
+		match e.eexpr with
+		| TField ({ eexpr = TConst TSuper }, f) ->
+			let n = field_name f in
+			if n <> field then assert false;
+			if Option.is_none !add_expr then
+				add_expr := Some { e with eexpr = TVar(v, Some this) };
+			mk_local v e.epos
+		| TConst TSuper -> assert false
+		| _ -> Type.map_expr loop e
+	in
+	let e = loop e in
+
+	match !add_expr with
+	| None -> e :: acc
+	| Some add_expr -> add_expr :: e :: acc
+
+let handle_class com cl =
+	let init = match cl.cl_init with
+		| None -> []
+		| Some i -> [i]
+	in
+	let init = List.fold_left (fun acc cf ->
+		match cf.cf_kind with
+			| Var v when Meta.has Meta.ReadOnly cf.cf_meta ->
+					if v.v_write <> AccNever && not (Meta.has Meta.CoreApi cl.cl_meta) then com.warning "@:readOnly variable declared without `never` setter modifier" cf.cf_pos;
+					(match cf.cf_expr with
+					| None -> com.warning "Uninitialized readonly variable" cf.cf_pos
+					| Some e -> ensure_simple_expr com e);
+					acc
+			| Var _
+			| Method MethDynamic when Type.is_physical_field cf ->
+				(match cf.cf_expr with
+				| Some e ->
+					(match cf.cf_params with
+					| [] ->
+						let var = mk (TField (ExprBuilder.make_static_this cl cf.cf_pos, FStatic(cl,cf))) cf.cf_type cf.cf_pos in
+						let ret = binop Ast.OpAssign var e cf.cf_type cf.cf_pos in
+						cf.cf_expr <- None;
+						ret :: acc
+					| _ ->
+						let params = List.map (fun _ -> t_dynamic) cf.cf_params in
+						let fn = apply_params cf.cf_params params in
+						let var = mk (TField (ExprBuilder.make_static_this cl cf.cf_pos, FStatic(cl,cf))) (fn cf.cf_type) cf.cf_pos in
+						let rec change_expr e =
+							Type.map_expr_type change_expr fn (fun v -> v.v_type <- fn v.v_type; v) e
+						in
+						let ret = binop Ast.OpAssign var (change_expr e) (fn cf.cf_type) cf.cf_pos in
+						cf.cf_expr <- None;
+						ret :: acc)
+				| None -> acc)
+			| _ -> acc
+	) init cl.cl_ordered_statics in
+	let init = List.rev init in
+	(match init with
+	| [] -> cl.cl_init <- None
+	| _ -> cl.cl_init <- Some (mk (TBlock init) com.basic.tvoid cl.cl_pos));
+
+	(* FIXME: find a way to tell OverloadingConstructor to execute this code even with empty constructors *)
+	let vars, funs = List.fold_left (fun (acc_vars,acc_funs) cf ->
+		match cf.cf_kind with
+		| Var v when Meta.has Meta.ReadOnly cf.cf_meta ->
+				if v.v_write <> AccNever && not (Meta.has Meta.CoreApi cl.cl_meta) then com.warning "@:readOnly variable declared without `never` setter modifier" cf.cf_pos;
+				Option.may (ensure_simple_expr com) cf.cf_expr;
+				(acc_vars,acc_funs)
+		| Var _
+		| Method MethDynamic ->
+			let is_var = match cf.cf_kind with Var _ -> true | _ -> false in
+			(match cf.cf_expr, cf.cf_params with
+			| Some e, [] ->
+				let var = mk (TField ((mk (TConst TThis) (TInst (cl, List.map snd cl.cl_params)) cf.cf_pos), FInstance(cl, List.map snd cl.cl_params, cf))) cf.cf_type cf.cf_pos in
+				let ret = binop Ast.OpAssign var e cf.cf_type cf.cf_pos in
+				cf.cf_expr <- None;
+				let is_override = List.memq cf cl.cl_overrides in
+
+				if is_override then begin
+					cl.cl_ordered_fields <- List.filter (fun f -> f.cf_name <> cf.cf_name) cl.cl_ordered_fields;
+					cl.cl_fields <- PMap.remove cf.cf_name cl.cl_fields;
+					acc_vars, handle_override_dynfun acc_funs ret var cf.cf_name
+				end else if is_var then
+					ret :: acc_vars, acc_funs
+				else
+					acc_vars, ret :: acc_funs
+			| Some e, _ ->
+				let params = List.map (fun _ -> t_dynamic) cf.cf_params in
+				let fn = apply_params cf.cf_params params in
+				let var = mk (TField ((mk (TConst TThis) (TInst (cl, List.map snd cl.cl_params)) cf.cf_pos), FInstance(cl, List.map snd cl.cl_params, cf))) cf.cf_type cf.cf_pos in
+				let rec change_expr e =
+					Type.map_expr_type (change_expr) fn (fun v -> v.v_type <- fn v.v_type; v) e
+				in
+
+				let ret = binop Ast.OpAssign var (change_expr e) (fn cf.cf_type) cf.cf_pos in
+				cf.cf_expr <- None;
+				let is_override = List.memq cf cl.cl_overrides in
+
+				if is_override then begin
+					cl.cl_ordered_fields <- List.filter (fun f -> f.cf_name <> cf.cf_name) cl.cl_ordered_fields;
+					cl.cl_fields <- PMap.remove cf.cf_name cl.cl_fields;
+					acc_vars, handle_override_dynfun acc_funs ret var cf.cf_name
+				end else if is_var then
+					ret :: acc_vars, acc_funs
+				else
+					acc_vars, ret :: acc_funs
+			| None, _ -> acc_vars,acc_funs)
+		| _ -> acc_vars,acc_funs
+	) ([],[]) cl.cl_ordered_fields
+	in
+	(* let vars = List.rev vars in *)
+	(* let funs = List.rev funs in *)
+	(* see if there is any *)
+	(match vars, funs with
+	| [], [] -> ()
+	| _ ->
+		(* if there is, we need to find the constructor *)
+		let ctors =
+			match cl.cl_constructor with
+			| Some ctor ->
+				ctor
+			| None ->
+				try
+					let sctor, sup, stl = OverloadingConstructor.prev_ctor cl (List.map snd cl.cl_params) in
+					let ctor = OverloadingConstructor.clone_ctors com sctor sup stl cl in
+					cl.cl_constructor <- Some ctor;
+					ctor
+				with Not_found ->
+					let ctor = mk_class_field "new" (TFun([], com.basic.tvoid)) false cl.cl_pos (Method MethNormal) [] in
+					ctor.cf_expr <- Some
+					{
+						eexpr = TFunction {
+							tf_args = [];
+							tf_type = com.basic.tvoid;
+							tf_expr = { eexpr = TBlock[]; etype = com.basic.tvoid; epos = cl.cl_pos };
+						};
+						etype = ctor.cf_type;
+						epos = ctor.cf_pos;
+					};
+					cl.cl_constructor <- Some ctor;
+					ctor
+		in
+		let process ctor =
+			let func =
+				match ctor.cf_expr with
+				| Some ({ eexpr = TFunction tf } as e) ->
+					let rec add_fn e =
+						match e.eexpr with
+						| TBlock(hd :: tl) ->
+							(match hd.eexpr with
+							| TCall ({ eexpr = TConst TSuper }, _) ->
+								if not (OverloadingConstructor.descends_from_native_or_skipctor cl) then
+									{ e with eexpr = TBlock (vars @ (hd :: (funs @ tl))) }
+								else
+									{ e with eexpr = TBlock (hd :: (vars @ funs @ tl)) }
+							| TBlock _ ->
+								{ e with eexpr = TBlock ((add_fn hd) :: tl) }
+							| _ ->
+								{ e with eexpr = TBlock (vars @ funs @ (hd :: tl)) })
+						| _ ->
+							Type.concat { e with eexpr = TBlock (vars @ funs) } e
+					in
+					let tf_expr = add_fn (mk_block tf.tf_expr) in
+					{ e with eexpr = TFunction { tf with tf_expr = tf_expr } }
+				| _ ->
+					assert false
+			in
+			ctor.cf_expr <- Some func
+		in
+		List.iter process (ctors :: ctors.cf_overloads)
+	)
+
+let mod_filter com md =
+	match md with
+	| TClassDecl cl when not cl.cl_extern ->
+		handle_class com cl
+	| _ -> ()
+
+let name = "init_funcs"
+let priority = solve_deps name [DBefore OverloadingConstructor.priority]
+
+let configure gen =
+	let run = (fun md -> mod_filter gen.gcon md; md) in
+	gen.gmodule_filters#add name (PCustom priority) run

+ 79 - 0
src/generators/gencommon/intDivisionSynf.ml

@@ -0,0 +1,79 @@
+(*
+	The Haxe Compiler
+	Copyright (C) 2005-2017  Haxe Foundation
+
+	This program is free software; you can redistribute it and/or
+	modify it under the terms of the GNU General Public License
+	as published by the Free Software Foundation; either version 2
+	of the License, or (at your option) any later version.
+
+	This program is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+
+	You should have received a copy of the GNU General Public License
+	along with this program; if not, write to the Free Software
+	Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+*)
+open Common
+open Ast
+open Type
+open Gencommon
+
+(*
+	On targets that support int division, this module will force a float division to be performed.
+	It will also look for casts to int or use of Std.int() to optimize this kind of operation.
+
+	dependencies:
+		since it depends on nothing, but many modules might generate division expressions,
+		it will be one of the last modules to run
+*)
+let init com =
+	let rec is_int t =
+		match follow t with
+		| TInst ({ cl_path = (["haxe";"lang"],"Null") }, [t]) ->
+			is_int t
+		| t ->
+			like_int t && not (like_i64 t)
+	in
+	let is_exactly_int t =
+		match follow t with
+		| TAbstract ({ a_path = ([],"Int") }, []) -> true
+		| _ -> false
+	in
+	let rec run e =
+		match e.eexpr with
+		| TBinop ((OpDiv as op), e1, e2) when is_int e1.etype && is_int e2.etype ->
+			{ e with eexpr = TBinop (op, mk_cast com.basic.tfloat (run e1), run e2) }
+
+		| TCall (
+				{ eexpr = TField (_, FStatic ({ cl_path = ([], "Std") }, { cf_name = "int" })) },
+				[ { eexpr = TBinop ((OpDiv as op), e1, e2) } as ebinop ]
+			) when is_int e1.etype && is_int e2.etype ->
+
+			let e = { ebinop with eexpr = TBinop (op, run e1, run e2); etype = com.basic.tint } in
+			if not (is_exactly_int e1.etype && is_exactly_int e2.etype) then
+				mk_cast com.basic.tint e
+			else
+				e
+
+		| TCast ({ eexpr = TBinop((OpDiv as op), e1, e2) } as ebinop, _ )
+		| TCast ({ eexpr = TBinop(((OpAssignOp OpDiv) as op), e1, e2) } as ebinop, _ ) when is_int e1.etype && is_int e2.etype && is_int e.etype ->
+			let ret = { ebinop with eexpr = TBinop (op, run e1, run e2); etype = e.etype } in
+			if not (is_exactly_int e1.etype && is_exactly_int e2.etype) then
+				mk_cast e.etype ret
+			else
+				e
+
+		| _ ->
+			Type.map_expr run e
+	in
+	run
+
+let name = "int_division_synf"
+let priority = solve_deps name [ DAfter ExpressionUnwrap.priority; DAfter ObjectDeclMap.priority; DAfter ArrayDeclSynf.priority ]
+
+let configure gen =
+	let run = init gen.gcon in
+	gen.gsyntax_filters#add name (PCustom priority) run

+ 43 - 0
src/generators/gencommon/interfaceProps.ml

@@ -0,0 +1,43 @@
+(*
+	The Haxe Compiler
+	Copyright (C) 2005-2017  Haxe Foundation
+
+	This program is free software; you can redistribute it and/or
+	modify it under the terms of the GNU General Public License
+	as published by the Free Software Foundation; either version 2
+	of the License, or (at your option) any later version.
+
+	This program is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+
+	You should have received a copy of the GNU General Public License
+	along with this program; if not, write to the Free Software
+	Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+*)
+open Globals
+open Type
+
+(*
+	This module filter will go through all declared properties, and see if they are conforming to a native interface.
+	If they are, it will add Meta.Property to it.
+*)
+let run = function
+	| TClassDecl ({ cl_interface = false; cl_extern = false } as cl) ->
+		let vars = List.fold_left (fun acc (iface,_) ->
+			if Meta.has Meta.CsNative iface.cl_meta then
+				let props = List.filter (fun cf -> match cf.cf_kind with Var { v_read = AccCall } | Var { v_write = AccCall } -> true | _ -> false) iface.cl_ordered_fields in
+				props @ acc
+			else
+				acc
+		) [] cl.cl_implements in
+		if vars <> [] then
+			let vars = List.map (fun cf -> cf.cf_name) vars in
+			List.iter (fun cf -> match cf.cf_kind with
+				| Var { v_read = AccCall } | Var { v_write = AccCall } when List.mem cf.cf_name vars ->
+					cf.cf_meta <- (Meta.Property, [], null_pos) :: cf.cf_meta
+				| _ -> ()
+			) cl.cl_ordered_fields
+	| _ ->
+		()

+ 84 - 0
src/generators/gencommon/interfaceVarsDeleteModf.ml

@@ -0,0 +1,84 @@
+(*
+	The Haxe Compiler
+	Copyright (C) 2005-2017  Haxe Foundation
+
+	This program is free software; you can redistribute it and/or
+	modify it under the terms of the GNU General Public License
+	as published by the Free Software Foundation; either version 2
+	of the License, or (at your option) any later version.
+
+	This program is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+
+	You should have received a copy of the GNU General Public License
+	along with this program; if not, write to the Free Software
+	Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+*)
+open Common
+open Globals
+open Type
+open Gencommon
+
+(* ******************************************* *)
+(* Interface Variables Removal Modf *)
+(* ******************************************* *)
+(*
+	This module filter will take care of sanitizing interfaces for targets that do not support
+	variables declaration in interfaces. By now this will mean that if anything is typed as the interface,
+	and a variable access is made, a FNotFound will be returned for the field_access, so
+	the field will be only accessible by reflection.
+	Speed-wise, ideally it would be best to create getProp/setProp functions in this case and change
+	the AST to call them when accessing by interface. (TODO)
+	But right now it will be accessed by reflection.
+*)
+let name = "interface_vars"
+let priority = solve_deps name []
+
+let configure gen =
+	let run md =
+		match md with
+		| TClassDecl ({ cl_interface = true } as cl) ->
+			let to_add = ref [] in
+			let fields = List.filter (fun cf ->
+				match cf.cf_kind with
+				| Var _ when gen.gcon.platform = Cs && Meta.has Meta.Event cf.cf_meta ->
+					true
+				| Var vkind when Type.is_physical_field cf || not (Meta.has Meta.Property cf.cf_meta) ->
+					(match vkind.v_read with
+						| AccCall ->
+							let newcf = mk_class_field ("get_" ^ cf.cf_name) (TFun([],cf.cf_type)) true cf.cf_pos (Method MethNormal) [] in
+							to_add := newcf :: !to_add;
+						| _ -> ()
+					);
+					(match vkind.v_write with
+						| AccCall ->
+							let newcf = mk_class_field ("set_" ^ cf.cf_name) (TFun(["val",false,cf.cf_type],cf.cf_type)) true cf.cf_pos (Method MethNormal) [] in
+							to_add := newcf :: !to_add;
+						| _ -> ()
+					);
+					cl.cl_fields <- PMap.remove cf.cf_name cl.cl_fields;
+					false
+				| Method MethDynamic ->
+					(* TODO OPTIMIZATION - add a `_dispatch` method to the interface which will call the dynamic function itself *)
+					cl.cl_fields <- PMap.remove cf.cf_name cl.cl_fields;
+					false
+				| _ ->
+					true
+			) cl.cl_ordered_fields in
+
+			cl.cl_ordered_fields <- fields;
+
+			List.iter (fun cf ->
+				match field_access gen (TInst(cl,List.map snd cl.cl_params)) cf.cf_name with
+				| FNotFound | FDynamicField _ ->
+					cl.cl_ordered_fields <- cf :: cl.cl_ordered_fields;
+					cl.cl_fields <- PMap.add cf.cf_name cf cl.cl_fields
+				| _ ->
+					()
+			) !to_add
+		| _ -> ()
+	in
+	let map md = run md; md in
+	gen.gmodule_filters#add name (PCustom priority) map

+ 98 - 0
src/generators/gencommon/normalize.ml

@@ -0,0 +1,98 @@
+(*
+	The Haxe Compiler
+	Copyright (C) 2005-2017  Haxe Foundation
+
+	This program is free software; you can redistribute it and/or
+	modify it under the terms of the GNU General Public License
+	as published by the Free Software Foundation; either version 2
+	of the License, or (at your option) any later version.
+
+	This program is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+
+	You should have received a copy of the GNU General Public License
+	along with this program; if not, write to the Free Software
+	Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+*)
+open Type
+open Gencommon
+
+(*
+	- Filters out enum constructor type parameters from the AST; See Issue #1796
+	- Filters out monomorphs
+	- Filters out all non-whitelisted AST metadata
+
+	dependencies:
+		No dependencies; but it still should be one of the first filters to run,
+		as it will help normalize the AST
+*)
+
+let rec filter_param t =
+	match t with
+	| TInst({ cl_kind = KTypeParameter _ } as c,_) when Meta.has Meta.EnumConstructorParam c.cl_meta ->
+		t_dynamic
+	| TMono r ->
+		(match !r with
+		| None -> t_dynamic
+		| Some t -> filter_param t)
+	| TInst(_,[]) | TEnum(_,[]) | TType(_,[]) | TAbstract(_,[]) ->
+		t
+	| TType(t,tl) ->
+		TType(t,List.map filter_param tl)
+	| TInst(c,tl) ->
+		TInst(c,List.map filter_param tl)
+	| TEnum(e,tl) ->
+		TEnum(e,List.map filter_param tl)
+	| TAbstract({ a_path = (["haxe";"extern"],"Rest") } as a,tl) ->
+		TAbstract(a, List.map filter_param tl)
+	| TAbstract({a_path = [],"Null"} as a,[t]) ->
+		TAbstract(a,[filter_param t])
+	| TAbstract(a,tl) when not (Meta.has Meta.CoreType a.a_meta) ->
+		filter_param (Abstract.get_underlying_type a tl)
+	| TAbstract(a,tl) ->
+		TAbstract(a, List.map filter_param tl)
+	| TAnon a ->
+		TAnon {
+			a_fields = PMap.map (fun f -> { f with cf_type = filter_param f.cf_type }) a.a_fields;
+			a_status = a.a_status;
+		}
+	| TFun(args,ret) ->
+		TFun(List.map (fun (n,o,t) -> (n,o,filter_param t)) args, filter_param ret)
+	| TDynamic _ ->
+		t
+	| TLazy f ->
+		filter_param (lazy_type f)
+
+let init_expr_filter allowed_metas =
+	let rec run e =
+		match e.eexpr with
+		| TMeta ((m,_,_), e) when not (Hashtbl.mem allowed_metas m) ->
+			run e
+		| _ ->
+			map_expr_type (fun e -> run e) filter_param (fun v -> v.v_type <- filter_param v.v_type; v) e
+	in
+	run
+
+let type_filter = function
+	| TClassDecl cl ->
+		let rec map cf =
+			cf.cf_type <- filter_param cf.cf_type;
+			List.iter map cf.cf_overloads
+		in
+		List.iter map cl.cl_ordered_fields;
+		List.iter map cl.cl_ordered_statics;
+		Option.may map cl.cl_constructor
+	| _ ->
+		()
+
+let name = "normalize_type"
+let priority = max_dep
+
+let configure gen ~allowed_metas =
+	let run = init_expr_filter allowed_metas in
+	gen.gexpr_filters#add name (PCustom priority) run;
+
+	let map md = type_filter md; md in
+	gen.gmodule_filters#add name (PCustom priority) map

+ 37 - 0
src/generators/gencommon/objectDeclMap.ml

@@ -0,0 +1,37 @@
+(*
+	The Haxe Compiler
+	Copyright (C) 2005-2017  Haxe Foundation
+
+	This program is free software; you can redistribute it and/or
+	modify it under the terms of the GNU General Public License
+	as published by the Free Software Foundation; either version 2
+	of the License, or (at your option) any later version.
+
+	This program is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+
+	You should have received a copy of the GNU General Public License
+	along with this program; if not, write to the Free Software
+	Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+*)
+open Type
+open Gencommon
+
+(* ******************************************* *)
+(* Object Declaration Mapper *)
+(* ******************************************* *)
+let name = "object_decl_map"
+let priority = solve_deps name []
+
+let configure gen map_fn =
+	let rec run e =
+		match e.eexpr with
+		| TObjectDecl odecl ->
+			let e = Type.map_expr run e in
+			(match e.eexpr with TObjectDecl odecl -> map_fn e odecl | _ -> assert false)
+		| _ ->
+			Type.map_expr run e
+	in
+	gen.gsyntax_filters#add name (PCustom priority) run

+ 437 - 0
src/generators/gencommon/overloadingConstructor.ml

@@ -0,0 +1,437 @@
+(*
+	The Haxe Compiler
+	Copyright (C) 2005-2017  Haxe Foundation
+
+	This program is free software; you can redistribute it and/or
+	modify it under the terms of the GNU General Public License
+	as published by the Free Software Foundation; either version 2
+	of the License, or (at your option) any later version.
+
+	This program is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+
+	You should have received a copy of the GNU General Public License
+	along with this program; if not, write to the Free Software
+	Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+*)
+open Option
+open Common
+open Type
+open Codegen
+open Gencommon
+
+(* ******************************************* *)
+(* overloading reflection constructors *)
+(* ******************************************* *)
+(*
+	this module works on languages that support function overloading and
+	enable function hiding via static functions.
+	it takes the constructor body out of the constructor and adds it to a special ctor
+	static function. The static function will receive the same parameters as the constructor,
+	plus the special "me" var, which will replace "this"
+
+	Then it always adds two constructors to the class: one that receives a special marker class,
+	indicating that the object should be constructed without executing constructor body,
+	and one that executes its normal constructor.
+	Both will only include a super() call to the superclasses' emtpy constructor.
+
+	This enables two things:
+		empty construction without the need of incompatibility with the platform's native construction method
+		the ability to call super() constructor in any place in the constructor
+*)
+let rec cur_ctor c tl =
+	match c.cl_constructor with
+	| Some ctor ->
+		ctor, c, tl
+	| None ->
+		match c.cl_super with
+		| None ->
+			raise Not_found
+		| Some (sup,stl) ->
+			cur_ctor sup (List.map (apply_params c.cl_params tl) stl)
+
+let rec prev_ctor c tl =
+	match c.cl_super with
+	| None ->
+		raise Not_found
+	| Some (sup,stl) ->
+		let stl = List.map (apply_params c.cl_params tl) stl in
+		match sup.cl_constructor with
+		| None -> prev_ctor sup stl
+		| Some ctor -> ctor, sup, stl
+
+let make_static_ctor_name cl =
+	let name = mk_internal_name "hx" "ctor" in
+	name ^ "_" ^ (String.concat "_" (fst cl.cl_path)) ^ "_" ^ (snd cl.cl_path)
+
+(* replaces super() call with last static constructor call *)
+let replace_super_call com c tl with_params me p follow_type =
+	let rec loop_super c tl =
+		match c.cl_super with
+		| None ->
+			raise Not_found
+		| Some(sup,stl) ->
+			let stl = List.map (apply_params c.cl_params tl) stl in
+			try
+				let static_ctor_name = make_static_ctor_name sup in
+				sup, stl, PMap.find static_ctor_name sup.cl_statics
+			with Not_found ->
+				loop_super sup stl
+	in
+	let sup, stl, cf = loop_super c tl in
+	let with_params = (mk (TLocal me) me.v_type p) :: with_params in
+	let cf =
+		try
+			(* choose best super function *)
+			List.iter (fun e -> replace_mono e.etype) with_params;
+			List.find (fun cf ->
+				replace_mono cf.cf_type;
+				let args, _ = get_fun (apply_params cf.cf_params stl cf.cf_type) in
+				try
+					List.for_all2 (fun (_,_,t) e -> try
+						let e_etype = follow_type e.etype in
+						let t = follow_type t in
+						unify e_etype t; true
+					with Unify_error _ ->
+						false
+					) args with_params
+				with Invalid_argument _ ->
+					false
+			) (cf :: cf.cf_overloads)
+		with Not_found ->
+			com.error "No suitable overload for the super call arguments was found" p; cf
+	in
+	{
+		eexpr = TCall(
+			{
+				eexpr = TField(ExprBuilder.make_static_this sup p, FStatic(sup,cf));
+				etype = apply_params cf.cf_params stl cf.cf_type;
+				epos = p
+			},
+			with_params
+		);
+		etype = com.basic.tvoid;
+		epos = p;
+	}
+
+(* will create a static counterpart of 'ctor', and replace its contents to a call to the static version*)
+let create_static_ctor com ~empty_ctor_expr cl ctor follow_type =
+	match Meta.has Meta.SkipCtor ctor.cf_meta with
+	| true -> ()
+	| false when is_none ctor.cf_expr -> ()
+	| false ->
+		let static_ctor_name = make_static_ctor_name cl in
+		(* create the static constructor *)
+		let ctor_types = List.map (fun (s,t) -> (s, TInst(map_param (get_cl_t t), []))) cl.cl_params in
+		let me = alloc_var "__hx_this" (TInst(cl, List.map snd ctor_types)) in
+		me.v_capture <- true;
+
+		let fn_args, _ = get_fun ctor.cf_type in
+		let ctor_params = List.map snd ctor_types in
+		let fn_type = TFun((me.v_name,false, me.v_type) :: List.map (fun (n,o,t) -> (n,o,apply_params cl.cl_params ctor_params t)) fn_args, com.basic.tvoid) in
+		let cur_tf_args = match ctor.cf_expr with
+		| Some { eexpr = TFunction(tf) } -> tf.tf_args
+		| _ -> assert false
+		in
+
+		let changed_tf_args = List.map (fun (v,_) -> (v,None)) cur_tf_args in
+
+		let local_map = Hashtbl.create (List.length cur_tf_args) in
+		let static_tf_args = (me, None) :: List.map (fun (v,b) ->
+			let new_v = alloc_var v.v_name (apply_params cl.cl_params ctor_params v.v_type) in
+			new_v.v_capture <- v.v_capture;
+			Hashtbl.add local_map v.v_id new_v;
+			(new_v, b)
+		) cur_tf_args in
+
+		let static_ctor = mk_class_field static_ctor_name fn_type false ctor.cf_pos (Method MethNormal) ctor_types in
+		static_ctor.cf_meta <- (Meta.Protected,[],ctor.cf_pos) :: static_ctor.cf_meta;
+
+		(* change ctor contents to reference the 'me' var instead of 'this' *)
+		let actual_super_call = ref None in
+		let rec map_expr ~is_first e = match e.eexpr with
+			| TCall (({ eexpr = TConst TSuper } as tsuper), params) -> (try
+				let params = List.map (fun e -> map_expr ~is_first:false e) params in
+				actual_super_call := Some { e with eexpr = TCall(tsuper, [empty_ctor_expr]) };
+				replace_super_call com cl ctor_params params me e.epos follow_type
+			with | Not_found ->
+				(* last static function was not found *)
+				actual_super_call := Some e;
+				if not is_first then
+					com.error "Super call must be the first call when extending native types" e.epos;
+				{ e with eexpr = TBlock([]) })
+			| TFunction tf when is_first ->
+				do_map ~is_first:true e
+			| TConst TThis ->
+				mk_local me e.epos
+			| TBlock (fst :: bl) ->
+				let fst = map_expr ~is_first:is_first fst in
+				{ e with eexpr = TBlock(fst :: List.map (fun e -> map_expr ~is_first:false e) bl); etype = apply_params cl.cl_params ctor_params e.etype }
+			| _ ->
+				do_map e
+		and do_map ?(is_first=false) e =
+			let do_t = apply_params cl.cl_params ctor_params in
+			let do_v v = try
+					Hashtbl.find local_map v.v_id
+				with | Not_found ->
+					v.v_type <- do_t v.v_type; v
+			in
+			Type.map_expr_type (map_expr ~is_first:is_first) do_t do_v e
+		in
+
+		let expr = do_map ~is_first:true (get ctor.cf_expr) in
+		let expr = match expr.eexpr with
+		| TFunction(tf) ->
+			{ expr with etype = fn_type; eexpr = TFunction({ tf with tf_args = static_tf_args }) }
+		| _ -> assert false in
+		static_ctor.cf_expr <- Some expr;
+		(* add to the statics *)
+		(try
+			let stat = PMap.find static_ctor_name cl.cl_statics in
+			stat.cf_overloads <- static_ctor :: stat.cf_overloads
+		with | Not_found ->
+			cl.cl_ordered_statics <- static_ctor :: cl.cl_ordered_statics;
+			cl.cl_statics <- PMap.add static_ctor_name static_ctor cl.cl_statics);
+		(* change current super call *)
+		match ctor.cf_expr with
+		| Some({ eexpr = TFunction(tf) } as e) ->
+			let block_contents, p = match !actual_super_call with
+			| None -> [], ctor.cf_pos
+			| Some super -> [super], super.epos
+			in
+			let block_contents = block_contents @ [{
+				eexpr = TCall(
+					{
+						eexpr = TField(
+							ExprBuilder.make_static_this cl p,
+							FStatic(cl, static_ctor));
+						etype = apply_params static_ctor.cf_params (List.map snd cl.cl_params) static_ctor.cf_type;
+						epos = p
+					},
+					[{ eexpr = TConst TThis; etype = TInst(cl, List.map snd cl.cl_params); epos = p }]
+					@ List.map (fun (v,_) -> mk_local v p) cur_tf_args
+				);
+				etype = com.basic.tvoid;
+				epos = p
+			}] in
+			ctor.cf_expr <- Some { e with eexpr = TFunction({ tf with tf_expr = { tf.tf_expr with eexpr = TBlock block_contents }; tf_args = changed_tf_args }) }
+		| _ -> assert false
+
+(* makes constructors that only call super() for the 'ctor' argument *)
+let clone_ctors com ctor sup stl cl =
+	let rec clone cf =
+		let ncf = mk_class_field "new" (apply_params sup.cl_params stl cf.cf_type) cf.cf_public cf.cf_pos cf.cf_kind cf.cf_params in
+		if Meta.has Meta.Protected cf.cf_meta then
+			ncf.cf_meta <- (Meta.Protected,[],ncf.cf_pos) :: ncf.cf_meta;
+		let args, ret = get_fun ncf.cf_type in
+		(* single expression: call to super() *)
+		let tf_args = List.map (fun (name,_,t) ->
+			(* the constructor will have no optional arguments, as presumably this will be handled by the underlying expr *)
+			alloc_var name t, None
+		) args in
+		let super_call =
+		{
+			eexpr = TCall(
+				{ eexpr = TConst TSuper; etype = TInst(cl, List.map snd cl.cl_params); epos = ctor.cf_pos },
+				List.map (fun (v,_) -> mk_local v ctor.cf_pos) tf_args);
+			etype = com.basic.tvoid;
+			epos = ctor.cf_pos;
+		} in
+		ncf.cf_expr <- Some
+		{
+			eexpr = TFunction {
+				tf_args = tf_args;
+				tf_type = com.basic.tvoid;
+				tf_expr = mk_block super_call;
+			};
+			etype = ncf.cf_type;
+			epos = ctor.cf_pos;
+		};
+		ncf
+	in
+	(* take off createEmpty *)
+	let all = List.filter (fun cf -> replace_mono cf.cf_type; not (Meta.has Meta.SkipCtor cf.cf_meta)) (ctor :: ctor.cf_overloads) in
+	let clones = List.map clone all in
+	match clones with
+	| [] ->
+		(* raise Not_found *)
+		assert false (* should never happen *)
+	| cf :: [] -> cf
+	| cf :: overl ->
+		cf.cf_meta <- (Meta.Overload,[],cf.cf_pos) :: cf.cf_meta;
+		cf.cf_overloads <- overl; cf
+
+let rec descends_from_native_or_skipctor cl =
+	not (is_hxgen (TClassDecl cl)) || Meta.has Meta.SkipCtor cl.cl_meta || match cl.cl_super with
+	| None -> false
+	| Some(c,_) -> descends_from_native_or_skipctor c
+
+let ensure_super_is_first com cf =
+	let rec loop e =
+		match e.eexpr with
+		| TBlock (b :: block) ->
+			loop b
+		| TBlock []
+		| TCall({ eexpr = TConst TSuper },_) -> ()
+		| _ ->
+			com.error "Types that derive from a native class must have its super() call as the first statement in the constructor" cf.cf_pos
+	in
+	match cf.cf_expr with
+	| None -> ()
+	| Some e -> Type.iter loop e
+
+let init com (empty_ctor_type : t) (empty_ctor_expr : texpr) (follow_type : t -> t) =
+	let basic = com.basic in
+	let should_change cl = not cl.cl_interface && (not cl.cl_extern || is_hxgen (TClassDecl cl)) && (match cl.cl_kind with KAbstractImpl _ -> false | _ -> true) in
+	let msize = List.length com.types in
+	let processed, empty_ctors = Hashtbl.create msize, Hashtbl.create msize in
+
+	let rec get_last_empty cl =
+		try
+			Hashtbl.find empty_ctors cl.cl_path
+		with | Not_found ->
+			match cl.cl_super with
+			| None -> raise Not_found
+			| Some (sup,_) -> get_last_empty sup
+	in
+
+	let rec change cl =
+		if not (Hashtbl.mem processed cl.cl_path) then begin
+			Hashtbl.add processed cl.cl_path true;
+
+			(* make sure we've processed the super types *)
+			Option.may (fun (super,_) -> if should_change super then change super) cl.cl_super;
+
+			(* implement static hx_ctor and reimplement constructors *)
+			(try
+				let ctor =
+					match cl.cl_constructor with
+					| Some ctor ->
+						ctor
+					| None ->
+						try
+							let sctor, sup, stl = prev_ctor cl (List.map snd cl.cl_params) in
+							(* we'll make constructors that will only call super() *)
+							let ctor = clone_ctors com sctor sup stl cl in
+							cl.cl_constructor <- Some ctor;
+							ctor
+						with Not_found -> (* create default constructor *)
+							let ctor = mk_class_field "new" (TFun ([], basic.tvoid)) false cl.cl_pos (Method MethNormal) [] in
+							ctor.cf_expr <- Some {
+								eexpr = TFunction {
+									tf_args = [];
+									tf_type = basic.tvoid;
+									tf_expr = mk (TBlock []) basic.tvoid cl.cl_pos;
+								};
+								etype = ctor.cf_type;
+								epos = ctor.cf_pos;
+							};
+							cl.cl_constructor <- Some ctor;
+							ctor
+				in
+				(* now that we made sure we have a constructor, exit if native gen *)
+				if not (is_hxgen (TClassDecl cl)) || Meta.has Meta.SkipCtor cl.cl_meta then begin
+					if descends_from_native_or_skipctor cl && is_some cl.cl_super then
+						List.iter (fun cf -> ensure_super_is_first com cf) (ctor :: ctor.cf_overloads);
+					raise Exit
+				end;
+
+				(* if cl descends from a native class, we cannot use the static constructor strategy *)
+				if descends_from_native_or_skipctor cl && is_some cl.cl_super then
+					List.iter (fun cf -> ensure_super_is_first com cf) (ctor :: ctor.cf_overloads)
+				else
+					(* now that we have a current ctor, create the static counterparts *)
+					List.iter (fun cf -> create_static_ctor com ~empty_ctor_expr:empty_ctor_expr cl cf follow_type) (ctor :: ctor.cf_overloads)
+			with Exit -> ());
+
+			(* implement empty ctor *)
+			(try
+				(* now that we made sure we have a constructor, exit if native gen *)
+				if not (is_hxgen (TClassDecl cl)) then raise Exit;
+
+				(* get first *)
+				let empty_type = TFun (["empty",false,empty_ctor_type],basic.tvoid) in
+				let super =
+					match cl.cl_super with
+					| None -> (* implement empty *)
+							[]
+					| Some (sup,_) ->
+						try
+							ignore (get_last_empty sup);
+							let esuper = mk (TConst TSuper) (TInst (cl, List.map snd cl.cl_params)) cl.cl_pos in
+							[mk (TCall (esuper, [empty_ctor_expr])) basic.tvoid cl.cl_pos]
+						with Not_found ->
+							try
+								(* super type is native: find super constructor with least arguments *)
+								let sctor, sup, stl = prev_ctor cl (List.map snd cl.cl_params) in
+								let rec loop remaining (best,n) =
+									match remaining with
+									| [] -> best
+									| cf :: r ->
+										let args,_ = get_fun cf.cf_type in
+										if (List.length args) < n then
+											loop r (cf,List.length args)
+										else
+											loop r (best,n)
+								in
+								let args,_ = get_fun sctor.cf_type in
+								let best = loop sctor.cf_overloads (sctor, List.length args) in
+								let args,_ = get_fun (apply_params sup.cl_params stl best.cf_type) in
+								let esuper = mk (TConst TSuper) (TInst (sup, stl)) cl.cl_pos in
+								[mk (TCall (esuper, List.map (fun (n,o,t) -> null t cl.cl_pos) args)) basic.tvoid cl.cl_pos]
+							with Not_found ->
+								(* extends native type, but no ctor found *)
+								[]
+				in
+				let ctor = mk_class_field "new" empty_type false cl.cl_pos (Method MethNormal) [] in
+				ctor.cf_expr <- Some {
+					eexpr = TFunction {
+						tf_type = basic.tvoid;
+						tf_args = [alloc_var "empty" empty_ctor_type, None];
+						tf_expr = mk (TBlock super) basic.tvoid cl.cl_pos
+					};
+					etype = empty_type;
+					epos = cl.cl_pos;
+				};
+				ctor.cf_meta <- [Meta.SkipCtor, [], ctor.cf_pos];
+				Hashtbl.add empty_ctors cl.cl_path ctor;
+				match cl.cl_constructor with
+				| None ->
+					cl.cl_constructor <- Some ctor
+				| Some c ->
+					c.cf_overloads <- ctor :: c.cf_overloads
+			with Exit -> ());
+		end
+	in
+
+	let module_filter md =
+		(match md with
+		| TClassDecl cl when should_change cl ->
+			change cl;
+		| _ ->
+			());
+		md
+	in
+	module_filter
+
+let init_expr_filter create_empty =
+	let rec run e =
+		match e.etype, e.eexpr with
+		| TInst (cl, params), TCall ({ eexpr = TField (_, FStatic ({cl_path = [],"Type"}, {cf_name = "createEmptyInstance"})) }, [{eexpr = TTypeExpr ((TClassDecl cl_arg) as mt_arg) }]) when cl == cl_arg && is_hxgen mt_arg ->
+			create_empty cl params e.epos
+		| _ ->
+			Type.map_expr run e
+	in
+	run
+
+let priority = 0.0
+let name = "overloading_constructor"
+
+let configure gen ~empty_ctor_type ~empty_ctor_expr =
+	gen.gtools.r_create_empty <- (fun cl params pos -> mk (TNew(cl,params,[empty_ctor_expr])) (TInst(cl,params)) pos);
+	let module_filter = init gen.gcon empty_ctor_type empty_ctor_expr (run_follow gen) in
+	gen.gmodule_filters#add name (PCustom priority) module_filter;
+	let expr_filter = init_expr_filter gen.gtools.r_create_empty in
+	gen.gexpr_filters#add name (PCustom priority) expr_filter

+ 729 - 0
src/generators/gencommon/realTypeParams.ml

@@ -0,0 +1,729 @@
+(*
+	The Haxe Compiler
+	Copyright (C) 2005-2017  Haxe Foundation
+
+	This program is free software; you can redistribute it and/or
+	modify it under the terms of the GNU General Public License
+	as published by the Free Software Foundation; either version 2
+	of the License, or (at your option) any later version.
+
+	This program is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+
+	You should have received a copy of the GNU General Public License
+	along with this program; if not, write to the Free Software
+	Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+*)
+open Option
+open Common
+open Ast
+open Type
+open Codegen
+open Gencommon
+
+(* ******************************************* *)
+(* Type Parameters *)
+(* ******************************************* *)
+(*
+	This module will handle type parameters. There are lots of changes we need to do to correctly support type parameters:
+
+	traverse will:
+		V Detect when parameterized function calls are made
+		* Detect when a parameterized class instance is being cast to another parameter
+		* Change new<> parameterized function calls
+		*
+
+	extras:
+		* On languages that support "real" type parameters, a Cast function is provided that will convert from a <Dynamic> to the requested type.
+			This cast will call createEmpty with the correct type, and then set each variable to the new form. Some types will be handled specially, namely the Native Array.
+			Other implementations may be delegated to the runtime.
+		* parameterized classes will implement a new interface (with only a Cast<> function added to it), so we can access the <Dynamic> type parameter for them. Also any reference to <Dynamic> will be replaced by a reference to this interface. (also on TTypeExpr - Std.is())
+		* Type parameter renaming to avoid name clash
+		* Detect type parameter casting and call Cast<> instead
+
+	for java:
+		* for specially assigned classes, parameters will be replaced by _d and _i versions of parameterized functions. This will only work for parameterized classes, not functions.
+
+	dependencies:
+		must run after casts are detected. This will be ensured at CastDetect module.
+*)
+(* ******************************************* *)
+(* Real Type Parameters Module *)
+(* ******************************************* *)
+(*
+	This submodule is by now specially made for the .NET platform. There might be other targets that will
+	make use of this, but it IS very specific.
+
+	On the .NET platform, generics are real specialized classes that are JIT compiled. For this reason, we cannot
+	cast from one type parameter to another. Also there is no common type for the type parameters, so for example
+	an instance of type Array<Int> will return false for instance is Array<object> .
+
+	So we need to:
+		1. create a common interface (without type parameters) (e.g. "Array") which will only contain a __Cast<> function, which will cast from one type into another
+		2. Implement the __Cast function. This part is a little hard, as we must identify all type parameter-dependent fields contained in the class and convert them.
+		In most cases the conversion will just be to call .__Cast<>() on the instances, or just a simple cast. But when the instance is a @:nativegen type, there will be no .__Cast
+		function, and we will need to deal with this case either at compile-time (added handlers - specially for NativeArray), or at runtime (adding new runtime handlers)
+		3. traverse the AST looking for casts involving type parameters, and replace them with .__Cast<>() calls. If type is @:nativegen, throw a warning. If really casting from one type parameter to another on a @:nativegen context, throw an error.
+
+
+	special literals:
+		it will use the special literal __typehandle__ that the target must implement in order to run this. This literal is a way to get the typehandle of e.g. the type parameters,
+		so we can compare them. In C# it's the equivalent of typeof(T).TypeHandle (TypeHandle compare is faster than System.Type.Equals())
+
+	dependencies:
+		(module filter) Interface creation must run AFTER enums are converted into classes, otherwise there is no way to tell parameterized enums to implement an interface
+		Must run AFTER CastDetect. This will be ensured per CastDetect
+
+*)
+let name = "real_type_params"
+let priority = max_dep -. 20.
+
+let rec has_type_params t =
+	match follow t with
+		| TInst( { cl_kind = KTypeParameter _ }, _) -> true
+		| TAbstract(_, params)
+		| TEnum(_, params)
+		| TInst(_, params) -> List.exists (fun t -> has_type_params t) params
+		| TFun(args,ret) ->
+			List.exists (fun (n,o,t) -> has_type_params t) args || has_type_params ret
+		| _ -> false
+
+let rec follow_all_md md =
+	let t = match md with
+		| TClassDecl { cl_kind = KAbstractImpl a } ->
+			TAbstract(a, List.map snd a.a_params)
+		| TClassDecl c ->
+			TInst(c, List.map snd c.cl_params)
+		| TEnumDecl e ->
+			TEnum(e, List.map snd e.e_params)
+		| TTypeDecl t ->
+			TType(t, List.map snd t.t_params)
+		| TAbstractDecl a ->
+			TAbstract(a, List.map snd a.a_params)
+	in
+	Abstract.follow_with_abstracts t
+
+let rec is_hxgeneric md =
+	match md with
+	| TClassDecl { cl_kind = KAbstractImpl a } ->
+		is_hxgeneric (TAbstractDecl a)
+	| TClassDecl(cl) ->
+		not (Meta.has Meta.NativeGeneric cl.cl_meta)
+	| TEnumDecl(e) ->
+		not (Meta.has Meta.NativeGeneric e.e_meta)
+	| TAbstractDecl(a) when Meta.has Meta.NativeGeneric a.a_meta ->
+		not (Meta.has Meta.NativeGeneric a.a_meta)
+	| md -> match follow_all_md md with
+		| TInst(cl,_) -> is_hxgeneric (TClassDecl cl)
+		| TEnum(e,_) -> is_hxgeneric (TEnumDecl e)
+		| TAbstract(a,_) -> not (Meta.has Meta.NativeGeneric a.a_meta)
+		| _ -> true
+
+let rec set_hxgeneric gen mds isfirst md =
+	let path = t_path md in
+	if List.exists (fun m -> path = t_path m) mds then begin
+		if isfirst then
+			None (* we still can't determine *)
+		else
+			Some true (* if we're in second pass and still can't determine, it's because it can be hxgeneric *)
+	end else begin
+		let has_unresolved = ref false in
+		let is_false v =
+			match v with
+				| Some false -> true
+				| None -> has_unresolved := true; false
+				| Some true -> false
+		in
+
+		let mds = md :: mds in
+		match md with
+			| TClassDecl(cl)	->
+				(* first see if any meta is present (already processed) *)
+				if Meta.has Meta.NativeGeneric cl.cl_meta then
+					Some false
+				else if Meta.has Meta.HaxeGeneric cl.cl_meta then
+					Some true
+				else if cl.cl_params = [] && is_hxgen md then
+					(cl.cl_meta <- (Meta.HaxeGeneric,[],cl.cl_pos) :: cl.cl_meta;
+					Some true)
+				else if cl.cl_params = [] then
+					(cl.cl_meta <- (Meta.NativeGeneric, [], cl.cl_pos) :: cl.cl_meta;
+					Some false)
+				else if not (is_hxgen md) then
+					(cl.cl_meta <- (Meta.NativeGeneric, [], cl.cl_pos) :: cl.cl_meta;
+					Some false)
+				else begin
+					(*
+						if it's not present, see if any superclass is nativegeneric.
+						nativegeneric is inherited, while hxgeneric can be later changed to nativegeneric
+					*)
+					(* on the first pass, our job is to find any evidence that makes it not be hxgeneric. Otherwise it will be hxgeneric *)
+					match cl.cl_super with
+						| Some (c,_) when is_false (set_hxgeneric gen mds isfirst (TClassDecl c)) ->
+							cl.cl_meta <- (Meta.NativeGeneric, [], cl.cl_pos) :: cl.cl_meta;
+							Some false
+						| _ ->
+							(* see if it's a generic class *)
+							match cl.cl_params with
+								| [] ->
+									(* if it's not, then it will follow hxgen *)
+									if is_hxgen (TClassDecl cl) then
+										cl.cl_meta <- (Meta.HaxeGeneric, [], cl.cl_pos) :: cl.cl_meta
+									else
+										cl.cl_meta <- (Meta.NativeGeneric, [], cl.cl_pos) :: cl.cl_meta;
+									Some true
+								| _ ->
+									(* if it is, loop through all fields + statics and look for non-hxgeneric
+										generic classes that have KTypeParameter as params *)
+									let rec loop cfs =
+										match cfs with
+											| [] -> false
+											| cf :: cfs ->
+												let t = follow (gen.greal_type cf.cf_type) in
+												match t with
+													| TInst( { cl_kind = KTypeParameter _ }, _ ) -> loop cfs
+													| TInst(cl,p) when has_type_params t && is_false (set_hxgeneric gen mds isfirst (TClassDecl cl)) ->
+														if not (Hashtbl.mem gen.gtparam_cast cl.cl_path) then true else loop cfs
+													| TEnum(e,p) when has_type_params t && is_false (set_hxgeneric gen mds isfirst (TEnumDecl e)) ->
+														if not (Hashtbl.mem gen.gtparam_cast e.e_path) then true else loop cfs
+													| _ -> loop cfs (* TAbstracts / Dynamics can't be generic *)
+									in
+									if loop cl.cl_ordered_fields then begin
+										cl.cl_meta <- (Meta.NativeGeneric, [], cl.cl_pos) :: cl.cl_meta;
+										Some false
+									end else if isfirst && !has_unresolved then
+										None
+									else begin
+										cl.cl_meta <- (Meta.HaxeGeneric, [], cl.cl_pos) :: cl.cl_meta;
+										Some true
+									end
+				end
+			| TEnumDecl e ->
+				if Meta.has Meta.NativeGeneric e.e_meta then
+					Some false
+				else if Meta.has Meta.HaxeGeneric e.e_meta then
+					Some true
+				else if not (is_hxgen (TEnumDecl e)) then begin
+					e.e_meta <- (Meta.NativeGeneric, [], e.e_pos) :: e.e_meta;
+					Some false
+				end else begin
+					(* if enum is not generic, then it's hxgeneric *)
+					match e.e_params with
+						| [] ->
+							e.e_meta <- (Meta.HaxeGeneric, [], e.e_pos) :: e.e_meta;
+							Some true
+						| _ ->
+							let rec loop efs =
+								match efs with
+									| [] -> false
+									| ef :: efs ->
+										let t = follow (gen.greal_type ef.ef_type) in
+										match t with
+											| TFun(args, _) ->
+												if List.exists (fun (n,o,t) ->
+													let t = follow t in
+													match t with
+														| TInst( { cl_kind = KTypeParameter _ }, _ ) ->
+															false
+														| TInst(cl,p) when has_type_params t && is_false (set_hxgeneric gen mds isfirst (TClassDecl cl)) ->
+															not (Hashtbl.mem gen.gtparam_cast cl.cl_path)
+														| TEnum(e,p) when has_type_params t && is_false (set_hxgeneric gen mds isfirst (TEnumDecl e)) ->
+															not (Hashtbl.mem gen.gtparam_cast e.e_path)
+														| _ -> false
+												) args then
+													true
+												else
+													loop efs
+											| _ -> loop efs
+							in
+							let efs = PMap.fold (fun ef acc -> ef :: acc) e.e_constrs [] in
+							if loop efs then begin
+								e.e_meta <- (Meta.NativeGeneric, [], e.e_pos) :: e.e_meta;
+								Some false
+							end else if isfirst && !has_unresolved then
+								None
+							else begin
+								e.e_meta <- (Meta.HaxeGeneric, [], e.e_pos) :: e.e_meta;
+								Some true
+							end
+				end
+			| _ -> assert false
+	end
+
+let set_hxgeneric gen md =
+	let ret = match md with
+		| TClassDecl { cl_kind = KAbstractImpl a } -> (match follow_all_md md with
+			| (TInst _ | TEnum _ as t) -> (
+				let md = match t with
+					| TInst(cl,_) -> TClassDecl cl
+					| TEnum(e,_) -> TEnumDecl e
+					| _ -> assert false
+				in
+				let ret = set_hxgeneric gen [] true md in
+				if ret = None then get (set_hxgeneric gen [] false md) else get ret)
+			| TAbstract(a,_) -> true
+			| _ -> true)
+		| _ -> match set_hxgeneric gen [] true md with
+			| None ->
+				get (set_hxgeneric gen [] false md)
+			| Some v ->
+				v
+	in
+	if not ret then begin
+		match md with
+		| TClassDecl c ->
+			let set_hxgeneric (_,param) = match follow param with
+				| TInst(c,_) ->
+					c.cl_meta <- (Meta.NativeGeneric, [], c.cl_pos) :: c.cl_meta
+				| _ -> ()
+			in
+			List.iter set_hxgeneric c.cl_params;
+			let rec handle_field cf =
+				List.iter set_hxgeneric cf.cf_params;
+				List.iter handle_field cf.cf_overloads
+			in
+			(match c.cl_kind with
+				| KAbstractImpl a ->
+					List.iter set_hxgeneric a.a_params;
+				| _ -> ());
+			List.iter handle_field c.cl_ordered_fields;
+			List.iter handle_field c.cl_ordered_statics
+		| _ -> ()
+	end;
+	ret
+
+let params_has_tparams params =
+	List.fold_left (fun acc t -> acc || has_type_params t) false params
+
+(* ******************************************* *)
+(* RealTypeParamsModf *)
+(* ******************************************* *)
+
+(*
+
+	This is the module filter of Real Type Parameters. It will traverse through all types and look for hxgeneric classes (only classes).
+	When found, a parameterless interface will be created and associated via the "ifaces" Hashtbl to the original class.
+	Also a "cast" function will be automatically generated which will handle unsafe downcasts to more specific type parameters (necessary for serialization)
+
+	dependencies:
+		Anything that may create hxgeneric classes must run before it.
+		Should run before ReflectionCFs (this dependency will be added to ReflectionCFs), so the added interfaces also get to be real IHxObject's
+
+*)
+
+module RealTypeParamsModf =
+struct
+
+	let set_only_hxgeneric gen =
+		let rec run md =
+			match md with
+				| TTypeDecl _ | TAbstractDecl _ -> md
+				| _ -> ignore (set_hxgeneric gen md); md
+		in
+		run
+
+	let name = "real_type_params_modf"
+
+	let priority = solve_deps name []
+
+	let rec get_fields gen cl params_cl params_cf acc =
+		let fields = List.fold_left (fun acc cf ->
+			match follow (gen.greal_type (gen.gfollow#run_f (cf.cf_type))) with
+				| TInst(cli, ((_ :: _) as p)) when (not (is_hxgeneric (TClassDecl cli))) && params_has_tparams p ->
+					(cf, apply_params cl.cl_params params_cl cf.cf_type, apply_params cl.cl_params params_cf cf.cf_type) :: acc
+				| TEnum(e, ((_ :: _) as p)) when not (is_hxgeneric (TEnumDecl e)) && params_has_tparams p ->
+					(cf, apply_params cl.cl_params params_cl cf.cf_type, apply_params cl.cl_params params_cf cf.cf_type) :: acc
+				| _ -> acc
+		) [] cl.cl_ordered_fields in
+		match cl.cl_super with
+			| Some(cs, tls) ->
+				get_fields gen cs (List.map (apply_params cl.cl_params params_cl) tls) (List.map (apply_params cl.cl_params params_cf) tls) (fields @ acc)
+			| None -> (fields @ acc)
+
+	let get_cast_name cl = String.concat "_" ((fst cl.cl_path) @ [snd cl.cl_path; "cast"]) (* explicitly define it *)
+
+	(* overrides all needed cast functions from super classes / interfaces to call the new cast function *)
+	let create_stub_casts gen cl cast_cfield =
+		(* go through superclasses and interfaces *)
+		let p = cl.cl_pos in
+		let this = { eexpr = TConst TThis; etype = (TInst(cl, List.map snd cl.cl_params)); epos = p } in
+
+		let rec loop curcls params level reverse_params =
+			if (level <> 0 || curcls.cl_interface) && params <> [] && is_hxgeneric (TClassDecl curcls) then begin
+				let cparams = List.map (fun (s,t) -> (s, TInst (map_param (get_cl_t t), []))) curcls.cl_params in
+				let name = get_cast_name curcls in
+				if not (PMap.mem name cl.cl_fields) then begin
+					let reverse_params = List.map (apply_params curcls.cl_params params) reverse_params in
+					let cfield = mk_class_field name (TFun([], t_dynamic)) false cl.cl_pos (Method MethNormal) cparams in
+					let field = { eexpr = TField(this, FInstance(cl,List.map snd cl.cl_params, cast_cfield)); etype = apply_params cast_cfield.cf_params reverse_params cast_cfield.cf_type; epos = p } in
+					let call =
+					{
+						eexpr = TCall(field, []);
+						etype = t_dynamic;
+						epos = p;
+					} in
+					let call = gen.gparam_func_call call field reverse_params [] in
+					let delay () =
+						cfield.cf_expr <-
+						Some {
+							eexpr = TFunction(
+							{
+								tf_args = [];
+								tf_type = t_dynamic;
+								tf_expr = mk_return call
+							});
+							etype = cfield.cf_type;
+							epos = p;
+						}
+					in
+					gen.gafter_filters_ended <- delay :: gen.gafter_filters_ended; (* do not let filters alter this expression content *)
+					cl.cl_ordered_fields <- cfield :: cl.cl_ordered_fields;
+					cl.cl_fields <- PMap.add cfield.cf_name cfield cl.cl_fields;
+					if level <> 0 then cl.cl_overrides <- cfield :: cl.cl_overrides
+				end
+			end;
+			let get_reverse super supertl =
+				List.map (apply_params super.cl_params supertl) reverse_params
+			in
+			(match curcls.cl_super with
+			| None -> ()
+			| Some(super, supertl) ->
+				let super_params = List.map (apply_params curcls.cl_params params) supertl in
+				loop super (super_params) (level + 1) (get_reverse super super_params));
+			List.iter (fun (iface, ifacetl) ->
+				let iface_params = List.map (apply_params curcls.cl_params params) ifacetl in
+				loop iface (iface_params) level (get_reverse iface iface_params);
+			) curcls.cl_implements
+		in
+		loop cl (List.map snd cl.cl_params) 0 (List.map snd cl.cl_params)
+
+	(*
+		Creates a cast classfield, with the desired name
+
+		Will also look for previous cast() definitions and override them, to reflect the current type and fields
+
+		FIXME: this function still doesn't support generics that extend generics, and are cast as one of its subclasses. This needs to be taken care, by
+		looking at previous superclasses and whenever a generic class is found, its cast argument must be overriden. the toughest part is to know how to type
+		the current type correctly.
+	*)
+	let create_cast_cfield gen cl name =
+		reset_temps();
+		let basic = gen.gcon.basic in
+		let cparams = List.map (fun (s,t) -> (s, TInst (map_param (get_cl_t t), []))) cl.cl_params in
+		let cfield = mk_class_field name (TFun([], t_dynamic)) false cl.cl_pos (Method MethNormal) cparams in
+		let params = List.map snd cparams in
+
+		let fields = get_fields gen cl (List.map snd cl.cl_params) params [] in
+
+		(* now create the contents of the function *)
+		(*
+			it will look something like:
+			if (typeof(T) == typeof(T2)) return this;
+
+			var new_me = new CurrentClass<T2>(EmptyInstnace);
+
+			for (field in Reflect.fields(this))
+			{
+				switch(field)
+				{
+					case "aNativeArray":
+						var newArray = new NativeArray(this.aNativeArray.Length);
+
+					default:
+						Reflect.setField(new_me, field, Reflect.field(this, field));
+				}
+			}
+		*)
+		let pos = cl.cl_pos in
+
+		let new_me_var = alloc_var "new_me" (TInst (cl, params)) in
+		let local_new_me = mk_local new_me_var pos in
+		let this = mk (TConst TThis) (TInst (cl, List.map snd cl.cl_params)) pos in
+		let field_var = alloc_var "field" basic.tstring in
+		let local_field = mk_local field_var pos in
+		let i_var = alloc_var "i" basic.tint in
+		let local_i = mk_local i_var pos in
+		let incr_i = mk (TUnop (Increment, Postfix, local_i)) basic.tint pos in
+		let fields_var = alloc_var "fields" (basic.tarray basic.tstring) in
+		let local_fields = mk_local fields_var pos in
+
+		let fields_to_cases fields =
+			let get_path t =
+				match follow t with
+				| TInst (cl,_) -> cl.cl_path
+				| TEnum (e,_) -> e.e_path
+				| TAbstract (a,_) -> a.a_path
+				| TMono _ | TDynamic _ -> ([], "Dynamic")
+				| _ -> assert false
+			in
+			List.map (fun (cf, t_cl, t_cf) ->
+				let this_field = mk (TField (this, FInstance (cl, List.map snd cl.cl_params, cf))) t_cl pos in
+				let expr =
+					binop
+						OpAssign
+						(mk (TField (local_new_me, FInstance(cl, List.map snd cl.cl_params, cf))) t_cf pos)
+						(try (Hashtbl.find gen.gtparam_cast (get_path t_cf)) this_field t_cf with Not_found -> (* if not found tparam cast, it shouldn't be a valid hxgeneric *) assert false)
+						t_cf
+						pos
+				in
+				[ExprBuilder.make_string gen.gcon cf.cf_name pos], expr
+			) fields
+		in
+
+		let mk_typehandle =
+			(fun cl -> mk (TCall (mk (TIdent "__typeof__") t_dynamic pos, [ExprBuilder.make_static_this cl pos])) t_dynamic pos)
+		in
+		let mk_eq cl1 cl2 =
+			binop OpEq (mk_typehandle cl1) (mk_typehandle cl2) basic.tbool pos
+		in
+		let rec mk_typehandle_cond thisparams cfparams =
+			match thisparams, cfparams with
+			| TInst (cl_this,[]) :: [], TInst (cl_cf,[]) :: [] ->
+				mk_eq cl_this cl_cf
+			| TInst (cl_this,[]) :: hd, TInst (cl_cf,[]) :: hd2 ->
+				binop OpBoolAnd (mk_eq cl_this cl_cf) (mk_typehandle_cond hd hd2) basic.tbool pos
+			| v :: hd, v2 :: hd2 ->
+				(match follow v, follow v2 with
+				| (TInst(cl1,[]) as v), (TInst(cl2,[]) as v2) ->
+					mk_typehandle_cond (v :: hd) (v2 :: hd2)
+				| _ ->
+					assert false)
+			| _ -> assert false
+		in
+		let fn = {
+			tf_args = [];
+			tf_type = t_dynamic;
+			tf_expr = mk (TBlock [
+				(* if (typeof(T) == typeof(T2)) return this *)
+				mk (TIf (mk_typehandle_cond (List.map snd cl.cl_params) params, mk_return this, None)) basic.tvoid pos;
+				(* var new_me = /*special create empty with tparams construct*/ *)
+				mk (TVar (new_me_var, Some (gen.gtools.r_create_empty cl params pos))) basic.tvoid pos;
+				(* var fields = Reflect.fields(this); *)
+				mk (TVar (fields_var, Some (gen.gtools.r_fields true this))) basic.tvoid pos;
+				(* var i = 0; *)
+				mk (TVar (i_var, Some (ExprBuilder.make_int gen.gcon 0 pos))) basic.tvoid pos;
+				(* while (i < fields.length) *)
+				mk (TWhile (
+					binop OpLt local_i (mk_field_access gen local_fields "length" pos) basic.tbool pos,
+					mk (TBlock [
+						(* var field = fields[i++]; *)
+						mk (TVar (field_var, Some (mk (TArray (local_fields, incr_i)) basic.tstring pos))) basic.tvoid pos;
+						(
+							(* default: Reflect.setField(new_me, field, Reflect.field(this, field)) *)
+							let edef = gen.gtools.r_set_field basic.tvoid local_new_me local_field (gen.gtools.r_field false basic.tvoid this local_field) in
+							if fields <> [] then
+								(* switch(field) { ... } *)
+								mk (TSwitch (local_field, fields_to_cases fields, Some edef)) basic.tvoid pos
+							else
+								edef;
+						)
+					]) basic.tvoid pos,
+					NormalWhile
+				)) basic.tvoid pos;
+				(* return new_me *)
+				mk_return local_new_me
+			]) t_dynamic pos
+		}
+		in
+		cfield.cf_expr <- Some (mk (TFunction fn) cfield.cf_type pos);
+		cfield
+
+	let create_static_cast_cf gen iface cf =
+		let p = iface.cl_pos in
+		let basic = gen.gcon.basic in
+		let cparams = List.map (fun (s,t) -> ("To_" ^ s, TInst (map_param (get_cl_t t), []))) cf.cf_params in
+		let me_type = TInst(iface,[]) in
+		let cfield = mk_class_field "__hx_cast" (TFun(["me",false,me_type], t_dynamic)) false iface.cl_pos (Method MethNormal) (cparams) in
+		let params = List.map snd cparams in
+
+		let me = alloc_var "me" me_type in
+		let field = { eexpr = TField(mk_local me p, FInstance(iface, List.map snd iface.cl_params, cf)); etype = apply_params cf.cf_params params cf.cf_type; epos = p } in
+		let call =
+		{
+			eexpr = TCall(field, []);
+			etype = t_dynamic;
+			epos = p;
+		} in
+		let call = gen.gparam_func_call call field params [] in
+
+		(* since object.someCall<ExplicitParameterDefinition>() isn't allowed on Haxe, we need to directly apply the params and delay this call *)
+		let delay () =
+			cfield.cf_expr <-
+			Some {
+				eexpr = TFunction(
+				{
+					tf_args = [me,None];
+					tf_type = t_dynamic;
+					tf_expr = mk_return {
+						eexpr = TIf(
+							{ eexpr = TBinop(Ast.OpNotEq, mk_local me p, null me.v_type p); etype = basic.tbool; epos = p },
+							call,
+							Some( null me.v_type p )
+						);
+						etype = t_dynamic;
+						epos = p;
+					}
+				});
+				etype = cfield.cf_type;
+				epos = p;
+			}
+		in
+		cfield, delay
+
+	let default_implementation gen ifaces base_generic =
+		let add_iface cl =
+			gen.gadd_to_module (TClassDecl cl) (max_dep);
+		in
+
+		let implement_stub_cast cthis iface tl =
+			let name = get_cast_name iface in
+			if not (PMap.mem name cthis.cl_fields) then begin
+				let cparams = List.map (fun (s,t) -> ("To_" ^ s, TInst(map_param (get_cl_t t), []))) iface.cl_params in
+				let field = mk_class_field name (TFun([],t_dynamic)) false iface.cl_pos (Method MethNormal) cparams in
+				let this = { eexpr = TConst TThis; etype = TInst(cthis, List.map snd cthis.cl_params); epos = cthis.cl_pos } in
+				field.cf_expr <- Some {
+					etype = TFun([],t_dynamic);
+					epos = this.epos;
+					eexpr = TFunction {
+						tf_type = t_dynamic;
+						tf_args = [];
+						tf_expr = mk_block (mk_return this)
+					}
+				};
+				cthis.cl_ordered_fields <- field :: cthis.cl_ordered_fields;
+				cthis.cl_fields <- PMap.add name field cthis.cl_fields
+			end
+		in
+
+		let rec run md =
+			match md with
+				| TClassDecl ({ cl_params = [] } as cl) ->
+					(* see if we're implementing any generic interface *)
+					let rec check (iface,tl) =
+						if tl <> [] && set_hxgeneric gen (TClassDecl iface) then
+							(* implement cast stub *)
+							implement_stub_cast cl iface tl;
+						List.iter (fun (s,stl) -> check (s, List.map (apply_params iface.cl_params tl) stl)) iface.cl_implements;
+					in
+					List.iter (check) cl.cl_implements;
+					md
+				| TClassDecl ({ cl_params = hd :: tl } as cl) when set_hxgeneric gen md ->
+					let iface = mk_class cl.cl_module cl.cl_path cl.cl_pos in
+					iface.cl_array_access <- Option.map (apply_params (cl.cl_params) (List.map (fun _ -> t_dynamic) cl.cl_params)) cl.cl_array_access;
+					iface.cl_extern <- cl.cl_extern;
+					iface.cl_module <- cl.cl_module;
+					iface.cl_private <- cl.cl_private;
+					iface.cl_meta <-
+						(Meta.HxGen, [], cl.cl_pos)
+						::
+						(Meta.Custom "generic_iface", [(EConst(Int(string_of_int(List.length cl.cl_params))), cl.cl_pos)], cl.cl_pos)
+						::
+						iface.cl_meta;
+					Hashtbl.add ifaces cl.cl_path iface;
+
+					iface.cl_implements <- (base_generic, []) :: iface.cl_implements;
+					iface.cl_interface <- true;
+					cl.cl_implements <- (iface, []) :: cl.cl_implements;
+
+					let name = get_cast_name cl in
+					let cast_cf = create_cast_cfield gen cl name in
+					if not cl.cl_interface then create_stub_casts gen cl cast_cf;
+
+					let rec loop c = match c.cl_super with
+						| None -> ()
+						| Some(sup,_) -> try
+							let siface = Hashtbl.find ifaces sup.cl_path in
+							iface.cl_implements <- (siface,[]) :: iface.cl_implements;
+							()
+						with | Not_found -> loop sup
+					in
+					loop cl;
+
+					(if not cl.cl_interface then cl.cl_ordered_fields <- cast_cf :: cl.cl_ordered_fields);
+					let iface_cf = mk_class_field name cast_cf.cf_type false cast_cf.cf_pos (Method MethNormal) cast_cf.cf_params in
+					let cast_static_cf, delay = create_static_cast_cf gen iface iface_cf in
+
+					cl.cl_ordered_statics <- cast_static_cf :: cl.cl_ordered_statics;
+					cl.cl_statics <- PMap.add cast_static_cf.cf_name cast_static_cf cl.cl_statics;
+					gen.gafter_filters_ended <- delay :: gen.gafter_filters_ended; (* do not let filters alter this expression content *)
+
+					iface_cf.cf_type <- cast_cf.cf_type;
+					iface.cl_fields <- PMap.add name iface_cf iface.cl_fields;
+					let fields = List.filter (fun cf -> match cf.cf_kind with
+						| Var _ | Method MethDynamic -> false
+						| _ ->
+							let is_override = List.memq cf cl.cl_overrides in
+							let cf_type = if is_override && not (Meta.has Meta.Overload cf.cf_meta) then
+								match find_first_declared_field gen cl cf.cf_name with
+									| Some(_,_,declared_t,_,_,_,_) -> declared_t
+									| _ -> assert false
+							else
+								cf.cf_type
+							in
+
+							not (has_type_params cf_type)
+						) cl.cl_ordered_fields
+					in
+					let fields = List.map (fun f -> mk_class_field f.cf_name f.cf_type f.cf_public f.cf_pos f.cf_kind f.cf_params) fields in
+					let fields = iface_cf :: fields in
+					iface.cl_ordered_fields <- fields;
+					List.iter (fun f -> iface.cl_fields <- PMap.add f.cf_name f iface.cl_fields) fields;
+
+					add_iface iface;
+					md
+				| TTypeDecl _ | TAbstractDecl _ -> md
+				| TEnumDecl _ ->
+					ignore (set_hxgeneric gen md);
+					md
+				| _ -> ignore (set_hxgeneric gen md); md
+		in
+		run
+
+	let configure gen mapping_func =
+		gen.gmodule_filters#add name (PCustom priority) mapping_func
+
+end;;
+
+(* create a common interface without type parameters and only a __Cast<> function *)
+let default_implementation gen (dyn_tparam_cast:texpr->t->texpr) ifaces =
+	let change_expr e cl iface params =
+		let field = mk_static_field_access_infer cl "__hx_cast" e.epos params in
+		let elist = [mk_cast (TInst(iface,[])) e] in
+		let call = { eexpr = TCall(field, elist); etype = t_dynamic; epos = e.epos } in
+
+		gen.gparam_func_call call field params elist
+	in
+
+	let rec run e =
+		match e.eexpr with
+				| TCast(cast_expr, _) ->
+					(* see if casting to a native generic class *)
+					let t = gen.greal_type e.etype in
+					let unifies =
+						let ctype = gen.greal_type cast_expr.etype in
+						match follow ctype with
+						| TInst(cl,_) -> (try
+							unify ctype t;
+							true
+						with | Unify_error el ->
+							false)
+						| _ -> false
+					in
+					let unifies = unifies && not (PMap.mem "cs_safe_casts" gen.gcon.defines) in
+					(match follow t with
+						| TInst(cl, p1 :: pl) when is_hxgeneric (TClassDecl cl) && not unifies && not (Meta.has Meta.Enum cl.cl_meta) ->
+							let iface = Hashtbl.find ifaces cl.cl_path in
+							mk_cast e.etype (change_expr (Type.map_expr run cast_expr) cl iface (p1 :: pl))
+						| _ -> Type.map_expr run e
+					)
+				| _ -> Type.map_expr run e
+	in
+	run
+
+let configure gen (dyn_tparam_cast:texpr->t->texpr) ifaces base_generic =
+	gen.ghas_tparam_cast_handler <- true;
+	let traverse = default_implementation gen dyn_tparam_cast ifaces in
+	gen.gsyntax_filters#add name (PCustom priority) traverse;
+	RealTypeParamsModf.configure gen (RealTypeParamsModf.default_implementation gen ifaces base_generic)

+ 1673 - 0
src/generators/gencommon/reflectionCFs.ml

@@ -0,0 +1,1673 @@
+(*
+	The Haxe Compiler
+	Copyright (C) 2005-2017  Haxe Foundation
+
+	This program is free software; you can redistribute it and/or
+	modify it under the terms of the GNU General Public License
+	as published by the Free Software Foundation; either version 2
+	of the License, or (at your option) any later version.
+
+	This program is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+
+	You should have received a copy of the GNU General Public License
+	along with this program; if not, write to the Free Software
+	Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+*)
+open Option
+open Common
+open Ast
+open Type
+open Codegen
+open Gencommon
+open ClosuresToClass
+
+(* ******************************************* *)
+(* Reflection-enabling Class fields *)
+(* ******************************************* *)
+(*
+	This is the most hardcore codegen part of the code. There's much to improve so this code can be more readable, but at least it's running correctly right now! This will be improved. (TODO)
+
+	This module will create class fields that enable reflection for targets that have a slow or inexistent reflection abilities. Because of the similarity
+	of strategies between what should have been different modules, they are all unified in this reflection-enabling class fields.
+
+	They include:
+		* Get(throwErrors, isCheck) / Set fields . Remember to allow implements Dynamic also.
+		* Invoke fields() -> You need to configure how many invoke_field fields there will be. + invokeDynamic
+		* Has field -> parameter in get field that returns __undefined__ if it doesn't exist.
+
+		* GetType -> return the current Class<> / Enum<>
+		* Fields() -> returns all the fields / static fields. Remember to allow implements Dynamic also
+
+		* Create(arguments array), CreateEmpty - calls new() or create empty
+		* getInstanceFields / getClassFields -> show even function fields, everything!
+
+		* deleteField -> only for implements Dynamic
+
+		for enums:
+		* createEnum -> invokeField for classes
+		* createEnumIndex -> use invokeField as well, and use numbers e.g. "0", "1", "2" .... For this, use "@:alias" metadata
+		* getEnumConstructs -> fields()
+
+		need to be solved outside:
+		* getEnumName
+		* enumIndex
+		*
+
+		need to be solved by haxe code:
+		* enumParameters -> for (field in Reflect.fields(enum)) arr.push(Reflect.field(enum, field))
+
+	Standard:
+		if a class contains a @:$enum metadata, it's treated as a converted enum to class
+
+
+	Optimizations:
+		* if optimize is true, all fields will be hashed by the same hashing function as neko (31 bits int : always positive). Every function that expects a string for the field will expect also an int, for the hash
+			a string (which is nullable for compile-time hashes) + an int.
+			At compile-time, a collision will throw an error (like neko).
+			At runtime, a collision will make a negative int. Negative ints will always resolve to a special Hash<> field which takes a string.
+		* if optimize is true, Reflect.field/setField will be replaced by either the runtime version (with already hashed string), either by the own .Field()/.SetField() HxObject's version,
+			if the type is detected to already be hxgen
+		* TODO: if for() optimization for arrays is disabled, we can replace for(field in Reflect.fields(obj)) to:
+			for (field in ( (Std.is(obj, HxObject) ? ((HxObject)obj).Fields() : Reflect.fields(obj)) )) // no array copying . for further optimization this could be guaranteed to return
+			the already hashed fields.
+
+	Mappings:
+		* if create Dynamic class is true, TObjectDecl will be mapped to new DynamicClass(fields, [hashedFields], values)
+		*
+
+	dependencies:
+		There is no big dependency from this target. Though it should be a syntax filter, mainly one of the first so most expression generation has already been done,
+		while the AST has its meaning close to haxe's.
+		Should run before InitFunction so it detects variables containing expressions as "always-execute" expressions, even when using CreateEmpty
+
+		* Must run before switch() syntax changes
+
+*)
+let name = "reflection_cfs"
+
+type rcf_hash_conflict_ctx = {
+	t : t;
+	add_names : texpr->texpr->texpr;
+	get_conflict : texpr->texpr->texpr->texpr;
+	set : texpr->texpr->texpr->texpr->texpr;
+	delete : texpr->texpr->texpr->texpr;
+}
+
+type rcf_ctx =
+{
+	rcf_gen : generator_ctx;
+	rcf_ft : ClosuresToClass.closures_ctx;
+	rcf_optimize : bool;
+
+	rcf_object_iface : tclass;
+
+	rcf_max_func_arity : int;
+
+	(*
+		the hash lookup function. can be an inlined expr or simply a function call.
+		its only needed features is that it should return the index of the key if found, and the
+		complement of the index of where it should be inserted if not found (Ints).
+
+		hash->hash_array->length->returning expression
+	*)
+	rcf_hash_function : texpr->texpr->texpr->texpr;
+
+	rcf_lookup_function : texpr->texpr;
+
+	(* hash_array->length->pos->value *)
+	rcf_insert_function : texpr->texpr->texpr->texpr->texpr;
+
+	(* hash_array->length->pos->value *)
+	rcf_remove_function : texpr->texpr->texpr->texpr;
+
+	rcf_hash_fields : (int, string) Hashtbl.t;
+
+	rcf_hash_paths : (path * int, string) Hashtbl.t;
+
+	rcf_hash_conflict_ctx : rcf_hash_conflict_ctx option;
+
+	rcf_mk_exception : string -> pos -> texpr;
+
+	(*
+		main expr -> field expr -> field string -> possible hash int (if optimize) -> possible set expr -> should_throw_exceptions -> changed expression
+
+		Changes a get / set field to the runtime resolution function
+	*)
+	rcf_on_getset_field : texpr->texpr->string->int32 option->texpr option->bool->texpr;
+
+	rcf_on_call_field : texpr->texpr->string->int32 option->texpr list->texpr;
+}
+
+let new_ctx gen ft object_iface optimize dynamic_getset_field dynamic_call_field hash_function lookup_function insert_function remove_function hash_conflict_ctx rcf_mk_exception =
+	{
+		rcf_gen = gen;
+		rcf_ft = ft;
+
+		rcf_optimize = optimize;
+
+		rcf_object_iface = object_iface;
+
+		rcf_max_func_arity = 10;
+
+		rcf_hash_function = hash_function;
+		rcf_lookup_function = lookup_function;
+
+		rcf_insert_function = insert_function;
+		rcf_remove_function = remove_function;
+
+		rcf_hash_fields = Hashtbl.create 100;
+		rcf_hash_paths = Hashtbl.create 100;
+
+		rcf_on_getset_field = dynamic_getset_field;
+		rcf_on_call_field = dynamic_call_field;
+		rcf_hash_conflict_ctx = hash_conflict_ctx;
+		rcf_mk_exception = rcf_mk_exception;
+	}
+
+(*
+	methods as a bool option is a little laziness of my part.
+		None means that methods are included with normal fields;
+		Some(true) means collect only methods
+		Some(false) means collect only fields (and MethDynamic fields)
+*)
+let collect_fields cl (methods : bool option) =
+	let collected = Hashtbl.create 0 in
+	let collect cf acc =
+		if Meta.has Meta.CompilerGenerated cf.cf_meta || Meta.has Meta.SkipReflection cf.cf_meta then
+			acc
+		else match methods, cf.cf_kind with
+			| None, _ when not (Hashtbl.mem collected cf.cf_name) -> Hashtbl.add collected cf.cf_name true; ([cf.cf_name], cf) :: acc
+			| Some true, Method MethDynamic -> acc
+			| Some true, Method _ when not (Hashtbl.mem collected cf.cf_name) -> Hashtbl.add collected cf.cf_name true; ([cf.cf_name], cf) :: acc
+			| Some false, Method MethDynamic
+			| Some false, Var _ when not (Hashtbl.mem collected cf.cf_name) -> Hashtbl.add collected cf.cf_name true; ([cf.cf_name], cf) :: acc
+			| _ -> acc
+	in
+	let collect_cfs cfs acc =
+		let rec loop cfs acc =
+			match cfs with
+				| [] -> acc
+				| hd :: tl -> loop tl (collect hd acc)
+		in
+		loop cfs acc
+	in
+	let rec loop cl acc =
+		let acc = collect_cfs cl.cl_ordered_fields acc in
+		match cl.cl_super with
+			| None -> acc
+			| Some(cl,_) ->
+				if not (is_hxgen (TClassDecl cl)) then loop cl acc else acc
+	in
+
+	loop cl []
+
+let hash_field ctx f pos =
+	let h = hash f in
+	(try
+		let f2 = Hashtbl.find ctx.rcf_hash_paths (ctx.rcf_gen.gcurrent_path, h) in
+		if f <> f2 then ctx.rcf_gen.gcon.error ("Field conflict between " ^ f ^ " and " ^ f2) pos
+	with Not_found ->
+		Hashtbl.add ctx.rcf_hash_paths (ctx.rcf_gen.gcurrent_path, h) f;
+		Hashtbl.replace ctx.rcf_hash_fields h f);
+	h
+
+(* ( tf_args, switch_var ) *)
+let field_type_args ctx pos =
+	match ctx.rcf_optimize with
+		| true ->
+			let field_name, field_hash = alloc_var "field" ctx.rcf_gen.gcon.basic.tstring, alloc_var "hash" ctx.rcf_gen.gcon.basic.tint in
+
+			[field_name, None; field_hash, None], field_hash
+		| false ->
+			let field_name = alloc_var "field" ctx.rcf_gen.gcon.basic.tstring in
+			[field_name, None], field_name
+
+let hash_field_i32 ctx pos field_name =
+	let i = hash_field ctx field_name pos in
+	let i = Int32.of_int (i) in
+	if i < Int32.zero then
+		Int32.logor (Int32.logand i (Int32.of_int 0x3FFFFFFF)) (Int32.shift_left Int32.one 30)
+	else i
+
+let switch_case ctx pos field_name =
+	match ctx.rcf_optimize with
+		| true ->
+			let i = hash_field_i32 ctx pos field_name in
+			mk (TConst (TInt i)) ctx.rcf_gen.gcon.basic.tint pos
+		| false ->
+			ExprBuilder.make_string ctx.rcf_gen.gcon field_name pos
+
+let call_super ctx fn_args ret_t cf cl this_t pos =
+	{
+		eexpr = TCall({
+			eexpr = TField({ eexpr = TConst(TSuper); etype = this_t; epos = pos }, FInstance(cl,List.map snd cl.cl_params,cf));
+			etype = TFun(fun_args fn_args, ret_t);
+			epos = pos;
+		}, List.map (fun (v,_) -> mk_local v pos) fn_args);
+		etype = ret_t;
+		epos = pos;
+	}
+
+
+let enumerate_dynamic_fields ctx cl when_found base_arr =
+	let gen = ctx.rcf_gen in
+	let basic = gen.gcon.basic in
+	let pos = cl.cl_pos in
+
+	let vtmp = alloc_var "i" basic.tint in
+
+	let mk_for arr len =
+		let t = if ctx.rcf_optimize then basic.tint else basic.tstring in
+		let convert_str e = if ctx.rcf_optimize then ctx.rcf_lookup_function e else e in
+		let tmpinc = { eexpr = TUnop(Ast.Increment, Ast.Postfix, mk_local vtmp pos); etype = basic.tint; epos = pos } in
+		[
+			{ eexpr = TBinop(OpAssign, mk_local vtmp pos, ExprBuilder.make_int ctx.rcf_gen.gcon 0 pos); etype = basic.tint; epos = pos };
+			{
+				eexpr = TWhile (
+					{ eexpr = TBinop(Ast.OpLt, mk_local vtmp pos, len); etype = basic.tbool; epos = pos },
+					mk_block (when_found (convert_str { eexpr = TArray (arr, tmpinc); etype = t; epos = pos })),
+					Ast.NormalWhile
+				);
+				etype = basic.tvoid;
+				epos = pos
+			}
+		]
+	in
+
+	let this_t = TInst(cl, List.map snd cl.cl_params) in
+	let this = { eexpr = TConst(TThis); etype = this_t; epos = pos } in
+	let mk_this field t = { (mk_field_access gen this field pos) with etype = t } in
+
+	{ eexpr = TVar (vtmp,None); etype = basic.tvoid; epos = pos }
+	::
+	if ctx.rcf_optimize then
+		mk_for (mk_this (mk_internal_name "hx" "hashes") (gen.gclasses.nativearray basic.tint)) (mk_this (mk_internal_name "hx" "length") basic.tint)
+		@
+		mk_for (mk_this (mk_internal_name "hx" "hashes_f") (gen.gclasses.nativearray basic.tint)) (mk_this (mk_internal_name "hx" "length_f") basic.tint)
+		@
+		(
+			let conflict_ctx = Option.get ctx.rcf_hash_conflict_ctx in
+			let ehead = mk_this (mk_internal_name "hx" "conflicts") conflict_ctx.t in
+			[conflict_ctx.add_names ehead base_arr]
+		)
+	else
+		mk_for (mk_this (mk_internal_name "hx" "hashes") (gen.gclasses.nativearray basic.tstring)) (mk_this (mk_internal_name "hx" "length") basic.tint)
+		@
+		mk_for (mk_this (mk_internal_name "hx" "hashes_f") (gen.gclasses.nativearray basic.tstring)) (mk_this (mk_internal_name "hx" "length_f") basic.tint)
+
+(* *********************
+		Dynamic lookup
+		*********************
+
+		This is the behavior of standard <implements Dynamic> classes. It will replace the error throwing
+		if a field doesn't exists when looking it up.
+
+		In order for it to work, an implementation for hash_function must be created.
+		hash_function is the function to be called/inlined that will allow us to lookup the hash into a sorted array of hashes.
+		A binary search or linear search algorithm may be implemented. The only need is that if not found, the NegBits of
+		the place where it should be inserted must be returned.
+*)
+let abstract_dyn_lookup_implementation ctx this field_local hash_local may_value is_float pos =
+	let gen = ctx.rcf_gen in
+	let basic = gen.gcon.basic in
+	let mk_this field t = { (mk_field_access gen this field pos) with etype = t } in
+	let a_t = if ctx.rcf_optimize then basic.tint else basic.tstring in
+	let hx_hashes = mk_this (mk_internal_name "hx" "hashes") (gen.gclasses.nativearray a_t) in
+	let hx_hashes_f = mk_this (mk_internal_name "hx" "hashes_f") (gen.gclasses.nativearray a_t) in
+	let hx_dynamics = mk_this (mk_internal_name "hx" "dynamics") (gen.gclasses.nativearray t_empty) in
+	let hx_dynamics_f = mk_this (mk_internal_name "hx" "dynamics_f") (gen.gclasses.nativearray basic.tfloat) in
+	let hx_length = mk_this (mk_internal_name "hx" "length") (basic.tint) in
+	let hx_length_f = mk_this (mk_internal_name "hx" "length_f") (basic.tint) in
+	let res = alloc_var "res" basic.tint in
+	let fst_hash, snd_hash, fst_dynamics, snd_dynamics, fst_length, snd_length =
+		if is_float then
+			hx_hashes_f, hx_hashes, hx_dynamics_f, hx_dynamics, hx_length_f, hx_length
+		else
+			hx_hashes, hx_hashes_f, hx_dynamics, hx_dynamics_f, hx_length, hx_length_f
+	in
+	let res_local = mk_local res pos in
+	let gte = {
+		eexpr = TBinop(Ast.OpGte, res_local, { eexpr = TConst(TInt(Int32.zero)); etype = basic.tint; epos = pos });
+		etype = basic.tbool;
+		epos = pos;
+	} in
+	let mk_tarray arr idx =
+		{
+			eexpr = TArray(arr, idx);
+			etype = gen.gclasses.nativearray_type arr.etype;
+			epos = pos;
+		}
+	in
+	let ret_t = if is_float then basic.tfloat else t_dynamic in
+
+	match may_value with
+		| None ->
+			(*
+				var res = lookup(this.__hx_hashes/f, hash);
+				if (res < 0)
+				{
+					res = lookup(this.__hx_hashes_f/_, hash);
+					if(res < 0)
+						return null;
+					else
+						return __hx_dynamics_f[res];
+				} else {
+					return __hx_dynamics[res];
+				}
+			*)
+			let block =
+			[
+				{ eexpr = TVar(res, Some(ctx.rcf_hash_function hash_local fst_hash fst_length)); etype = basic.tvoid; epos = pos };
+				{ eexpr = TIf(gte, mk_return (mk_tarray fst_dynamics res_local), Some({
+					eexpr = TBlock(
+					[
+						{ eexpr = TBinop(Ast.OpAssign, res_local, ctx.rcf_hash_function hash_local snd_hash snd_length); etype = basic.tint; epos = pos };
+						{ eexpr = TIf(gte, mk_return (mk_tarray snd_dynamics res_local), None); etype = ret_t; epos = pos }
+					]);
+					etype = ret_t;
+					epos = pos;
+				})); etype = ret_t; epos = pos }
+			] in
+
+			if ctx.rcf_optimize then
+				let conflict_ctx = Option.get ctx.rcf_hash_conflict_ctx in
+				let ehead = mk_this (mk_internal_name "hx" "conflicts") conflict_ctx.t in
+				let vconflict = alloc_var "conflict" conflict_ctx.t in
+				let local_conflict = mk_local vconflict pos in
+				[mk (TIf (
+					mk (TBinop (OpLt, hash_local, ExprBuilder.make_int gen.gcon 0 pos)) basic.tbool pos,
+					mk (TBlock [
+						mk (TVar (vconflict, Some (conflict_ctx.get_conflict ehead hash_local field_local))) basic.tvoid pos;
+						mk (TIf (
+							mk (TBinop (OpNotEq, local_conflict, mk (TConst TNull) local_conflict.etype pos)) basic.tbool pos,
+							mk_return (Codegen.field local_conflict "value" t_dynamic pos),
+							None
+						)) basic.tvoid pos;
+					]) basic.tvoid pos,
+					Some (mk (TBlock block) basic.tvoid pos)
+				)) basic.tvoid pos]
+			else
+				block
+		| Some value_local ->
+			(*
+				//if is not float:
+				//if (isNumber(value_local)) return this.__hx_setField_f(field, getNumber(value_local), false(not static));
+				var res = lookup(this.__hx_hashes/f, hash);
+				if (res >= 0)
+				{
+					return __hx_dynamics/f[res] = value_local;
+				} else {
+					res = lookup(this.__hx_hashes_f/_, hash);
+					if (res >= 0)
+					{
+						__hx_dynamics_f/_.splice(res,1);
+						__hx_hashes_f/_.splice(res,1);
+					}
+				}
+
+				__hx_hashses/_f.insert(~res, hash);
+				__hx_dynamics/_f.insert(~res, value_local);
+				return value_local;
+			*)
+			let neg_res = { eexpr = TUnop(Ast.NegBits, Ast.Prefix, res_local); etype = basic.tint; epos = pos } in
+
+			let res2 = alloc_var "res2" basic.tint in
+			let res2_local = mk_local res2 pos in
+			let gte2 = {
+				eexpr = TBinop(Ast.OpGte, res2_local, { eexpr = TConst(TInt(Int32.zero)); etype = basic.tint; epos = pos });
+				etype = basic.tbool;
+				epos = pos;
+			} in
+
+			let block =
+			[
+				{ eexpr = TVar(res, Some(ctx.rcf_hash_function hash_local fst_hash fst_length)); etype = basic.tvoid; epos = pos };
+				{
+					eexpr = TIf(gte,
+						mk_return { eexpr = TBinop(Ast.OpAssign, mk_tarray fst_dynamics res_local, value_local); etype = value_local.etype; epos = pos },
+						Some({ eexpr = TBlock([
+							{ eexpr = TVar( res2, Some(ctx.rcf_hash_function hash_local snd_hash snd_length)); etype = basic.tvoid; epos = pos };
+							{
+								eexpr = TIf(gte2, { eexpr = TBlock([
+									ctx.rcf_remove_function snd_hash snd_length res2_local;
+									ctx.rcf_remove_function snd_dynamics snd_length res2_local;
+									mk (TUnop(Decrement,Postfix,snd_length)) basic.tint pos
+								]); etype = t_dynamic; epos = pos }, None);
+								etype = t_dynamic;
+								epos = pos;
+							}
+						]); etype = t_dynamic; epos = pos }));
+					etype = t_dynamic;
+					epos = pos;
+				};
+				ctx.rcf_insert_function fst_hash fst_length neg_res hash_local;
+				ctx.rcf_insert_function fst_dynamics fst_length neg_res value_local;
+				mk (TUnop(Increment,Postfix,fst_length)) basic.tint pos;
+			] in
+
+			let block =
+				if ctx.rcf_optimize then
+					let conflict_ctx = Option.get ctx.rcf_hash_conflict_ctx in
+					let ehead = mk_this (mk_internal_name "hx" "conflicts") conflict_ctx.t in
+					[mk (TIf (
+						mk (TBinop (OpLt, hash_local, ExprBuilder.make_int gen.gcon 0 pos)) basic.tbool pos,
+						conflict_ctx.set ehead hash_local field_local value_local,
+						Some (mk (TBlock block) basic.tvoid pos)
+					)) basic.tvoid pos]
+				else
+					block
+			in
+			block @ [mk_return value_local]
+
+let get_delete_field ctx cl is_dynamic =
+	let pos = cl.cl_pos in
+	let this_t = TInst(cl, List.map snd cl.cl_params) in
+	let this = { eexpr = TConst(TThis); etype = this_t; epos = pos } in
+	let gen = ctx.rcf_gen in
+	let basic = gen.gcon.basic in
+	let tf_args, switch_var = field_type_args ctx pos in
+	let local_switch_var = mk_local switch_var pos in
+	let fun_type = TFun(fun_args tf_args,basic.tbool) in
+	let cf = mk_class_field (mk_internal_name "hx" "deleteField") fun_type false pos (Method MethNormal) [] in
+	let body = if is_dynamic then begin
+		let mk_this field t = { (mk_field_access gen this field pos) with etype = t } in
+		let a_t = if ctx.rcf_optimize then basic.tint else basic.tstring in
+		let hx_hashes = mk_this (mk_internal_name "hx" "hashes") (gen.gclasses.nativearray a_t) in
+		let hx_hashes_f = mk_this (mk_internal_name "hx" "hashes_f") (gen.gclasses.nativearray a_t) in
+		let hx_dynamics = mk_this (mk_internal_name "hx" "dynamics") (gen.gclasses.nativearray t_empty) in
+		let hx_dynamics_f = mk_this (mk_internal_name "hx" "dynamics_f") (gen.gclasses.nativearray basic.tfloat) in
+		let hx_length = mk_this (mk_internal_name "hx" "length") (basic.tint) in
+		let hx_length_f = mk_this (mk_internal_name "hx" "length_f") (basic.tint) in
+		let res = alloc_var "res" basic.tint in
+		let res_local = mk_local res pos in
+		let gte = {
+			eexpr = TBinop(Ast.OpGte, res_local, { eexpr = TConst(TInt(Int32.zero)); etype = basic.tint; epos = pos });
+			etype = basic.tbool;
+			epos = pos;
+		} in
+		(*
+			var res = lookup(this.__hx_hashes, hash);
+			if (res >= 0)
+			{
+				__hx_dynamics.splice(res,1);
+				__hx_hashes.splice(res,1);
+
+				return true;
+			} else {
+				res = lookup(this.__hx_hashes_f, hash);
+				if (res >= 0)
+				{
+					__hx_dynamics_f.splice(res,1);
+					__hx_hashes_f.splice(res,1);
+
+					return true;
+				}
+			}
+
+			return false;
+		*)
+		let common = [
+			{ eexpr = TVar(res,Some(ctx.rcf_hash_function local_switch_var hx_hashes hx_length)); etype = basic.tvoid; epos = pos };
+			{
+				eexpr = TIf(gte, { eexpr = TBlock([
+					ctx.rcf_remove_function hx_hashes hx_length res_local;
+					ctx.rcf_remove_function hx_dynamics hx_length res_local;
+					mk (TUnop(Decrement,Postfix,hx_length)) basic.tint pos;
+					mk_return { eexpr = TConst(TBool true); etype = basic.tbool; epos = pos }
+				]); etype = t_dynamic; epos = pos }, Some({ eexpr = TBlock([
+					{ eexpr = TBinop(Ast.OpAssign, res_local, ctx.rcf_hash_function local_switch_var hx_hashes_f hx_length_f); etype = basic.tint; epos = pos };
+					{ eexpr = TIf(gte, { eexpr = TBlock([
+						ctx.rcf_remove_function hx_hashes_f hx_length_f res_local;
+						ctx.rcf_remove_function hx_dynamics_f hx_length_f res_local;
+						mk (TUnop(Decrement,Postfix,hx_length_f)) basic.tint pos;
+						mk_return { eexpr = TConst(TBool true); etype = basic.tbool; epos = pos }
+					]); etype = t_dynamic; epos = pos }, None); etype = t_dynamic; epos = pos }
+				]); etype = t_dynamic; epos = pos }));
+				etype = t_dynamic;
+				epos = pos;
+			};
+			mk_return { eexpr = TConst(TBool false); etype = basic.tbool; epos = pos }
+		] in
+
+		if ctx.rcf_optimize then
+			let v_name = match tf_args with (v,_) :: _ -> v | _ -> assert false in
+			let local_name = mk_local v_name pos in
+			let conflict_ctx = Option.get ctx.rcf_hash_conflict_ctx in
+			let ehead = mk_this (mk_internal_name "hx" "conflicts") conflict_ctx.t in
+			(mk (TIf (
+				binop OpLt local_switch_var (ExprBuilder.make_int gen.gcon 0 pos) basic.tbool pos,
+				mk_return (conflict_ctx.delete ehead local_switch_var local_name),
+				None
+			)) basic.tvoid pos) :: common
+		else
+			common
+	end else
+	[
+		mk_return { eexpr = TConst(TBool false); etype = basic.tbool; epos = pos }
+	] in
+
+	(* create function *)
+	let fn =
+	{
+		tf_args = tf_args;
+		tf_type = basic.tbool;
+		tf_expr = { eexpr = TBlock(body); etype = t_dynamic; epos = pos }
+	} in
+	cf.cf_expr <- Some({ eexpr = TFunction(fn); etype = fun_type; epos = pos });
+	cf
+
+let rec is_first_dynamic cl =
+	match cl.cl_super with
+		| Some(cl,_) ->
+			if is_some cl.cl_dynamic then false else is_first_dynamic cl
+		| None -> true
+
+let is_override cl = match cl.cl_super with
+	| Some (cl, _) when is_hxgen (TClassDecl cl) -> true
+	| _ -> false
+
+(* WARNING: this will only work if overloading contructors is possible on target language *)
+let implement_dynamic_object_ctor ctx cl =
+	let rec is_side_effects_free e =
+		match e.eexpr with
+			| TConst _
+			| TLocal _
+			| TFunction _
+			| TTypeExpr _ ->
+				true
+			| TNew(clnew,[],params) when clnew == cl ->
+				List.for_all is_side_effects_free params
+			| TUnop(Increment,_,_)
+			| TUnop(Decrement,_,_)
+			| TBinop(OpAssign,_,_)
+			| TBinop(OpAssignOp _,_,_) ->
+				false
+			| TUnop(_,_,e) ->
+				is_side_effects_free e
+			| TArray(e1,e2)
+			| TBinop(_,e1,e2) ->
+				is_side_effects_free e1 && is_side_effects_free e2
+			| TIf(cond,e1,Some e2) ->
+				is_side_effects_free cond && is_side_effects_free e1 && is_side_effects_free e2
+			| TField(e,_)
+			| TParenthesis e | TMeta(_,e) -> is_side_effects_free e
+			| TArrayDecl el -> List.for_all is_side_effects_free el
+			| TCast(e,_) -> is_side_effects_free e
+			| _ -> false
+	in
+
+	let pos = cl.cl_pos in
+	let gen = ctx.rcf_gen in
+	let basic = gen.gcon.basic in
+	let hasht = if ctx.rcf_optimize then basic.tint else basic.tstring in
+
+	let hashes_field = mk_internal_name "hx" "hashes", gen.gclasses.nativearray hasht in
+	let hashes_f_field = mk_internal_name "hx" "hashes_f", gen.gclasses.nativearray hasht in
+	let dynamics_field = mk_internal_name "hx" "dynamics", gen.gclasses.nativearray t_empty in
+	let dynamics_f_field = mk_internal_name "hx" "dynamics_f", gen.gclasses.nativearray basic.tfloat in
+	let fields =
+	[
+		hashes_field;
+		dynamics_field;
+		hashes_f_field;
+		dynamics_f_field;
+	] in
+
+	let hashes_var = alloc_var (fst hashes_field) (snd hashes_field) in
+	let hashes_f_var = alloc_var (fst hashes_f_field) (snd hashes_f_field) in
+	let tf_args = [
+		hashes_var, None;
+		alloc_var (fst dynamics_field) (snd dynamics_field), None;
+		hashes_f_var, None;
+		alloc_var (fst dynamics_f_field) (snd dynamics_f_field), None;
+	] in
+
+	let this = { eexpr = TConst TThis; etype = TInst(cl, List.map snd cl.cl_params); epos = pos } in
+	let mk_this field t = { (mk_field_access gen this field pos) with etype = t } in
+	let fun_t = TFun(fun_args tf_args,basic.tvoid) in
+	let ctor = mk_class_field "new" fun_t true pos (Method MethNormal) [] in
+	ctor.cf_expr <- Some(
+	{
+		eexpr = TFunction({
+			tf_args = tf_args;
+			tf_type = basic.tvoid;
+			tf_expr =
+			{
+				eexpr = TBlock(
+					List.map (fun (v,_) ->
+						{ eexpr = TBinop(Ast.OpAssign, mk_this v.v_name v.v_type, mk_local v pos); etype = v.v_type; epos = pos }
+					) tf_args
+					@
+					[
+						mk (TBinop(OpAssign, mk_this (mk_internal_name "hx" "length") basic.tint, gen.gclasses.nativearray_len (mk_local hashes_var pos) pos)) basic.tint pos;
+						mk (TBinop(OpAssign, mk_this (mk_internal_name "hx" "length_f") basic.tint, gen.gclasses.nativearray_len (mk_local hashes_f_var pos) pos)) basic.tint pos;
+					]
+				);
+				etype = basic.tvoid;
+				epos = pos
+			}
+		});
+		etype = fun_t;
+		epos = pos
+	});
+
+	add_constructor cl ctor;
+	(* default ctor also *)
+	let ctor = mk_class_field "new" (TFun([],basic.tvoid)) false pos (Method MethNormal) [] in
+	ctor.cf_expr <- Some {
+		eexpr = TFunction {
+			tf_type = basic.tvoid;
+			tf_args = [];
+			tf_expr = {
+				eexpr = TBlock(List.map (fun (f,t) ->
+					{ eexpr = TBinop(Ast.OpAssign, mk_this f t,{ eexpr = TCall(mk (TIdent "__array__") t_dynamic pos, []); etype = t; epos = pos; }); etype = t; epos = pos }
+				) fields);
+				etype = basic.tvoid;
+				epos = pos;
+			}
+		};
+		etype = ctor.cf_type;
+		epos = pos;
+	};
+	add_constructor cl ctor;
+	(* and finally we will return a function that transforms a TObjectDecl into a new DynamicObject() call *)
+	let rec loop objdecl acc acc_f =
+		match objdecl with
+			| [] -> acc,acc_f
+			| (name,expr) :: tl ->
+				let real_t = gen.greal_type expr.etype in
+				match follow expr.etype with
+					| TInst ( { cl_path = ["haxe"], "Int64" }, [] ) ->
+						loop tl ((name, gen.ghandle_cast t_dynamic real_t expr) :: acc) acc_f
+					| _ ->
+						if like_float real_t && not (like_i64 real_t) then
+							loop tl acc ((name, gen.ghandle_cast basic.tfloat real_t expr) :: acc_f)
+						else
+							loop tl ((name, gen.ghandle_cast t_dynamic real_t expr) :: acc) acc_f
+	in
+
+	let may_hash_field s =
+		if ctx.rcf_optimize then begin
+			mk (TConst (TInt (hash_field_i32 ctx pos s))) basic.tint pos
+		end else begin
+			ExprBuilder.make_string gen.gcon s pos
+		end
+	in
+
+	let do_objdecl e objdecl =
+		let exprs_before = ref [] in
+		let rec change_exprs decl acc = match decl with
+			| (name,expr) :: tl ->
+				if is_side_effects_free expr then
+					change_exprs tl ((name,expr) :: acc)
+				else begin
+					let var = mk_temp "odecl" expr.etype in
+					exprs_before := { eexpr = TVar(var,Some expr); etype = basic.tvoid; epos = expr.epos } :: !exprs_before;
+					change_exprs tl ((name,mk_local var expr.epos) :: acc)
+				end
+			| [] -> acc
+		in
+		let objdecl = change_exprs objdecl [] in
+
+		let odecl, odecl_f = loop objdecl [] [] in
+		let changed_expr = List.map (fun (s,e) -> (may_hash_field s,e)) in
+		let odecl, odecl_f = changed_expr odecl, changed_expr odecl_f in
+		let sort_fn (e1,_) (e2,_) =
+			match e1.eexpr, e2.eexpr with
+				| TConst(TInt i1), TConst(TInt i2) -> compare i1 i2
+				| TConst(TString s1), TConst(TString s2) -> compare s1 s2
+				| _ -> assert false
+		in
+
+		let odecl, odecl_f = List.sort sort_fn odecl, List.sort sort_fn odecl_f in
+
+		let ret = {
+			e with eexpr = TNew(cl,[],
+				[
+					mk_nativearray_decl gen hasht (List.map fst odecl) pos;
+					mk_nativearray_decl gen t_empty (List.map snd odecl) pos;
+					mk_nativearray_decl gen hasht (List.map fst odecl_f) pos;
+					mk_nativearray_decl gen basic.tfloat (List.map snd odecl_f) pos;
+				]);
+		} in
+		match !exprs_before with
+			| [] -> ret
+			| block ->
+				{
+					eexpr = TBlock(List.rev block @ [ret]);
+					etype = ret.etype;
+					epos = ret.epos;
+				}
+	in
+	do_objdecl
+
+let implement_dynamics ctx cl =
+	let pos = cl.cl_pos in
+	let is_override = is_override cl in
+	if is_some cl.cl_dynamic then begin
+		if is_first_dynamic cl then begin
+			(*
+				* add hx_hashes, hx_hashes_f, hx_dynamics, hx_dynamics_f to class
+				* implement hx_deleteField
+			*)
+			let gen = ctx.rcf_gen in
+			let basic = gen.gcon.basic in
+			let hasht = if ctx.rcf_optimize then basic.tint else basic.tstring in
+
+			let new_fields =
+			[
+				mk_class_field (mk_internal_name "hx" "hashes") (gen.gclasses.nativearray hasht) false pos (Var { v_read = AccNormal; v_write = AccNormal }) [];
+				mk_class_field (mk_internal_name "hx" "dynamics") (gen.gclasses.nativearray t_empty) false pos (Var { v_read = AccNormal; v_write = AccNormal }) [];
+				mk_class_field (mk_internal_name "hx" "hashes_f") (gen.gclasses.nativearray hasht) false pos (Var { v_read = AccNormal; v_write = AccNormal }) [];
+				mk_class_field (mk_internal_name "hx" "dynamics_f") (gen.gclasses.nativearray basic.tfloat) false pos (Var { v_read = AccNormal; v_write = AccNormal }) [];
+			] in
+
+			(if cl.cl_path <> (["haxe"; "lang"], "DynamicObject") then
+				List.iter (fun cf -> cf.cf_expr <- Some { eexpr = TCall(mk (TIdent "__array__") t_dynamic pos, []); etype = cf.cf_type; epos = cf.cf_pos }) new_fields
+			);
+
+			let new_fields =
+				if ctx.rcf_optimize then
+					let f = mk_class_field (mk_internal_name "hx" "conflicts") (Option.get ctx.rcf_hash_conflict_ctx).t false pos (Var { v_read = AccNormal; v_write = AccNormal }) [] in
+					f :: new_fields
+				else
+					new_fields
+			in
+
+			let delete = get_delete_field ctx cl true in
+
+			let new_fields = new_fields @ [
+				mk_class_field (mk_internal_name "hx" "length") (basic.tint) false pos (Var { v_read = AccNormal; v_write = AccNormal }) [];
+				mk_class_field (mk_internal_name "hx" "length_f") (basic.tint) false pos (Var { v_read = AccNormal; v_write = AccNormal }) [];
+				delete;
+			] in
+
+			List.iter (fun cf ->
+				cl.cl_fields <- PMap.add cf.cf_name cf cl.cl_fields
+			) new_fields;
+
+	(*
+			let rec last_ctor cl =
+				match cl.cl_constructor with
+					| None -> (match cl.cl_super with | None -> None | Some (cl,_) -> last_ctor cl)
+					| Some c -> Some c
+			in
+	*)
+			(*
+				in order for the next to work, we need to execute our script before InitFunction, so the expressions inside the variables are initialized by the constructor
+			*)
+			(*
+				Now we need to add their initialization.
+				This will consist of different parts:
+					Check if there are constructors. If not, create one and add initialization to it (calling super, ok)
+					If there are, add as first statement (or second if there is a super() call in the first)
+					If class has @:dynamicObject meta, also create another new() class with its parameters as constructor arguments
+			*)
+
+			cl.cl_ordered_fields <- cl.cl_ordered_fields @ new_fields;
+			if is_override then cl.cl_overrides <- delete :: cl.cl_overrides
+		end
+	end else if not is_override then begin
+		let delete = get_delete_field ctx cl false in
+		cl.cl_ordered_fields <- cl.cl_ordered_fields @ [delete];
+		cl.cl_fields <- PMap.add delete.cf_name delete cl.cl_fields
+	end
+
+
+(*
+	Implements:
+		__hx_lookupField(field:String, throwErrors:Bool, isCheck:Bool, handleProperties:Bool, isFirst:Bool):Dynamic
+
+		__hx_lookupField_f(field:String, throwErrors:Bool, handleProperties:Bool, isFirst:Bool):Float
+
+		__hx_lookupSetField(field:String, value:Dynamic, handleProperties:Bool, isFirst:Bool):Dynamic;
+
+		__hx_lookupSetField(field:String, value:Float, handleProperties:Bool, isFirst:Bool):Float;
+*)
+let implement_final_lookup ctx cl =
+	let gen = ctx.rcf_gen in
+	let basic = gen.gcon.basic in
+	let pos = cl.cl_pos in
+	let is_override = is_override cl in
+
+	let this = { eexpr = TConst(TThis); etype = TInst(cl, List.map snd cl.cl_params); epos = pos } in
+
+	let mk_throw str pos =
+		let e = ctx.rcf_mk_exception str pos in
+		ExprBuilder.make_throw e pos
+	in
+
+	(*
+		this function will create the class fields and call callback for each version
+
+		callback : is_float fields_args switch_var throw_errors_option is_check_option value_option : texpr list
+	*)
+	let create_cfs is_dynamic callback =
+		let create_cf is_float is_set =
+			let name = mk_internal_name "hx" ( (if is_set then "lookupSetField" else "lookupField") ^ (if is_float then "_f" else "") ) in
+			let field_args, switch_var = field_type_args ctx pos in
+			let ret_t = if is_float then basic.tfloat else t_dynamic in
+			let tf_args, throw_errors_opt =
+				if is_set then
+					field_args, None
+				else
+					let v = alloc_var "throwErrors" basic.tbool in
+					field_args @ [v,None], Some v
+			in
+			let tf_args, is_check_opt =
+				if is_set || is_float then
+					tf_args, None
+				else
+					let v = alloc_var "isCheck" basic.tbool in
+					tf_args @ [v,None], Some v
+			in
+			let tf_args, value_opt =
+				if not is_set then
+					tf_args, None
+				else
+					let v = alloc_var "value" ret_t in
+					field_args @ [v,None], Some v
+			in
+
+			let fun_t = TFun(fun_args tf_args, ret_t) in
+			let cf = mk_class_field name fun_t false pos (Method MethNormal) [] in
+			let block = callback is_float field_args switch_var throw_errors_opt is_check_opt value_opt in
+			let block = if not is_set then let tl = begin
+				let throw_errors_local = mk_local (get throw_errors_opt) pos in
+				let mk_check_throw msg =
+				{
+					eexpr = TIf(throw_errors_local, mk_throw msg pos, Some (mk_return (null ret_t pos)));
+					etype = ret_t;
+					epos = pos
+				} in
+
+				let mk_may_check_throw msg = if is_dynamic then mk_return (null ret_t pos) else mk_check_throw msg in
+				if is_float then begin
+					[
+						mk_may_check_throw "Field not found or incompatible field type.";
+					]
+				end else begin
+					let is_check_local = mk_local (get is_check_opt) pos in
+					[
+						{
+							eexpr = TIf(is_check_local, mk_return (undefined pos), Some( mk_may_check_throw "Field not found." ));
+							etype = ret_t;
+							epos = pos;
+						}
+					]
+				end
+			end in block @ tl else block in
+			cf.cf_expr <- Some(
+				{
+					eexpr = TFunction({
+						tf_args = tf_args;
+						tf_type = ret_t;
+						tf_expr = { eexpr = TBlock(block); etype = ret_t; epos = pos }
+					});
+					etype = fun_t;
+					epos = pos
+				}
+			);
+			cf
+		in
+		let cfs =
+		[
+			create_cf false false;
+			create_cf true false;
+			create_cf false true;
+			create_cf true true
+		] in
+		cl.cl_ordered_fields <- cl.cl_ordered_fields @ cfs;
+		List.iter (fun cf ->
+			cl.cl_fields <- PMap.add cf.cf_name cf cl.cl_fields;
+			if is_override then cl.cl_overrides <- cf :: cl.cl_overrides
+		) cfs
+	in
+
+	if is_some cl.cl_dynamic then begin
+		(* let abstract_dyn_lookup_implementation ctx this hash_local may_value is_float pos = *)
+		(* callback : is_float fields_args switch_var throw_errors_option is_check_option value_option : texpr list *)
+		if is_first_dynamic cl then
+			create_cfs true (fun is_float fields_args switch_var _ _ value_opt ->
+				let v_name = match fields_args with (v,_) :: _ -> v | _ -> assert false in
+				abstract_dyn_lookup_implementation ctx this (mk_local v_name pos) (mk_local switch_var pos) (Option.map (fun v -> mk_local v pos) value_opt) is_float pos
+			)
+	end else if not is_override then begin
+		create_cfs false (fun is_float fields_args switch_var _ _ value_opt ->
+			match value_opt with
+				| None -> (* is not set *)
+					[]
+				| Some _ -> (* is set *)
+					if is_float then
+						[ mk_throw "Cannot access field for writing or incompatible type." pos ]
+					else
+						[ mk_throw "Cannot access field for writing." pos ]
+		)
+	end
+
+(* *)
+let implement_get_set ctx cl =
+	let gen = ctx.rcf_gen in
+	let mk_cfield is_set is_float =
+		let pos = cl.cl_pos in
+		let basic = ctx.rcf_gen.gcon.basic in
+		let tf_args, switch_var = field_type_args ctx pos in
+		let field_args = tf_args in
+		let local_switch_var = { eexpr = TLocal(switch_var); etype = switch_var.v_type; epos = pos } in
+
+		let handle_prop = alloc_var "handleProperties" basic.tbool in
+		let handle_prop_local = mk_local handle_prop pos in
+
+		let this = { eexpr = TConst TThis; etype = TInst(cl, List.map snd cl.cl_params); epos = pos } in
+		let mk_this_call_raw name fun_t params =
+			{ eexpr = TCall( { (mk_field_access gen this name pos) with etype = fun_t; }, params ); etype = snd (get_fun fun_t); epos = pos }
+		in
+
+		let fun_type = ref (TFun([], basic.tvoid)) in
+		let fun_name = mk_internal_name "hx" ( (if is_set then "setField" else "getField") ^ (if is_float then "_f" else "") ) in
+		let cfield = mk_class_field fun_name !fun_type false pos (Method MethNormal) [] in
+
+		let maybe_cast e = e in
+
+		let t = TInst(cl, List.map snd cl.cl_params) in
+
+		(* if it's not latest hxgen class -> check super *)
+		let mk_do_default args do_default =
+			match cl.cl_super with
+				| None -> fun () -> maybe_cast (do_default ())
+				| Some (super, sparams) when not (is_hxgen (TClassDecl super)) ->
+					fun () -> maybe_cast (do_default ())
+				| _ ->
+					fun () ->
+						mk_return {
+							eexpr = TCall(
+								{ eexpr = TField({ eexpr = TConst TSuper; etype = t; epos = pos }, FInstance(cl, List.map snd cl.cl_params, cfield)); etype = !fun_type; epos = pos },
+								(List.map (fun (v,_) -> mk_local v pos) args) );
+							etype = if is_float then basic.tfloat else t_dynamic;
+							epos = pos;
+						};
+		in
+
+		(* if it is set function, there are some different set fields to do *)
+		let do_default, do_field, tf_args = if is_set then begin
+			let value_var = alloc_var "value" (if is_float then basic.tfloat else t_dynamic) in
+			let value_local = { eexpr = TLocal(value_var); etype = value_var.v_type; epos = pos } in
+			let tf_args = tf_args @ [value_var,None; handle_prop, None; ] in
+			let lookup_name = mk_internal_name "hx" ("lookupSetField" ^ if is_float then "_f" else "") in
+
+			let do_default =
+					fun () ->
+						mk_return (mk_this_call_raw lookup_name (TFun(fun_args (field_args @ [value_var,None]),value_var.v_type)) ( List.map (fun (v,_) -> mk_local v pos) field_args @ [ value_local ] ))
+			in
+
+			let do_field cf cf_type =
+				let get_field ethis = { eexpr = TField (ethis, FInstance(cl, List.map snd cl.cl_params, cf)); etype = cf_type; epos = pos } in
+				let this = { eexpr = TConst(TThis); etype = t; epos = pos } in
+				let value_local = if is_float then match follow cf_type with
+					| TInst({ cl_kind = KTypeParameter _ }, _) ->
+						mk_cast t_dynamic value_local
+					| _ ->
+						value_local
+					else
+						value_local
+				in
+
+				let ret =
+				{
+					eexpr = TBlock([
+						{
+							eexpr = TBinop(Ast.OpAssign,
+								get_field this,
+								mk_cast cf_type value_local);
+							etype = cf_type;
+							epos = pos;
+						};
+						mk_return value_local
+					]);
+					etype = cf_type;
+					epos = pos;
+				} in
+				match cf.cf_kind with
+					| Var { v_write = AccCall } ->
+						let bl =
+						[
+							mk_this_call_raw ("set_" ^ cf.cf_name) (TFun(["value",false,cf.cf_type], cf.cf_type)) [ value_local ];
+							mk_return value_local
+						] in
+						if not (Type.is_physical_field cf) then
+							{ eexpr = TBlock bl; etype = value_local.etype; epos = pos }
+						else
+							{
+								eexpr = TIf(
+									handle_prop_local,
+									{ eexpr = TBlock bl; etype = value_local.etype; epos = pos },
+									Some ret);
+								etype = value_local.etype;
+								epos = pos;
+							}
+					| _ ->
+						ret
+			in
+
+			(mk_do_default tf_args do_default, do_field, tf_args)
+		end else begin
+			let throw_errors = alloc_var "throwErrors" basic.tbool in
+			let throw_errors_local = mk_local throw_errors pos in
+			let do_default, tf_args = if not is_float then begin
+				let is_check = alloc_var "isCheck" basic.tbool in
+				let is_check_local = mk_local is_check pos in
+
+				let tf_args = tf_args @ [ throw_errors,None; ] in
+
+				(* default: if (isCheck) return __undefined__ else if(throwErrors) throw "Field not found"; else return null; *)
+				let lookup_name = mk_internal_name "hx" "lookupField" in
+				let do_default =
+						fun () ->
+							mk_return (mk_this_call_raw lookup_name (TFun(fun_args (field_args @ [throw_errors,None;is_check,None; ]),t_dynamic)) ( List.map (fun (v,_) -> mk_local v pos) field_args @ [ throw_errors_local; is_check_local; ] ))
+				in
+
+				(do_default, tf_args @ [ is_check,None; handle_prop,None; ])
+			end else begin
+				let tf_args = tf_args @ [ throw_errors,None; ] in
+
+				let lookup_name = mk_internal_name "hx" "lookupField_f" in
+				let do_default =
+						fun () ->
+							mk_return (mk_this_call_raw lookup_name (TFun(fun_args (field_args @ [throw_errors,None; ]),basic.tfloat)) ( List.map (fun (v,_) -> mk_local v pos) field_args @ [ throw_errors_local; ] ))
+				in
+
+				(do_default, tf_args @ [ handle_prop,None; ])
+			end in
+
+			let get_field cf cf_type ethis cl name =
+				match cf.cf_kind with
+					| Var { v_read = AccCall } when not (Type.is_physical_field cf) ->
+						mk_this_call_raw ("get_" ^ cf.cf_name) (TFun(["value",false,cf.cf_type], cf.cf_type)) []
+					| Var { v_read = AccCall } ->
+						{
+							eexpr = TIf(
+								handle_prop_local,
+								mk_this_call_raw ("get_" ^ cf.cf_name) (TFun(["value",false,cf.cf_type], cf.cf_type)) [],
+								Some { eexpr = TField (ethis, FInstance(cl, List.map snd cl.cl_params, cf)); etype = cf_type; epos = pos }
+							);
+							etype = cf_type;
+							epos = pos;
+						}
+					| Var _
+					| Method MethDynamic -> { eexpr = TField (ethis, FInstance(cl,List.map snd cl.cl_params,cf)); etype = cf_type; epos = pos }
+					| _ ->
+							{ eexpr = TField (this, FClosure(Some (cl,List.map snd cl.cl_params), cf)); etype = cf_type; epos = pos }
+			in
+
+			let do_field cf cf_type =
+				let this = { eexpr = TConst(TThis); etype = t; epos = pos } in
+				match is_float, follow cf_type with
+					| true, TInst( { cl_kind = KTypeParameter _ }, _ ) ->
+						mk_return (mk_cast basic.tfloat (mk_cast t_dynamic (get_field cf cf_type this cl cf.cf_name)))
+					| _ ->
+						mk_return (maybe_cast (get_field cf cf_type this cl cf.cf_name ))
+			in
+			(mk_do_default tf_args do_default, do_field, tf_args)
+		end in
+
+		let get_fields() =
+			let ret = collect_fields cl ( if is_float || is_set then Some (false) else None ) in
+			let ret = if is_set then List.filter (fun (_,cf) ->
+				match cf.cf_kind with
+				(* | Var { v_write = AccNever } -> false *)
+				| _ -> not (Meta.has Meta.ReadOnly cf.cf_meta)) ret
+			else
+				List.filter (fun (_,cf) ->
+				match cf.cf_kind with
+				(* | Var { v_read = AccNever } -> false *)
+				| _ -> true) ret in
+			if is_float then
+				List.filter (fun (_,cf) -> (* TODO: maybe really apply_params in cf.cf_type. The benefits would be limited, though *)
+					match follow (ctx.rcf_gen.greal_type (ctx.rcf_gen.gfollow#run_f cf.cf_type)) with
+						| TDynamic _ | TMono _
+						| TInst ({ cl_kind = KTypeParameter _ }, _) -> true
+						| t when like_float t && not (like_i64 t) -> true
+						| _ -> false
+				) ret
+			else
+				(* dynamic will always contain all references *)
+				ret
+		in
+
+		(* now we have do_default, do_field and tf_args *)
+		(* so create the switch expr *)
+		fun_type := TFun(List.map (fun (v,_) -> (v.v_name, false, v.v_type)) tf_args, if is_float then basic.tfloat else t_dynamic );
+		let has_fields = ref false in
+
+		let content =
+			let fields = get_fields() in
+			let fields = List.filter (fun (_, cf) -> match is_set, cf.cf_kind with
+				| true, Var { v_write = AccCall } -> true
+				| false, Var { v_read = AccCall } -> true
+				| _ -> Type.is_physical_field cf) fields
+			in
+			(if fields <> [] then has_fields := true);
+			let cases = List.map (fun (names, cf) ->
+				(if names = [] then assert false);
+				(List.map (switch_case ctx pos) names, do_field cf cf.cf_type)
+			) fields in
+			let default = Some(do_default()) in
+
+			mk_block { eexpr = TSwitch(local_switch_var, cases, default); etype = basic.tvoid; epos = pos }
+		in
+
+		let is_override = match cl.cl_super with
+			| Some (cl, _) when is_hxgen (TClassDecl cl) -> true
+			| _ -> false
+		in
+
+		if !has_fields || (not is_override) then begin
+			let func =
+			{
+				tf_args = tf_args;
+				tf_type = if is_float then basic.tfloat else t_dynamic;
+				tf_expr = content;
+			} in
+
+			let func = { eexpr = TFunction(func); etype = !fun_type; epos = pos } in
+
+			cfield.cf_type <- !fun_type;
+			cfield.cf_expr <- Some func;
+
+			cl.cl_ordered_fields <- cl.cl_ordered_fields @ [cfield];
+			cl.cl_fields <- PMap.add fun_name cfield cl.cl_fields;
+
+			(if is_override then cl.cl_overrides <- cfield	:: cl.cl_overrides)
+		end else ()
+	in
+	mk_cfield true true;
+	mk_cfield true false;
+	mk_cfield false false;
+	mk_cfield false true
+
+let implement_getFields ctx cl =
+	let gen = ctx.rcf_gen in
+	let basic = gen.gcon.basic in
+	let pos = cl.cl_pos in
+
+	(*
+		function __hx_getFields(baseArr:Array<String>)
+		{
+			//add all variable fields
+			//then:
+			super.__hx_getFields(baseArr);
+		}
+	*)
+	let name = mk_internal_name "hx" "getFields" in
+	let v_base_arr = alloc_var "baseArr" (basic.tarray basic.tstring) in
+	let base_arr = mk_local v_base_arr pos in
+
+	let tf_args = [(v_base_arr,None)] in
+	let t = TFun(fun_args tf_args, basic.tvoid) in
+	let cf = mk_class_field name t false pos (Method MethNormal) [] in
+
+	let e_pushfield = mk_field_access gen base_arr "push" pos in
+	let mk_push value = mk (TCall (e_pushfield, [value])) basic.tint pos in
+
+	let has_value = ref false in
+	let map_fields =
+		List.map (fun (_,cf) ->
+			match cf.cf_kind with
+				| Var _
+				| Method MethDynamic when not (List.memq cf cl.cl_overrides) ->
+					has_value := true;
+					mk_push (ExprBuilder.make_string gen.gcon cf.cf_name pos)
+				| _ -> null basic.tvoid pos
+		)
+	in
+
+	(*
+		if it is first_dynamic, then we need to enumerate the dynamic fields
+	*)
+	let exprs =
+		if is_some cl.cl_dynamic && is_first_dynamic cl then begin
+			has_value := true;
+			enumerate_dynamic_fields ctx cl mk_push base_arr
+		end else
+			[]
+	in
+
+	let exprs =
+		if is_override cl then
+			let tparams = List.map snd cl.cl_params in
+			let esuper = mk (TConst TSuper) (TInst(cl, tparams)) pos in
+			let efield = mk (TField (esuper, FInstance (cl, tparams, cf))) t pos in
+			exprs @ [mk (TCall (efield, [base_arr])) basic.tvoid pos]
+		else
+			exprs
+	in
+
+	let exprs = map_fields (collect_fields cl (Some false)) @ exprs in
+
+	cf.cf_expr <- Some {
+		eexpr = TFunction({
+			tf_args = tf_args;
+			tf_type = basic.tvoid;
+			tf_expr = mk (TBlock exprs) basic.tvoid pos
+		});
+		etype = t;
+		epos = pos
+	};
+
+	if !has_value || not (is_override cl) then begin
+		cl.cl_ordered_fields <- cl.cl_ordered_fields @ [cf];
+		cl.cl_fields <- PMap.add cf.cf_name cf cl.cl_fields;
+		(if is_override cl then cl.cl_overrides <- cf :: cl.cl_overrides)
+	end
+
+
+let implement_invokeField ctx slow_invoke cl =
+	(*
+		There are two ways to implement an haxe reflection-enabled class:
+		When we extend a non-hxgen class, and when we extend the base HxObject class.
+
+		Because of the added boiler plate we'd add every time we extend a non-hxgen class to implement a big IHxObject
+		interface, we'll handle the cases differently when implementing each interface.
+
+		At the IHxObject interface, there's only invokeDynamic(field, args[]), while at the HxObject class there are
+		the other, more optimized methods, that follow the Function class interface.
+
+		Since this will only be called by the Closure class, this conversion can be properly dealt with later.
+
+		TODO: create the faster version. By now only invokeDynamic will be implemented
+	*)
+	let gen = ctx.rcf_gen in
+	let basic = gen.gcon.basic in
+	let pos = cl.cl_pos in
+
+	let has_method = ref false in
+
+	let is_override = ref false in
+	let rec extends_hxobject cl =
+		match cl.cl_super with
+			| None -> true
+			| Some (cl,_) when is_hxgen (TClassDecl cl) -> is_override := true; extends_hxobject cl
+			| _ -> false
+	in
+
+	let field_args, switch_var = field_type_args ctx cl.cl_pos in
+	let field_args_exprs = List.map (fun (v,_) -> mk_local v pos) field_args in
+
+	let dynamic_arg = alloc_var "dynargs" (gen.gclasses.nativearray t_dynamic) in
+	let all_args = field_args @ [ dynamic_arg, None ] in
+	let fun_t = TFun(fun_args all_args, t_dynamic) in
+
+	let this_t = TInst(cl, List.map snd cl.cl_params) in
+	let this = { eexpr = TConst(TThis); etype = this_t; epos = pos } in
+
+	let mk_this_call_raw name fun_t params =
+		{ eexpr = TCall( { (mk_field_access gen this name pos) with etype = fun_t }, params ); etype = snd (get_fun fun_t); epos = pos }
+	in
+
+	let extends_hxobject = extends_hxobject cl in
+	ignore extends_hxobject;
+
+	(* creates a invokeField of the class fields listed here *)
+	(*
+		function invokeField(field, dynargs)
+		{
+			switch(field)
+			{
+				case "a": this.a(dynargs[0], dynargs[1], dynargs[2]...);
+				default: super.invokeField //or this.getField(field).invokeDynamic(dynargs)
+			}
+		}
+	*)
+
+	let dyn_fun = mk_class_field (mk_internal_name "hx" "invokeField") fun_t false cl.cl_pos (Method MethNormal) [] in
+
+	let mk_switch_dyn cfs old =
+		let get_case (names,cf) =
+			has_method := true;
+			let i = ref 0 in
+			let dyn_arg_local = mk_local dynamic_arg pos in
+			let cases = List.map (switch_case ctx pos) names in
+
+			let mk_this_call cf params =
+				let t = apply_params cf.cf_params (List.map (fun _ -> t_dynamic) cf.cf_params) cf.cf_type in
+				mk_this_call_raw cf.cf_name t params
+			in
+			(cases,
+				mk_return (
+					mk_this_call cf (List.map (fun (name,_,t) ->
+						let ret = { eexpr = TArray(dyn_arg_local, ExprBuilder.make_int ctx.rcf_gen.gcon !i pos); etype = t_dynamic; epos = pos } in
+						incr i;
+						ret
+					) (fst (get_fun (cf.cf_type))))
+				)
+			)
+		in
+
+		let cfs = List.filter (fun (_,cf) -> match cf.cf_kind with
+			| Method _ -> if List.memq cf cl.cl_overrides then false else true
+			| _ -> true) cfs
+		in
+
+		let cases = List.map get_case cfs in
+		let cases = match old with
+			| [] -> cases
+			| _ ->
+				let ncases = List.map (fun cf -> switch_case ctx pos cf.cf_name) old in
+				( ncases, mk_return (slow_invoke this (mk_local (fst (List.hd field_args)) pos) (mk_local dynamic_arg pos)) ) :: cases
+		in
+
+		let default = if !is_override then
+			mk_return (call_super ctx all_args t_dynamic dyn_fun cl this_t pos)
+		else (
+			let field = begin
+				let fun_name = mk_internal_name "hx" "getField" in
+				let tf_args, _ = field_type_args ctx pos in
+				let tf_args, args = fun_args tf_args, field_args_exprs in
+
+				let tf_args, args = tf_args @ ["throwErrors",false, basic.tbool],       args @ [ExprBuilder.make_bool gen.gcon true pos] in
+				let tf_args, args = tf_args @ ["isCheck", false, basic.tbool],          args @ [ExprBuilder.make_bool gen.gcon false pos] in
+				let tf_args, args = tf_args @ ["handleProperties", false, basic.tbool], args @ [ExprBuilder.make_bool gen.gcon false pos] in
+
+				mk (TCall ({ (mk_field_access gen this fun_name pos) with etype = TFun(tf_args, t_dynamic) }, args)) t_dynamic pos
+			end in
+			let field = mk_cast (TInst(ctx.rcf_ft.func_class,[])) field in
+			mk_return {
+				eexpr = TCall(
+					mk_field_access gen field (mk_internal_name "hx" "invokeDynamic") pos,
+					[mk_local dynamic_arg pos]);
+				etype = t_dynamic;
+				epos = pos
+			} )
+		in
+
+		{
+			eexpr = TSwitch(mk_local switch_var pos, cases, Some default);
+			etype = basic.tvoid;
+			epos = pos;
+		}
+	in
+
+	let contents =
+		let nonstatics = collect_fields cl (Some true) in
+
+		let old_nonstatics = ref [] in
+
+		let nonstatics =
+			List.filter (fun (n,cf) ->
+				let is_old = not (PMap.mem cf.cf_name cl.cl_fields) || List.memq cf cl.cl_overrides in
+				(if is_old then old_nonstatics := cf :: !old_nonstatics);
+				not is_old
+			) nonstatics
+		in
+
+		mk_switch_dyn nonstatics !old_nonstatics
+	in
+
+	dyn_fun.cf_expr <- Some
+		{
+			eexpr = TFunction(
+			{
+				tf_args = all_args;
+				tf_type = t_dynamic;
+				tf_expr = mk_block contents;
+			});
+			etype = TFun(fun_args all_args, t_dynamic);
+			epos = pos;
+		};
+	if !is_override && not (!has_method) then () else begin
+		cl.cl_ordered_fields <- cl.cl_ordered_fields @ [dyn_fun];
+		cl.cl_fields <- PMap.add dyn_fun.cf_name dyn_fun cl.cl_fields;
+		(if !is_override then cl.cl_overrides <- dyn_fun :: cl.cl_overrides)
+	end
+
+let implement_varargs_cl ctx cl =
+	let pos = cl.cl_pos in
+	let gen = ctx.rcf_gen in
+
+	let this_t = TInst(cl, List.map snd cl.cl_params) in
+	let this = { eexpr = TConst(TThis); etype = this_t ; epos = pos } in
+	let mk_this field t = { (mk_field_access gen this field pos) with etype = t } in
+
+	let invokedyn = mk_internal_name "hx" "invokeDynamic" in
+	let idyn_t = TFun([mk_internal_name "fn" "dynargs", false, gen.gclasses.nativearray t_dynamic], t_dynamic) in
+	let this_idyn = mk_this invokedyn idyn_t in
+
+	let map_fn arity ret vars api =
+
+		let rec loop i acc =
+			if i < 0 then
+				acc
+			else
+				let obj = api i t_dynamic None in
+				loop (i - 1) (obj :: acc)
+		in
+
+		let call_arg = if arity = (-1) then
+			api (-1) t_dynamic None
+		else if arity = 0 then
+			null (gen.gclasses.nativearray t_empty) pos
+		else
+			mk_nativearray_decl gen t_empty (loop (arity - 1) []) pos
+		in
+
+		let expr = {
+			eexpr = TCall(
+				this_idyn,
+				[ call_arg ]
+			);
+			etype = t_dynamic;
+			epos = pos
+		} in
+
+		let expr = if like_float ret && not (like_int ret) then mk_cast ret expr else expr in
+
+		mk_return expr
+	in
+
+	let all_cfs = List.filter (fun cf -> cf.cf_name <> "new" && cf.cf_name <> (invokedyn) && match cf.cf_kind with Method _ -> true | _ -> false) (ctx.rcf_ft.map_base_classfields cl map_fn) in
+
+	cl.cl_ordered_fields <- cl.cl_ordered_fields @ all_cfs;
+	List.iter (fun cf ->
+		cl.cl_fields <- PMap.add cf.cf_name cf cl.cl_fields
+	) all_cfs;
+
+	List.iter (fun cf ->
+		cl.cl_overrides <- cf :: cl.cl_overrides
+	) cl.cl_ordered_fields
+
+let implement_closure_cl ctx cl =
+	let pos = cl.cl_pos in
+	let gen = ctx.rcf_gen in
+	let basic = gen.gcon.basic in
+
+	let field_args, _ = field_type_args ctx pos in
+	let obj_arg = alloc_var "target" (TInst(ctx.rcf_object_iface, [])) in
+
+	let this_t = TInst(cl, List.map snd cl.cl_params) in
+	let this = { eexpr = TConst(TThis); etype = this_t ; epos = pos } in
+	let mk_this field t = { (mk_field_access gen this field pos) with etype = t } in
+
+	let tf_args = field_args @ [obj_arg, None] in
+	let cfs, ctor_body = List.fold_left (fun (acc_cf,acc_expr) (v,_) ->
+		let cf = mk_class_field v.v_name v.v_type false pos (Var { v_read = AccNormal; v_write = AccNormal } ) [] in
+		let expr = { eexpr = TBinop(Ast.OpAssign, mk_this v.v_name v.v_type, mk_local v pos); etype = v.v_type; epos = pos } in
+		(cf :: acc_cf, expr :: acc_expr)
+	) ([], [])	tf_args in
+
+	let map_fn arity ret vars api =
+		let this_obj = mk_this "target" (TInst(ctx.rcf_object_iface, [])) in
+
+		let rec loop i acc =
+			if i < 0 then
+				acc
+			else
+				let obj = api i t_dynamic None in
+				loop (i - 1) (obj :: acc)
+		in
+
+		let call_arg = if arity = (-1) then
+			api (-1) t_dynamic None
+		else if arity = 0 then
+			null (gen.gclasses.nativearray t_empty) pos
+		else
+			mk_nativearray_decl gen t_empty  (loop (arity - 1) []) pos
+		in
+
+		let expr = {
+			eexpr = TCall(
+				mk_field_access gen this_obj (mk_internal_name "hx" "invokeField") pos,
+				(List.map (fun (v,_) -> mk_this v.v_name v.v_type) field_args) @ [ call_arg ]
+			);
+			etype = t_dynamic;
+			epos = pos
+		} in
+
+		let expr = if like_float ret && not (like_int ret) then mk_cast ret expr else expr in
+
+		mk_return expr
+	in
+
+	let all_cfs = List.filter (fun cf -> cf.cf_name <> "new" && match cf.cf_kind with Method _ -> true | _ -> false) (ctx.rcf_ft.map_base_classfields cl map_fn) in
+
+	List.iter (fun cf ->
+		cl.cl_overrides <- cf :: cl.cl_overrides
+	) all_cfs;
+	let all_cfs = cfs @ all_cfs in
+
+	cl.cl_ordered_fields <- cl.cl_ordered_fields @ all_cfs;
+	List.iter (fun cf ->
+		cl.cl_fields <- PMap.add cf.cf_name cf cl.cl_fields
+	) all_cfs;
+
+	let ctor_t = TFun(fun_args tf_args, basic.tvoid) in
+	let ctor_cf = mk_class_field "new" ctor_t true pos (Method MethNormal) [] in
+	ctor_cf.cf_expr <- Some {
+		eexpr = TFunction({
+			tf_args = tf_args;
+			tf_type = basic.tvoid;
+			tf_expr = { eexpr = TBlock({
+				eexpr = TCall({ eexpr = TConst(TSuper); etype = TInst(cl,[]); epos = pos }, [ExprBuilder.make_int ctx.rcf_gen.gcon (-1) pos; ExprBuilder.make_int ctx.rcf_gen.gcon (-1) pos]);
+				etype = basic.tvoid;
+				epos = pos
+			} :: ctor_body); etype = basic.tvoid; epos = pos }
+		});
+		etype = ctor_t;
+		epos = pos
+	};
+
+	cl.cl_constructor <- Some ctor_cf;
+
+	let closure_fun eclosure e field is_static =
+		let f = ExprBuilder.make_string gen.gcon field eclosure.epos in
+		let args = if ctx.rcf_optimize then [ f; { eexpr = TConst(TInt (hash_field_i32 ctx eclosure.epos field)); etype = basic.tint; epos = eclosure.epos } ] else [ f ] in
+		let args = args @ [ mk_cast (TInst(ctx.rcf_object_iface, [])) e ] in
+
+		{ eclosure with eexpr = TNew(cl,[],args) }
+	in
+	closure_fun
+
+let get_closure_func ctx closure_cl =
+	let gen = ctx.rcf_gen in
+	let basic = gen.gcon.basic in
+	let closure_func eclosure e field is_static =
+		mk_cast eclosure.etype { eclosure with
+			eexpr = TNew(closure_cl, [], [
+				e;
+				ExprBuilder.make_string gen.gcon field eclosure.epos
+			] @ (
+				if ctx.rcf_optimize then [ { eexpr = TConst(TInt (hash_field_i32 ctx eclosure.epos field)); etype = basic.tint; epos = eclosure.epos } ] else []
+			));
+			etype = TInst(closure_cl,[])
+		}
+	in
+	closure_func
+
+(*
+		main expr -> field expr -> field string -> possible set expr -> should_throw_exceptions -> changed expression
+
+		Changes a get / set
+	*
+	mutable rcf_on_getset_field : texpr->texpr->string->texpr option->bool->texpr;*)
+
+let configure_dynamic_field_access ctx =
+	let gen = ctx.rcf_gen in
+	let is_dynamic fexpr field =
+		match (field_access_esp gen (gen.greal_type fexpr.etype) field) with
+		| FEnumField _
+		| FClassField _ -> false
+		| _ -> true
+	in
+
+	let maybe_hash = if ctx.rcf_optimize then fun str pos -> Some (hash_field_i32 ctx pos str) else fun str pos -> None in
+	DynamicFieldAccess.configure gen is_dynamic
+		(fun expr fexpr field set is_unsafe ->
+			let hash = maybe_hash field fexpr.epos in
+			ctx.rcf_on_getset_field expr fexpr field hash set is_unsafe
+		)
+		(fun ecall fexpr field call_list ->
+			let hash = maybe_hash field fexpr.epos in
+			ctx.rcf_on_call_field ecall fexpr field hash call_list
+		);
+	()
+
+
+(* ******************************************* *)
+(* UniversalBaseClass *)
+(* ******************************************* *)
+(*
+	Sets the universal base class for hxgen types (HxObject / IHxObject)
+
+	dependencies:
+		As a rule, it should be one of the last module filters to run so any @:hxgen class created in the process
+		-Should- only run after RealTypeParams.Modf
+*)
+module UniversalBaseClass =
+struct
+	let name = "rcf_universal_base_class"
+	let priority = min_dep +. 10.
+
+	let configure gen baseclass baseinterface basedynamic =
+		let rec run md =
+			if is_hxgen md then
+				match md with
+				| TClassDecl ({ cl_interface = true } as cl) when cl.cl_path <> baseclass.cl_path && cl.cl_path <> baseinterface.cl_path && cl.cl_path <> basedynamic.cl_path ->
+					cl.cl_implements <- (baseinterface, []) :: cl.cl_implements
+				| TClassDecl ({ cl_kind = KAbstractImpl _ }) ->
+					(* don't add any base classes to abstract implementations *)
+					()
+				| TClassDecl ({ cl_super = None } as cl) when cl.cl_path <> baseclass.cl_path && cl.cl_path <> baseinterface.cl_path && cl.cl_path <> basedynamic.cl_path ->
+					if is_some cl.cl_dynamic then
+						cl.cl_super <- Some (basedynamic,[])
+					else
+						cl.cl_super <- Some (baseclass,[])
+				| TClassDecl ({ cl_super = Some(super,_) } as cl) when cl.cl_path <> baseclass.cl_path && cl.cl_path <> baseinterface.cl_path && not (is_hxgen (TClassDecl super)) ->
+					cl.cl_implements <- (baseinterface, []) :: cl.cl_implements
+				| _ ->
+					()
+		in
+		let map md = run md; md in
+		gen.gmodule_filters#add name (PCustom priority) map
+end;;
+
+
+(*
+	Priority: must run AFTER UniversalBaseClass
+*)
+let priority = solve_deps name [DAfter UniversalBaseClass.priority]
+
+let configure ctx baseinterface ~slow_invoke =
+	let run md =
+		(match md with
+		| TClassDecl ({ cl_extern = false } as cl) when is_hxgen md && ( not cl.cl_interface || cl.cl_path = baseinterface.cl_path ) && (match cl.cl_kind with KAbstractImpl _ -> false | _ -> true) ->
+			implement_dynamics ctx cl;
+			if not (PMap.mem (mk_internal_name "hx" "lookupField") cl.cl_fields) then implement_final_lookup ctx cl;
+			if not (PMap.mem (mk_internal_name "hx" "getField") cl.cl_fields) then implement_get_set ctx cl;
+			if not (PMap.mem (mk_internal_name "hx" "invokeField") cl.cl_fields) then implement_invokeField ctx slow_invoke cl;
+			if not (PMap.mem (mk_internal_name "hx" "getFields") cl.cl_fields) then implement_getFields ctx cl;
+		| _ -> ());
+		md
+	in
+	ctx.rcf_gen.gmodule_filters#add name (PCustom priority) run

+ 101 - 0
src/generators/gencommon/renameTypeParameters.ml

@@ -0,0 +1,101 @@
+(*
+	The Haxe Compiler
+	Copyright (C) 2005-2017  Haxe Foundation
+
+	This program is free software; you can redistribute it and/or
+	modify it under the terms of the GNU General Public License
+	as published by the Free Software Foundation; either version 2
+	of the License, or (at your option) any later version.
+
+	This program is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+
+	You should have received a copy of the GNU General Public License
+	along with this program; if not, write to the Free Software
+	Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+*)
+open Type
+
+(* ******************************************* *)
+(* Rename Type Parameters *)
+(* ******************************************* *)
+(*
+	This module should run after everything is already applied,
+	it will look for possible type parameter name clashing and change the classes names to a
+*)
+let run types =
+	let i = ref 0 in
+	let found_types = ref PMap.empty in
+	let check_type name on_changed =
+		let rec loop name =
+			incr i;
+			let changed_name = (name ^ (string_of_int !i)) in
+			if PMap.mem changed_name !found_types then loop name else changed_name
+		in
+		if PMap.mem name !found_types then begin
+			let new_name = loop name in
+			found_types := PMap.add new_name true !found_types;
+			on_changed new_name
+		end else found_types := PMap.add name true !found_types
+	in
+
+	let get_cls t =
+		match follow t with
+		| TInst(cl,_) -> cl
+		| _ -> assert false
+	in
+
+	let iter_types (nt,t) =
+		let cls = get_cls t in
+		let orig = cls.cl_path in
+		check_type (snd orig) (fun name -> cls.cl_path <- (fst orig, name))
+	in
+
+	let save_params save params =
+		List.fold_left (fun save (_,t) ->
+			let cls = get_cls t in
+			(cls.cl_path,t) :: save) save params
+	in
+
+	List.iter (function
+		| TClassDecl cl ->
+			i := 0;
+
+			let save = [] in
+
+			found_types := PMap.empty;
+			let save = save_params save cl.cl_params in
+			List.iter iter_types cl.cl_params;
+			let cur_found_types = !found_types in
+			let save = ref save in
+			List.iter (fun cf ->
+				found_types := cur_found_types;
+				save := save_params !save cf.cf_params;
+				List.iter iter_types cf.cf_params
+			) (cl.cl_ordered_fields @ cl.cl_ordered_statics);
+
+			if !save <> [] then begin
+				let save = !save in
+				let res = cl.cl_restore in
+				cl.cl_restore <- (fun () ->
+					res();
+					List.iter (fun (path,t) ->
+						let cls = get_cls t in
+						cls.cl_path <- path) save
+				);
+			end
+
+		| TEnumDecl ( ({ e_params = hd :: tl }) ) ->
+			i := 0;
+			found_types := PMap.empty;
+			List.iter iter_types (hd :: tl)
+
+		| TAbstractDecl { a_params = hd :: tl } ->
+			i := 0;
+			found_types := PMap.empty;
+			List.iter iter_types (hd :: tl)
+
+		| _ -> ()
+	) types

+ 101 - 0
src/generators/gencommon/setHXGen.ml

@@ -0,0 +1,101 @@
+(*
+	The Haxe Compiler
+	Copyright (C) 2005-2017  Haxe Foundation
+
+	This program is free software; you can redistribute it and/or
+	modify it under the terms of the GNU General Public License
+	as published by the Free Software Foundation; either version 2
+	of the License, or (at your option) any later version.
+
+	This program is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+
+	You should have received a copy of the GNU General Public License
+	along with this program; if not, write to the Free Software
+	Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+*)
+open Common
+open Type
+
+(* ******************************************* *)
+(* set hxgen module *)
+(* ******************************************* *)
+(*
+	Goes through all module types and adds the @:hxGen or @:nativeGen meta to them.
+	Basically, everything that is extern is assumed to not be hxgen, unless meta :hxGen is set,
+	and everything that is not extern is assumed to be hxgen, unless meta :nativeGgen is set.
+*)
+
+(*
+	The only option is to run this filter eagerly, because it must be one of the first filters to run,
+	since many others depend of it.
+*)
+let run_filter com types =
+	let rec is_hxgen md =
+		match md with
+		| TClassDecl { cl_kind = KAbstractImpl a } ->
+			is_hxgen (TAbstractDecl a)
+		| TClassDecl cl ->
+			let rec is_hxgen_class (c,_) =
+				if c.cl_extern then begin
+					if Meta.has Meta.HxGen c.cl_meta then
+						true
+					else
+						Option.map_default (is_hxgen_class) false c.cl_super || List.exists is_hxgen_class c.cl_implements
+				end else begin
+					if Meta.has Meta.NativeChildren c.cl_meta || Meta.has Meta.NativeGen c.cl_meta || Meta.has Meta.Struct c.cl_meta then
+						Option.map_default is_hxgen_class false c.cl_super || List.exists is_hxgen_class c.cl_implements
+					else
+						let rec has_nativec (c,p) =
+							if is_hxgen_class (c,p) then
+								false
+							else if Meta.has Meta.Struct c.cl_meta then begin
+								com.error ("Struct types cannot be subclassed") c.cl_pos;
+								true
+							end else
+								(Meta.has Meta.NativeChildren c.cl_meta && not (Option.map_default is_hxgen_class false c.cl_super || List.exists is_hxgen_class c.cl_implements))
+								|| Option.map_default has_nativec false c.cl_super
+						in
+						if Option.map_default has_nativec false c.cl_super && not (List.exists is_hxgen_class c.cl_implements) then
+							false
+						else
+							true
+				end
+			in
+			is_hxgen_class (cl,[])
+		| TEnumDecl e ->
+			if e.e_extern then
+				Meta.has Meta.HxGen e.e_meta
+			else if Meta.has Meta.NativeGen e.e_meta then
+				if Meta.has Meta.FlatEnum e.e_meta then
+					false
+				else begin
+					com.error "Only flat enums may be @:nativeGen" e.e_pos;
+					true
+				end
+			else
+				true
+		| TAbstractDecl a when Meta.has Meta.CoreType a.a_meta ->
+			not (Meta.has Meta.NativeGen a.a_meta)
+		| TAbstractDecl a ->
+			(match follow a.a_this with
+			| TInst _ | TEnum _ | TAbstract _ ->
+				is_hxgen (module_type_of_type (follow a.a_this))
+			| _ ->
+				not (Meta.has Meta.NativeGen a.a_meta))
+		| TTypeDecl t -> (* TODO see when would we use this *)
+			false
+	in
+
+	let filter md =
+		let meta = if is_hxgen md then Meta.HxGen else Meta.NativeGen in
+		match md with
+		| TClassDecl cl -> cl.cl_meta <- (meta, [], cl.cl_pos) :: cl.cl_meta
+		| TEnumDecl e -> e.e_meta <- (meta, [], e.e_pos) :: e.e_meta
+		| TTypeDecl t -> t.t_meta <- (meta, [], t.t_pos) :: t.t_meta
+		| TAbstractDecl a -> a.a_meta <- (meta, [], a.a_pos) :: a.a_meta
+	in
+
+	List.iter filter types

+ 162 - 0
src/generators/gencommon/switchToIf.ml

@@ -0,0 +1,162 @@
+(*
+	The Haxe Compiler
+	Copyright (C) 2005-2017  Haxe Foundation
+
+	This program is free software; you can redistribute it and/or
+	modify it under the terms of the GNU General Public License
+	as published by the Free Software Foundation; either version 2
+	of the License, or (at your option) any later version.
+
+	This program is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+
+	You should have received a copy of the GNU General Public License
+	along with this program; if not, write to the Free Software
+	Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+*)
+open Common
+open Type
+open Codegen
+open Gencommon
+
+(* ******************************************* *)
+(* SwitchToIf *)
+(* ******************************************* *)
+(*
+	A syntax filter which changes switch expressions to if() else if() else if() ...
+
+	Also it handles switches on native enums (which are not converted to classes) by
+	rewriting the switch expression to what's supported directly by the targets.
+*)
+let name = "switch_to_if"
+let priority = solve_deps name []
+
+let rec simplify_expr e =
+	match e.eexpr with
+	| TParenthesis e | TMeta (_, e) -> simplify_expr e
+	| _ -> e
+
+let configure gen (should_convert:texpr->bool) =
+	let basic = gen.gcon.basic in
+	let rec run e =
+		match e.eexpr with
+		| TSwitch (cond, cases, default) when should_convert e ->
+			let cond_etype, should_cache =
+				match gen.gfollow#run_f cond.etype with
+				| TAbstract ({ a_path = [], "Null" }, [t]) ->
+					let rec take_off_nullable t =
+						match gen.gfollow#run_f t with
+						| TAbstract ({ a_path = [], "Null" }, [t]) -> take_off_nullable t
+						| _ -> t
+					in
+					take_off_nullable t, true
+				| _ ->
+					cond.etype, false
+			in
+
+			if should_cache && not (should_convert { e with eexpr = TSwitch ({ cond with etype = cond_etype }, cases, default) }) then begin
+				{ e with eexpr = TSwitch (mk_cast cond_etype (run cond), List.map (fun (cs,e) -> (List.map run cs, run e)) cases, Option.map run default) }
+			end else begin
+				let local, fst_block =
+					match cond.eexpr, should_cache with
+					| TLocal _, false ->
+						cond, []
+					| _ ->
+						let var = mk_temp "switch" cond_etype in
+						let cond = run cond in
+						let cond = if should_cache then mk_cast cond_etype cond else cond in
+						mk_local var cond.epos, [ mk (TVar (var,Some cond)) basic.tvoid cond.epos ]
+				in
+
+				let mk_eq cond =
+					mk (TBinop (Ast.OpEq, local, cond)) basic.tbool cond.epos
+				in
+
+				let rec mk_many_cond conds =
+					match conds with
+					| cond :: [] ->
+						mk_eq cond
+					| cond :: tl ->
+						mk (TBinop (Ast.OpBoolOr, mk_eq (run cond), mk_many_cond tl)) basic.tbool cond.epos
+					| [] ->
+						assert false
+				in
+
+				let mk_many_cond conds =
+					let ret = mk_many_cond conds in
+					(*
+						this might be considered a hack. But since we're on a syntax filter and
+						the condition is guaranteed to not have run twice, we can really run the
+						expr filters again for it (to change e.g. OpEq accordingly)
+					*)
+					gen.gexpr_filters#run ret
+				in
+
+				let rec loop cases =
+					match cases with
+					| (conds, e) :: [] ->
+						mk (TIf (mk_many_cond conds, run e, Option.map run default)) e.etype e.epos
+					| (conds, e) :: tl ->
+						mk (TIf (mk_many_cond conds, run e, Some (loop tl))) e.etype e.epos
+					| [] ->
+						match default with
+						| None ->
+							raise Exit
+						| Some d ->
+							run d
+				in
+
+				try
+					{ e with eexpr = TBlock (fst_block @ [loop cases]) }
+				with Exit ->
+					{ e with eexpr = TBlock [] }
+			end
+
+		(*
+			Convert a switch on a non-class enum (e.g. native enums) to the native switch,
+			effectively chancing `switch enumIndex(e) { case 1: ...; case 2: ...; }` to
+			`switch e { case MyEnum.A: ...; case MyEnum.B: ...; }`, which is supported natively
+			by some target languages like Java and C#.
+		*)
+		| TSwitch (cond, cases, default) ->
+			begin
+				try
+					match (simplify_expr cond).eexpr with
+					| TEnumIndex enum
+					| TCall  ({ eexpr = TField (_, FStatic ({ cl_path = [],"Type" }, { cf_name = "enumIndex" })) }, [enum]) ->
+						let real_enum =
+							match enum.etype with
+							| TEnum (e, _) -> e
+							| _ -> raise Not_found
+						in
+						if Meta.has Meta.Class real_enum.e_meta then
+							raise Not_found;
+
+						let fields = Hashtbl.create (List.length real_enum.e_names) in
+						PMap.iter (fun _ ef -> Hashtbl.add fields ef.ef_index ef) real_enum.e_constrs;
+
+						let enum_expr = ExprBuilder.make_typeexpr (TEnumDecl real_enum) e.epos in
+						let cases = List.map (fun (patterns, body) ->
+							let patterns = List.map (fun e ->
+								match e.eexpr with
+								| TConst (TInt i) ->
+									let ef = Hashtbl.find fields (Int32.to_int i) in
+									{ e with eexpr = TField (enum_expr, FEnum (real_enum, ef)); etype = TEnum (real_enum, List.map (fun _ -> t_dynamic) real_enum.e_params) }
+								| _ ->
+									raise Not_found
+							) patterns in
+							let body = run body in
+							patterns, body
+						) cases in
+						{ e with eexpr = TSwitch (enum, cases, Option.map run default) }
+					| _ ->
+						raise Not_found
+				with Not_found ->
+					Type.map_expr run e
+			end
+		| _ ->
+			Type.map_expr run e
+	in
+	gen.gsyntax_filters#add name (PCustom priority) run

+ 104 - 0
src/generators/gencommon/tArrayTransform.ml

@@ -0,0 +1,104 @@
+(*
+	The Haxe Compiler
+	Copyright (C) 2005-2017  Haxe Foundation
+
+	This program is free software; you can redistribute it and/or
+	modify it under the terms of the GNU General Public License
+	as published by the Free Software Foundation; either version 2
+	of the License, or (at your option) any later version.
+
+	This program is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+
+	You should have received a copy of the GNU General Public License
+	along with this program; if not, write to the Free Software
+	Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+*)
+open Common
+open Ast
+open Type
+open Gencommon
+
+(* ******************************************* *)
+(* Dynamic TArray Handling *)
+(* ******************************************* *)
+(*
+	In some languages you cannot overload the [] operator,
+	so we need to decide what is kept as TArray and what gets mapped.
+
+	depends on:
+		(syntax) must run before expression/statment normalization because it may generate complex expressions
+		(ok) must run before binop transformations because it may generate some untreated binop ops
+		(ok) must run before dynamic field access is transformed into reflection
+*)
+let name = "dyn_tarray"
+let priority = solve_deps name [DBefore DynamicOperators.priority; DBefore DynamicFieldAccess.priority]
+
+(* should change signature: tarray expr -> binop operation -> should change? *)
+let configure gen (should_change:texpr->Ast.binop option->bool) (get_fun:string) (set_fun:string) =
+	let basic = gen.gcon.basic in
+	let mk_get e e1 e2 =
+		let efield = mk_field_access gen e1 get_fun e.epos in
+		{ e with eexpr = TCall(efield, [e2]) }
+	in
+	let mk_set e e1 e2 evalue =
+		let efield = mk_field_access gen e1 set_fun e.epos in
+		{ e with eexpr = TCall(efield, [e2; evalue]) }
+	in
+	let rec run e =
+		match e.eexpr with
+			| TArray(e1, e2) ->
+				(* e1 should always be a var; no need to map there *)
+				if should_change e None then mk_get e (run e1) (run e2) else Type.map_expr run e
+			| TBinop (Ast.OpAssign, ({ eexpr = TArray(e1a,e2a) } as earray), evalue) when should_change earray (Some Ast.OpAssign) ->
+				mk_set e (run e1a) (run e2a) (run evalue)
+			| TBinop (Ast.OpAssignOp op,({ eexpr = TArray(e1a,e2a) } as earray) , evalue) when should_change earray (Some (Ast.OpAssignOp op)) ->
+				(* cache all arguments in vars so they don't get executed twice *)
+				(* let ensure_local gen block name e = *)
+				let block = ref [] in
+
+				let arr_local = ensure_local gen.gcon block "array" (run e1a) in
+				let idx_local = ensure_local gen.gcon block "index" (run e2a) in
+				block := (mk_set e arr_local idx_local ( { e with eexpr=TBinop(op, mk_get earray arr_local idx_local, run evalue) } )) :: !block;
+
+				{ e with eexpr = TBlock (List.rev !block) }
+			| TUnop(op, flag, ({ eexpr = TArray(e1a, e2a) } as earray)) ->
+				if should_change earray None && match op with | Not | Neg -> false | _ -> true then begin
+
+					let block = ref [] in
+
+					let actual_t = match op with
+						| Ast.Increment | Ast.Decrement -> (match follow earray.etype with
+							| TInst _ | TAbstract _ | TEnum _ -> earray.etype
+							| _ -> basic.tfloat)
+						| Ast.Not -> basic.tbool
+						| _ -> basic.tint
+					in
+
+					let val_v = mk_temp "arrVal" actual_t in
+					let ret_v = mk_temp "arrRet" actual_t in
+
+					let arr_local = ensure_local gen.gcon block "arr" (run e1a) in
+					let idx_local = ensure_local gen.gcon block "arrIndex" (run e2a) in
+
+					let val_local = { earray with eexpr = TLocal(val_v) } in
+					let ret_local = { earray with eexpr = TLocal(ret_v) } in
+					(* var idx = 1; var val = x._get(idx); var ret = val++; x._set(idx, val); ret; *)
+					block := { eexpr = TVar(val_v, Some(mk_get earray arr_local idx_local)); (* var val = x._get(idx) *)
+											etype = gen.gcon.basic.tvoid;
+											epos = e2a.epos
+										} :: !block;
+					block := { eexpr = TVar(ret_v, Some { e with eexpr = TUnop(op, flag, val_local) }); (* var ret = val++ *)
+											etype = gen.gcon.basic.tvoid;
+											epos = e2a.epos
+										} :: !block;
+					block := (mk_set e arr_local idx_local val_local) (*x._set(idx,val)*) :: !block;
+					block := ret_local :: !block;
+					{ e with eexpr = TBlock (List.rev !block) }
+				end else
+					Type.map_expr run e
+			| _ -> Type.map_expr run e
+	in
+	gen.gexpr_filters#add "dyn_tarray" (PCustom priority) run

+ 60 - 0
src/generators/gencommon/unnecessaryCastsRemoval.ml

@@ -0,0 +1,60 @@
+(*
+	The Haxe Compiler
+	Copyright (C) 2005-2017  Haxe Foundation
+
+	This program is free software; you can redistribute it and/or
+	modify it under the terms of the GNU General Public License
+	as published by the Free Software Foundation; either version 2
+	of the License, or (at your option) any later version.
+
+	This program is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+
+	You should have received a copy of the GNU General Public License
+	along with this program; if not, write to the Free Software
+	Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+*)
+open Gencommon
+open Type
+
+(*
+	This module will take care of simplifying unnecessary casts, specially those made by the compiler
+	when inlining. Right now, it will only take care of casts used as a statement, which are always useless;
+
+	TODO: Take care of more cases, e.g. when the to and from types are the same
+
+	dependencies:
+		This must run after CastDetection, but before ExpressionUnwrap
+*)
+let rec take_off_cast run e =
+	match e.eexpr with
+	| TCast (c, _) -> take_off_cast run c
+	| _ -> run e
+
+let rec traverse e =
+	match e.eexpr with
+	| TBlock bl ->
+		let bl = List.map (take_off_cast traverse) bl in
+		{ e with eexpr = TBlock bl }
+	| TTry (block, catches) ->
+		{ e with eexpr = TTry(traverse (mk_block block), List.map (fun (v,block) -> (v, traverse (mk_block block))) catches) }
+	| TSwitch (cond,el_e_l, default) ->
+		{ e with eexpr = TSwitch(cond, List.map (fun (el,e) -> (el, traverse (mk_block e))) el_e_l, Option.map (fun e -> traverse (mk_block e)) default) }
+	| TWhile (cond,block,flag) ->
+		{e with eexpr = TWhile(cond,traverse (mk_block block), flag) }
+	| TIf (cond, eif, eelse) ->
+		{ e with eexpr = TIf(cond, traverse (mk_block eif), Option.map (fun e -> traverse (mk_block e)) eelse) }
+	| TFor (v,it,block) ->
+		{ e with eexpr = TFor(v,it, traverse (mk_block block)) }
+	| TFunction (tfunc) ->
+		{ e with eexpr = TFunction({ tfunc with tf_expr = traverse (mk_block tfunc.tf_expr) }) }
+	| _ ->
+		e (* if expression doesn't have a block, we will exit *)
+
+let name = "casts_removal"
+let priority = solve_deps name [DAfter CastDetect.priority; DBefore ExpressionUnwrap.priority]
+
+let configure gen =
+	gen.gsyntax_filters#add name (PCustom priority) traverse

+ 212 - 0
src/generators/gencommon/unreachableCodeEliminationSynf.ml

@@ -0,0 +1,212 @@
+(*
+	The Haxe Compiler
+	Copyright (C) 2005-2017  Haxe Foundation
+
+	This program is free software; you can redistribute it and/or
+	modify it under the terms of the GNU General Public License
+	as published by the Free Software Foundation; either version 2
+	of the License, or (at your option) any later version.
+
+	This program is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+
+	You should have received a copy of the GNU General Public License
+	along with this program; if not, write to the Free Software
+	Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+*)
+open Common
+open Ast
+open Type
+open Gencommon
+
+(*
+	In some source code platforms, the code won't compile if there is Unreachable code, so this filter will take off any unreachable code.
+		If the parameter "handle_switch_break" is set to true, it will already add a "break" statement on switch cases when suitable;
+			in order to not confuse with while break, it will be a special expression __sbreak__
+		If the parameter "handle_not_final_returns" is set to true, it will also add final returns when functions are detected to be lacking of them.
+			(Will respect __fallback__ expressions)
+		If the parameter "java_mode" is set to true, some additional checks following the java unreachable specs
+			(http://docs.oracle.com/javase/specs/jls/se7/html/jls-14.html#jls-14.21) will be added
+
+	dependencies:
+		This must run before SwitchBreakSynf (see SwitchBreakSynf dependecy value)
+		This must be the LAST syntax filter to run. It expects ExpressionUnwrap to have run correctly, since this will only work for source-code based targets
+*)
+type uexpr_kind =
+	| Normal
+	| BreaksLoop
+	| BreaksFunction
+
+let aggregate_kind e1 e2 =
+	match e1, e2 with
+		| Normal, _
+		| _, Normal -> Normal
+		| BreaksLoop, _
+		| _, BreaksLoop -> BreaksLoop
+		| BreaksFunction, BreaksFunction -> BreaksFunction
+
+let aggregate_constant op c1 c2=
+	match op, c1, c2 with
+		| OpEq, Some v1, Some v2 -> Some (TBool (v1 = v2))
+		| OpNotEq, Some v1, Some v2 -> Some (TBool (v1 <> v2))
+		| OpBoolOr, Some (TBool v1) , Some (TBool v2) -> Some (TBool (v1 || v2))
+		| OpBoolAnd, Some (TBool v1) , Some (TBool v2) -> Some (TBool (v1 && v2))
+		| OpAssign, _, Some v2 -> Some v2
+		| _ -> None
+
+let rec get_constant_expr e =
+	match e.eexpr with
+		| TConst (v) -> Some v
+		| TBinop(op, v1, v2) -> aggregate_constant op (get_constant_expr v1) (get_constant_expr v2)
+		| TParenthesis(e) | TMeta(_,e) -> get_constant_expr e
+		| _ -> None
+
+let init com java_mode =
+	let should_warn = false in
+
+	let do_warn =
+		if should_warn then com.warning "Unreachable code" else (fun pos -> ())
+	in
+
+	let return_loop expr kind =
+		match kind with
+			| Normal | BreaksLoop -> expr, Normal
+			| _ -> expr, kind
+	in
+
+	let mk_sbreak = mk (TIdent "__sbreak__") t_dynamic in
+
+	let rec has_fallback expr = match expr.eexpr with
+		| TBlock(bl) -> (match List.rev bl with
+			| { eexpr = TIdent "__fallback__" } :: _ -> true
+			| ({ eexpr = TBlock(_) } as bl) :: _ -> has_fallback bl
+			| _ -> false)
+		| TIdent "__fallback__" -> true
+		| _ -> false
+	in
+
+	let handle_case = fun (expr,kind) ->
+		match kind with
+		| Normal when has_fallback expr -> expr
+		| Normal -> Type.concat expr (mk_sbreak expr.epos)
+		| BreaksLoop | BreaksFunction -> expr
+	in
+
+	let has_break = ref false in
+
+	let rec process_expr expr =
+		match expr.eexpr with
+			| TMeta (m,expr) ->
+				let expr,kind = process_expr expr in
+				{ expr with eexpr = TMeta (m, expr) }, kind
+			| TReturn _ | TThrow _ -> expr, BreaksFunction
+			| TContinue -> expr, BreaksLoop
+			| TBreak -> has_break := true; expr, BreaksLoop
+			| TCall( { eexpr = TIdent "__goto__" }, _ ) -> expr, BreaksLoop
+
+			| TBlock bl ->
+				let new_block = ref [] in
+				let is_unreachable = ref false in
+				let ret_kind = ref Normal in
+
+				List.iter (fun e ->
+					if !is_unreachable then
+						do_warn e.epos
+					else begin
+						let changed_e, kind = process_expr e in
+						new_block := changed_e :: !new_block;
+						match kind with
+							| BreaksLoop | BreaksFunction ->
+								ret_kind := kind;
+								is_unreachable := true
+							| _ -> ()
+					end
+				) bl;
+
+				{ expr with eexpr = TBlock(List.rev !new_block) }, !ret_kind
+			| TFunction tf ->
+				let changed, kind = process_expr tf.tf_expr in
+				let changed = if not (ExtType.is_void tf.tf_type) && kind <> BreaksFunction then
+					Type.concat changed (Codegen.mk_return (null tf.tf_type expr.epos))
+				else
+					changed
+				in
+
+				{ expr with eexpr = TFunction({ tf with tf_expr = changed }) }, Normal
+			| TFor(var, cond, block) ->
+				let last_has_break = !has_break in
+				has_break := false;
+
+				let changed_block, _ = process_expr block in
+				has_break := last_has_break;
+				let expr = { expr with eexpr = TFor(var, cond, changed_block) } in
+				return_loop expr Normal
+			| TIf(cond, eif, None) ->
+				if java_mode then
+					match get_constant_expr cond with
+						| Some (TBool true) ->
+							process_expr eif
+						| _ ->
+							{ expr with eexpr = TIf(cond, fst (process_expr eif), None) }, Normal
+				else
+					{ expr with eexpr = TIf(cond, fst (process_expr eif), None) }, Normal
+			| TIf(cond, eif, Some eelse) ->
+				let eif, eif_k = process_expr eif in
+				let eelse, eelse_k = process_expr eelse in
+				let k = aggregate_kind eif_k eelse_k in
+				{ expr with eexpr = TIf(cond, eif, Some eelse) }, k
+			| TWhile(cond, block, flag) ->
+				let last_has_break = !has_break in
+				has_break := false;
+
+				let block, k = process_expr block in
+				if java_mode then
+					match get_constant_expr cond, flag, !has_break with
+						| Some (TBool true), _, false ->
+							has_break := last_has_break;
+							{ expr with eexpr = TWhile(cond, block, flag) }, BreaksFunction
+						| Some (TBool false), NormalWhile, _ ->
+							has_break := last_has_break;
+							do_warn expr.epos;
+							null expr.etype expr.epos, Normal
+						| _ ->
+							has_break := last_has_break;
+							return_loop { expr with eexpr = TWhile(cond,block,flag) } Normal
+				else begin
+					has_break := last_has_break;
+					return_loop { expr with eexpr = TWhile(cond,block,flag) } Normal
+				end
+			| TSwitch(cond, el_e_l, None) ->
+				{ expr with eexpr = TSwitch(cond, List.map (fun (el, e) -> (el, handle_case (process_expr e))) el_e_l, None) }, Normal
+			| TSwitch(cond, el_e_l, Some def) ->
+				let def, k = process_expr def in
+				let def = handle_case (def, k) in
+				let k = ref k in
+				let ret = { expr with eexpr = TSwitch(cond, List.map (fun (el, e) ->
+					let e, ek = process_expr e in
+					k := aggregate_kind !k ek;
+					(el, handle_case (e, ek))
+				) el_e_l, Some def) } in
+				ret, !k
+			| TTry (e, catches) ->
+				let e, k = process_expr e in
+				let k = ref k in
+				let ret = { expr with eexpr = TTry(e, List.map (fun (v, e) ->
+					let e, ek = process_expr e in
+					k := aggregate_kind !k ek;
+					(v, e)
+				) catches) } in
+				ret, !k
+			| _ -> expr, Normal
+	in
+
+	let run e = fst (process_expr e) in
+	run
+
+let priority = min_dep -. 100.0
+
+let configure gen java_mode =
+	let run = init gen.gcon java_mode in
+	gen.gsyntax_filters#add "unreachable_synf" (PCustom priority) run

Tiedoston diff-näkymää rajattu, sillä se on liian suuri
+ 242 - 145
src/generators/gencpp.ml


+ 2430 - 3717
src/generators/gencs.ml

@@ -17,8 +17,8 @@
 	Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  *)
 
+open ReflectionCFs
 open Globals
-open Gencommon.ReflectionCFs
 open Ast
 open Common
 open Type
@@ -29,11 +29,6 @@ open Printf
 open Option
 open ExtString
 
-let netname_to_hx name =
-	let len = String.length name in
-	let chr = String.get name 0 in
-	String.make 1 (Char.uppercase chr) ^ (String.sub name 1 (len-1))
-
 let rec is_cs_basic_type t =
 	match follow t with
 		| TInst( { cl_path = (["haxe"], "Int32") }, [] )
@@ -54,52 +49,8 @@ let rec is_cs_basic_type t =
 		| TInst(cl, _) when Meta.has Meta.Struct cl.cl_meta -> true
 		| _ -> false
 
-(* see http://msdn.microsoft.com/en-us/library/2sk3x8a7(v=vs.71).aspx *)
-let cs_binops =
-	[Ast.OpAdd, "op_Addition";
-	Ast.OpSub, "op_Subtraction";
-	Ast.OpMult, "op_Multiply";
-	Ast.OpDiv, "op_Division";
-	Ast.OpMod, "op_Modulus";
-	Ast.OpXor, "op_ExclusiveOr";
-	Ast.OpOr, "op_BitwiseOr";
-	Ast.OpAnd, "op_BitwiseAnd";
-	Ast.OpBoolAnd, "op_LogicalAnd";
-	Ast.OpBoolOr, "op_LogicalOr";
-	Ast.OpAssign, "op_Assign";
-	Ast.OpShl, "op_LeftShift";
-	Ast.OpShr, "op_RightShift";
-	Ast.OpShr, "op_SignedRightShift";
-	Ast.OpUShr, "op_UnsignedRightShift";
-	Ast.OpEq, "op_Equality";
-	Ast.OpGt, "op_GreaterThan";
-	Ast.OpLt, "op_LessThan";
-	Ast.OpNotEq, "op_Inequality";
-	Ast.OpGte, "op_GreaterThanOrEqual";
-	Ast.OpLte, "op_LessThanOrEqual";
-	Ast.OpAssignOp Ast.OpMult, "op_MultiplicationAssignment";
-	Ast.OpAssignOp Ast.OpSub, "op_SubtractionAssignment";
-	Ast.OpAssignOp Ast.OpXor, "op_ExclusiveOrAssignment";
-	Ast.OpAssignOp Ast.OpShl, "op_LeftShiftAssignment";
-	Ast.OpAssignOp Ast.OpMod, "op_ModulusAssignment";
-	Ast.OpAssignOp Ast.OpAdd, "op_AdditionAssignment";
-	Ast.OpAssignOp Ast.OpAnd, "op_BitwiseAndAssignment";
-	Ast.OpAssignOp Ast.OpOr, "op_BitwiseOrAssignment";
-	(* op_Comma *)
-	Ast.OpAssignOp Ast.OpDiv, "op_DivisionAssignment";]
-
-let cs_unops =
-	[Ast.Decrement, "op_Decrement";
-	Ast.Increment, "op_Increment";
-	Ast.Neg, "op_UnaryNegation";
-	Ast.Not, "op_LogicalNot";
-	Ast.NegBits, "op_OnesComplement"]
-
-let binops_names = List.fold_left (fun acc (op,n) -> PMap.add n op acc) PMap.empty cs_binops
-let unops_names = List.fold_left (fun acc (op,n) -> PMap.add n op acc) PMap.empty cs_unops
-
-let get_item = "get_Item"
-let set_item = "set_Item"
+let binops_names = List.fold_left (fun acc (op,n) -> PMap.add n op acc) PMap.empty Dotnet.cs_binops
+let unops_names = List.fold_left (fun acc (op,n) -> PMap.add n op acc) PMap.empty Dotnet.cs_unops
 
 let is_tparam t =
 	match follow t with
@@ -143,20 +94,20 @@ let is_pointer gen t =
 let rec is_null t =
 	match t with
 		| TInst( { cl_path = (["haxe"; "lang"], "Null") }, _ )
-		| TType( { t_path = ([], "Null") }, _ ) -> true
+		| TAbstract( { a_path = ([], "Null") }, _ ) -> true
 		| TType( t, tl ) -> is_null (apply_params t.t_params tl t.t_type)
 		| TMono r ->
 			(match !r with
 			| Some t -> is_null t
 			| _ -> false)
 		| TLazy f ->
-			is_null (!f())
+			is_null (lazy_type f)
 		| _ -> false
 
 let rec get_ptr e = match e.eexpr with
 	| TParenthesis e | TMeta(_,e)
 	| TCast(e,_) -> get_ptr e
-	| TCall( { eexpr = TLocal({ v_name = "__ptr__" }) }, [ e ] ) ->
+	| TCall( { eexpr = TIdent "__ptr__" }, [ e ] ) ->
 		Some e
 	| _ -> None
 
@@ -173,11 +124,6 @@ let parse_explicit_iface =
 		get_iface split []
 	in parse_explicit_iface
 
-let is_string t =
-	match follow t with
-		| TInst( { cl_path = ([], "String") }, [] ) -> true
-		| _ -> false
-
 let rec change_md = function
 	| TAbstractDecl(a) when Meta.has Meta.Delegate a.a_meta && not (Meta.has Meta.CoreType a.a_meta) ->
 		change_md (t_to_md a.a_this)
@@ -206,7 +152,7 @@ let in_runtime_class gen =
 module CSharpSpecificESynf =
 struct
 	let name = "csharp_specific_e"
-	let priority = solve_deps name [DBefore ExpressionUnwrap.priority; DBefore ClassInstance.priority; DAfter TryCatchWrapper.priority]
+	let priority = solve_deps name [DBefore ExpressionUnwrap.priority; DBefore ClassInstance.priority]
 
 	let get_cl_from_t t =
 		match follow t with
@@ -222,8 +168,6 @@ struct
 		let basic = gen.gcon.basic in
 		let uint = match get_type gen ([], "UInt") with | TTypeDecl t -> TType(t, []) | TAbstractDecl a -> TAbstract(a, []) | _ -> assert false in
 
-		let is_var = alloc_var "__is__" t_dynamic in
-
 		let rec run e =
 			match e.eexpr with
 				(* Std.is() *)
@@ -238,7 +182,7 @@ struct
 					) ->
 					let md = change_md md in
 					let mk_is obj md =
-						{ e with eexpr = TCall( { eexpr = TLocal is_var; etype = t_dynamic; epos = e.epos }, [
+						{ e with eexpr = TCall( { eexpr = TIdent "__is__"; etype = t_dynamic; epos = e.epos }, [
 							obj;
 							{ eexpr = TTypeExpr md; etype = t_dynamic (* this is after all a syntax filter *); epos = e.epos }
 						] ) }
@@ -257,7 +201,7 @@ struct
 						match obj.eexpr with
 							| TLocal(v) -> f obj
 							| _ ->
-								let var = mk_temp gen "is" obj.etype in
+								let var = mk_temp "is" obj.etype in
 								let added = { obj with eexpr = TVar(var, Some(obj)); etype = basic.tvoid } in
 								let local = mk_local var obj.epos in
 								{
@@ -331,7 +275,7 @@ struct
 					in
 
 					let mk_local obj =
-						let var = mk_temp gen "opUshr" obj.etype in
+						let var = mk_temp "opUshr" obj.etype in
 						let added = { obj with eexpr = TVar(var, Some(obj)); etype = basic.tvoid } in
 						let local = mk_local var obj.epos in
 						local, added
@@ -359,8 +303,7 @@ struct
 
 				| _ -> Type.map_expr run e
 		in
-		let map e = Some(run e) in
-		gen.gsyntax_filters#add ~name:name ~priority:(PCustom priority) map
+		gen.gsyntax_filters#add name (PCustom priority) run
 end;;
 
 (* ******************************************* *)
@@ -399,7 +342,6 @@ struct
 			| _ -> assert false
 		in
 		let string_ext = get_cl ( get_type gen (["haxe";"lang"], "StringExt")) in
-		let clstring = match basic.tstring with | TInst(cl,_) -> cl | _ -> assert false in
 		let ti64 = match ( get_type gen (["cs"], "Int64") ) with | TTypeDecl t -> TType(t,[]) | TAbstractDecl a -> TAbstract(a,[]) | _ -> assert false in
 		let boxed_ptr =
 			if Common.defined gen.gcon Define.Unsafe then
@@ -417,7 +359,6 @@ struct
 
 		let is_cl t = match gen.greal_type t with | TInst ( { cl_path = (["System"], "Type") }, [] ) -> true | _ -> false in
 
-		let as_var = alloc_var "__as__" t_dynamic in
 		let fast_cast = Common.defined gen.gcon Define.FastCast in
 
 		let rec run e =
@@ -514,24 +455,14 @@ struct
 					if is_cs_basic_type (gen.greal_type e.etype) || is_tparam (gen.greal_type e.etype) then
 						{ e with eexpr = TCast(run expr, Some(TClassDecl null_class)) }
 					else
-						{ e with eexpr = TCall(mk_local as_var e.epos, [run expr]) }
+						{ e with eexpr = TCall(mk (TIdent "__as__") t_dynamic e.epos, [run expr]) }
 
 				| TCast(expr, _) when (is_string e.etype) && (not (is_string expr.etype)) && not (in_runtime_class gen) ->
 					{ e with eexpr = TCall( mk_static_field_access_infer runtime_cl "toString" expr.epos [], [run expr] ) }
-				| TBinop( (Ast.OpNotEq as op), e1, e2)
-				| TBinop( (Ast.OpEq as op), e1, e2) when is_string e1.etype || is_string e2.etype ->
-					let mk_ret e = match op with | Ast.OpNotEq -> { e with eexpr = TUnop(Ast.Not, Ast.Prefix, e) } | _ -> e in
-					mk_ret { e with
-						eexpr = TCall({
-							eexpr = TField(ExprBuilder.make_static_this clstring e.epos, FDynamic "Equals");
-							etype = TFun(["obj1",false,basic.tstring; "obj2",false,basic.tstring], basic.tbool);
-							epos = e1.epos
-						}, [ run e1; run e2 ])
-					}
 
 				| TCast(expr, _) when is_tparam e.etype && not (in_runtime_class gen) && not (Common.defined gen.gcon Define.EraseGenerics) ->
 					let static = mk_static_field_access_infer (runtime_cl) "genericCast" e.epos [e.etype] in
-					{ e with eexpr = TCall(static, [mk_local (alloc_var "$type_param" e.etype) expr.epos; run expr]); }
+					{ e with eexpr = TCall(static, [mk (TIdent "$type_param") e.etype expr.epos; run expr]); }
 
 				| TBinop( (Ast.OpNotEq as op), e1, e2)
 				| TBinop( (Ast.OpEq as op), e1, e2) when is_struct e1.etype || is_struct e2.etype ->
@@ -555,8 +486,7 @@ struct
 
 				| _ -> Type.map_expr run e
 		in
-		let map e = Some(run e) in
-		gen.gsyntax_filters#add ~name:name ~priority:(PCustom priority) map
+		gen.gsyntax_filters#add name (PCustom priority) run
 end;;
 
 let add_cast_handler gen =
@@ -587,10 +517,10 @@ let add_cast_handler gen =
 		let old_param = get_narr_param e.etype in
 		let new_param = get_narr_param to_t in
 
-		let new_v = mk_temp gen "new_arr" to_t in
-		let i = mk_temp gen "i" basic.tint in
+		let new_v = mk_temp "new_arr" to_t in
+		let i = mk_temp "i" basic.tint in
 		let old_len = mk_field_access gen e "Length" e.epos in
-		let obj_v = mk_temp gen "obj" t_dynamic in
+		let obj_v = mk_temp "obj" t_dynamic in
 		let check_null = {eexpr = TBinop(Ast.OpNotEq, e, null e.etype e.epos); etype = basic.tbool; epos = e.epos} in
 		let block = [
 			{
@@ -713,3828 +643,2611 @@ let rec get_fun_modifiers meta access modifiers =
 		| (Meta.Custom ("?prop_impl" | ":cs_event_impl"),[],_) :: meta -> get_fun_modifiers meta "private" modifiers
 		| _ :: meta -> get_fun_modifiers meta access modifiers
 
-(* this was the way I found to pass the generator context to be accessible across all functions here *)
-(* so 'configure' is almost 'top-level' and will have all functions needed to make this work *)
-let configure gen =
-	let native_arr_cl = get_cl ( get_type gen (["cs"], "NativeArray") ) in
-	gen.gclasses.nativearray <- (fun t -> TInst(native_arr_cl,[t]));
-	gen.gclasses.nativearray_type <- (function TInst(_,[t]) -> t | _ -> assert false);
-	gen.gclasses.nativearray_len <- (fun e p -> mk_field_access gen e "Length" p);
+let generate con =
+	(try
+		let gen = new_ctx con in
+		let basic = con.basic in
 
-	let basic = gen.gcon.basic in
+		if Common.defined_value con Define.Dce = "no" then begin
+			let m = { null_module with m_id = alloc_mid(); m_path = ["haxe";"lang"],"DceNo" } in
+			let cl = mk_class m (["haxe";"lang"],"DceNo") null_pos in
+			gen.gtypes_list <- (TClassDecl cl) :: gen.gtypes_list;
+			Hashtbl.add gen.gtypes cl.cl_path (TClassDecl cl)
+		end;
 
-	let erase_generics = Common.defined gen.gcon Define.EraseGenerics in
-	let fn_cl = get_cl (get_type gen (["haxe";"lang"],"Function")) in
-	let null_t = if erase_generics then null_class else (get_cl (get_type gen (["haxe";"lang"],"Null")) ) in
-	let runtime_cl = get_cl (get_type gen (["haxe";"lang"],"Runtime")) in
-	let no_root = Common.defined gen.gcon Define.NoRoot in
-	let change_id name = try
-			Hashtbl.find reserved name
-		with | Not_found ->
-			let ret = String.concat "." (String.nsplit name "#") in
-			List.hd (String.nsplit ret "`")
-	in
+		(* make the basic functions in C# *)
+		let type_cl = get_cl ( get_type gen (["System"], "Type")) in
+		let basic_fns =
+		[
+			mk_class_field "Equals" (TFun(["obj",false,t_dynamic], basic.tbool)) true null_pos (Method MethNormal) [];
+			mk_class_field "ToString" (TFun([], basic.tstring)) true null_pos (Method MethNormal) [];
+			mk_class_field "GetHashCode" (TFun([], basic.tint)) true null_pos (Method MethNormal) [];
+			mk_class_field "GetType" (TFun([], TInst(type_cl, []))) true null_pos (Method MethNormal) [];
+		] in
+		List.iter (fun cf -> gen.gbase_class_fields <- PMap.add cf.cf_name cf gen.gbase_class_fields) basic_fns;
+
+		let native_arr_cl = get_cl ( get_type gen (["cs"], "NativeArray") ) in
+		gen.gclasses.nativearray <- (fun t -> TInst(native_arr_cl,[t]));
+		gen.gclasses.nativearray_type <- (function TInst(_,[t]) -> t | _ -> assert false);
+		gen.gclasses.nativearray_len <- (fun e p -> mk_field_access gen e "Length" p);
+
+		let erase_generics = Common.defined gen.gcon Define.EraseGenerics in
+		let fn_cl = get_cl (get_type gen (["haxe";"lang"],"Function")) in
+		let null_t = if erase_generics then null_class else (get_cl (get_type gen (["haxe";"lang"],"Null")) ) in
+		let runtime_cl = get_cl (get_type gen (["haxe";"lang"],"Runtime")) in
+		let no_root = Common.defined gen.gcon Define.NoRoot in
+		let change_id name = try
+				Hashtbl.find reserved name
+			with | Not_found ->
+				let ret = String.concat "." (String.nsplit name "#") in
+				List.hd (String.nsplit ret "`")
+		in
 
-	let change_clname n = change_id n in
-
-	let change_ns_params_root md ns params =
-		let ns,params = List.fold_left (fun (ns,params) nspart -> try
-			let part, nparams = String.split nspart "`" in
-			let nparams = int_of_string nparams in
-			let rec loop i needed params =
-				if i = nparams then
-					(List.rev needed,params)
-				else
-					loop (i+1) (List.hd params :: needed) (List.tl params)
+		let change_clname n = change_id n in
+
+		let change_ns_params_root md ns params =
+			let ns,params = List.fold_left (fun (ns,params) nspart -> try
+				let part, nparams = String.split nspart "`" in
+				let nparams = int_of_string nparams in
+				let rec loop i needed params =
+					if i = nparams then
+						(List.rev needed,params)
+					else
+						loop (i+1) (List.hd params :: needed) (List.tl params)
+				in
+				let needed,params = loop 0 [] params in
+				let part = change_id part in
+				(part ^ "<" ^ (String.concat ", " needed) ^ ">")::ns, params
+			with _ -> (* Invalid_string / int_of_string *)
+				(change_id nspart)::ns, params
+			) ([],params) ns
 			in
-			let needed,params = loop 0 [] params in
-			let part = change_id part in
-			(part ^ "<" ^ (String.concat ", " needed) ^ ">")::ns, params
-		with _ -> (* Invalid_string / int_of_string *)
-			(change_id nspart)::ns, params
-		) ([],params) ns
+			List.rev ns,params
 		in
-		List.rev ns,params
-	in
 
-	let change_ns_params md params ns = if no_root then match ns with
-			| [] when is_hxgen md -> ["haxe";"root"], params
-			| [s] when (t_infos md).mt_private && is_hxgen md -> ["haxe";"root";s], params
-			| [] -> (match md with
-				| TClassDecl { cl_path = ([],"Std" | [],"Math") } -> ["haxe";"root"], params
-				| TClassDecl { cl_meta = m } when Meta.has Meta.Enum m -> ["haxe";"root"], params
-				| _ -> [], params)
-			| ns when params = [] -> List.map change_id ns, params
-			| ns ->
+		let change_ns_params md params ns = if no_root then (
+			let needs_root md = is_hxgen md || match md with
+				| TClassDecl cl when (Meta.has Meta.Enum cl.cl_meta) && (Meta.has Meta.CompilerGenerated cl.cl_meta) ->
+					(* this will match our compiler-generated enum constructor classes *)
+					true
+				| _ ->
+					false
+			in
+			match ns with
+				| [] when needs_root md -> ["haxe";"root"], params
+				| [s] when (t_infos md).mt_private && needs_root md -> ["haxe";"root";s], params
+				| [] -> (match md with
+					| TClassDecl { cl_path = ([],"Std" | [],"Math") } -> ["haxe";"root"], params
+					| TClassDecl { cl_meta = m } when Meta.has Meta.Enum m -> ["haxe";"root"], params
+					| _ -> [], params)
+				| ns when params = [] -> List.map change_id ns, params
+				| ns ->
+					change_ns_params_root md ns params)
+			else if params = [] then
+				List.map change_id ns, params
+			else
 				change_ns_params_root md ns params
-		else if params = [] then
-			List.map change_id ns, params
-		else
-			change_ns_params_root md ns params
-	in
+		in
 
-	let change_ns md ns =
-		let ns, _ = change_ns_params md [] ns in
-		ns
-	in
+		let change_ns md ns =
+			let ns, _ = change_ns_params md [] ns in
+			ns
+		in
 
-	let change_field = change_id in
-	let write_id w name = write w (change_id name) in
-	let write_field w name = write w (change_field name) in
+		let change_field = change_id in
+		let write_id w name = write w (change_id name) in
+		let write_field w name = write w (change_field name) in
 
-	let ptr =
-		if Common.defined gen.gcon Define.Unsafe then
-			get_abstract (get_type gen (["cs"],"Pointer"))
-		else
-			null_abstract
-	in
+		let ptr =
+			if Common.defined gen.gcon Define.Unsafe then
+				get_abstract (get_type gen (["cs"],"Pointer"))
+			else
+				null_abstract
+		in
 
-	let is_hxgeneric md =
-		TypeParams.RealTypeParams.is_hxgeneric md
-	in
+		let is_hxgeneric md =
+			RealTypeParams.is_hxgeneric md
+		in
 
-	let rec field_is_hxgeneric e = match e.eexpr with
-		| TParenthesis e | TMeta(_,e) -> field_is_hxgeneric e
-		| TField(_, (FStatic(cl,_) | FInstance(cl,_,_)) ) ->
-			(* print_endline ("is_hxgeneric " ^ s_type_path cl.cl_path ^ " : " ^ string_of_bool (is_hxgeneric (TClassDecl cl))); *)
-			is_hxgeneric (TClassDecl cl)
-		| _ -> true
-	in
+		let rec field_is_hxgeneric e = match e.eexpr with
+			| TParenthesis e | TMeta(_,e) -> field_is_hxgeneric e
+			| TField(_, (FStatic(cl,_) | FInstance(cl,_,_)) ) ->
+				(* print_endline ("is_hxgeneric " ^ s_type_path cl.cl_path ^ " : " ^ string_of_bool (is_hxgeneric (TClassDecl cl))); *)
+				is_hxgeneric (TClassDecl cl)
+			| _ -> true
+		in
 
-	gen.gfollow#add ~name:"follow_basic" (fun t -> match t with
-			| TAbstract ({ a_path = ([], "Bool") },[])
-			| TAbstract ({ a_path = ([], "Void") },[])
-			| TAbstract ({ a_path = ([],"Float") },[])
-			| TAbstract ({ a_path = ([],"Int") },[])
-			| TAbstract ({ a_path = [],"UInt" },[])
-			| TType ({ t_path = ["cs"], "Int64" },[])
-			| TAbstract ({ a_path = ["cs"], "Int64" },[])
-			| TType ({ t_path = ["cs"],"UInt64" },[])
-			| TAbstract ({ a_path = ["cs"],"UInt64" },[])
-			| TType ({ t_path = ["cs"],"UInt8" },[])
-			| TAbstract ({ a_path = ["cs"],"UInt8" },[])
-			| TType ({ t_path = ["cs"],"Int8" },[])
-			| TAbstract ({ a_path = ["cs"],"Int8" },[])
-			| TType ({ t_path = ["cs"],"Int16" },[])
-			| TAbstract ({ a_path = ["cs"],"Int16" },[])
-			| TType ({ t_path = ["cs"],"UInt16" },[])
-			| TAbstract ({ a_path = ["cs"],"UInt16" },[])
-			| TType ({ t_path = ["cs"],"Char16" },[])
-			| TAbstract ({ a_path = ["cs"],"Char16" },[])
-			| TType ({ t_path = ["cs"],"Ref" },_)
-			| TAbstract ({ a_path = ["cs"],"Ref" },_)
-			| TType ({ t_path = ["cs"],"Out" },_)
-			| TAbstract ({ a_path = ["cs"],"Out" },_)
-			| TType ({ t_path = [],"Single" },[])
-			| TAbstract ({ a_path = [],"Single" },[]) -> Some t
-			| TType (({ t_path = [],"Null" } as tdef),[t2]) ->
-					Some (TType(tdef,[follow (gen.gfollow#run_f t2)]))
-			| TAbstract({ a_path = ["cs"],"PointerAccess" },[t]) ->
-					Some (TAbstract(ptr,[t]))
-			| TAbstract (a, pl) when not (Meta.has Meta.CoreType a.a_meta) ->
-					Some (gen.gfollow#run_f ( Abstract.get_underlying_type a pl) )
-			| TAbstract( { a_path = ([], "EnumValue") }, _	)
-			| TInst( { cl_path = ([], "EnumValue") }, _  ) -> Some t_dynamic
-			| _ -> None);
-
-	let module_s_params md params =
-		let md = change_md md in
-		let path = (t_infos md).mt_path in
-		match path with
-			| ([], "String") -> "string", params
-			| ([], "Null") -> s_type_path (change_ns md ["haxe"; "lang"], change_clname "Null"), params
-			| (ns,clname) ->
-				let ns, params = change_ns_params md params ns in
-				s_type_path (ns, change_clname clname), params
-	in
+		gen.gfollow#add "follow_basic" PZero (fun t -> match t with
+				| TAbstract ({ a_path = ([], "Bool") },[])
+				| TAbstract ({ a_path = ([], "Void") },[])
+				| TAbstract ({ a_path = ([],"Float") },[])
+				| TAbstract ({ a_path = ([],"Int") },[])
+				| TAbstract ({ a_path = [],"UInt" },[])
+				| TType ({ t_path = ["cs"], "Int64" },[])
+				| TAbstract ({ a_path = ["cs"], "Int64" },[])
+				| TType ({ t_path = ["cs"],"UInt64" },[])
+				| TAbstract ({ a_path = ["cs"],"UInt64" },[])
+				| TType ({ t_path = ["cs"],"UInt8" },[])
+				| TAbstract ({ a_path = ["cs"],"UInt8" },[])
+				| TType ({ t_path = ["cs"],"Int8" },[])
+				| TAbstract ({ a_path = ["cs"],"Int8" },[])
+				| TType ({ t_path = ["cs"],"Int16" },[])
+				| TAbstract ({ a_path = ["cs"],"Int16" },[])
+				| TType ({ t_path = ["cs"],"UInt16" },[])
+				| TAbstract ({ a_path = ["cs"],"UInt16" },[])
+				| TType ({ t_path = ["cs"],"Char16" },[])
+				| TAbstract ({ a_path = ["cs"],"Char16" },[])
+				| TType ({ t_path = ["cs"],"Ref" },_)
+				| TAbstract ({ a_path = ["cs"],"Ref" },_)
+				| TType ({ t_path = ["cs"],"Out" },_)
+				| TAbstract ({ a_path = ["cs"],"Out" },_)
+				| TType ({ t_path = [],"Single" },[])
+				| TAbstract ({ a_path = [],"Single" },[]) -> Some t
+				| TAbstract (({ a_path = [],"Null" } as tab),[t2]) ->
+						Some (TAbstract(tab,[follow (gen.gfollow#run_f t2)]))
+				| TAbstract({ a_path = ["cs"],"PointerAccess" },[t]) ->
+						Some (TAbstract(ptr,[t]))
+				| TAbstract (a, pl) when not (Meta.has Meta.CoreType a.a_meta) ->
+						Some (gen.gfollow#run_f ( Abstract.get_underlying_type a pl) )
+				| TAbstract( { a_path = ([], "EnumValue") }, _	)
+				| TInst( { cl_path = ([], "EnumValue") }, _  ) -> Some t_dynamic
+				| _ -> None);
+
+		let module_s_params md params =
+			let md = change_md md in
+			let path = (t_infos md).mt_path in
+			match path with
+				| ([], "String") -> "string", params
+				| ([], "Null") -> s_type_path (change_ns md ["haxe"; "lang"], change_clname "Null"), params
+				| (ns,clname) ->
+					let ns, params = change_ns_params md params ns in
+					s_type_path (ns, change_clname clname), params
+		in
 
-	let module_s md =
-		fst (module_s_params md [])
-	in
+		let module_s md =
+			fst (module_s_params md [])
+		in
 
-	let ifaces = Hashtbl.create 1 in
+		let ifaces = Hashtbl.create 1 in
 
-	let ti64 = match ( get_type gen (["cs"], "Int64") ) with | TTypeDecl t -> TType(t,[]) | TAbstractDecl a -> TAbstract(a,[]) | _ -> assert false in
+		let ti64 = match ( get_type gen (["cs"], "Int64") ) with | TTypeDecl t -> TType(t,[]) | TAbstractDecl a -> TAbstract(a,[]) | _ -> assert false in
 
-	let ttype = get_cl ( get_type gen (["System"], "Type") ) in
+		let ttype = get_cl ( get_type gen (["System"], "Type") ) in
 
-	let has_tdyn tl =
-		List.exists (fun t -> match follow t with
-		| TDynamic _ | TMono _ -> true
-		| _ -> false
-	) tl
-	in
+		let has_tdyn tl =
+			List.exists (fun t -> match follow t with
+			| TDynamic _ | TMono _ -> true
+			| _ -> false
+		) tl
+		in
 
-	let rec real_type t =
-		let t = gen.gfollow#run_f t in
-		let ret = match t with
-			| TAbstract (a, pl) when not (Meta.has Meta.CoreType a.a_meta) ->
-				real_type (Abstract.get_underlying_type a pl)
-			| TAbstract ({ a_path = (["cs";"_Flags"], "EnumUnderlying") }, [t]) ->
-				real_type t
-			| TInst( { cl_path = (["cs";"system"], "String") }, [] ) ->
-				gen.gcon.basic.tstring;
-			| TInst( { cl_path = (["haxe"], "Int32") }, [] ) -> gen.gcon.basic.tint
-			| TInst( { cl_path = (["haxe"], "Int64") }, [] ) -> ti64
-			| TAbstract( { a_path = [],"Class" }, _ )
-			| TAbstract( { a_path = [],"Enum" }, _ )
-			| TAbstract( { a_path = ["haxe";"extern"],"Rest" }, _ )
-			| TInst( { cl_path = ([], "Class") }, _ )
-			| TInst( { cl_path = ([], "Enum") }, _ ) -> TInst(ttype,[])
-			| TInst( ({ cl_kind = KTypeParameter _ } as cl), _ ) when erase_generics && not (Meta.has Meta.NativeGeneric cl.cl_meta) ->
-				t_dynamic
-			| TInst({ cl_kind = KExpr _ }, _) -> t_dynamic
-			| TEnum(_, [])
-			| TInst(_, []) -> t
-			| TInst(cl, params) when
-				has_tdyn params &&
-				Hashtbl.mem ifaces cl.cl_path ->
-					TInst(Hashtbl.find ifaces cl.cl_path, [])
-			| TEnum(e, params) ->
-				TEnum(e, List.map (fun _ -> t_dynamic) params)
-			| TInst(cl, params) when Meta.has Meta.Enum cl.cl_meta ->
-				TInst(cl, List.map (fun _ -> t_dynamic) params)
-			| TInst(cl, params) -> TInst(cl, change_param_type (TClassDecl cl) params)
-			| TType({ t_path = ([], "Null") }, [t]) ->
+		let rec real_type t =
+			let t = gen.gfollow#run_f t in
+			let ret = match t with
+				| TAbstract({ a_path = ([], "Null") }, [t]) ->
+					(*
+						Null<> handling is a little tricky.
+						It will only change to haxe.lang.Null<> when the actual type is non-nullable or a type parameter
+						It works on cases such as Hash<T> returning Null<T> since cast_detect will invoke real_type at the original type,
+						Null<T>, which will then return the type haxe.lang.Null<>
+					*)
+					if erase_generics then
+						if is_cs_basic_type t then
+							t_dynamic
+						else
+							real_type t
+					else
+						(match real_type t with
+							| TInst( { cl_kind = KTypeParameter _ }, _ ) -> TInst(null_t, [t])
+							| t when is_cs_basic_type t -> TInst(null_t, [t])
+							| _ -> real_type t)
+				| TAbstract (a, pl) when not (Meta.has Meta.CoreType a.a_meta) ->
+					real_type (Abstract.get_underlying_type a pl)
+				| TAbstract ({ a_path = (["cs";"_Flags"], "EnumUnderlying") }, [t]) ->
+					real_type t
+				| TInst( { cl_path = (["cs";"system"], "String") }, [] ) ->
+					gen.gcon.basic.tstring;
+				| TInst( { cl_path = (["haxe"], "Int32") }, [] ) -> gen.gcon.basic.tint
+				| TInst( { cl_path = (["haxe"], "Int64") }, [] ) -> ti64
+				| TAbstract( { a_path = [],"Class" }, _ )
+				| TAbstract( { a_path = [],"Enum" }, _ )
+				| TAbstract( { a_path = ["haxe";"extern"],"Rest" }, _ )
+				| TInst( { cl_path = ([], "Class") }, _ )
+				| TInst( { cl_path = ([], "Enum") }, _ ) -> TInst(ttype,[])
+				| TInst( ({ cl_kind = KTypeParameter _ } as cl), _ ) when erase_generics && not (Meta.has Meta.NativeGeneric cl.cl_meta) ->
+					t_dynamic
+				| TInst({ cl_kind = KExpr _ }, _) -> t_dynamic
+				| TEnum(_, [])
+				| TInst(_, []) -> t
+				| TInst(cl, params) when
+					has_tdyn params &&
+					Hashtbl.mem ifaces cl.cl_path ->
+						TInst(Hashtbl.find ifaces cl.cl_path, [])
+				| TEnum(e, params) ->
+					TEnum(e, List.map (fun _ -> t_dynamic) params)
+				| TInst(cl, params) when Meta.has Meta.Enum cl.cl_meta ->
+					TInst(cl, List.map (fun _ -> t_dynamic) params)
+				| TInst(cl, params) -> TInst(cl, change_param_type (TClassDecl cl) params)
+				| TAbstract _
+				| TType _ -> t
+				| TAnon (anon) when (match !(anon.a_status) with | Statics _ | EnumStatics _ | AbstractStatics _ -> true | _ -> false) -> t
+				| TFun _ -> TInst(fn_cl,[])
+				| _ -> t_dynamic
+			in
+			ret
+		and
+
+		(*
+			On hxcs, the only type parameters allowed to be declared are the basic c# types.
+			That's made like this to avoid casting problems when type parameters in this case
+			add nothing to performance, since the memory layout is always the same.
+
+			To avoid confusion between Generic<Dynamic> (which has a different meaning in hxcs AST),
+			all those references are using dynamic_anon, which means Generic<{}>
+		*)
+		change_param_type md tl =
+			let types = match md with
+				| TClassDecl c -> c.cl_params
+				| TEnumDecl e -> []
+				| TAbstractDecl a -> a.a_params
+				| TTypeDecl t -> t.t_params
+			in
+			let is_hxgeneric = if types = [] then is_hxgen md else (RealTypeParams.is_hxgeneric md) in
+			let ret t =
+				let t_changed = real_type t in
+				match is_hxgeneric, t_changed with
+				| false, _ -> t
 				(*
-					Null<> handling is a little tricky.
-					It will only change to haxe.lang.Null<> when the actual type is non-nullable or a type parameter
-					It works on cases such as Hash<T> returning Null<T> since cast_detect will invoke real_type at the original type,
-					Null<T>, which will then return the type haxe.lang.Null<>
+					Because Null<> types need a special compiler treatment for many operations (e.g. boxing/unboxing),
+					Null<> type parameters will be transformed into Dynamic.
 				*)
-				if erase_generics then
-					if is_cs_basic_type t then
-						t_dynamic
-					else
-						real_type t
-				else
-					(match real_type t with
-						| TInst( { cl_kind = KTypeParameter _ }, _ ) -> TInst(null_t, [t])
-						| t when is_cs_basic_type t -> TInst(null_t, [t])
-						| _ -> real_type t)
-			| TAbstract _
-			| TType _ -> t
-			| TAnon (anon) when (match !(anon.a_status) with | Statics _ | EnumStatics _ | AbstractStatics _ -> true | _ -> false) -> t
-			| TFun _ -> TInst(fn_cl,[])
-			| _ -> t_dynamic
+				| true, TInst ( { cl_path = (["haxe";"lang"], "Null") }, _ ) -> dynamic_anon
+				| true, TInst ( { cl_kind = KTypeParameter _ }, _ ) -> t
+				| true, TInst _
+				| true, TEnum _
+				| true, TAbstract _ when is_cs_basic_type t_changed -> t
+				| true, TDynamic _ -> t
+				| true, x ->
+					dynamic_anon
+			in
+			if is_hxgeneric && (erase_generics || List.exists (fun t -> match follow t with | TDynamic _ -> true | _ -> false) tl) then
+				List.map (fun _ -> t_dynamic) tl
+			else
+				List.map ret tl
 		in
-		ret
-	and
-
-	(*
-		On hxcs, the only type parameters allowed to be declared are the basic c# types.
-		That's made like this to avoid casting problems when type parameters in this case
-		add nothing to performance, since the memory layout is always the same.
 
-		To avoid confusion between Generic<Dynamic> (which has a different meaning in hxcs AST),
-		all those references are using dynamic_anon, which means Generic<{}>
-	*)
-	change_param_type md tl =
-		let types = match md with
-			| TClassDecl c -> c.cl_params
-			| TEnumDecl e -> []
-			| TAbstractDecl a -> a.a_params
-			| TTypeDecl t -> t.t_params
-		in
-		let is_hxgeneric = if types = [] then is_hxgen md else (TypeParams.RealTypeParams.is_hxgeneric md) in
-		let ret t =
-			let t_changed = real_type t in
-			match is_hxgeneric, t_changed with
-			| false, _ -> t
-			(*
-				Because Null<> types need a special compiler treatment for many operations (e.g. boxing/unboxing),
-				Null<> type parameters will be transformed into Dynamic.
-			*)
-			| true, TInst ( { cl_path = (["haxe";"lang"], "Null") }, _ ) -> dynamic_anon
-			| true, TInst ( { cl_kind = KTypeParameter _ }, _ ) -> t
-			| true, TInst _
-			| true, TEnum _
-			| true, TAbstract _ when is_cs_basic_type t_changed -> t
-			| true, TDynamic _ -> t
-			| true, x ->
-				dynamic_anon
+		let is_dynamic t = match real_type t with
+			| TMono _ | TDynamic _
+			| TInst({ cl_kind = KTypeParameter _ }, _) -> true
+			| TAnon anon ->
+				(match !(anon.a_status) with
+					| EnumStatics _ | Statics _ -> false
+					| _ -> true
+				)
+			| _ -> false
 		in
-		if is_hxgeneric && (erase_generics || List.exists (fun t -> match follow t with | TDynamic _ -> true | _ -> false) tl) then
-			List.map (fun _ -> t_dynamic) tl
-		else
-			List.map ret tl
-	in
-
-	let is_dynamic t = match real_type t with
-		| TMono _ | TDynamic _
-		| TInst({ cl_kind = KTypeParameter _ }, _) -> true
-		| TAnon anon ->
-			(match !(anon.a_status) with
-				| EnumStatics _ | Statics _ -> false
-				| _ -> true
-			)
-		| _ -> false
-	in
 
-	let rec t_s t =
-		match real_type t with
-			(* basic types *)
-			| TAbstract ({ a_path = ([], "Bool") },[]) -> "bool"
-			| TAbstract ({ a_path = ([], "Void") },[]) -> "object"
-			| TAbstract ({ a_path = ([],"Float") },[]) -> "double"
-			| TAbstract ({ a_path = ([],"Int") },[]) -> "int"
-			| TAbstract ({ a_path = [],"UInt" },[]) -> "uint"
-			| TType ({ t_path = ["cs"], "Int64" },[])
-			| TAbstract ({ a_path = ["cs"], "Int64" },[]) -> "long"
-			| TType ({ t_path = ["cs"],"UInt64" },[])
-			| TAbstract ({ a_path = ["cs"],"UInt64" },[]) -> "ulong"
-			| TType ({ t_path = ["cs"],"UInt8" },[])
-			| TAbstract ({ a_path = ["cs"],"UInt8" },[]) -> "byte"
-			| TType ({ t_path = ["cs"],"Int8" },[])
-			| TAbstract ({ a_path = ["cs"],"Int8" },[]) -> "sbyte"
-			| TType ({ t_path = ["cs"],"Int16" },[])
-			| TAbstract ({ a_path = ["cs"],"Int16" },[]) -> "short"
-			| TType ({ t_path = ["cs"],"UInt16" },[])
-			| TAbstract ({ a_path = ["cs"],"UInt16" },[]) -> "ushort"
-			| TType ({ t_path = ["cs"],"Char16" },[])
-			| TAbstract ({ a_path = ["cs"],"Char16" },[]) -> "char"
-			| TType ({ t_path = [],"Single" },[])
-			| TAbstract ({ a_path = [],"Single" },[]) -> "float"
-			| TInst ({ cl_path = ["haxe"],"Int32" },[])
-			| TAbstract ({ a_path = ["haxe"],"Int32" },[]) -> "int"
-			| TInst ({ cl_path = ["haxe"],"Int64" },[])
-			| TAbstract ({ a_path = ["haxe"],"Int64" },[]) -> "long"
-			| TInst ({ cl_path = ([], "Dynamic") },_)
-			| TAbstract ({ a_path = ([], "Dynamic") },_) -> "object"
-			| TType ({ t_path = ["cs"],"Out" },[t])
-			| TAbstract ({ a_path = ["cs"],"Out" },[t])
-			| TType ({ t_path = ["cs"],"Ref" },[t])
-			| TAbstract ({ a_path = ["cs"],"Ref" },[t]) -> t_s t
-			| TInst({ cl_path = (["cs"], "NativeArray") }, [param]) ->
-				let rec check_t_s t =
-					match real_type t with
-						| TInst({ cl_path = (["cs"], "NativeArray") }, [param]) ->
-							(check_t_s param) ^ "[]"
-						| _ -> t_s (run_follow gen t)
-				in
-				(check_t_s param) ^ "[]"
-			| TInst({ cl_path = (["cs"], "Pointer") },[t])
-			| TAbstract({ a_path = (["cs"], "Pointer") },[t])->
-				let ret = t_s t in
-				(if ret = "object" then "void" else ret) ^ "*"
-			(* end of basic types *)
-			| TInst ({ cl_kind = KTypeParameter _; cl_path=p }, []) -> snd p
-			| TMono r -> (match !r with | None -> "object" | Some t -> t_s (run_follow gen t))
-			| TInst ({ cl_path = [], "String" }, []) -> "string"
-			| TEnum (e, params) -> ("global::" ^ (module_s (TEnumDecl e)))
-			| TInst (cl, _ :: _) when Meta.has Meta.Enum cl.cl_meta ->
-				"global::" ^ module_s (TClassDecl cl)
-			| TInst (({ cl_path = p } as cl), params) -> (path_param_s (TClassDecl cl) p params)
-			| TType (({ t_path = p } as t), params) -> (path_param_s (TTypeDecl t) p params)
-			| TAnon (anon) ->
-				(match !(anon.a_status) with
-					| Statics _ | EnumStatics _ -> "System.Type"
-					| _ -> "object")
-			| TDynamic _ -> "object"
-			| TAbstract(a,pl) when not (Meta.has Meta.CoreType a.a_meta) ->
-				t_s (Abstract.get_underlying_type a pl)
-			(* No Lazy type nor Function type made. That's because function types will be at this point be converted into other types *)
-			| _ -> if !strict_mode then begin trace ("[ !TypeError " ^ (Type.s_type (Type.print_context()) t) ^ " ]"); assert false end else "[ !TypeError " ^ (Type.s_type (Type.print_context()) t) ^ " ]"
-
-	and path_param_s md path params =
-			match params with
-				| [] -> "global::" ^ module_s md
-				| _ when erase_generics && is_hxgeneric md ->
-					"global::" ^ module_s md
-				| _ ->
-					let params = (List.map (fun t -> t_s t) (change_param_type md params)) in
-					let str,params = module_s_params md params in
-					if params = [] then
-						"global::" ^ str
-					else
-						sprintf "global::%s<%s>" str (String.concat ", " params)
-	in
+		let rec t_s t =
+			match real_type t with
+				(* basic types *)
+				| TAbstract ({ a_path = ([], "Bool") },[]) -> "bool"
+				| TAbstract ({ a_path = ([], "Void") },[]) -> "object"
+				| TAbstract ({ a_path = ([],"Float") },[]) -> "double"
+				| TAbstract ({ a_path = ([],"Int") },[]) -> "int"
+				| TAbstract ({ a_path = [],"UInt" },[]) -> "uint"
+				| TType ({ t_path = ["cs"], "Int64" },[])
+				| TAbstract ({ a_path = ["cs"], "Int64" },[]) -> "long"
+				| TType ({ t_path = ["cs"],"UInt64" },[])
+				| TAbstract ({ a_path = ["cs"],"UInt64" },[]) -> "ulong"
+				| TType ({ t_path = ["cs"],"UInt8" },[])
+				| TAbstract ({ a_path = ["cs"],"UInt8" },[]) -> "byte"
+				| TType ({ t_path = ["cs"],"Int8" },[])
+				| TAbstract ({ a_path = ["cs"],"Int8" },[]) -> "sbyte"
+				| TType ({ t_path = ["cs"],"Int16" },[])
+				| TAbstract ({ a_path = ["cs"],"Int16" },[]) -> "short"
+				| TType ({ t_path = ["cs"],"UInt16" },[])
+				| TAbstract ({ a_path = ["cs"],"UInt16" },[]) -> "ushort"
+				| TType ({ t_path = ["cs"],"Char16" },[])
+				| TAbstract ({ a_path = ["cs"],"Char16" },[]) -> "char"
+				| TType ({ t_path = [],"Single" },[])
+				| TAbstract ({ a_path = [],"Single" },[]) -> "float"
+				| TInst ({ cl_path = ["haxe"],"Int32" },[])
+				| TAbstract ({ a_path = ["haxe"],"Int32" },[]) -> "int"
+				| TInst ({ cl_path = ["haxe"],"Int64" },[])
+				| TAbstract ({ a_path = ["haxe"],"Int64" },[]) -> "long"
+				| TInst ({ cl_path = ([], "Dynamic") },_)
+				| TAbstract ({ a_path = ([], "Dynamic") },_) -> "object"
+				| TType ({ t_path = ["cs"],"Out" },[t])
+				| TAbstract ({ a_path = ["cs"],"Out" },[t])
+				| TType ({ t_path = ["cs"],"Ref" },[t])
+				| TAbstract ({ a_path = ["cs"],"Ref" },[t]) -> t_s t
+				| TInst({ cl_path = (["cs"], "NativeArray") }, [param]) ->
+					let rec check_t_s t =
+						match real_type t with
+							| TInst({ cl_path = (["cs"], "NativeArray") }, [param]) ->
+								(check_t_s param) ^ "[]"
+							| _ -> t_s (run_follow gen t)
+					in
+					(check_t_s param) ^ "[]"
+				| TInst({ cl_path = (["cs"], "Pointer") },[t])
+				| TAbstract({ a_path = (["cs"], "Pointer") },[t])->
+					let ret = t_s t in
+					(if ret = "object" then "void" else ret) ^ "*"
+				(* end of basic types *)
+				| TInst ({ cl_kind = KTypeParameter _; cl_path=p }, []) -> snd p
+				| TMono r -> (match !r with | None -> "object" | Some t -> t_s (run_follow gen t))
+				| TInst ({ cl_path = [], "String" }, []) -> "string"
+				| TEnum (e, params) -> ("global::" ^ (module_s (TEnumDecl e)))
+				| TInst (cl, _ :: _) when Meta.has Meta.Enum cl.cl_meta ->
+					"global::" ^ module_s (TClassDecl cl)
+				| TInst (({ cl_path = p } as cl), params) -> (path_param_s (TClassDecl cl) p params)
+				| TType (({ t_path = p } as t), params) -> (path_param_s (TTypeDecl t) p params)
+				| TAnon (anon) ->
+					(match !(anon.a_status) with
+						| Statics _ | EnumStatics _ -> "System.Type"
+						| _ -> "object")
+				| TDynamic _ -> "object"
+				| TAbstract(a,pl) when not (Meta.has Meta.CoreType a.a_meta) ->
+					t_s (Abstract.get_underlying_type a pl)
+				(* No Lazy type nor Function type made. That's because function types will be at this point be converted into other types *)
+				| _ -> if !strict_mode then begin trace ("[ !TypeError " ^ (Type.s_type (Type.print_context()) t) ^ " ]"); assert false end else "[ !TypeError " ^ (Type.s_type (Type.print_context()) t) ^ " ]"
+
+		and path_param_s md path params =
+				match params with
+					| [] -> "global::" ^ module_s md
+					| _ when erase_generics && is_hxgeneric md ->
+						"global::" ^ module_s md
+					| _ ->
+						let params = (List.map (fun t -> t_s t) (change_param_type md params)) in
+						let str,params = module_s_params md params in
+						if params = [] then
+							"global::" ^ str
+						else
+							sprintf "global::%s<%s>" str (String.concat ", " params)
+		in
 
-	let rett_s t =
-		match t with
-			| TAbstract ({ a_path = ([], "Void") },[]) -> "void"
-			| _ -> t_s t
-	in
+		let rett_s t =
+			match t with
+				| TAbstract ({ a_path = ([], "Void") },[]) -> "void"
+				| _ -> t_s t
+		in
 
-	let escape ichar b =
-		match ichar with
-			| 92 (* \ *) -> Buffer.add_string b "\\\\"
-			| 39 (* ' *) -> Buffer.add_string b "\\\'"
-			| 34 -> Buffer.add_string b "\\\""
-			| 13 (* \r *) -> Buffer.add_string b "\\r"
-			| 10 (* \n *) -> Buffer.add_string b "\\n"
-			| 9 (* \t *) -> Buffer.add_string b "\\t"
-			| c when c < 32 || (c >= 127 && c <= 0xFFFF) -> Buffer.add_string b (Printf.sprintf "\\u%.4x" c)
-			| c when c > 0xFFFF -> Buffer.add_string b (Printf.sprintf "\\U%.8x" c)
-			| c -> Buffer.add_char b (Char.chr c)
-	in
+		let escape ichar b =
+			match ichar with
+				| 92 (* \ *) -> Buffer.add_string b "\\\\"
+				| 39 (* ' *) -> Buffer.add_string b "\\\'"
+				| 34 -> Buffer.add_string b "\\\""
+				| 13 (* \r *) -> Buffer.add_string b "\\r"
+				| 10 (* \n *) -> Buffer.add_string b "\\n"
+				| 9 (* \t *) -> Buffer.add_string b "\\t"
+				| c when c < 32 || (c >= 127 && c <= 0xFFFF) -> Buffer.add_string b (Printf.sprintf "\\u%.4x" c)
+				| c when c > 0xFFFF -> Buffer.add_string b (Printf.sprintf "\\U%.8x" c)
+				| c -> Buffer.add_char b (Char.chr c)
+		in
 
-	let escape s =
-		let b = Buffer.create 0 in
-		(try
-			UTF8.validate s;
-			UTF8.iter (fun c -> escape (UChar.code c) b) s
-		with
-			UTF8.Malformed_code ->
-				String.iter (fun c -> escape (Char.code c) b) s
-		);
-		Buffer.contents b
-	in
+		let escape s =
+			let b = Buffer.create 0 in
+			(try
+				UTF8.validate s;
+				UTF8.iter (fun c -> escape (UChar.code c) b) s
+			with
+				UTF8.Malformed_code ->
+					String.iter (fun c -> escape (Char.code c) b) s
+			);
+			Buffer.contents b
+		in
 
-	let has_semicolon e =
-		match e.eexpr with
-			| TBlock _ | TFor _ | TSwitch _ | TTry _ | TIf _ -> false
-			| TWhile (_,_,flag) when flag = Ast.NormalWhile -> false
-			| _ -> true
-	in
+		let has_semicolon e =
+			match e.eexpr with
+				| TBlock _ | TFor _ | TSwitch _ | TTry _ | TIf _ -> false
+				| TWhile (_,_,flag) when flag = Ast.NormalWhile -> false
+				| _ -> true
+		in
 
-	let in_value = ref false in
-
-	let rec md_s md =
-		let md = follow_module (gen.gfollow#run_f) md in
-		match md with
-			| TClassDecl ({ cl_params = [] } as cl) ->
-				t_s (TInst(cl,[]))
-			| TClassDecl (cl) when not (is_hxgen md) ->
-				t_s (TInst(cl,List.map (fun t -> t_dynamic) cl.cl_params))
-			| TEnumDecl ({ e_params = [] } as e) ->
-				t_s (TEnum(e,[]))
-			| TEnumDecl (e) when not (is_hxgen md) ->
-				t_s (TEnum(e,List.map (fun t -> t_dynamic) e.e_params))
-			| TClassDecl cl ->
-				t_s (TInst(cl,[]))
-			| TEnumDecl e ->
-				t_s (TEnum(e,[]))
-			| TTypeDecl t ->
-				t_s (TType(t, List.map (fun t -> t_dynamic) t.t_params))
-			| TAbstractDecl a ->
-				t_s (TAbstract(a, List.map(fun t -> t_dynamic) a.a_params))
-	in
+		let in_value = ref false in
+
+		let rec md_s md =
+			let md = follow_module (gen.gfollow#run_f) md in
+			match md with
+				| TClassDecl ({ cl_params = [] } as cl) ->
+					t_s (TInst(cl,[]))
+				| TClassDecl (cl) when not (is_hxgen md) ->
+					t_s (TInst(cl,List.map (fun t -> t_dynamic) cl.cl_params))
+				| TEnumDecl ({ e_params = [] } as e) ->
+					t_s (TEnum(e,[]))
+				| TEnumDecl (e) when not (is_hxgen md) ->
+					t_s (TEnum(e,List.map (fun t -> t_dynamic) e.e_params))
+				| TClassDecl cl ->
+					t_s (TInst(cl,[]))
+				| TEnumDecl e ->
+					t_s (TEnum(e,[]))
+				| TTypeDecl t ->
+					t_s (TType(t, List.map (fun t -> t_dynamic) t.t_params))
+				| TAbstractDecl a ->
+					t_s (TAbstract(a, List.map(fun t -> t_dynamic) a.a_params))
+		in
 
-	let rec ensure_local e explain =
-		match e.eexpr with
-			| TLocal _ -> e
-			| TCast(e,_)
-			| TParenthesis e | TMeta(_,e) -> ensure_local e explain
-			| _ -> gen.gcon.error ("This function argument " ^ explain ^ " must be a local variable.") e.epos; e
-	in
+		let rec ensure_local e explain =
+			match e.eexpr with
+				| TLocal _ -> e
+				| TCast(e,_)
+				| TParenthesis e | TMeta(_,e) -> ensure_local e explain
+				| _ -> gen.gcon.error ("This function argument " ^ explain ^ " must be a local variable.") e.epos; e
+		in
 
-	let rec ensure_refout e explain =
-		match e.eexpr with
-			| TField _ | TLocal _ -> e
-			| TCast(e,_)
-			| TParenthesis e | TMeta(_,e) -> ensure_refout e explain
-			| _ -> gen.gcon.error ("This function argument " ^ explain ^ " must be a local variable.") e.epos; e
-	in
+		let rec ensure_refout e explain =
+			match e.eexpr with
+				| TField _ | TLocal _ -> e
+				| TCast(e,_)
+				| TParenthesis e | TMeta(_,e) -> ensure_refout e explain
+				| _ -> gen.gcon.error ("This function argument " ^ explain ^ " must be a local variable.") e.epos; e
+		in
 
-	let last_line = ref (-1) in
-	let begin_block w = write w "{"; push_indent w; newline w; last_line := -1 in
-	let end_block w = pop_indent w; (if w.sw_has_content then newline w); write w "}"; newline w; last_line := -1 in
-	let skip_line_directives = (not gen.gcon.debug && not (Common.defined gen.gcon Define.NoCompilation)) || Common.defined gen.gcon Define.RealPosition in
-	let line_directive =
-		if skip_line_directives then
-			fun w p -> ()
-		else fun w p ->
-			if p.pfile <> null_pos.pfile then (* Compiler Error CS1560 https://msdn.microsoft.com/en-us/library/z3t5e5sw(v=vs.90).aspx *)
-			let cur_line = Lexer.get_error_line p in
-			let file = Path.get_full_path p.pfile in
-			if cur_line <> ((!last_line)+1) then
-				let line = Ast.s_escape file in
-				if String.length line <= 256 then
-					begin print w "#line %d \"%s\"" cur_line line; newline w end
-				else (* Compiler Error CS1560 https://msdn.microsoft.com/en-us/library/z3t5e5sw(v=vs.90).aspx *)
-					begin print w "//line %d \"%s\"" cur_line line; newline w end;
-			last_line := cur_line
-	in
-	let line_reset_directive =
-		if skip_line_directives then
-			fun w -> ()
-		else fun w ->
-			print w "#line default"
-	in
+		let last_line = ref (-1) in
+		let begin_block w = write w "{"; push_indent w; newline w; last_line := -1 in
+		let end_block w = pop_indent w; (if w.sw_has_content then newline w); write w "}"; newline w; last_line := -1 in
+		let skip_line_directives = (not gen.gcon.debug && not (Common.defined gen.gcon Define.NoCompilation)) || Common.defined gen.gcon Define.RealPosition in
+		let line_directive =
+			if skip_line_directives then
+				fun w p -> ()
+			else fun w p ->
+				if p.pfile <> null_pos.pfile then (* Compiler Error CS1560 https://msdn.microsoft.com/en-us/library/z3t5e5sw(v=vs.90).aspx *)
+				let cur_line = Lexer.get_error_line p in
+				let file = Path.get_full_path p.pfile in
+				if cur_line <> ((!last_line)+1) then
+					let line = Ast.s_escape file in
+					if String.length line <= 256 then
+						begin print w "#line %d \"%s\"" cur_line line; newline w end
+					else (* Compiler Error CS1560 https://msdn.microsoft.com/en-us/library/z3t5e5sw(v=vs.90).aspx *)
+						begin print w "//line %d \"%s\"" cur_line line; newline w end;
+				last_line := cur_line
+		in
+		let line_reset_directive =
+			if skip_line_directives then
+				fun w -> ()
+			else fun w ->
+				print w "#line default"
+		in
 
-	let rec extract_tparams params el =
-		match el with
-			| ({ eexpr = TLocal({ v_name = "$type_param" }) } as tp) :: tl ->
-				extract_tparams (tp.etype :: params) tl
-			| _ -> (params, el)
-	in
+		let rec extract_tparams params el =
+			match el with
+				| ({ eexpr = TIdent "$type_param" } as tp) :: tl ->
+					extract_tparams (tp.etype :: params) tl
+				| _ -> (params, el)
+		in
 
-	let is_extern_prop t name = match follow (run_follow gen t), field_access gen t name with
-		| TInst({ cl_interface = true; cl_extern = true } as cl, _), FNotFound ->
-			not (is_hxgen (TClassDecl cl))
-		| _, FClassField(_,_,decl,v,_,t,_) ->
-			Type.is_extern_field v && (Meta.has Meta.Property v.cf_meta || (decl.cl_extern && not (is_hxgen (TClassDecl decl))))
-		| _ -> false
-	in
+		let is_extern_prop t name = match follow (run_follow gen t), field_access gen t name with
+			| TInst({ cl_interface = true; cl_extern = true } as cl, _), FNotFound ->
+				not (is_hxgen (TClassDecl cl))
+			| _, FClassField(_,_,decl,v,_,t,_) ->
+				not (Type.is_physical_field v) && (Meta.has Meta.Property v.cf_meta || (decl.cl_extern && not (is_hxgen (TClassDecl decl))))
+			| _ -> false
+		in
 
-	let is_event t name = match follow (run_follow gen t), field_access gen t name with
-		| TInst({ cl_interface = true; cl_extern = true } as cl, _), FNotFound ->
-			not (is_hxgen (TClassDecl cl))
-		| _, FClassField(_,_,decl,v,_,_,_) ->
-			Meta.has Meta.Event v.cf_meta
-		| _ -> false
-	in
+		let is_event t name = match follow (run_follow gen t), field_access gen t name with
+			| TInst({ cl_interface = true; cl_extern = true } as cl, _), FNotFound ->
+				not (is_hxgen (TClassDecl cl))
+			| _, FClassField(_,_,decl,v,_,_,_) ->
+				Meta.has Meta.Event v.cf_meta
+			| _ -> false
+		in
 
-	let extract_statements expr =
-		let ret = ref [] in
-		let rec loop expr = match expr.eexpr with
-			| TCall ({ eexpr = TLocal {
-					v_name = "__is__" | "__typeof__" | "__array__" | "__sizeof__" | "__delegate__"
-				} }, el) ->
-				List.iter loop el
-			| TNew ({ cl_path = (["cs"], "NativeArray") }, params, [ size ]) ->
-				()
-			| TUnop (Ast.Increment, _, _)
-			| TUnop (Ast.Decrement, _, _)
-			| TBinop (Ast.OpAssign, _, _)
-			| TBinop (Ast.OpAssignOp _, _, _)
-			| TLocal { v_name = "__fallback__" }
-			| TLocal { v_name = "__sbreak__" } ->
-				ret := expr :: !ret
-			| TConst _
-			| TLocal _
-			| TArray _
-			| TBinop _
-			| TField _
-			| TEnumParameter _
-			| TTypeExpr _
-			| TObjectDecl _
-			| TArrayDecl _
-			| TCast _
-			| TMeta _
-			| TParenthesis _
-			| TUnop _ ->
-				Type.iter loop expr
-			| TFunction _ -> () (* do not extract parameters from inside of it *)
-			| _ ->
-				ret := expr :: !ret
+		let extract_statements expr =
+			let ret = ref [] in
+			let rec loop expr = match expr.eexpr with
+				| TCall ({ eexpr = TIdent ("__is__" | "__typeof__" | "__array__" | "__sizeof__" | "__delegate__")}, el) ->
+					List.iter loop el
+				| TNew ({ cl_path = (["cs"], "NativeArray") }, params, [ size ]) ->
+					()
+				| TUnop (Ast.Increment, _, _)
+				| TUnop (Ast.Decrement, _, _)
+				| TBinop (Ast.OpAssign, _, _)
+				| TBinop (Ast.OpAssignOp _, _, _)
+				| TIdent "__fallback__"
+				| TIdent "__sbreak__" ->
+					ret := expr :: !ret
+				| TConst _
+				| TLocal _
+				| TArray _
+				| TBinop _
+				| TField _
+				| TEnumParameter _
+				| TTypeExpr _
+				| TObjectDecl _
+				| TArrayDecl _
+				| TCast _
+				| TParenthesis _
+				| TUnop _ ->
+					Type.iter loop expr
+				| TFunction _ -> () (* do not extract parameters from inside of it *)
+				| _ ->
+					ret := expr :: !ret
+			in
+			loop expr;
+			(* [expr] *)
+			List.rev !ret
 		in
-		loop expr;
-		(* [expr] *)
-		List.rev !ret
-	in
 
-	let expr_s w e =
-		last_line := -1;
-		in_value := false;
-		let rec expr_s w e =
-			let was_in_value = !in_value in
-			in_value := true;
-			(match e.eexpr with
-				| TCall({ eexpr = TField(ef,f) }, (_ :: _ as args) ) when (field_name f) = "get_Item" ->
-					expr_s w ef;
-					write w "[";
-					let first = ref true in
-					List.iter (fun f ->
-						if !first then first := false else write w ", ";
-						expr_s w f
-					) args;
-					write w "]"
-				| TCall({ eexpr = TField(ef,f) }, (_ :: _ :: _ as args) ) when (field_name f) = "set_Item" ->
-					expr_s w ef;
-					write w "[";
-					let args, value = match List.rev args with
-						| v :: args -> List.rev args, v
-						| _ -> assert false
-					in
-					let first = ref true in
-					List.iter (fun f ->
-						if !first then first := false else write w ", ";
-						expr_s w f
-					) args;
-					write w "] = ";
-					expr_s w value
-				| TCall( ({ eexpr = TField(ef,f) } as e), [ev] ) when String.starts_with (field_name f) "add_" ->
-					let name = field_name f in
-					let propname = String.sub name 4 (String.length name - 4) in
-					if is_event (gen.greal_type ef.etype) propname then begin
+		let expr_s w e =
+			last_line := -1;
+			in_value := false;
+			let rec expr_s w e =
+				let was_in_value = !in_value in
+				in_value := true;
+				(match e.eexpr with
+					| TCall({ eexpr = TField(ef,f) }, (_ :: _ as args) ) when (field_name f) = "get_Item" ->
 						expr_s w ef;
-						write w ".";
-						write_field w propname;
-						write w " += ";
-						expr_s w ev
-					end else
-						do_call w e [ev]
-				| TCall( ({ eexpr = TField(ef,f) } as e), [ev] ) when String.starts_with (field_name f) "remove_" ->
-					let name = field_name f in
-					let propname = String.sub name 7 (String.length name - 7) in
-					if is_event (gen.greal_type ef.etype) propname then begin
-						expr_s w ef;
-						write w ".";
-						write_field w propname;
-						write w " -= ";
-						expr_s w ev
-					end else
-						do_call w e [ev]
-				| TCall( ({ eexpr = TField(ef,f) } as e), [] ) when String.starts_with (field_name f) "get_" ->
-					let name = field_name f in
-					let propname = String.sub name 4 (String.length name - 4) in
-					if is_extern_prop (gen.greal_type ef.etype) propname then begin
-						expr_s w ef;
-						write w ".";
-						write_field w propname
-					end else
-						do_call w e []
-				| TCall( ({ eexpr = TField(ef,f) } as e), [v] ) when String.starts_with (field_name f) "set_" ->
-					let name = field_name f in
-					let propname = String.sub name 4 (String.length name - 4) in
-					if is_extern_prop (gen.greal_type ef.etype) propname then begin
+						write w "[";
+						let first = ref true in
+						List.iter (fun f ->
+							if !first then first := false else write w ", ";
+							expr_s w f
+						) args;
+						write w "]"
+					| TCall({ eexpr = TField(ef,f) }, (_ :: _ :: _ as args) ) when (field_name f) = "set_Item" ->
 						expr_s w ef;
+						write w "[";
+						let args, value = match List.rev args with
+							| v :: args -> List.rev args, v
+							| _ -> assert false
+						in
+						let first = ref true in
+						List.iter (fun f ->
+							if !first then first := false else write w ", ";
+							expr_s w f
+						) args;
+						write w "] = ";
+						expr_s w value
+					| TCall( ({ eexpr = TField(ef,f) } as e), [ev] ) when String.starts_with (field_name f) "add_" ->
+						let name = field_name f in
+						let propname = String.sub name 4 (String.length name - 4) in
+						if is_event (gen.greal_type ef.etype) propname then begin
+							expr_s w ef;
+							write w ".";
+							write_field w propname;
+							write w " += ";
+							expr_s w ev
+						end else
+							do_call w e [ev]
+					| TCall( ({ eexpr = TField(ef,f) } as e), [ev] ) when String.starts_with (field_name f) "remove_" ->
+						let name = field_name f in
+						let propname = String.sub name 7 (String.length name - 7) in
+						if is_event (gen.greal_type ef.etype) propname then begin
+							expr_s w ef;
+							write w ".";
+							write_field w propname;
+							write w " -= ";
+							expr_s w ev
+						end else
+							do_call w e [ev]
+					| TCall( ({ eexpr = TField(ef,f) } as e), [] ) when String.starts_with (field_name f) "get_" ->
+						let name = field_name f in
+						let propname = String.sub name 4 (String.length name - 4) in
+						if is_extern_prop (gen.greal_type ef.etype) propname then begin
+							expr_s w ef;
+							write w ".";
+							write_field w propname
+						end else
+							do_call w e []
+					| TCall( ({ eexpr = TField(ef,f) } as e), [v] ) when String.starts_with (field_name f) "set_" ->
+						let name = field_name f in
+						let propname = String.sub name 4 (String.length name - 4) in
+						if is_extern_prop (gen.greal_type ef.etype) propname then begin
+							expr_s w ef;
+							write w ".";
+							write_field w propname;
+							write w " = ";
+							expr_s w v
+						end else
+							do_call w e [v]
+					| TField (e, (FStatic(_, cf) | FInstance(_, _, cf))) when Meta.has Meta.Native cf.cf_meta ->
+						let rec loop meta = match meta with
+							| (Meta.Native, [EConst (String s), _],_) :: _ ->
+								expr_s w e; write w "."; write_field w s
+							| _ :: tl -> loop tl
+							| [] -> expr_s w e; write w "."; write_field w (cf.cf_name)
+						in
+						loop cf.cf_meta
+					| TConst c ->
+						(match c with
+							| TInt i32 ->
+								write w (Int32.to_string i32);
+								(* these suffixes won't work because of the cast detector which will set each constant to its expected type *)
+								(*match real_type e.etype with
+									| TType( { t_path = (["haxe";"_Int64"], "NativeInt64") }, [] ) -> write w "L";
+									| _ -> ()
+								*)
+							| TFloat s ->
+								write w s;
+								(if String.get s (String.length s - 1) = '.' then write w "0");
+								(*match real_type e.etype with
+									| TType( { t_path = ([], "Single") }, [] ) -> write w "f"
+									| _ -> ()
+								*)
+							| TString s ->
+								write w "\"";
+								write w (escape s);
+								write w "\""
+							| TBool b -> write w (if b then "true" else "false")
+							| TNull when is_cs_basic_type e.etype || is_tparam e.etype ->
+								write w "default(";
+								write w (t_s e.etype);
+								write w ")"
+							| TNull -> write w "null"
+							| TThis -> write w "this"
+							| TSuper -> write w "base")
+					| TCast({ eexpr = TConst(TNull) }, _) ->
+								write w "default(";
+								write w (t_s e.etype);
+								write w ")"
+					| TIdent "__sbreak__" -> write w "break"
+					| TIdent "__undefined__" ->
+						write w (t_s (TInst(runtime_cl, List.map (fun _ -> t_dynamic) runtime_cl.cl_params)));
+						write w ".undefined";
+					| TIdent "__typeof__" -> write w "typeof"
+					| TIdent "__sizeof__" -> write w "sizeof"
+					| TLocal var ->
+						write_id w var.v_name
+					| TField (_, FEnum(e, ef)) ->
+						let s = ef.ef_name in
+						print w "%s." ("global::" ^ module_s (TEnumDecl e)); write_field w s
+					| TArray (e1, e2) ->
+						expr_s w e1; write w "["; expr_s w e2; write w "]"
+					| TBinop ((Ast.OpAssign as op), e1, e2)
+					| TBinop ((Ast.OpAssignOp _ as op), e1, e2) ->
+						expr_s w e1; write w ( " " ^ (Ast.s_binop op) ^ " " ); expr_s w e2
+					| TBinop (op, e1, e2) ->
+						write w "( ";
+						expr_s w e1; write w ( " " ^ (Ast.s_binop op) ^ " " ); expr_s w e2;
+						write w " )"
+					| TField ({ eexpr = TTypeExpr mt }, s) ->
+						(match mt with
+							| TClassDecl { cl_path = (["haxe"], "Int64") } -> write w ("global::" ^ module_s mt)
+							| TClassDecl { cl_path = (["haxe"], "Int32") } -> write w ("global::" ^ module_s mt)
+							| TClassDecl { cl_interface = true } ->
+									write w ("global::" ^ module_s mt);
+									write w "__Statics_";
+							| TClassDecl cl -> write w (t_s (TInst(cl, List.map (fun _ -> t_empty) cl.cl_params)))
+							| TEnumDecl en -> write w (t_s (TEnum(en, List.map (fun _ -> t_empty) en.e_params)))
+							| TTypeDecl td -> write w (t_s (gen.gfollow#run_f (TType(td, List.map (fun _ -> t_empty) td.t_params))))
+							| TAbstractDecl a -> write w (t_s (TAbstract(a, List.map (fun _ -> t_empty) a.a_params)))
+						);
 						write w ".";
-						write_field w propname;
-						write w " = ";
-						expr_s w v
-					end else
-						do_call w e [v]
-				| TField (e, (FStatic(_, cf) | FInstance(_, _, cf))) when Meta.has Meta.Native cf.cf_meta ->
-					let rec loop meta = match meta with
-						| (Meta.Native, [EConst (String s), _],_) :: _ ->
-							expr_s w e; write w "."; write_field w s
-						| _ :: tl -> loop tl
-						| [] -> expr_s w e; write w "."; write_field w (cf.cf_name)
-					in
-					loop cf.cf_meta
-				| TConst c ->
-					(match c with
-						| TInt i32 ->
-							write w (Int32.to_string i32);
-							(* these suffixes won't work because of the cast detector which will set each constant to its expected type *)
-							(*match real_type e.etype with
-								| TType( { t_path = (["haxe";"_Int64"], "NativeInt64") }, [] ) -> write w "L";
-								| _ -> ()
-							*)
-						| TFloat s ->
-							write w s;
-							(if String.get s (String.length s - 1) = '.' then write w "0");
-							(*match real_type e.etype with
-								| TType( { t_path = ([], "Single") }, [] ) -> write w "f"
-								| _ -> ()
-							*)
-						| TString s ->
-							write w "\"";
-							write w (escape s);
-							write w "\""
-						| TBool b -> write w (if b then "true" else "false")
-						| TNull when is_cs_basic_type e.etype || is_tparam e.etype ->
-							write w "default(";
-							write w (t_s e.etype);
-							write w ")"
-						| TNull -> write w "null"
-						| TThis -> write w "this"
-						| TSuper -> write w "base")
-				| TCast({ eexpr = TConst(TNull) }, _) ->
-							write w "default(";
-							write w (t_s e.etype);
-							write w ")"
-				| TLocal { v_name = "__sbreak__" } -> write w "break"
-				| TLocal { v_name = "__undefined__" } ->
-					write w (t_s (TInst(runtime_cl, List.map (fun _ -> t_dynamic) runtime_cl.cl_params)));
-					write w ".undefined";
-				| TLocal { v_name = "__typeof__" } -> write w "typeof"
-				| TLocal { v_name = "__sizeof__" } -> write w "sizeof"
-				| TLocal var ->
-					write_id w var.v_name
-				| TField (_, FEnum(e, ef)) ->
-					let s = ef.ef_name in
-					print w "%s." ("global::" ^ module_s (TEnumDecl e)); write_field w s
-				| TArray (e1, e2) ->
-					expr_s w e1; write w "["; expr_s w e2; write w "]"
-				| TBinop ((Ast.OpAssign as op), e1, e2)
-				| TBinop ((Ast.OpAssignOp _ as op), e1, e2) ->
-					expr_s w e1; write w ( " " ^ (Ast.s_binop op) ^ " " ); expr_s w e2
-				| TBinop (op, e1, e2) ->
-					write w "( ";
-					expr_s w e1; write w ( " " ^ (Ast.s_binop op) ^ " " ); expr_s w e2;
-					write w " )"
-				| TField ({ eexpr = TTypeExpr mt }, s) ->
-					(match mt with
-						| TClassDecl { cl_path = (["haxe"], "Int64") } -> write w ("global::" ^ module_s mt)
-						| TClassDecl { cl_path = (["haxe"], "Int32") } -> write w ("global::" ^ module_s mt)
-						| TClassDecl { cl_interface = true } ->
-								write w ("global::" ^ module_s mt);
-								write w "__Statics_";
-						| TClassDecl cl -> write w (t_s (TInst(cl, List.map (fun _ -> t_empty) cl.cl_params)))
-						| TEnumDecl en -> write w (t_s (TEnum(en, List.map (fun _ -> t_empty) en.e_params)))
-						| TTypeDecl td -> write w (t_s (gen.gfollow#run_f (TType(td, List.map (fun _ -> t_empty) td.t_params))))
-						| TAbstractDecl a -> write w (t_s (TAbstract(a, List.map (fun _ -> t_empty) a.a_params)))
-					);
-					write w ".";
-					write_field w (field_name s)
-				| TField (e, s) when is_pointer gen e.etype ->
-					(* take off the extra cast if possible *)
-					let e = match e.eexpr with
-						| TCast(e1,_) when CastDetect.type_iseq gen e.etype e1.etype ->
-							e1
-						| _ -> e
-					in
-					expr_s w e; write w "->"; write_field w (field_name s)
-				| TField (e, s) ->
-					expr_s w e; write w "."; write_field w (field_name s)
-				| TTypeExpr mt ->
-					(match change_md mt with
-						| TClassDecl { cl_path = (["haxe"], "Int64") } -> write w ("global::" ^ module_s mt)
-						| TClassDecl { cl_path = (["haxe"], "Int32") } -> write w ("global::" ^ module_s mt)
-						| TClassDecl cl -> write w (t_s (TInst(cl, List.map (fun _ -> t_empty) cl.cl_params)));
-						| TEnumDecl en -> write w (t_s (TEnum(en, List.map (fun _ -> t_empty) en.e_params)))
-						| TTypeDecl td -> write w (t_s (gen.gfollow#run_f (TType(td, List.map (fun _ -> t_empty) td.t_params))))
-						| TAbstractDecl a -> write w (t_s (TAbstract(a, List.map (fun _ -> t_empty) a.a_params)))
-					)
-				| TParenthesis e ->
-					write w "("; expr_s w e; write w ")"
-				| TMeta (_,e) ->
-						expr_s w e
-				| TArrayDecl el
-				| TCall ({ eexpr = TLocal { v_name = "__array__" } }, el)
-				| TCall ({ eexpr = TField(_, FStatic({ cl_path = (["cs"],"NativeArray") }, { cf_name = "make" })) }, el) ->
-					let _, el = extract_tparams [] el in
-					print w "new %s" (t_s e.etype);
-					write w "{";
-					ignore (List.fold_left (fun acc e ->
-						(if acc <> 0 then write w ", ");
+						write_field w (field_name s)
+					| TField (e, s) when is_pointer gen e.etype ->
+						(* take off the extra cast if possible *)
+						let e = match e.eexpr with
+							| TCast(e1,_) when CastDetect.type_iseq gen e.etype e1.etype ->
+								e1
+							| _ -> e
+						in
+						expr_s w e; write w "->"; write_field w (field_name s)
+					| TField (e, s) ->
+						expr_s w e; write w "."; write_field w (field_name s)
+					| TTypeExpr mt ->
+						(match change_md mt with
+							| TClassDecl { cl_path = (["haxe"], "Int64") } -> write w ("global::" ^ module_s mt)
+							| TClassDecl { cl_path = (["haxe"], "Int32") } -> write w ("global::" ^ module_s mt)
+							| TClassDecl cl -> write w (t_s (TInst(cl, List.map (fun _ -> t_empty) cl.cl_params)));
+							| TEnumDecl en -> write w (t_s (TEnum(en, List.map (fun _ -> t_empty) en.e_params)))
+							| TTypeDecl td -> write w (t_s (gen.gfollow#run_f (TType(td, List.map (fun _ -> t_empty) td.t_params))))
+							| TAbstractDecl a -> write w (t_s (TAbstract(a, List.map (fun _ -> t_empty) a.a_params)))
+						)
+					| TParenthesis e ->
+						write w "("; expr_s w e; write w ")"
+					| TMeta ((Meta.LoopLabel,[(EConst(Int n),_)],_), e) ->
+						(match e.eexpr with
+						| TFor _ | TWhile _ ->
+							expr_s w e;
+							print w "label%s: {}" n
+						| TBreak -> print w "goto label%s" n
+						| _ -> assert false)
+					| TMeta (_,e) ->
+								expr_s w e
+					| TArrayDecl el
+					| TCall ({ eexpr = TIdent "__array__" }, el)
+					| TCall ({ eexpr = TField(_, FStatic({ cl_path = (["cs"],"NativeArray") }, { cf_name = "make" })) }, el) ->
+						let _, el = extract_tparams [] el in
+						print w "new %s" (t_s e.etype);
+						write w "{";
+						ignore (List.fold_left (fun acc e ->
+							(if acc <> 0 then write w ", ");
+							expr_s w e;
+							acc + 1
+						) 0 el);
+						write w "}"
+					| TCall ({ eexpr = TIdent "__delegate__" }, [del]) ->
+						expr_s w del
+					| TCall ({ eexpr = TIdent "__is__" }, [ expr; { eexpr = TTypeExpr(md) } ] ) ->
+						write w "( ";
+						expr_s w expr;
+						write w " is ";
+						write w (md_s md);
+						write w " )"
+					| TCall ({ eexpr = TIdent "__as__" }, [ expr; { eexpr = TTypeExpr(md) } ] ) ->
+						write w "( ";
+						expr_s w expr;
+						write w " as ";
+						write w (md_s md);
+						write w " )"
+					| TCall ({ eexpr = TIdent "__as__" }, expr :: _ ) ->
+						write w "( ";
+						expr_s w expr;
+						write w " as ";
+						write w (t_s e.etype);
+						write w " )";
+					| TCall ({ eexpr = TIdent "__cs__" }, [ { eexpr = TConst(TString(s)) } ] ) ->
+						write w s
+					| TCall ({ eexpr = TIdent "__cs__" }, { eexpr = TConst(TString(s)) } :: tl ) ->
+						Codegen.interpolate_code gen.gcon s tl (write w) (expr_s w) e.epos
+					| TCall ({ eexpr = TIdent "__stackalloc__" }, [ e ] ) ->
+						write w "stackalloc byte[";
 						expr_s w e;
-						acc + 1
-					) 0 el);
-					write w "}"
-				| TCall ({ eexpr = TLocal { v_name = "__delegate__" } }, [del]) ->
-					expr_s w del
-				| TCall ({ eexpr = TLocal( { v_name = "__is__" } ) }, [ expr; { eexpr = TTypeExpr(md) } ] ) ->
-					write w "( ";
-					expr_s w expr;
-					write w " is ";
-					write w (md_s md);
-					write w " )"
-				| TCall ({ eexpr = TLocal( { v_name = "__as__" } ) }, [ expr; { eexpr = TTypeExpr(md) } ] ) ->
-					write w "( ";
-					expr_s w expr;
-					write w " as ";
-					write w (md_s md);
-					write w " )"
-				| TCall ({ eexpr = TLocal( { v_name = "__as__" } ) }, expr :: _ ) ->
-					write w "( ";
-					expr_s w expr;
-					write w " as ";
-					write w (t_s e.etype);
-					write w " )";
-				| TCall ({ eexpr = TLocal( { v_name = "__cs__" } ) }, [ { eexpr = TConst(TString(s)) } ] ) ->
-					write w s
-				| TCall ({ eexpr = TLocal( { v_name = "__cs__" } ) }, { eexpr = TConst(TString(s)) } :: tl ) ->
-					Codegen.interpolate_code gen.gcon s tl (write w) (expr_s w) e.epos
-				| TCall ({ eexpr = TLocal( { v_name = "__stackalloc__" } ) }, [ e ] ) ->
-					write w "stackalloc byte[";
-					expr_s w e;
-					write w "]"
-				| TCall ({ eexpr = TLocal( { v_name = "__unsafe__" } ) }, [ e ] ) ->
-					write w "unsafe";
-					expr_s w (mk_block e)
-				| TCall ({ eexpr = TLocal( { v_name = "__checked__" } ) }, [ e ] ) ->
-					write w "checked";
-					expr_s w (mk_block e)
-				| TCall ({ eexpr = TLocal( { v_name = "__lock__" } ) }, [ eobj; eblock ] ) ->
-					write w "lock(";
-					expr_s w eobj;
-					write w ")";
-					expr_s w (mk_block eblock)
-				| TCall ({ eexpr = TLocal( { v_name = "__fixed__" } ) }, [ e ] ) ->
-					let fixeds = ref [] in
-					let rec loop = function
-						| ({ eexpr = TVar(v, Some(e) ) } as expr) :: tl when is_pointer gen v.v_type ->
-							let e = match get_ptr e with
-								| None -> e
-								| Some e -> e
-							in
-							fixeds := (v,e,expr) :: !fixeds;
-							loop tl;
-						| el when !fixeds <> [] ->
-							let rec loop fx acc = match fx with
-								| (v,e,expr) :: tl ->
-									write w "fixed(";
-									let vf = mk_temp gen "fixed" v.v_type in
-									expr_s w { expr with eexpr = TVar(vf, Some e) };
-									write w ") ";
-									begin_block w;
-									expr_s w { expr with eexpr = TVar(v, Some (mk_local vf expr.epos)) };
-									write w ";";
-									newline w;
-									loop tl (acc + 1)
-								| [] -> acc
-							in
-							let nblocks = loop (List.rev !fixeds) 0 in
-							in_value := false;
-							expr_s w { e with eexpr = TBlock el };
-							for i = 1 to nblocks do
-								end_block w
-							done
-						| _ ->
-							trace (debug_expr e);
-							gen.gcon.error "Invalid 'fixed' keyword format" e.epos
-					in
-					(match e.eexpr with
-						| TBlock bl -> loop bl
-						| _ ->
-							trace "not block";
-							trace (debug_expr e);
-							gen.gcon.error "Invalid 'fixed' keyword format" e.epos
-					)
-				| TCall ({ eexpr = TLocal( { v_name = "__addressOf__" } ) }, [ e ] ) ->
-					let e = ensure_local e "for addressOf" in
-					write w "&";
-					expr_s w e
-				| TCall ({ eexpr = TLocal( { v_name = "__valueOf__" } ) }, [ e ] ) ->
-					write w "*(";
-					expr_s w e;
-					write w ")"
-				| TCall ({ eexpr = TLocal( { v_name = "__goto__" } ) }, [ { eexpr = TConst(TInt v) } ] ) ->
-					print w "goto label%ld" v
-				| TCall ({ eexpr = TLocal( { v_name = "__label__" } ) }, [ { eexpr = TConst(TInt v) } ] ) ->
-					print w "label%ld: {}" v
-				| TCall ({ eexpr = TLocal( { v_name = "__rethrow__" } ) }, _) ->
-					write w "throw"
-				(* operator overloading handling *)
-				| TCall({ eexpr = TField(ef, FInstance(cl,_,{ cf_name = "__get" })) }, [idx]) when not (is_hxgen (TClassDecl cl)) ->
-					expr_s w { e with eexpr = TArray(ef, idx) }
-				| TCall({ eexpr = TField(ef, FInstance(cl,_,{ cf_name = "__set" })) }, [idx; v]) when not (is_hxgen (TClassDecl cl)) ->
-					expr_s w { e with eexpr = TBinop(Ast.OpAssign, { e with eexpr = TArray(ef, idx) }, v) }
-				| TCall({ eexpr = TField(ef, FStatic(_,cf)) }, el) when PMap.mem cf.cf_name binops_names ->
-					let _, elr = extract_tparams [] el in
-					(match elr with
-					| [e1;e2] ->
-						expr_s w { e with eexpr = TBinop(PMap.find cf.cf_name binops_names, e1, e2) }
-					| _ -> do_call w e el)
-				| TCall({ eexpr = TField(ef, FStatic(_,cf)) }, el) when PMap.mem cf.cf_name unops_names ->
-					(match extract_tparams [] el with
-					| _, [e1] ->
-						expr_s w { e with eexpr = TUnop(PMap.find cf.cf_name unops_names, Ast.Prefix,e1) }
-					| _ -> do_call w e el)
-				| TCall (e, el) ->
-					do_call w e el
-				| TNew (({ cl_path = (["cs"], "NativeArray") } as cl), params, [ size ]) ->
-					let rec check_t_s t times =
-						match real_type t with
-							| TInst({ cl_path = (["cs"], "NativeArray") }, [param]) ->
-								(check_t_s param (times+1))
-							| _ ->
-								print w "new %s[" (t_s (run_follow gen t));
-								expr_s w size;
-								print w "]";
-								let rec loop i =
-									if i <= 0 then () else (write w "[]"; loop (i-1))
+						write w "]"
+					| TCall ({ eexpr = TIdent "__unsafe__" }, [ e ] ) ->
+						write w "unsafe";
+						expr_s w (mk_block e)
+					| TCall ({ eexpr = TIdent "__checked__" }, [ e ] ) ->
+						write w "checked";
+						expr_s w (mk_block e)
+					| TCall ({ eexpr = TIdent "__lock__" }, [ eobj; eblock ] ) ->
+						write w "lock(";
+						expr_s w eobj;
+						write w ")";
+						expr_s w (mk_block eblock)
+					| TCall ({ eexpr = TIdent "__fixed__" }, [ e ] ) ->
+						let fixeds = ref [] in
+						let rec loop = function
+							| ({ eexpr = TVar(v, Some(e) ) } as expr) :: tl when is_pointer gen v.v_type ->
+								let e = match get_ptr e with
+									| None -> e
+									| Some e -> e
 								in
-								loop (times - 1)
-					in
-					check_t_s (TInst(cl, params)) 0
-				| TNew ({ cl_path = ([], "String") } as cl, [], el) ->
-					write w "new ";
-					write w (t_s (TInst(cl, [])));
-					write w "(";
-					ignore (List.fold_left (fun acc e ->
-						(if acc <> 0 then write w ", ");
-						expr_s w e;
-						acc + 1
-					) 0 el);
-					write w ")"
-				| TNew ({ cl_kind = KTypeParameter _ } as cl, params, el) ->
-					print w "default(%s) /* This code should never be reached. It was produced by the use of @:generic on a new type parameter instance: %s */" (t_s (TInst(cl,params))) (path_param_s (TClassDecl cl) cl.cl_path params)
-				| TNew (cl, params, el) ->
-					write w "new ";
-					write w (path_param_s (TClassDecl cl) cl.cl_path params);
-					write w "(";
-					ignore (List.fold_left (fun acc e ->
-						(if acc <> 0 then write w ", ");
+								fixeds := (v,e,expr) :: !fixeds;
+								loop tl;
+							| el when !fixeds <> [] ->
+								let rec loop fx acc = match fx with
+									| (v,e,expr) :: tl ->
+										write w "fixed(";
+										let vf = mk_temp "fixed" v.v_type in
+										expr_s w { expr with eexpr = TVar(vf, Some e) };
+										write w ") ";
+										begin_block w;
+										expr_s w { expr with eexpr = TVar(v, Some (mk_local vf expr.epos)) };
+										write w ";";
+										newline w;
+										loop tl (acc + 1)
+									| [] -> acc
+								in
+								let nblocks = loop (List.rev !fixeds) 0 in
+								in_value := false;
+								expr_s w { e with eexpr = TBlock el };
+								for i = 1 to nblocks do
+									end_block w
+								done
+							| _ ->
+								trace (debug_expr e);
+								gen.gcon.error "Invalid 'fixed' keyword format" e.epos
+						in
+						(match e.eexpr with
+							| TBlock bl -> loop bl
+							| _ ->
+								trace "not block";
+								trace (debug_expr e);
+								gen.gcon.error "Invalid 'fixed' keyword format" e.epos
+						)
+					| TCall ({ eexpr = TIdent "__addressOf__" }, [ e ] ) ->
+						let e = ensure_local e "for addressOf" in
+						write w "&";
+						expr_s w e
+					| TCall ({ eexpr = TIdent "__valueOf__" }, [ e ] ) ->
+						write w "*(";
 						expr_s w e;
-						acc + 1
-					) 0 el);
-					write w ")"
-				| TUnop ((Ast.Increment as op), flag, e)
-				| TUnop ((Ast.Decrement as op), flag, e) ->
-					(match flag with
-						| Ast.Prefix -> write w ( " " ^ (Ast.s_unop op) ^ " " ); expr_s w e
-						| Ast.Postfix -> expr_s w e; write w (Ast.s_unop op))
-				| TUnop (op, flag, e) ->
-					(match flag with
-						| Ast.Prefix -> write w ( " " ^ (Ast.s_unop op) ^ " (" ); expr_s w e; write w ") "
-						| Ast.Postfix -> write w "("; expr_s w e; write w (") " ^ Ast.s_unop op))
-				| TVar (var, eopt) ->
-					print w "%s " (t_s var.v_type);
-					write_id w var.v_name;
-					(match eopt with
-						| None ->
-							write w " = ";
-							expr_s w (null var.v_type e.epos)
-						| Some e ->
-							write w " = ";
-							expr_s w e
-					)
-				| TBlock [e] when was_in_value ->
-					expr_s w e
-				| TBlock el ->
-					begin_block w;
-					List.iter (fun e ->
+						write w ")"
+					(* operator overloading handling *)
+					| TCall({ eexpr = TField(ef, FInstance(cl,_,{ cf_name = "__get" })) }, [idx]) when not (is_hxgen (TClassDecl cl)) ->
+						expr_s w { e with eexpr = TArray(ef, idx) }
+					| TCall({ eexpr = TField(ef, FInstance(cl,_,{ cf_name = "__set" })) }, [idx; v]) when not (is_hxgen (TClassDecl cl)) ->
+						expr_s w { e with eexpr = TBinop(Ast.OpAssign, { e with eexpr = TArray(ef, idx) }, v) }
+					| TCall({ eexpr = TField(ef, FStatic(_,cf)) }, el) when PMap.mem cf.cf_name binops_names ->
+						let _, elr = extract_tparams [] el in
+						(match elr with
+						| [e1;e2] ->
+							expr_s w { e with eexpr = TBinop(PMap.find cf.cf_name binops_names, e1, e2) }
+						| _ -> do_call w e el)
+					| TCall({ eexpr = TField(ef, FStatic(_,cf)) }, el) when PMap.mem cf.cf_name unops_names ->
+						(match extract_tparams [] el with
+						| _, [e1] ->
+							expr_s w { e with eexpr = TUnop(PMap.find cf.cf_name unops_names, Ast.Prefix,e1) }
+						| _ -> do_call w e el)
+					| TCall (e, el) ->
+						do_call w e el
+					| TNew (({ cl_path = (["cs"], "NativeArray") } as cl), params, [ size ]) ->
+						let rec check_t_s t times =
+							match real_type t with
+								| TInst({ cl_path = (["cs"], "NativeArray") }, [param]) ->
+									(check_t_s param (times+1))
+								| _ ->
+									print w "new %s[" (t_s (run_follow gen t));
+									expr_s w size;
+									print w "]";
+									let rec loop i =
+										if i <= 0 then () else (write w "[]"; loop (i-1))
+									in
+									loop (times - 1)
+						in
+						check_t_s (TInst(cl, params)) 0
+					| TNew ({ cl_path = ([], "String") } as cl, [], el) ->
+						write w "new ";
+						write w (t_s (TInst(cl, [])));
+						write w "(";
+						ignore (List.fold_left (fun acc e ->
+							(if acc <> 0 then write w ", ");
+							expr_s w e;
+							acc + 1
+						) 0 el);
+						write w ")"
+					| TNew ({ cl_kind = KTypeParameter _ } as cl, params, el) ->
+						print w "default(%s) /* This code should never be reached. It was produced by the use of @:generic on a new type parameter instance: %s */" (t_s (TInst(cl,params))) (path_param_s (TClassDecl cl) cl.cl_path params)
+					| TNew (cl, params, el) ->
+						write w "new ";
+						write w (path_param_s (TClassDecl cl) cl.cl_path params);
+						write w "(";
+						ignore (List.fold_left (fun acc e ->
+							(if acc <> 0 then write w ", ");
+							expr_s w e;
+							acc + 1
+						) 0 el);
+						write w ")"
+					| TUnop ((Ast.Increment as op), flag, e)
+					| TUnop ((Ast.Decrement as op), flag, e) ->
+						(match flag with
+							| Ast.Prefix -> write w ( " " ^ (Ast.s_unop op) ^ " " ); expr_s w e
+							| Ast.Postfix -> expr_s w e; write w (Ast.s_unop op))
+					| TUnop (op, flag, e) ->
+						(match flag with
+							| Ast.Prefix -> write w ( " " ^ (Ast.s_unop op) ^ " (" ); expr_s w e; write w ") "
+							| Ast.Postfix -> write w "("; expr_s w e; write w (") " ^ Ast.s_unop op))
+					| TVar (var, eopt) ->
+						print w "%s " (t_s var.v_type);
+						write_id w var.v_name;
+						(match eopt with
+							| None ->
+								write w " = ";
+								expr_s w (null var.v_type e.epos)
+							| Some e ->
+								write w " = ";
+								expr_s w e
+						)
+					| TBlock [e] when was_in_value ->
+						expr_s w e
+					| TBlock el ->
+						begin_block w;
 						List.iter (fun e ->
-							line_directive w e.epos;
+							List.iter (fun e ->
+								line_directive w e.epos;
+								in_value := false;
+								expr_s w e;
+								(if has_semicolon e then write w ";");
+								newline w
+							) (extract_statements e)
+						) el;
+						end_block w
+					| TIf (econd, e1, Some(eelse)) when was_in_value ->
+						let base = t_s e.etype in
+						write w "( ";
+						expr_s w (mk_paren econd);
+						write w " ? ";
+						if t_s e1.etype <> base then
+							expr_s w (mk_cast e.etype e1)
+						else
+							expr_s w (mk_paren e1);
+
+						write w " : ";
+						if t_s eelse.etype <> base then
+							expr_s w (mk_cast e.etype eelse)
+						else
+							expr_s w (mk_paren eelse);
+						write w " )";
+					| TIf (econd, e1, eelse) ->
+						write w "if ";
+						expr_s w (mk_paren econd);
+						write w " ";
+						in_value := false;
+						expr_s w (mk_block e1);
+						(match eelse with
+							| None -> ()
+							| Some e ->
+								write w "else ";
+								in_value := false;
+								let e = match e.eexpr with
+									| TIf _ -> e
+									| TBlock [{eexpr = TIf _} as e] -> e
+									| _ -> mk_block e
+								in
+								expr_s w e
+						)
+					| TWhile (econd, eblock, flag) ->
+						(match flag with
+							| Ast.NormalWhile ->
+								write w "while ";
+								expr_s w (mk_paren econd);
+								write w " ";
+								in_value := false;
+								expr_s w (mk_block eblock)
+							| Ast.DoWhile ->
+								write w "do ";
+								in_value := false;
+								expr_s w (mk_block eblock);
+								write w "while ";
+								in_value := true;
+								expr_s w (mk_paren econd);
+						)
+					| TSwitch (econd, ele_l, default) ->
+						write w "switch ";
+						expr_s w (mk_paren econd);
+						write w " ";
+						begin_block w;
+						List.iter (fun (el, e) ->
+							List.iter (fun e ->
+								write w "case ";
+								in_value := true;
+								expr_s w e;
+								write w ":";
+								newline w;
+							) el;
 							in_value := false;
-							expr_s w e;
-							(if has_semicolon e then write w ";");
+							expr_s w (mk_block e);
+							newline w;
 							newline w
-						) (extract_statements e)
-					) el;
-					end_block w
-				| TIf (econd, e1, Some(eelse)) when was_in_value ->
-					let base = t_s e.etype in
-					write w "( ";
-					expr_s w (mk_paren econd);
-					write w " ? ";
-					if t_s e1.etype <> base then
-						expr_s w (mk_cast e.etype e1)
-					else
-						expr_s w (mk_paren e1);
-
-					write w " : ";
-					if t_s eelse.etype <> base then
-						expr_s w (mk_cast e.etype eelse)
-					else
-						expr_s w (mk_paren eelse);
-					write w " )";
-				| TIf (econd, e1, eelse) ->
-					write w "if ";
-					expr_s w (mk_paren econd);
-					write w " ";
-					in_value := false;
-					expr_s w (mk_block e1);
-					(match eelse with
-						| None -> ()
-						| Some e ->
-							write w "else ";
-							in_value := false;
-							let e = match e.eexpr with
-								| TIf _ -> e
-								| TBlock [{eexpr = TIf _} as e] -> e
-								| _ -> mk_block e
-							in
-							expr_s w e
-					)
-				| TWhile (econd, eblock, flag) ->
-					(match flag with
-						| Ast.NormalWhile ->
-							write w "while ";
-							expr_s w (mk_paren econd);
-							write w " ";
-							in_value := false;
-							expr_s w (mk_block eblock)
-						| Ast.DoWhile ->
-							write w "do ";
+						) ele_l;
+						if is_some default then begin
+							write w "default:";
+							newline w;
 							in_value := false;
-							expr_s w (mk_block eblock);
-							write w "while ";
-							in_value := true;
-							expr_s w (mk_paren econd);
-					)
-				| TSwitch (econd, ele_l, default) ->
-					write w "switch ";
-					expr_s w (mk_paren econd);
-					write w " ";
-					begin_block w;
-					List.iter (fun (el, e) ->
-						List.iter (fun e ->
-							write w "case ";
-							in_value := true;
-							expr_s w e;
-							write w ":";
+							expr_s w (get default);
 							newline w;
-						) el;
-						in_value := false;
-						expr_s w (mk_block e);
-						newline w;
-						newline w
-					) ele_l;
-					if is_some default then begin
-						write w "default:";
-						newline w;
-						in_value := false;
-						expr_s w (get default);
-						newline w;
-					end;
-					end_block w
-				| TTry (tryexpr, ve_l) ->
-					write w "try ";
-					in_value := false;
-					expr_s w (mk_block tryexpr);
-					List.iter (fun (var, e) ->
-						print w "catch (%s %s)" (t_s var.v_type) (var.v_name);
+						end;
+						end_block w
+					| TTry (tryexpr, ve_l) ->
+						write w "try ";
 						in_value := false;
-						expr_s w (mk_block e);
-						newline w
-					) ve_l
-				| TReturn eopt ->
-					write w "return";
-					if is_some eopt then (write w " "; expr_s w (get eopt))
-				| TBreak -> write w "break"
-				| TContinue -> write w "continue"
-				| TThrow e ->
-					write w "throw ";
-					expr_s w e
-				| TCast (e1,md_t) ->
-					((*match gen.gfollow#run_f e.etype with
-						| TType({ t_path = ([], "UInt") }, []) ->
-							write w "( unchecked ((uint) ";
-							expr_s w e1;
-							write w ") )"
-						| _ ->*)
-							(* FIXME I'm ignoring module type *)
-							print w "((%s) (" (t_s e.etype);
-							expr_s w e1;
-							write w ") )"
-					)
-				| TFor (_,_,content) ->
-					write w "[ for not supported ";
-					expr_s w content;
-					write w " ]";
-					if !strict_mode then assert false
-				| TObjectDecl _ -> write w "[ obj decl not supported ]"; if !strict_mode then assert false
-				| TFunction _ -> write w "[ func decl not supported ]"; if !strict_mode then assert false
-				| TEnumParameter _ -> write w "[ enum parameter not supported ]"; if !strict_mode then assert false
-		)
-		and do_call w e el =
-			let params, el = extract_tparams [] el in
-			let params = List.rev params in
-
-			expr_s w e;
-
-			(match params with
-				| _ :: _ when not (erase_generics && field_is_hxgeneric e) ->
-					let md = match e.eexpr with
-						| TField(ef, _) ->
-							t_to_md (run_follow gen ef.etype)
-						| _ -> assert false
-					in
-					write w "<";
-					ignore (List.fold_left (fun acc t ->
-						(if acc <> 0 then write w ", ");
-						write w (t_s t);
-						acc + 1
-					) 0 (change_param_type md params));
-					write w ">"
-				| _ -> ()
-			);
+						expr_s w (mk_block tryexpr);
+						List.iter (fun (var, e) ->
+							print w "catch (%s %s)" (t_s var.v_type) (var.v_name);
+							in_value := false;
+							expr_s w (mk_block e);
+							newline w
+						) ve_l
+					| TReturn eopt ->
+						write w "return";
+						if is_some eopt then (write w " "; expr_s w (get eopt))
+					| TBreak -> write w "break"
+					| TContinue -> write w "continue"
+					| TThrow { eexpr = TIdent "__rethrow__" } ->
+						write w "throw"
+					| TThrow e ->
+						write w "throw ";
+						expr_s w e
+					| TCast (e1,md_t) ->
+						((*match gen.gfollow#run_f e.etype with
+							| TType({ t_path = ([], "UInt") }, []) ->
+								write w "( unchecked ((uint) ";
+								expr_s w e1;
+								write w ") )"
+							| _ ->*)
+								(* FIXME I'm ignoring module type *)
+								print w "((%s) (" (t_s e.etype);
+								expr_s w e1;
+								write w ") )"
+						)
+					| TFor (_,_,content) ->
+						write w "[ for not supported ";
+						expr_s w content;
+						write w " ]";
+						if !strict_mode then assert false
+					| TObjectDecl _ -> write w "[ obj decl not supported ]"; if !strict_mode then assert false
+					| TFunction _ -> write w "[ func decl not supported ]"; if !strict_mode then assert false
+					| TEnumParameter _ -> write w "[ enum parameter not supported ]"; if !strict_mode then assert false
+					| TEnumIndex _ -> write w "[ enum index not supported ]"; if !strict_mode then assert false
+					| TIdent s -> write w "[ ident not supported ]"; if !strict_mode then assert false
+			)
+			and do_call w e el =
+				let params, el = extract_tparams [] el in
+				let params = List.rev params in
+
+				expr_s w e;
+
+				(match params with
+					| _ :: _ when not (erase_generics && field_is_hxgeneric e) ->
+						let md = match e.eexpr with
+							| TField(ef, _) ->
+								t_to_md (run_follow gen ef.etype)
+							| _ -> assert false
+						in
+						write w "<";
+						ignore (List.fold_left (fun acc t ->
+							(if acc <> 0 then write w ", ");
+							write w (t_s t);
+							acc + 1
+						) 0 (change_param_type md params));
+						write w ">"
+					| _ -> ()
+				);
 
-			let rec loop acc elist tlist =
-				match elist, tlist with
-					| e :: etl, (_,_,t) :: ttl ->
-						(if acc <> 0 then write w ", ");
-						(match real_type t with
-							| TType({ t_path = (["cs"], "Ref") }, _)
-							| TAbstract ({ a_path = (["cs"], "Ref") },_) ->
-								let e = ensure_refout e "of type cs.Ref" in
-								write w "ref ";
-								expr_s w e
-							| TType({ t_path = (["cs"], "Out") }, _)
-							| TAbstract ({ a_path = (["cs"], "Out") },_) ->
-								let e = ensure_refout e "of type cs.Out" in
-								write w "out ";
-								expr_s w e
-							| _ ->
-								expr_s w e
-						);
-						loop (acc + 1) etl ttl
-					| e :: etl, [] ->
-						(if acc <> 0 then write w ", ");
-						expr_s w e;
-						loop (acc + 1) etl []
-					| _ -> ()
-			in
-			write w "(";
-			let ft = match follow e.etype with
-				| TFun(args,_) -> args
-				| _ -> []
-			in
+				let rec loop acc elist tlist =
+					match elist, tlist with
+						| e :: etl, (_,_,t) :: ttl ->
+							(if acc <> 0 then write w ", ");
+							(match real_type t with
+								| TType({ t_path = (["cs"], "Ref") }, _)
+								| TAbstract ({ a_path = (["cs"], "Ref") },_) ->
+									let e = ensure_refout e "of type cs.Ref" in
+									write w "ref ";
+									expr_s w e
+								| TType({ t_path = (["cs"], "Out") }, _)
+								| TAbstract ({ a_path = (["cs"], "Out") },_) ->
+									let e = ensure_refout e "of type cs.Out" in
+									write w "out ";
+									expr_s w e
+								| _ ->
+									expr_s w e
+							);
+							loop (acc + 1) etl ttl
+						| e :: etl, [] ->
+							(if acc <> 0 then write w ", ");
+							expr_s w e;
+							loop (acc + 1) etl []
+						| _ -> ()
+				in
+				write w "(";
+				let ft = match follow e.etype with
+					| TFun(args,_) -> args
+					| _ -> []
+				in
 
-			loop 0 el ft;
+				loop 0 el ft;
 
-			write w ")"
+				write w ")"
+			in
+			expr_s w e
 		in
-		expr_s w e
-	in
-
-	let rec gen_fpart_attrib w = function
-		| EConst( Ident i ), _ ->
-			write w i
-		| EField( ef, f ), _ ->
-			gen_fpart_attrib w ef;
-			write w ".";
-			write w f
-		| _, p ->
-			gen.gcon.error "Invalid expression inside @:meta metadata" p
-	in
 
-	let rec gen_spart w = function
-		| EConst c, p -> (match c with
-			| Int s | Float s | Ident s ->
-				write w s
-			| String s ->
-				write w "\"";
-				write w (escape s);
-				write w "\""
-			| _ -> gen.gcon.error "Invalid expression inside @:meta metadata" p)
-		| EField( ef, f ), _ ->
-			gen_spart w ef;
-			write w ".";
-			write w f
-		| EBinop( Ast.OpAssign, (EConst (Ident s), _), e2 ), _ ->
-			write w s;
-			write w " = ";
-			gen_spart w e2
-		| EArrayDecl( el ), _ ->
-			write w "new[] {";
-			let fst = ref true in
-			List.iter (fun e ->
-				if !fst then fst := false else write w ", ";
-				gen_spart w e
-			) el;
-			write w "}"
-		| ECall(fpart,args), _ ->
-			gen_fpart_attrib w fpart;
-			write w "(";
-			let fst = ref true in
-			List.iter (fun e ->
-				if !fst then fst := false else write w ", ";
-				gen_spart w e
-			) args;
-			write w ")"
-		| _, p ->
-			gen.gcon.error "Invalid expression inside @:meta metadata" p
-	in
+		let rec gen_fpart_attrib w = function
+			| EConst( Ident i ), _ ->
+				write w i
+			| EField( ef, f ), _ ->
+				gen_fpart_attrib w ef;
+				write w ".";
+				write w f
+			| _, p ->
+				gen.gcon.error "Invalid expression inside @:meta metadata" p
+		in
 
-	let gen_attributes w metadata =
-		List.iter (function
-			| Meta.Meta, [EConst(String s), _], _ ->
-				write w "[";
+		let rec gen_spart w = function
+			| EConst c, p -> (match c with
+				| Int s | Float s | Ident s ->
+					write w s
+				| String s ->
+					write w "\"";
+					write w (escape s);
+					write w "\""
+				| _ -> gen.gcon.error "Invalid expression inside @:meta metadata" p)
+			| EField( ef, f ), _ ->
+				gen_spart w ef;
+				write w ".";
+				write w f
+			| EBinop( Ast.OpAssign, (EConst (Ident s), _), e2 ), _ ->
 				write w s;
-				write w "]";
-				newline w
-			| Meta.Meta, [meta], _ ->
-				write w "[";
-				gen_spart w meta;
-				write w "]";
-				newline w
-			| _ -> ()
-		) metadata
-	in
-
-	let gen_nocompletion w metadata =
-		if Meta.has Meta.NoCompletion metadata then begin
-			write w "[global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)]";
-			newline w
-		end;
-	in
+				write w " = ";
+				gen_spart w e2
+			| EArrayDecl( el ), _ ->
+				write w "new[] {";
+				let fst = ref true in
+				List.iter (fun e ->
+					if !fst then fst := false else write w ", ";
+					gen_spart w e
+				) el;
+				write w "}"
+			| ECall(fpart,args), _ ->
+				gen_fpart_attrib w fpart;
+				write w "(";
+				let fst = ref true in
+				List.iter (fun e ->
+					if !fst then fst := false else write w ", ";
+					gen_spart w e
+				) args;
+				write w ")"
+			| _, p ->
+				gen.gcon.error "Invalid expression inside @:meta metadata" p
+		in
 
-	let argt_s t =
-		let w = new_source_writer () in
-		let rec run t =
-			match t with
-				| TType (tdef,p) ->
-					gen_attributes w tdef.t_meta;
-					run (follow_once t)
-				| TMono r ->
-					(match !r with
-					| Some t -> run t
-					| _ -> () (* avoid infinite loop / should be the same in this context *))
-				| TLazy f ->
-					run (!f())
+		let gen_attributes w metadata =
+			List.iter (function
+				| Meta.Meta, [EConst(String s), _], _ ->
+					write w "[";
+					write w s;
+					write w "]";
+					newline w
+				| Meta.Meta, [meta], _ ->
+					write w "[";
+					gen_spart w meta;
+					write w "]";
+					newline w
 				| _ -> ()
+			) metadata
 		in
-		run t;
-		let ret = match run_follow gen t with
-			| TType ({ t_path = (["cs"], "Ref") }, [t])
-			| TAbstract ({ a_path = (["cs"], "Ref") },[t]) -> "ref " ^ t_s t
-			| TType ({ t_path = (["cs"], "Out") }, [t])
-			| TAbstract ({ a_path = (["cs"], "Out") },[t]) -> "out " ^ t_s t
-			| t -> t_s t
-		in
-		let c = contents w in
-		if c <> "" then
-			c ^ " " ^ ret
-		else
-			ret
-	in
-
-	let get_string_params cl cl_params =
-		let hxgen = is_hxgen (TClassDecl cl) in
-		match cl_params with
-			| (_ :: _) when not (erase_generics && is_hxgeneric (TClassDecl cl)) ->
-				let get_param_name t = match follow t with TInst(cl, _) -> snd cl.cl_path | _ -> assert false in
-				let params = sprintf "<%s>" (String.concat ", " (List.map (fun (_, tcl) -> get_param_name tcl) cl_params)) in
-				let params_extends =
-					if hxgen
-					(* this is temprorary, see https://github.com/HaxeFoundation/haxe/issues/3526 *)
-					|| not (Meta.has (Meta.Custom ":nativeTypeConstraints") cl.cl_meta)
-					then
-						[""]
-					else
-						List.fold_left (fun acc (name, t) ->
-							match run_follow gen t with
-								| TInst({cl_kind = KTypeParameter constraints}, _) when constraints <> [] ->
-									(* base class should come before interface constraints *)
-									let base_class_constraints = ref [] in
-									let other_constraints = List.fold_left (fun acc t ->
-										match follow t with
-											(* string is implicitly sealed, maybe haxe should have it final as well *)
-											| TInst ({ cl_path=[],"String" }, []) ->
-												acc
-
-											(* non-sealed class *)
-											| TInst ({ cl_interface = false; cl_meta = meta},_) when not (Meta.has Meta.Final meta) ->
-												base_class_constraints := (t_s t) :: !base_class_constraints;
-												acc;
-
-											(* interface *)
-											| TInst ({ cl_interface = true}, _) ->
-												(t_s t) :: acc
-
-											(* skip anything other *)
-											| _ ->
-												acc
-									) [] constraints in
-
-									let s_constraints = (!base_class_constraints @ other_constraints) in
-									if s_constraints <> [] then
-										(sprintf " where %s : %s" (get_param_name t) (String.concat ", " s_constraints) :: acc)
-									else
-										acc;
-								| _ -> acc
-						) [] cl_params in
-				(params, String.concat " " params_extends)
-			| _ -> ("","")
-	in
-
-	let gen_field_decl w visibility v_n modifiers t n =
-		let parts = ref [] in
-		if visibility <> "" then parts := visibility :: !parts;
-		if v_n <> "" then parts := v_n :: !parts;
-		if modifiers <> [] then parts := modifiers @ !parts;
-		if t <> "" then parts := t :: !parts;
-		parts := n :: !parts;
-		write w (String.concat " " (List.rev !parts));
-	in
-
-	let rec gen_event w is_static cl (event,t,custom,add,remove) =
-		let is_interface = cl.cl_interface in
-		let visibility = if is_interface then "" else "public" in
-		let visibility, modifiers = get_fun_modifiers event.cf_meta visibility ["event"] in
-		let v_n = if is_static then "static" else "" in
-		gen_field_decl w visibility v_n modifiers (t_s (run_follow gen t)) (change_field event.cf_name);
-		if custom && not is_interface then begin
-			write w " ";
-			begin_block w;
-			print w "add { _add_%s(value); }" event.cf_name;
-			newline w;
-			print w "remove { _remove_%s(value); }" event.cf_name;
-			newline w;
-			end_block w;
-			newline w;
-		end else
-			write w ";\n";
-		newline w;
-	in
 
-	let rec gen_prop w is_static cl is_final (prop,t,get,set) =
-		gen_attributes w prop.cf_meta;
-		let is_interface = cl.cl_interface in
-		let fn_is_final = function
-			| None -> true
-			| Some ({ cf_kind = Method mkind } as m) ->
-				(match mkind with | MethInline -> true | _ -> false) || Meta.has Meta.Final m.cf_meta
-			| _ -> assert false
+		let gen_nocompletion w metadata =
+			if Meta.has Meta.NoCompletion metadata then begin
+				write w "[global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)]";
+				newline w
+			end;
 		in
-		let is_virtual = not (is_interface || is_final || Meta.has Meta.Final prop.cf_meta || fn_is_final get || fn_is_final set) in
 
-		let fn_is_override = function
-			| Some cf -> List.memq cf cl.cl_overrides
-			| None -> false
-		in
-		let is_override = fn_is_override get || fn_is_override set in
-		let visibility = if is_interface then "" else "public" in
-		let visibility, modifiers = get_fun_modifiers prop.cf_meta visibility [] in
-		let v_n = if is_static then "static" else if is_override && not is_interface then "override" else if is_virtual then "virtual" else "" in
-		gen_nocompletion w prop.cf_meta;
-
-		gen_field_decl w visibility v_n modifiers (t_s (run_follow gen t)) (change_field prop.cf_name);
-
-		let check cf = match cf with
-			| Some ({ cf_overloads = o :: _ } as cf) ->
-					gen.gcon.error "Property functions with more than one overload is currently unsupported" cf.cf_pos;
-					gen.gcon.error "Property functions with more than one overload is currently unsupported" o.cf_pos
-			| _ -> ()
+		let argt_s t =
+			let w = new_source_writer () in
+			let rec run t =
+				match t with
+					| TType (tdef,p) ->
+						gen_attributes w tdef.t_meta;
+						run (follow_once t)
+					| TMono r ->
+						(match !r with
+						| Some t -> run t
+						| _ -> () (* avoid infinite loop / should be the same in this context *))
+					| TLazy f ->
+						run (lazy_type f)
+					| _ -> ()
+			in
+			run t;
+			let ret = match run_follow gen t with
+				| TType ({ t_path = (["cs"], "Ref") }, [t])
+				| TAbstract ({ a_path = (["cs"], "Ref") },[t]) -> "ref " ^ t_s t
+				| TType ({ t_path = (["cs"], "Out") }, [t])
+				| TAbstract ({ a_path = (["cs"], "Out") },[t]) -> "out " ^ t_s t
+				| t -> t_s t
+			in
+			let c = contents w in
+			if c <> "" then
+				c ^ " " ^ ret
+			else
+				ret
 		in
-		check get;
-		check set;
-
-		write w " ";
-		if is_interface then begin
-			write w "{ ";
-			let s = ref "" in
-			(match prop.cf_kind with Var { v_read = AccCall } -> write w "get;"; s := " "; | _ -> ());
-			(match prop.cf_kind with Var { v_write = AccCall } -> print w "%sset;" !s | _ -> ());
-			write w " }";
-			newline w;
-		end else begin
-			begin_block w;
-			(match get with
-				| Some cf ->
-					print w "get { return _get_%s(); }" prop.cf_name;
-					newline w;
-					cf.cf_meta <- (Meta.Custom "?prop_impl", [], null_pos) :: cf.cf_meta;
-				| None -> ());
-			(match set with
-				| Some cf ->
-					print w "set { _set_%s(value); }" prop.cf_name;
-					newline w;
-					cf.cf_meta <- (Meta.Custom "?prop_impl", [], null_pos) :: cf.cf_meta;
-				| None -> ());
-			end_block w;
-			newline w;
-			newline w;
-		end;
-	in
-
-	let needs_unchecked e =
-		let rec loop e = match e.eexpr with
-		(* a non-zero integer constant means that we want unchecked context *)
-		| TConst (TInt i) when i <> Int32.zero ->
-			raise Exit
-
-		(* don't recurse into explicit checked blocks *)
-		| TCall ({ eexpr = TLocal({ v_name = "__checked__" }) }, _) ->
-			()
-
-		(* skip reflection field hashes as they are safe *)
-		| TNew ({ cl_path = (["haxe"; "lang"],"DynamicObject") }, [], [_; e1; _; e2]) ->
-			loop e1;
-			loop e2
-		| TNew ({ cl_path = (["haxe"; "lang"],"Closure") }, [], [eo; _; _]) ->
-			loop eo
-		| TCall ({ eexpr = TField (_, FStatic ({ cl_path = ["haxe"; "lang"],"Runtime" },
-				 { cf_name = "getField" | "setField" | "getField_f" | "setField_f" | "callField" })) },
-				 eo :: _ :: _ :: rest) ->
-			loop eo;
-			List.iter loop rest
 
-		| _ ->
-			Type.iter loop e
+		let get_string_params cl cl_params =
+			let hxgen = is_hxgen (TClassDecl cl) in
+			match cl_params with
+				| (_ :: _) when not (erase_generics && is_hxgeneric (TClassDecl cl)) ->
+					let get_param_name t = match follow t with TInst(cl, _) -> snd cl.cl_path | _ -> assert false in
+					let params = sprintf "<%s>" (String.concat ", " (List.map (fun (_, tcl) -> get_param_name tcl) cl_params)) in
+					let params_extends =
+						if hxgen
+						(* this is temprorary, see https://github.com/HaxeFoundation/haxe/issues/3526 *)
+						|| not (Meta.has (Meta.Custom ":nativeTypeConstraints") cl.cl_meta)
+						then
+							[""]
+						else
+							List.fold_left (fun acc (name, t) ->
+								match run_follow gen t with
+									| TInst({cl_kind = KTypeParameter constraints}, _) when constraints <> [] ->
+										(* base class should come before interface constraints *)
+										let base_class_constraints = ref [] in
+										let other_constraints = List.fold_left (fun acc t ->
+											match follow t with
+												(* string is implicitly sealed, maybe haxe should have it final as well *)
+												| TInst ({ cl_path=[],"String" }, []) ->
+													acc
+
+												(* non-sealed class *)
+												| TInst ({ cl_interface = false; cl_meta = meta},_) when not (Meta.has Meta.Final meta) ->
+													base_class_constraints := (t_s t) :: !base_class_constraints;
+													acc;
+
+												(* interface *)
+												| TInst ({ cl_interface = true}, _) ->
+													(t_s t) :: acc
+
+												(* skip anything other *)
+												| _ ->
+													acc
+										) [] constraints in
+
+										let s_constraints = (!base_class_constraints @ other_constraints) in
+										if s_constraints <> [] then
+											(sprintf " where %s : %s" (get_param_name t) (String.concat ", " s_constraints) :: acc)
+										else
+											acc;
+									| _ -> acc
+							) [] cl_params in
+					(params, String.concat " " params_extends)
+				| _ -> ("","")
 		in
-		try (loop e; false) with Exit -> true
-	in
 
-	let rec gen_class_field w ?(is_overload=false) is_static cl is_final cf =
-		gen_attributes w cf.cf_meta;
-		let is_interface = cl.cl_interface in
-		let name, is_new, is_explicit_iface = match cf.cf_name with
-			| "new" -> snd cl.cl_path, true, false
-			| name when String.contains name '.' ->
-				let fn_name, path = parse_explicit_iface name in
-				(s_type_path path) ^ "." ^ fn_name, false, true
-			| name -> try
-				let binop = PMap.find name binops_names in
-				"operator " ^ s_binop binop, false, false
-			with | Not_found -> try
-				let unop = PMap.find name unops_names in
-				"operator " ^ s_unop unop, false, false
-			with | Not_found ->
-				if Meta.has (Meta.Custom "?prop_impl") cf.cf_meta || Meta.has (Meta.Custom ":cs_event_impl") cf.cf_meta then
-					"_" ^ name, false, false
-				else
-					name, false, false
+		let gen_field_decl w visibility v_n modifiers t n =
+			let parts = ref [] in
+			if visibility <> "" then parts := visibility :: !parts;
+			if v_n <> "" then parts := v_n :: !parts;
+			if modifiers <> [] then parts := modifiers @ !parts;
+			if t <> "" then parts := t :: !parts;
+			parts := n :: !parts;
+			write w (String.concat " " (List.rev !parts));
 		in
-		let rec loop_static cl =
-			match is_static, cl.cl_super with
-				| false, _ -> []
-				| true, None -> []
-				| true, Some(cl,_) ->
-					 (try
-							let cf2 = PMap.find cf.cf_name cl.cl_statics in
-							CastDetect.type_eq gen EqStrict cf.cf_type cf2.cf_type;
-							["new"]
-						with
-							| Not_found | Unify_error _ ->
-									loop_static cl
-						)
-		in
-		let modf = loop_static cl in
-
-		(match cf.cf_kind with
-			| Var _
-			| Method (MethDynamic) when not (Type.is_extern_field cf) ->
-				(if is_overload || List.exists (fun cf -> cf.cf_expr <> None) cf.cf_overloads then
-					gen.gcon.error "Only normal (non-dynamic) methods can be overloaded" cf.cf_pos);
-				if not is_interface then begin
-					let access, modifiers = get_fun_modifiers cf.cf_meta "public" [] in
-					let modifiers = modifiers @ modf in
-					gen_nocompletion w cf.cf_meta;
-					gen_field_decl w access (if is_static then "static" else "") modifiers (t_s (run_follow gen cf.cf_type)) (change_field name);
-					(match cf.cf_expr with
-						| Some e ->
-							write w " = ";
-							expr_s w e;
-						| None -> ()
-					);
-					write w ";"
-				end (* TODO see how (get,set) variable handle when they are interfaces *)
-			| Method _ when Type.is_extern_field cf || (match cl.cl_kind, cf.cf_expr with | KAbstractImpl _, None -> true | _ -> false) ->
-				List.iter (fun cf -> if cl.cl_interface || cf.cf_expr <> None then
-					gen_class_field w ~is_overload:true is_static cl (Meta.has Meta.Final cf.cf_meta) cf
-				) cf.cf_overloads
-			| Var _ | Method MethDynamic -> ()
-			| Method mkind ->
-				List.iter (fun cf ->
-					if cl.cl_interface || cf.cf_expr <> None then
-						gen_class_field w ~is_overload:true is_static cl (Meta.has Meta.Final cf.cf_meta) cf
-				) cf.cf_overloads;
-				let is_virtual = not is_final && match mkind with | MethInline -> false | _ when not is_new -> true | _ -> false in
-				let is_virtual = if not is_virtual || Meta.has Meta.Final cf.cf_meta then false else is_virtual in
-				let is_override = List.memq cf cl.cl_overrides in
-				let is_override = is_override || match cf.cf_name, follow cf.cf_type with
-					| "Equals", TFun([_,_,targ], tret) ->
-						(match follow targ, follow tret with
-							| TDynamic _, TAbstract({ a_path = ([], "Bool") }, []) -> true
-							| _ -> false)
-					| "GetHashCode", TFun([],_) -> true
-					| _ -> false
-				in
-				let is_override = if Meta.has (Meta.Custom "?prop_impl") cf.cf_meta then false else is_override in
-
-				let is_virtual = is_virtual && not (Meta.has Meta.Final cl.cl_meta) && not (is_interface) in
-				let visibility = if is_interface then "" else "public" in
-
-				let visibility, modifiers = get_fun_modifiers cf.cf_meta visibility [] in
-				let modifiers = modifiers @ modf in
-				let visibility, is_virtual = if is_explicit_iface then "",false else if visibility = "private" then "private",false else visibility, is_virtual in
-				let v_n = if is_static then "static" else if is_override && not is_interface then "override" else if is_virtual then "virtual" else "" in
-				let cf_type = if is_override && not is_overload && not (Meta.has Meta.Overload cf.cf_meta) then match field_access gen (TInst(cl, List.map snd cl.cl_params)) cf.cf_name with | FClassField(_,_,_,_,_,actual_t,_) -> actual_t | _ -> assert false else cf.cf_type in
-				let ret_type, args = match follow cf_type with | TFun (strbtl, t) -> (t, strbtl) | _ -> assert false in
-				gen_nocompletion w cf.cf_meta;
-
-				(* public static void funcName *)
-				gen_field_decl w visibility v_n modifiers (if not is_new then (rett_s (run_follow gen ret_type)) else "") (change_field name);
-
-				let params, params_ext = get_string_params cl cf.cf_params in
-				(* <T>(string arg1, object arg2) with T : object *)
-				(match cf.cf_expr with
-				| Some { eexpr = TFunction tf } ->
-						print w "%s(%s)%s" (params) (String.concat ", " (List.map2 (fun (var, _) (_,_,t) -> sprintf "%s %s" (argt_s t) (change_id var.v_name)) tf.tf_args args)) (params_ext)
-				| _ ->
-						print w "%s(%s)%s" (params) (String.concat ", " (List.map (fun (name, _, t) -> sprintf "%s %s" (argt_s t) (change_id name)) args)) (params_ext)
-				);
-				if is_interface then
-					write w ";"
-				else begin
-					write w " ";
-					let rec loop meta =
-						match meta with
-							| [] ->
-								let expr = match cf.cf_expr with
-									| None -> mk (TBlock([])) t_dynamic null_pos
-									| Some s ->
-										match s.eexpr with
-											| TFunction tf ->
-												mk_block (tf.tf_expr)
-											| _ -> assert false (* FIXME *)
-								in
 
-								let write_method_expr e =
-									match e.eexpr with
-									| TBlock [] ->
-										begin_block w;
-										end_block w
-									| TBlock _ ->
-										let unchecked = needs_unchecked e in
-										if unchecked then (begin_block w; write w "unchecked ");
-										let t = Common.timer ["expression to string"] in
-										expr_s w e;
-										t();
-										line_reset_directive w;
-										if unchecked then end_block w
-									| _ ->
-										assert false
-								in
+		let rec gen_event w is_static cl (event,t,custom,add,remove) =
+			let is_interface = cl.cl_interface in
+			let visibility = if is_interface then "" else "public" in
+			let visibility, modifiers = get_fun_modifiers event.cf_meta visibility ["event"] in
+			let v_n = if is_static then "static" else "" in
+			gen_field_decl w visibility v_n modifiers (t_s (run_follow gen t)) (change_field event.cf_name);
+			if custom && not is_interface then begin
+				write w " ";
+				begin_block w;
+				print w "add { _add_%s(value); }" event.cf_name;
+				newline w;
+				print w "remove { _remove_%s(value); }" event.cf_name;
+				newline w;
+				end_block w;
+				newline w;
+			end else
+				write w ";\n";
+			newline w;
+		in
 
-								(if is_new then begin
-									let rec get_super_call el =
-										match el with
-											| ( { eexpr = TCall( { eexpr = TConst(TSuper) }, _) } as call) :: rest ->
-												Some call, rest
-											| ( { eexpr = TBlock(bl) } as block ) :: rest ->
-												let ret, mapped = get_super_call bl in
-												ret, ( { block with eexpr = TBlock(mapped) } :: rest )
-											| _ ->
-												None, el
-									in
-									match expr.eexpr with
-										| TBlock(bl) ->
-											let super_call, rest = get_super_call bl in
-											(match super_call with
-												| None -> ()
-												| Some sc ->
-													write w ": ";
-													let t = Common.timer ["expression to string"] in
-													expr_s w sc;
-													write w " ";
-													t()
-											);
-											write_method_expr { expr with eexpr = TBlock(rest) }
-										| _ -> assert false
-								end else
-									write_method_expr expr
-								)
-							| (Meta.FunctionCode, [Ast.EConst (Ast.String contents),_],_) :: tl ->
-								begin_block w;
-								write w contents;
-								end_block w
-							| _ :: tl -> loop tl
-					in
-					loop cf.cf_meta
+		let rec gen_prop w is_static cl is_final (prop,t,get,set) =
+			gen_attributes w prop.cf_meta;
+			let is_interface = cl.cl_interface in
+			let fn_is_final = function
+				| None -> true
+				| Some ({ cf_kind = Method mkind } as m) ->
+					(match mkind with | MethInline -> true | _ -> false) || Meta.has Meta.Final m.cf_meta
+				| _ -> assert false
+			in
+			let is_virtual = not (is_interface || is_final || Meta.has Meta.Final prop.cf_meta || fn_is_final get || fn_is_final set) in
 
-				end);
-			newline w;
-			newline w;
-	in
+			let fn_is_override = function
+				| Some cf -> List.memq cf cl.cl_overrides
+				| None -> false
+			in
+			let is_override = fn_is_override get || fn_is_override set in
+			let visibility = if is_interface then "" else "public" in
+			let visibility, modifiers = get_fun_modifiers prop.cf_meta visibility [] in
+			let v_n = if is_static then "static" else if is_override && not is_interface then "override" else if is_virtual then "virtual" else "" in
+			gen_nocompletion w prop.cf_meta;
+
+			gen_field_decl w visibility v_n modifiers (t_s (run_follow gen t)) (change_field prop.cf_name);
+
+			let check cf = match cf with
+				| Some ({ cf_overloads = o :: _ } as cf) ->
+						gen.gcon.error "Property functions with more than one overload is currently unsupported" cf.cf_pos;
+						gen.gcon.error "Property functions with more than one overload is currently unsupported" o.cf_pos
+				| _ -> ()
+			in
+			check get;
+			check set;
 
-	let check_special_behaviors w cl = match cl.cl_kind with
-	| KAbstractImpl _ -> ()
-	| _ ->
-		(* get/set pairs *)
-		let pairs = ref PMap.empty in
-		(try
-			let get = PMap.find "__get" cl.cl_fields in
-			List.iter (fun cf ->
-				let args,ret = get_fun cf.cf_type in
-				match args with
-				| [_,_,idx] -> pairs := PMap.add (t_s idx) ( t_s ret, Some cf, None ) !pairs
-				| _ -> gen.gcon.warning "The __get function must have exactly one argument (the index)" cf.cf_pos
-			) (get :: get.cf_overloads)
-		with | Not_found -> ());
-		(try
-			let set = PMap.find "__set" cl.cl_fields in
-			List.iter (fun cf ->
-				let args, ret = get_fun cf.cf_type in
-				match args with
-				| [_,_,idx; _,_,v] -> (try
-					let vt, g, _ = PMap.find (t_s idx) !pairs in
-					let tvt = t_s v in
-					if vt <> tvt then gen.gcon.warning "The __get function of same index has a different type from this __set function" cf.cf_pos;
-					pairs := PMap.add (t_s idx) (vt, g, Some cf) !pairs
-				with | Not_found ->
-					pairs := PMap.add (t_s idx) (t_s v, None, Some cf) !pairs)
-				| _ ->
-					gen.gcon.warning "The __set function must have exactly two arguments (index, value)" cf.cf_pos
-			) (set :: set.cf_overloads)
-		with | Not_found -> ());
-		PMap.iter (fun idx (v, get, set) ->
-			print w "public %s this[%s index]" v idx;
+			write w " ";
+			if is_interface then begin
+				write w "{ ";
+				let s = ref "" in
+				(match prop.cf_kind with Var { v_read = AccCall } -> write w "get;"; s := " "; | _ -> ());
+				(match prop.cf_kind with Var { v_write = AccCall } -> print w "%sset;" !s | _ -> ());
+				write w " }";
+				newline w;
+			end else begin
 				begin_block w;
 				(match get with
-				| None -> ()
-				| Some _ ->
-					write w "get";
-					begin_block w;
-					write w "return this.__get(index);";
-					end_block w);
+					| Some cf ->
+						print w "get { return _get_%s(); }" prop.cf_name;
+						newline w;
+						cf.cf_meta <- (Meta.Custom "?prop_impl", [], null_pos) :: cf.cf_meta;
+					| None -> ());
 				(match set with
-				| None -> ()
-				| Some _ ->
-					write w "set";
-					begin_block w;
-					write w "this.__set(index,value);";
-					end_block w);
-				end_block w) !pairs;
-		(if not (PMap.is_empty !pairs) then try
-			let get = PMap.find "__get" cl.cl_fields in
-			let idx_t, v_t = match follow get.cf_type with
-				| TFun([_,_,arg_t],ret_t) ->
-					t_s (run_follow gen arg_t), t_s (run_follow gen ret_t)
-				| _ -> gen.gcon.error "The __get function must be a function with one argument. " get.cf_pos; assert false
-			in
-			List.iter (fun (cl,args) ->
-				match cl.cl_array_access with
-					| None -> ()
-					| Some t ->
-						let changed_t = apply_params cl.cl_params (List.map (fun _ -> t_dynamic) cl.cl_params) t in
-						let t_as_s = t_s (run_follow gen changed_t) in
-						print w "%s %s.this[int key]" t_as_s (t_s (TInst(cl, args)));
-							begin_block w;
-							write w "get";
-							begin_block w;
-								print w "return ((%s) this.__get(key));" t_as_s;
-							end_block w;
-							write w "set";
-							begin_block w;
-								print w "this.__set(key, (%s) value);" v_t;
-							end_block w;
-						end_block w;
+					| Some cf ->
+						print w "set { _set_%s(value); }" prop.cf_name;
 						newline w;
-						newline w
-			) cl.cl_implements
-		with | Not_found -> ());
-		if cl.cl_interface && is_hxgen (TClassDecl cl) && is_some cl.cl_array_access then begin
-			let changed_t = apply_params cl.cl_params (List.map (fun _ -> t_dynamic) cl.cl_params) (get cl.cl_array_access) in
-			print w "%s this[int key]" (t_s (run_follow gen changed_t));
-			begin_block w;
-				write w "get;";
+						cf.cf_meta <- (Meta.Custom "?prop_impl", [], null_pos) :: cf.cf_meta;
+					| None -> ());
+				end_block w;
 				newline w;
-				write w "set;";
 				newline w;
-			end_block w;
-			newline w;
-			newline w
-		end;
-		(try
-			if cl.cl_interface then raise Not_found;
-			let cf = PMap.find "toString" cl.cl_fields in
-			(if List.exists (fun c -> c.cf_name = "toString") cl.cl_overrides then raise Not_found);
-			(match cf.cf_type with
-				| TFun([], ret) ->
-					(match real_type ret with
-						| TInst( { cl_path = ([], "String") }, []) ->
-							write w "public override string ToString()";
-							begin_block w;
-							write w "return this.toString();";
-							end_block w;
-							newline w;
-							newline w
-						| _ ->
-							gen.gcon.error "A toString() function should return a String!" cf.cf_pos
-					)
-				| _ -> ()
-			)
-		with | Not_found -> ());
-		(try
-			if cl.cl_interface then raise Not_found;
-			let cf = PMap.find "finalize" cl.cl_fields in
-			(if List.exists (fun c -> c.cf_name = "finalize") cl.cl_overrides then raise Not_found);
-			(match cf.cf_type with
-				| TFun([], ret) ->
-					(match real_type ret with
-						| TAbstract( { a_path = ([], "Void") }, []) ->
-							write w "~";
-							write w (snd cl.cl_path);
-							write w "()";
-							begin_block w;
-							write w "this.finalize();";
-							end_block w;
-							newline w;
-							newline w
-						| _ ->
-							gen.gcon.error "A finalize() function should be Void->Void!" cf.cf_pos
-					)
-				| _ -> ()
-			)
-		with | Not_found -> ());
-		(* properties *)
-		let handle_prop static f =
-			match f.cf_kind with
-			| Method _ -> ()
-			| Var v when not (Type.is_extern_field f) -> ()
-			| Var v ->
-				let prop acc = match acc with
-					| AccNo | AccNever | AccCall -> true
-					| _ -> false
-				in
-				if prop v.v_read && prop v.v_write && (v.v_read = AccCall || v.v_write = AccCall) then begin
-					let this = if static then
-						ExprBuilder.make_static_this cl f.cf_pos
-					else
-						{ eexpr = TConst TThis; etype = TInst(cl,List.map snd cl.cl_params); epos = f.cf_pos }
-					in
-					print w "public %s%s %s" (if static then "static " else "") (t_s f.cf_type) (netname_to_hx f.cf_name);
-					begin_block w;
-					(match v.v_read with
-					| AccCall ->
-						write w "get";
-						begin_block w;
-						write w "return ";
-						expr_s w this;
-						print w ".get_%s();" f.cf_name;
-						end_block w
-					| _ -> ());
-					(match v.v_write with
-					| AccCall ->
-						write w "set";
-						begin_block w;
-						expr_s w this;
-						print w ".set_%s(value);" f.cf_name;
-						end_block w
-					| _ -> ());
-					end_block w;
-				end
+			end;
 		in
-		if Meta.has Meta.BridgeProperties cl.cl_meta then begin
-			List.iter (handle_prop true) cl.cl_ordered_statics;
-			List.iter (handle_prop false) cl.cl_ordered_fields;
-		end
-	in
 
-	let gen_class w cl =
-		write w "#pragma warning disable 109, 114, 219, 429, 168, 162";
-		newline w;
-		let should_close = match change_ns (TClassDecl cl) (fst (cl.cl_path)) with
-			| [] -> false
-			| ns ->
-				print w "namespace %s " (String.concat "." ns);
-				begin_block w;
-				true
-		in
+		let needs_unchecked e =
+			let rec loop e = match e.eexpr with
+			(* a non-zero integer constant means that we want unchecked context *)
+			| TConst (TInt i) when i <> Int32.zero ->
+				raise Exit
 
-		(try
-			let _,m,_ = Meta.get (Meta.Custom "generic_iface") cl.cl_meta in
-			let rec loop i acc =
-				if i == 0 then
-					acc
-				else
-					"object" :: (loop (pred i) acc)
+			(* don't recurse into explicit checked blocks *)
+			| TCall ({ eexpr = TIdent "__checked__" }, _) ->
+				()
+
+			(* skip reflection field hashes as they are safe *)
+			| TNew ({ cl_path = (["haxe"; "lang"],"DynamicObject") }, [], [_; e1; _; e2]) ->
+				loop e1;
+				loop e2
+			| TNew ({ cl_path = (["haxe"; "lang"],"Closure") }, [], [eo; _; _]) ->
+				loop eo
+			| TCall ({ eexpr = TField (_, FStatic ({ cl_path = ["haxe"; "lang"],"Runtime" },
+					{ cf_name = "getField" | "setField" | "getField_f" | "setField_f" | "callField" })) },
+					eo :: _ :: _ :: rest) ->
+				loop eo;
+				List.iter loop rest
+
+			| _ ->
+				Type.iter loop e
 			in
-			let tparams = loop (match m with [(EConst(Int s),_)] -> int_of_string s | _ -> assert false) [] in
-			cl.cl_meta <- (Meta.Meta, [
-				EConst(String("global::haxe.lang.GenericInterface(typeof(global::" ^ module_s (TClassDecl cl) ^ "<" ^ String.concat ", " tparams ^ ">))") ), cl.cl_pos
-			], cl.cl_pos) :: cl.cl_meta
-		with Not_found ->
-			());
-
-		gen_attributes w cl.cl_meta;
-
-		let is_main =
-			match gen.gcon.main_class with
-				| Some ( (_,"Main") as path) when path = cl.cl_path && not cl.cl_interface ->
-					(*
-						for cases where the main class is called Main, there will be a problem with creating the entry point there.
-						In this special case, a special entry point class will be created
-					*)
-					write w "public class EntryPoint__Main ";
-					begin_block w;
-					write w "public static void Main() ";
-					begin_block w;
-					(if Hashtbl.mem gen.gtypes (["cs"], "Boot") then write w "global::cs.Boot.init();"; newline w);
-					(match gen.gcon.main with
-						| None ->
-							expr_s w { eexpr = TTypeExpr(TClassDecl cl); etype = t_dynamic; epos = null_pos };
-							write w ".main();"
-						| Some expr ->
-							expr_s w (mk_block expr));
-					end_block w;
-					end_block w;
-					newline w;
-					false
-				| Some path when path = cl.cl_path && not cl.cl_interface -> true
-				| _ -> false
+			try (loop e; false) with Exit -> true
 		in
 
-		let clt, access, modifiers = get_class_modifiers cl.cl_meta (if cl.cl_interface then "interface" else "class") "public" [] in
-		let is_final = clt = "struct" || Meta.has Meta.Final cl.cl_meta in
-
-		let modifiers = [access] @ modifiers in
-		print w "%s %s %s" (String.concat " " modifiers) clt (change_clname (snd cl.cl_path));
-		(* type parameters *)
-		let params, params_ext = get_string_params cl cl.cl_params in
-		let extends_implements = (match cl.cl_super with | None -> [] | Some (cl,p) -> [path_param_s (TClassDecl cl) cl.cl_path p]) @ (List.map (fun (cl,p) -> path_param_s (TClassDecl cl) cl.cl_path p) cl.cl_implements) in
-		(match extends_implements with
-			| [] -> print w "%s%s " params params_ext
-			| _ -> print w "%s : %s%s " params (String.concat ", " extends_implements) params_ext);
-		(* class head ok: *)
-		(* public class Test<A> : X, Y, Z where A : Y *)
-		begin_block w;
-		newline w;
-		(* our constructor is expected to be a normal "new" function *
-		if !strict_mode && is_some cl.cl_constructor then assert false;*)
-
-		let rec loop meta =
-			match meta with
-				| [] -> ()
-				| (Meta.ClassCode, [Ast.EConst (Ast.String contents),_],_) :: tl ->
-					write w contents
-				| _ :: tl -> loop tl
-		in
-		loop cl.cl_meta;
+		let rec gen_class_field w ?(is_overload=false) is_static cl is_final cf =
+			gen_attributes w cf.cf_meta;
+			let is_interface = cl.cl_interface in
+			let name, is_new, is_explicit_iface = match cf.cf_name with
+				| "new" -> snd cl.cl_path, true, false
+				| name when String.contains name '.' ->
+					let fn_name, path = parse_explicit_iface name in
+					(s_type_path path) ^ "." ^ fn_name, false, true
+				| name -> try
+					let binop = PMap.find name binops_names in
+					"operator " ^ s_binop binop, false, false
+				with | Not_found -> try
+					let unop = PMap.find name unops_names in
+					"operator " ^ s_unop unop, false, false
+				with | Not_found ->
+					if Meta.has (Meta.Custom "?prop_impl") cf.cf_meta || Meta.has (Meta.Custom ":cs_event_impl") cf.cf_meta then
+						"_" ^ name, false, false
+					else
+						name, false, false
+			in
+			let rec loop_static cl =
+				match is_static, cl.cl_super with
+					| false, _ -> []
+					| true, None -> []
+					| true, Some(cl,_) ->
+						(try
+								let cf2 = PMap.find cf.cf_name cl.cl_statics in
+								CastDetect.type_eq gen EqStrict cf.cf_type cf2.cf_type;
+								["new"]
+							with
+								| Not_found | Unify_error _ ->
+										loop_static cl
+							)
+			in
+			let modf = loop_static cl in
+
+			(match cf.cf_kind with
+				| Var _
+				| Method (MethDynamic) when Type.is_physical_field cf ->
+					(if is_overload || List.exists (fun cf -> cf.cf_expr <> None) cf.cf_overloads then
+						gen.gcon.error "Only normal (non-dynamic) methods can be overloaded" cf.cf_pos);
+					if not is_interface then begin
+						let access, modifiers = get_fun_modifiers cf.cf_meta "public" [] in
+						let modifiers = modifiers @ modf in
+						gen_nocompletion w cf.cf_meta;
+						gen_field_decl w access (if is_static then "static" else "") modifiers (t_s (run_follow gen cf.cf_type)) (change_field name);
+						(match cf.cf_expr with
+							| Some e ->
+								write w " = ";
+								expr_s w e;
+							| None -> ()
+						);
+						write w ";"
+					end (* TODO see how (get,set) variable handle when they are interfaces *)
+				| Method _ when not (Type.is_physical_field cf) || (match cl.cl_kind, cf.cf_expr with | KAbstractImpl _, None -> true | _ -> false) ->
+					List.iter (fun cf -> if cl.cl_interface || cf.cf_expr <> None then
+						gen_class_field w ~is_overload:true is_static cl (Meta.has Meta.Final cf.cf_meta) cf
+					) cf.cf_overloads
+				| Var _ | Method MethDynamic -> ()
+				| Method _ when is_new && Meta.has Meta.Struct cl.cl_meta && fst (get_fun cf.cf_type) = [] ->
+						(* make sure that the method is empty *)
+						let rec check_empty expr = match expr.eexpr with
+							| TBlock(bl) -> bl = [] || List.for_all check_empty bl
+							| TMeta(_,e) -> check_empty e
+							| TParenthesis(e) -> check_empty e
+							| TConst(TNull) -> true
+							| TFunction(tf) -> check_empty tf.tf_expr
+							| _ -> false
+						in
+						(match cf.cf_expr with
+							| Some e ->
+								if not (check_empty e) then
+									gen.gcon.error "The body of a zero argument constructor of a struct should be empty" e.epos
+							| _ -> ());
+						List.iter (fun cf ->
+							if cl.cl_interface || cf.cf_expr <> None then
+								gen_class_field w ~is_overload:true is_static cl (Meta.has Meta.Final cf.cf_meta) cf
+						) cf.cf_overloads;
+				| Method mkind ->
+					List.iter (fun cf ->
+						if cl.cl_interface || cf.cf_expr <> None then
+							gen_class_field w ~is_overload:true is_static cl (Meta.has Meta.Final cf.cf_meta) cf
+					) cf.cf_overloads;
+					let is_virtual = not is_final && match mkind with | MethInline -> false | _ when not is_new -> true | _ -> false in
+					let is_virtual = if not is_virtual || Meta.has Meta.Final cf.cf_meta then false else is_virtual in
+					let is_override = List.memq cf cl.cl_overrides in
+					let is_override = is_override || match cf.cf_name, follow cf.cf_type with
+						| "Equals", TFun([_,_,targ], tret) ->
+							(match follow targ, follow tret with
+								| TDynamic _, TAbstract({ a_path = ([], "Bool") }, []) -> true
+								| _ -> false)
+						| "GetHashCode", TFun([],_) -> true
+						| _ -> false
+					in
+					let is_override = if Meta.has (Meta.Custom "?prop_impl") cf.cf_meta then false else is_override in
 
-		if is_main then begin
-			write w "public static void Main()";
-			begin_block w;
-			(if Hashtbl.mem gen.gtypes (["cs"], "Boot") then write w "global::cs.Boot.init();"; newline w);
-			(match gen.gcon.main with
-				| None ->
-					write w "main();";
-				| Some expr ->
-						expr_s w (mk_block expr));
-			end_block w
-		end;
+					let is_virtual = is_virtual && not (Meta.has Meta.Final cl.cl_meta) && not (is_interface) in
+					let visibility = if is_interface then "" else "public" in
 
-		(match cl.cl_init with
-			| None -> ()
-			| Some init ->
-				print w "static %s() " (snd cl.cl_path);
-				if needs_unchecked init then begin
-					begin_block w;
-					write w "unchecked ";
-					expr_s w (mk_block init);
-					end_block w;
-				end else
-					expr_s w (mk_block init);
-				line_reset_directive w;
-				newline w;
-				newline w
-		);
+					let visibility, modifiers = get_fun_modifiers cf.cf_meta visibility [] in
+					let modifiers = modifiers @ modf in
+					let visibility, is_virtual = if is_explicit_iface then "",false else if visibility = "private" then "private",false else visibility, is_virtual in
+					let v_n = if is_static then "static" else if is_override && not is_interface then "override" else if is_virtual then "virtual" else "" in
+					let cf_type = if is_override && not is_overload && not (Meta.has Meta.Overload cf.cf_meta) then match field_access gen (TInst(cl, List.map snd cl.cl_params)) cf.cf_name with | FClassField(_,_,_,_,_,actual_t,_) -> actual_t | _ -> assert false else cf.cf_type in
+					let ret_type, args = match follow cf_type with | TFun (strbtl, t) -> (t, strbtl) | _ -> assert false in
+					gen_nocompletion w cf.cf_meta;
 
-		(* collect properties and events *)
-		let partition cf cflist =
-			let events, props, nonprops = ref [], ref [], ref [] in
+					(* public static void funcName *)
+					gen_field_decl w visibility v_n modifiers (if not is_new then (rett_s (run_follow gen ret_type)) else "") (change_field name);
 
-			List.iter (fun v -> match v.cf_kind with
-				| Var { v_read = AccCall } | Var { v_write = AccCall } when Type.is_extern_field v && Meta.has Meta.Property v.cf_meta ->
-					props := (v.cf_name, ref (v, v.cf_type, None, None)) :: !props;
-				| Var { v_read = AccNormal; v_write = AccNormal } when Meta.has Meta.Event v.cf_meta ->
-					events := (v.cf_name, ref (v, v.cf_type, false, None, None)) :: !events;
-				| _ ->
-					nonprops := v :: !nonprops;
-			) cflist;
-
-			let events, nonprops = !events, !nonprops in
-
-			let t = TInst(cl, List.map snd cl.cl_params) in
-			let find_prop name = try
-					List.assoc name !props
-				with | Not_found -> match field_access gen t name with
-					| FClassField (_,_,decl,v,_,t,_) when is_extern_prop (TInst(cl,List.map snd cl.cl_params)) name ->
-						let ret = ref (v,t,None,None) in
-						props := (name, ret) :: !props;
-						ret
-					| _ -> raise Not_found
-			in
+					let params, params_ext = get_string_params cl cf.cf_params in
+					(* <T>(string arg1, object arg2) with T : object *)
+					(match cf.cf_expr with
+					| Some { eexpr = TFunction tf } ->
+							print w "%s(%s)%s" (params) (String.concat ", " (List.map2 (fun (var, _) (_,_,t) -> sprintf "%s %s" (argt_s t) (change_id var.v_name)) tf.tf_args args)) (params_ext)
+					| _ ->
+							print w "%s(%s)%s" (params) (String.concat ", " (List.map (fun (name, _, t) -> sprintf "%s %s" (argt_s t) (change_id name)) args)) (params_ext)
+					);
+					if is_interface then
+						write w ";"
+					else begin
+						write w " ";
+						let rec loop meta =
+							match meta with
+								| [] ->
+									let expr = match cf.cf_expr with
+										| None -> mk (TBlock([])) t_dynamic null_pos
+										| Some s ->
+											match s.eexpr with
+												| TFunction tf ->
+													mk_block (tf.tf_expr)
+												| _ -> assert false (* FIXME *)
+									in
 
-			let find_event name = List.assoc name events in
+									let write_method_expr e =
+										match e.eexpr with
+										| TBlock [] ->
+											begin_block w;
+											end_block w
+										| TBlock _ ->
+											let unchecked = needs_unchecked e in
+											if unchecked then (begin_block w; write w "unchecked ");
+											let t = Common.timer ["expression to string"] in
+											expr_s w e;
+											t();
+											line_reset_directive w;
+											if unchecked then end_block w
+										| _ ->
+											assert false
+									in
 
-			let is_empty_function cf = match cf.cf_expr with
-				| Some {eexpr = TFunction { tf_expr = {eexpr = TBlock []}}} -> true
-				| _ -> false
-			in
+									(if is_new then begin
+										let rec get_super_call el =
+											match el with
+												| ( { eexpr = TCall( { eexpr = TConst(TSuper) }, _) } as call) :: rest ->
+													Some call, rest
+												| ( { eexpr = TBlock(bl) } as block ) :: rest ->
+													let ret, mapped = get_super_call bl in
+													ret, ( { block with eexpr = TBlock(mapped) } :: rest )
+												| _ ->
+													None, el
+										in
+										match expr.eexpr with
+											| TBlock(bl) ->
+												let super_call, rest = get_super_call bl in
+												(match super_call with
+													| None -> ()
+													| Some sc ->
+														write w ": ";
+														let t = Common.timer ["expression to string"] in
+														expr_s w sc;
+														write w " ";
+														t()
+												);
+												write_method_expr { expr with eexpr = TBlock(rest) }
+											| _ -> assert false
+									end else
+										write_method_expr expr
+									)
+								| (Meta.FunctionCode, [Ast.EConst (Ast.String contents),_],_) :: tl ->
+									begin_block w;
+									write w contents;
+									end_block w
+								| _ :: tl -> loop tl
+						in
+						loop cf.cf_meta
 
-			let interf = cl.cl_interface in
-			(* get all functions that are getters/setters *)
-			let nonprops = List.filter (function
-				| cf when String.starts_with cf.cf_name "get_" -> (try
-					(* find the property *)
-					let prop = find_prop (String.sub cf.cf_name 4 (String.length cf.cf_name - 4)) in
-					let v, t, get, set = !prop in
-					assert (get = None);
-					prop := (v,t,Some cf,set);
-					not interf
-				with | Not_found -> true)
-				| cf when String.starts_with cf.cf_name "set_" -> (try
-					(* find the property *)
-					let prop = find_prop (String.sub cf.cf_name 4 (String.length cf.cf_name - 4)) in
-					let v, t, get, set = !prop in
-					assert (set = None);
-					prop := (v,t,get,Some cf);
-					not interf
-				with | Not_found -> true)
-				| cf when String.starts_with cf.cf_name "add_" -> (try
-					let event = find_event (String.sub cf.cf_name 4 (String.length cf.cf_name - 4)) in
-					let v, t, _, add, remove = !event in
-					assert (add = None);
-					let custom = not (is_empty_function cf) in
-					event := (v, t, custom, Some cf, remove);
-					false
-				with | Not_found -> true)
-				| cf when String.starts_with cf.cf_name "remove_" -> (try
-					let event = find_event (String.sub cf.cf_name 7 (String.length cf.cf_name - 7)) in
-					let v, t, _, add, remove = !event in
-					assert (remove = None);
-					let custom = not (is_empty_function cf) in
-					event := (v, t, custom, add, Some cf);
-					false
-				with | Not_found -> true)
-				| _ -> true
-			) nonprops in
-
-			let nonprops = ref nonprops in
-			List.iter (fun (n,r) ->
-				let ev, t, custom, add, remove = !r in
-				match add, remove with
-				| Some add, Some remove ->
-					if custom && not cl.cl_interface then
-						nonprops := add :: remove :: !nonprops
-				| _ -> assert false (* shouldn't happen because Filters.check_cs_events makes sure methods are present *)
-			) events;
-
-			let evts = List.map (fun(_,v) -> !v) events in
-			let ret = List.map (fun (_,v) -> !v) !props in
-			let ret = List.filter (function | (_,_,None,None) -> false | _ -> true) ret in
-			evts, ret, List.rev !nonprops
+					end);
+				newline w;
+				newline w;
 		in
 
-		let fevents, fprops, fnonprops = partition cl cl.cl_ordered_fields in
-		let sevents, sprops, snonprops = partition cl cl.cl_ordered_statics in
-		(if is_some cl.cl_constructor then gen_class_field w false cl is_final (get cl.cl_constructor));
-		if not cl.cl_interface then begin
-			(* we don't want to generate properties for abstract implementation classes, because they don't have object to work with *)
-			List.iter (gen_event w true cl) sevents;
-			if (match cl.cl_kind with KAbstractImpl _ -> false | _ -> true) then List.iter (gen_prop w true cl is_final) sprops;
-			List.iter (gen_class_field w true cl is_final) snonprops
-		end;
-		List.iter (gen_event w false cl) fevents;
-		List.iter (gen_prop w false cl is_final) fprops;
-		List.iter (gen_class_field w false cl is_final) fnonprops;
-		check_special_behaviors w cl;
-		end_block w;
-		if cl.cl_interface && cl.cl_ordered_statics <> [] then begin
-			print w "public class %s__Statics_" (snd cl.cl_path);
-			begin_block w;
-			List.iter (gen_class_field w true { cl with cl_interface = false } is_final) cl.cl_ordered_statics;
-			end_block w
-		end;
-		if should_close then end_block w
-	in
-
-
-	let gen_enum w e =
-		let should_close = match change_ns (TEnumDecl e) (fst e.e_path) with
-			| [] -> false
-			| ns ->
-				print w "namespace %s" (String.concat "." ns);
+		let check_special_behaviors w cl = match cl.cl_kind with
+		| KAbstractImpl _ -> ()
+		| _ ->
+			(* get/set pairs *)
+			let pairs = ref PMap.empty in
+			(try
+				let get = PMap.find "__get" cl.cl_fields in
+				List.iter (fun cf ->
+					let args,ret = get_fun cf.cf_type in
+					match args with
+					| [_,_,idx] -> pairs := PMap.add (t_s idx) ( t_s ret, Some cf, None ) !pairs
+					| _ -> gen.gcon.warning "The __get function must have exactly one argument (the index)" cf.cf_pos
+				) (get :: get.cf_overloads)
+			with | Not_found -> ());
+			(try
+				let set = PMap.find "__set" cl.cl_fields in
+				List.iter (fun cf ->
+					let args, ret = get_fun cf.cf_type in
+					match args with
+					| [_,_,idx; _,_,v] -> (try
+						let vt, g, _ = PMap.find (t_s idx) !pairs in
+						let tvt = t_s v in
+						if vt <> tvt then gen.gcon.warning "The __get function of same index has a different type from this __set function" cf.cf_pos;
+						pairs := PMap.add (t_s idx) (vt, g, Some cf) !pairs
+					with | Not_found ->
+						pairs := PMap.add (t_s idx) (t_s v, None, Some cf) !pairs)
+					| _ ->
+						gen.gcon.warning "The __set function must have exactly two arguments (index, value)" cf.cf_pos
+				) (set :: set.cf_overloads)
+			with | Not_found -> ());
+			PMap.iter (fun idx (v, get, set) ->
+				print w "public %s this[%s index]" v idx;
+					begin_block w;
+					(match get with
+					| None -> ()
+					| Some _ ->
+						write w "get";
+						begin_block w;
+						write w "return this.__get(index);";
+						end_block w);
+					(match set with
+					| None -> ()
+					| Some _ ->
+						write w "set";
+						begin_block w;
+						write w "this.__set(index,value);";
+						end_block w);
+					end_block w) !pairs;
+			(if not (PMap.is_empty !pairs) then try
+				let get = PMap.find "__get" cl.cl_fields in
+				let idx_t, v_t = match follow get.cf_type with
+					| TFun([_,_,arg_t],ret_t) ->
+						t_s (run_follow gen arg_t), t_s (run_follow gen ret_t)
+					| _ -> gen.gcon.error "The __get function must be a function with one argument. " get.cf_pos; assert false
+				in
+				List.iter (fun (cl,args) ->
+					match cl.cl_array_access with
+						| None -> ()
+						| Some t ->
+							let changed_t = apply_params cl.cl_params (List.map (fun _ -> t_dynamic) cl.cl_params) t in
+							let t_as_s = t_s (run_follow gen changed_t) in
+							print w "%s %s.this[int key]" t_as_s (t_s (TInst(cl, args)));
+								begin_block w;
+								write w "get";
+								begin_block w;
+									print w "return ((%s) this.__get(key));" t_as_s;
+								end_block w;
+								write w "set";
+								begin_block w;
+									print w "this.__set(key, (%s) value);" v_t;
+								end_block w;
+							end_block w;
+							newline w;
+							newline w
+				) cl.cl_implements
+			with | Not_found -> ());
+			if cl.cl_interface && is_hxgen (TClassDecl cl) && is_some cl.cl_array_access then begin
+				let changed_t = apply_params cl.cl_params (List.map (fun _ -> t_dynamic) cl.cl_params) (get cl.cl_array_access) in
+				print w "%s this[int key]" (t_s (run_follow gen changed_t));
 				begin_block w;
-				true
-		in
-		gen_attributes w e.e_meta;
-
-		print w "public enum %s" (change_clname (snd e.e_path));
-		begin_block w;
-		write w (String.concat ", " (List.map (change_id) e.e_names));
-		end_block w;
-
-		if should_close then end_block w
-	in
-
-	let module_type_gen w md_tp =
-		let file_start = len w = 0 in
-		let requires_root = no_root && file_start in
-		if file_start then
-			Codegen.map_source_header gen.gcon (fun s -> print w "// %s\n" s);
-		reset_temps();
-		match md_tp with
-			| TClassDecl cl ->
-				if not cl.cl_extern then begin
-					(if requires_root then write w "using haxe.root;\n"; newline w;);
-					gen_class w cl;
+					write w "get;";
 					newline w;
-					newline w
-				end;
-				(not cl.cl_extern)
-			| TEnumDecl e ->
-				if not e.e_extern && not (Meta.has Meta.Class e.e_meta) then begin
-					(if requires_root then write w "using haxe.root;\n"; newline w;);
-					gen_enum w e;
+					write w "set;";
 					newline w;
-					newline w
-				end;
-				(not e.e_extern)
-			| TAbstractDecl _
-			| TTypeDecl _ ->
-				false
-	in
-
-	(* generate source code *)
-	init_ctx gen;
-
-	Hashtbl.add gen.gspecial_vars "__rethrow__" true;
-	Hashtbl.add gen.gspecial_vars "__typeof__" true;
-	Hashtbl.add gen.gspecial_vars "__label__" true;
-	Hashtbl.add gen.gspecial_vars "__goto__" true;
-	Hashtbl.add gen.gspecial_vars "__is__" true;
-	Hashtbl.add gen.gspecial_vars "__as__" true;
-	Hashtbl.add gen.gspecial_vars "__cs__" true;
-
-	Hashtbl.add gen.gspecial_vars "__checked__" true;
-	Hashtbl.add gen.gspecial_vars "__lock__" true;
-	Hashtbl.add gen.gspecial_vars "__fixed__" true;
-	Hashtbl.add gen.gspecial_vars "__unsafe__" true;
-	Hashtbl.add gen.gspecial_vars "__addressOf__" true;
-	Hashtbl.add gen.gspecial_vars "__valueOf__" true;
-	Hashtbl.add gen.gspecial_vars "__sizeof__" true;
-	Hashtbl.add gen.gspecial_vars "__stackalloc__" true;
-
-	Hashtbl.add gen.gspecial_vars "__delegate__" true;
-	Hashtbl.add gen.gspecial_vars "__array__" true;
-	Hashtbl.add gen.gspecial_vars "__ptr__" true;
-
-	Hashtbl.add gen.gsupported_conversions (["haxe"; "lang"], "Null") (fun t1 t2 -> true);
-	let last_needs_box = gen.gneeds_box in
-	gen.gneeds_box <- (fun t -> match (gen.greal_type t) with
-		| TAbstract( ( { a_path = ["cs"], "Pointer" }, _ ) )
-		| TInst( { cl_path = ["cs"], "Pointer" }, _ )
-		| TInst( { cl_path = (["haxe"; "lang"], "Null") }, _ ) -> true
-		| _ -> last_needs_box t);
-
-	gen.greal_type <- real_type;
-	gen.greal_type_param <- change_param_type;
-
-	SetHXGen.run_filter gen;
-
-	(* before running the filters, follow all possible types *)
-	(* this is needed so our module transformations don't break some core features *)
-	(* like multitype selection *)
-	let run_follow_gen = run_follow gen in
-	let rec type_map e = Type.map_expr_type (fun e->type_map e) (run_follow_gen)	(fun tvar-> tvar.v_type <- (run_follow_gen tvar.v_type); tvar) e in
-	let super_map (cl,tl) = (cl, List.map run_follow_gen tl) in
-	List.iter (function
-		| TClassDecl cl ->
-				let all_fields = (Option.map_default (fun cf -> [cf]) [] cl.cl_constructor) @ cl.cl_ordered_fields @ cl.cl_ordered_statics in
-				List.iter (fun cf ->
-					cf.cf_type <- run_follow_gen cf.cf_type;
-					cf.cf_expr <- Option.map type_map cf.cf_expr;
-
-					(* add @:skipReflection to @:event vars *)
-					match cf.cf_kind with
-					| Var _ when (Meta.has Meta.Event cf.cf_meta) && not (Meta.has Meta.SkipReflection cf.cf_meta) ->
-						cf.cf_meta <- (Meta.SkipReflection, [], null_pos) :: cf.cf_meta;
+				end_block w;
+				newline w;
+				newline w
+			end;
+			(try
+				if cl.cl_interface then raise Not_found;
+				let cf = PMap.find "toString" cl.cl_fields in
+				(if List.exists (fun c -> c.cf_name = "toString") cl.cl_overrides then raise Not_found);
+				(match cf.cf_type with
+					| TFun([], ret) ->
+						(match real_type ret with
+							| TInst( { cl_path = ([], "String") }, []) ->
+								write w "public override string ToString()";
+								begin_block w;
+								write w "return this.toString();";
+								end_block w;
+								newline w;
+								newline w
+							| _ ->
+								gen.gcon.error "A toString() function should return a String!" cf.cf_pos
+						)
 					| _ -> ()
+				)
+			with | Not_found -> ());
+			(try
+				if cl.cl_interface then raise Not_found;
+				let cf = PMap.find "finalize" cl.cl_fields in
+				(if List.exists (fun c -> c.cf_name = "finalize") cl.cl_overrides then raise Not_found);
+				(match cf.cf_type with
+					| TFun([], ret) ->
+						(match real_type ret with
+							| TAbstract( { a_path = ([], "Void") }, []) ->
+								write w "~";
+								write w (snd cl.cl_path);
+								write w "()";
+								begin_block w;
+								write w "this.finalize();";
+								end_block w;
+								newline w;
+								newline w
+							| _ ->
+								gen.gcon.error "A finalize() function should be Void->Void!" cf.cf_pos
+						)
+					| _ -> ()
+				)
+			with | Not_found -> ());
+			(* properties *)
+			let handle_prop static f =
+				match f.cf_kind with
+				| Method _ -> ()
+				| Var v when Type.is_physical_field f -> ()
+				| Var v ->
+					let prop acc = match acc with
+						| AccNo | AccNever | AccCall -> true
+						| _ -> false
+					in
+					if prop v.v_read && prop v.v_write && (v.v_read = AccCall || v.v_write = AccCall) then begin
+						let this = if static then
+							ExprBuilder.make_static_this cl f.cf_pos
+						else
+							{ eexpr = TConst TThis; etype = TInst(cl,List.map snd cl.cl_params); epos = f.cf_pos }
+						in
+						print w "public %s%s %s" (if static then "static " else "") (t_s f.cf_type) (Dotnet.netname_to_hx f.cf_name);
+						begin_block w;
+						(match v.v_read with
+						| AccCall ->
+							write w "get";
+							begin_block w;
+							write w "return ";
+							expr_s w this;
+							print w ".get_%s();" f.cf_name;
+							end_block w
+						| _ -> ());
+						(match v.v_write with
+						| AccCall ->
+							write w "set";
+							begin_block w;
+							expr_s w this;
+							print w ".set_%s(value);" f.cf_name;
+							end_block w
+						| _ -> ());
+						end_block w;
+					end
+			in
+			if Meta.has Meta.BridgeProperties cl.cl_meta then begin
+				List.iter (handle_prop true) cl.cl_ordered_statics;
+				List.iter (handle_prop false) cl.cl_ordered_fields;
+			end
+		in
 
-				) all_fields;
-			 cl.cl_dynamic <- Option.map run_follow_gen cl.cl_dynamic;
-			 cl.cl_array_access <- Option.map run_follow_gen cl.cl_array_access;
-			 cl.cl_init <- Option.map type_map cl.cl_init;
-			 cl.cl_super <- Option.map super_map cl.cl_super;
-			 cl.cl_implements <- List.map super_map cl.cl_implements
-		| _ -> ()
-		) gen.gtypes_list;
-
-	let tp_v = alloc_var "$type_param" t_dynamic in
-	let mk_tp t pos = { eexpr = TLocal(tp_v); etype = t; epos = pos } in
-	gen.gparam_func_call <- (fun ecall efield params elist ->
-		match efield.eexpr with
-		| TField(_, FEnum _) ->
-			{ ecall with eexpr = TCall(efield, elist) }
-		| _ ->
-			{ ecall with eexpr = TCall(efield, (List.map (fun t -> mk_tp t ecall.epos) params) @ elist) }
-	);
-
-	if not erase_generics then HardNullableSynf.configure gen
-		(fun e ->
-			match e.eexpr, real_type e.etype with
-				| TConst TThis, _ when gen.gcurrent_path = (["haxe";"lang"], "Null") ->
-					e
-				| _, TInst({ cl_path = (["haxe";"lang"], "Null") }, [t]) ->
-					let e = { e with eexpr = TParenthesis(e) } in
-					{ (mk_field_access gen e "value" e.epos) with etype = t }
-				| _ ->
-					trace (debug_type e.etype); gen.gcon.error "This expression is not a Nullable expression" e.epos; assert false
-		)
-		(fun v t has_value ->
-			match has_value, real_type v.etype with
-				| true, TDynamic _ | true, TAnon _ | true, TMono _ ->
-					{
-						eexpr = TCall(mk_static_field_access_infer null_t "ofDynamic" v.epos [t], [mk_tp t v.epos; v]);
-						etype = TInst(null_t, [t]);
-						epos = v.epos
-					}
-				| _ ->
-					{ eexpr = TNew(null_t, [t], [gen.ghandle_cast t v.etype v; { eexpr = TConst(TBool has_value); etype = gen.gcon.basic.tbool; epos = v.epos } ]); etype = TInst(null_t, [t]); epos = v.epos }
-		)
-		(fun e ->
-			{
-				eexpr = TCall(
-					{ (mk_field_access gen { (mk_paren e) with etype = real_type e.etype } "toDynamic" e.epos) with etype = TFun([], t_dynamic) },
-					[]);
-				etype = t_dynamic;
-				epos = e.epos
-			}
-		)
-		(fun e ->
-			mk_field_access gen { e with etype = real_type e.etype } "hasValue" e.epos
-		)
-		(fun e1 e2 ->
-			mk (TCall(mk_field_access gen e1 "Equals" e1.epos, [e2])) basic.tbool e1.epos
-		);
-
-
-	let explicit_fn_name c tl fname =
-		path_param_s (TClassDecl c) c.cl_path tl ^ "." ^ fname
-	in
-
-	FixOverrides.configure ~explicit_fn_name:explicit_fn_name ~get_vmtype:real_type gen;
-	Normalize.configure gen ~metas:(Hashtbl.create 0);
-
-	AbstractImplementationFix.configure gen;
+		let gen_class w cl =
+			write w "#pragma warning disable 109, 114, 219, 429, 168, 162";
+			newline w;
+			let should_close = match change_ns (TClassDecl cl) (fst (cl.cl_path)) with
+				| [] -> false
+				| ns ->
+					print w "namespace %s " (String.concat "." ns);
+					begin_block w;
+					true
+			in
 
-	IteratorsInterface.configure gen;
+			(try
+				let _,m,_ = Meta.get (Meta.Custom "generic_iface") cl.cl_meta in
+				let rec loop i acc =
+					if i == 0 then
+						acc
+					else
+						"object" :: (loop (pred i) acc)
+				in
+				let tparams = loop (match m with [(EConst(Int s),_)] -> int_of_string s | _ -> assert false) [] in
+				cl.cl_meta <- (Meta.Meta, [
+					EConst(String("global::haxe.lang.GenericInterface(typeof(global::" ^ module_s (TClassDecl cl) ^ "<" ^ String.concat ", " tparams ^ ">))") ), cl.cl_pos
+				], cl.cl_pos) :: cl.cl_meta
+			with Not_found ->
+				());
+
+			gen_attributes w cl.cl_meta;
+
+			let is_main =
+				match gen.gcon.main_class with
+					| Some ( (_,"Main") as path) when path = cl.cl_path && not cl.cl_interface ->
+						(*
+							for cases where the main class is called Main, there will be a problem with creating the entry point there.
+							In this special case, a special entry point class will be created
+						*)
+						write w "public class EntryPoint__Main ";
+						begin_block w;
+						write w "public static void Main() ";
+						begin_block w;
+						(if Hashtbl.mem gen.gtypes (["cs"], "Boot") then write w "global::cs.Boot.init();"; newline w);
+						(match gen.gcon.main with
+							| None ->
+								expr_s w { eexpr = TTypeExpr(TClassDecl cl); etype = t_dynamic; epos = null_pos };
+								write w ".main();"
+							| Some expr ->
+								expr_s w (mk_block expr));
+						end_block w;
+						end_block w;
+						newline w;
+						false
+					| Some path when path = cl.cl_path && not cl.cl_interface -> true
+					| _ -> false
+			in
 
-	let closure_t = ClosuresToClass.DoubleAndDynamicClosureImpl.get_ctx gen (get_cl (get_type gen (["haxe";"lang"],"Function"))) 6 in
-	ClosuresToClass.configure gen closure_t;
+			let clt, access, modifiers = get_class_modifiers cl.cl_meta (if cl.cl_interface then "interface" else "class") "public" [] in
+			let is_final = clt = "struct" || Meta.has Meta.Final cl.cl_meta in
+
+			let modifiers = [access] @ modifiers in
+			print w "%s %s %s" (String.concat " " modifiers) clt (change_clname (snd cl.cl_path));
+			(* type parameters *)
+			let params, params_ext = get_string_params cl cl.cl_params in
+			let extends_implements = (match cl.cl_super with | None -> [] | Some (cl,p) -> [path_param_s (TClassDecl cl) cl.cl_path p]) @ (List.map (fun (cl,p) -> path_param_s (TClassDecl cl) cl.cl_path p) cl.cl_implements) in
+			(match extends_implements with
+				| [] -> print w "%s%s " params params_ext
+				| _ -> print w "%s : %s%s " params (String.concat ", " extends_implements) params_ext);
+			(* class head ok: *)
+			(* public class Test<A> : X, Y, Z where A : Y *)
+			begin_block w;
+			newline w;
+			(* our constructor is expected to be a normal "new" function *
+			if !strict_mode && is_some cl.cl_constructor then assert false;*)
+
+			let rec loop meta =
+				match meta with
+					| [] -> ()
+					| (Meta.ClassCode, [Ast.EConst (Ast.String contents),_],_) :: tl ->
+						write w contents
+					| _ :: tl -> loop tl
+			in
+			loop cl.cl_meta;
 
-	let enum_base = (get_cl (get_type gen (["haxe";"lang"],"Enum")) ) in
-	let param_enum_base = (get_cl (get_type gen (["haxe";"lang"],"ParamEnum")) ) in
-	EnumToClass.configure gen (Some (fun e -> mk_cast gen.gcon.basic.tint e)) true true enum_base param_enum_base;
+			if is_main then begin
+				write w "public static void Main()";
+				begin_block w;
+				(if Hashtbl.mem gen.gtypes (["cs"], "Boot") then write w "global::cs.Boot.init();"; newline w);
+				(match gen.gcon.main with
+					| None ->
+						write w "main();";
+					| Some expr ->
+							expr_s w (mk_block expr));
+				end_block w
+			end;
 
-	InterfaceVarsDeleteModf.configure gen;
-	InterfaceProps.configure gen;
+			(match cl.cl_init with
+				| None -> ()
+				| Some init ->
+					print w "static %s() " (snd cl.cl_path);
+					if needs_unchecked init then begin
+						begin_block w;
+						write w "unchecked ";
+						expr_s w (mk_block init);
+						end_block w;
+					end else
+						expr_s w (mk_block init);
+					line_reset_directive w;
+					newline w;
+					newline w
+			);
 
-	let dynamic_object = (get_cl (get_type gen (["haxe";"lang"],"DynamicObject")) ) in
+			(* collect properties and events *)
+			let partition cf cflist =
+				let events, props, nonprops = ref [], ref [], ref [] in
 
-	let object_iface = get_cl (get_type gen (["haxe";"lang"],"IHxObject")) in
+				List.iter (fun v -> match v.cf_kind with
+					| Var { v_read = AccCall } | Var { v_write = AccCall } when not (Type.is_physical_field v) && Meta.has Meta.Property v.cf_meta ->
+						props := (v.cf_name, ref (v, v.cf_type, None, None)) :: !props;
+					| Var { v_read = AccNormal; v_write = AccNormal } when Meta.has Meta.Event v.cf_meta ->
+						events := (v.cf_name, ref (v, v.cf_type, false, None, None)) :: !events;
+					| _ ->
+						nonprops := v :: !nonprops;
+				) cflist;
+
+				let events, nonprops = !events, !nonprops in
+
+				let t = TInst(cl, List.map snd cl.cl_params) in
+				let find_prop name = try
+						List.assoc name !props
+					with | Not_found -> match field_access gen t name with
+						| FClassField (_,_,decl,v,_,t,_) when is_extern_prop (TInst(cl,List.map snd cl.cl_params)) name ->
+							let ret = ref (v,t,None,None) in
+							props := (name, ret) :: !props;
+							ret
+						| _ -> raise Not_found
+				in
 
-	let empty_en = match get_type gen (["haxe";"lang"], "EmptyObject") with TEnumDecl e -> e | _ -> assert false in
-	let empty_ctor_type = TEnum(empty_en, []) in
-	let empty_en_expr = mk (TTypeExpr (TEnumDecl empty_en)) (TAnon { a_fields = PMap.empty; a_status = ref (EnumStatics empty_en) }) null_pos in
-	let empty_ctor_expr = mk (TField (empty_en_expr, FEnum(empty_en, PMap.find "EMPTY" empty_en.e_constrs))) empty_ctor_type null_pos in
-	OverloadingConstructor.configure ~empty_ctor_type:empty_ctor_type ~empty_ctor_expr:empty_ctor_expr gen;
+				let find_event name = List.assoc name events in
 
-	let rcf_static_find = mk_static_field_access_infer (get_cl (get_type gen (["haxe";"lang"], "FieldLookup"))) "findHash" null_pos [] in
-	let rcf_static_lookup = mk_static_field_access_infer (get_cl (get_type gen (["haxe";"lang"], "FieldLookup"))) "lookupHash" null_pos [] in
+				let is_empty_function cf = match cf.cf_expr with
+					| Some {eexpr = TFunction { tf_expr = {eexpr = TBlock []}}} -> true
+					| _ -> false
+				in
 
-	let rcf_static_insert, rcf_static_remove =
-		if erase_generics then begin
-			let get_specialized_postfix t = match t with
-				| TAbstract({a_path = [],("Float" | "Int" as name)}, _) -> name
-				| TAnon _ | TDynamic _ -> "Dynamic"
-				| _ -> print_endline (debug_type t); assert false
+				let interf = cl.cl_interface in
+				(* get all functions that are getters/setters *)
+				let nonprops = List.filter (function
+					| cf when String.starts_with cf.cf_name "get_" -> (try
+						(* find the property *)
+						let prop = find_prop (String.sub cf.cf_name 4 (String.length cf.cf_name - 4)) in
+						let v, t, get, set = !prop in
+						assert (get = None);
+						prop := (v,t,Some cf,set);
+						not interf
+					with | Not_found -> true)
+					| cf when String.starts_with cf.cf_name "set_" -> (try
+						(* find the property *)
+						let prop = find_prop (String.sub cf.cf_name 4 (String.length cf.cf_name - 4)) in
+						let v, t, get, set = !prop in
+						assert (set = None);
+						prop := (v,t,get,Some cf);
+						not interf
+					with | Not_found -> true)
+					| cf when String.starts_with cf.cf_name "add_" -> (try
+						let event = find_event (String.sub cf.cf_name 4 (String.length cf.cf_name - 4)) in
+						let v, t, _, add, remove = !event in
+						assert (add = None);
+						let custom = not (is_empty_function cf) in
+						event := (v, t, custom, Some cf, remove);
+						false
+					with | Not_found -> true)
+					| cf when String.starts_with cf.cf_name "remove_" -> (try
+						let event = find_event (String.sub cf.cf_name 7 (String.length cf.cf_name - 7)) in
+						let v, t, _, add, remove = !event in
+						assert (remove = None);
+						let custom = not (is_empty_function cf) in
+						event := (v, t, custom, add, Some cf);
+						false
+					with | Not_found -> true)
+					| _ -> true
+				) nonprops in
+
+				let nonprops = ref nonprops in
+				List.iter (fun (n,r) ->
+					let ev, t, custom, add, remove = !r in
+					match add, remove with
+					| Some add, Some remove ->
+						if custom && not cl.cl_interface then
+							nonprops := add :: remove :: !nonprops
+					| _ -> assert false (* shouldn't happen because Filters.check_cs_events makes sure methods are present *)
+				) events;
+
+				let evts = List.map (fun(_,v) -> !v) events in
+				let ret = List.map (fun (_,v) -> !v) !props in
+				let ret = List.filter (function | (_,_,None,None) -> false | _ -> true) ret in
+				evts, ret, List.rev !nonprops
 			in
-			(fun t -> mk_static_field_access_infer (get_cl (get_type gen (["haxe";"lang"], "FieldLookup"))) ("insert" ^ get_specialized_postfix t) null_pos []),
-			(fun t -> mk_static_field_access_infer (get_cl (get_type gen (["haxe";"lang"], "FieldLookup"))) ("remove" ^ get_specialized_postfix t) null_pos [])
-		end else
-			(fun t -> mk_static_field_access_infer (get_cl (get_type gen (["haxe";"lang"], "FieldLookup"))) "insert" null_pos [t]),
-			(fun t -> mk_static_field_access_infer (get_cl (get_type gen (["haxe";"lang"], "FieldLookup"))) "remove" null_pos [t])
-	in
-
-	let can_be_float = like_float in
-
-	let rcf_on_getset_field main_expr field_expr field may_hash may_set is_unsafe =
-		let is_float = can_be_float (real_type main_expr.etype) in
-		let fn_name = if is_some may_set then "setField" else "getField" in
-		let fn_name = if is_float then fn_name ^ "_f" else fn_name in
-		let pos = field_expr.epos in
 
-		let is_unsafe = { eexpr = TConst(TBool is_unsafe); etype = basic.tbool; epos = pos } in
-
-		let should_cast = match main_expr.etype with | TAbstract({ a_path = ([], "Float") }, []) -> false | _ -> true in
-		let infer = mk_static_field_access_infer runtime_cl fn_name field_expr.epos [] in
-		let first_args =
-			[ field_expr; { eexpr = TConst(TString field); etype = basic.tstring; epos = pos } ]
-			@ if is_some may_hash then [ { eexpr = TConst(TInt (get may_hash)); etype = basic.tint; epos = pos } ] else []
-		in
-		let args = first_args @ match is_float, may_set with
-			| true, Some(set) ->
-				[ if should_cast then mk_cast basic.tfloat set else set ]
-			| false, Some(set) ->
-				[ set ]
-			| _ ->
-				[ is_unsafe ]
+			let fevents, fprops, fnonprops = partition cl cl.cl_ordered_fields in
+			let sevents, sprops, snonprops = partition cl cl.cl_ordered_statics in
+			(if is_some cl.cl_constructor then gen_class_field w false cl is_final (get cl.cl_constructor));
+			if not cl.cl_interface then begin
+				(* we don't want to generate properties for abstract implementation classes, because they don't have object to work with *)
+				List.iter (gen_event w true cl) sevents;
+				if (match cl.cl_kind with KAbstractImpl _ -> false | _ -> true) then List.iter (gen_prop w true cl is_final) sprops;
+				List.iter (gen_class_field w true cl is_final) snonprops
+			end;
+			List.iter (gen_event w false cl) fevents;
+			List.iter (gen_prop w false cl is_final) fprops;
+			List.iter (gen_class_field w false cl is_final) fnonprops;
+			check_special_behaviors w cl;
+			end_block w;
+			if cl.cl_interface && cl.cl_ordered_statics <> [] then begin
+				print w "public class %s__Statics_" (snd cl.cl_path);
+				begin_block w;
+				List.iter (gen_class_field w true { cl with cl_interface = false } is_final) cl.cl_ordered_statics;
+				end_block w
+			end;
+			if should_close then end_block w
 		in
 
-		let call = { main_expr with eexpr = TCall(infer,args) } in
-		let call = if is_float && should_cast then mk_cast main_expr.etype call else call in
-		call
-	in
 
-	let rcf_on_call_field ecall field_expr field may_hash args =
-		let infer = mk_static_field_access_infer runtime_cl "callField" field_expr.epos [] in
+		let gen_enum w e =
+			let should_close = match change_ns (TEnumDecl e) (fst e.e_path) with
+				| [] -> false
+				| ns ->
+					print w "namespace %s" (String.concat "." ns);
+					begin_block w;
+					true
+			in
+			gen_attributes w e.e_meta;
 
-		let hash_arg = match may_hash with
-			| None -> []
-			| Some h -> [ { eexpr = TConst(TInt h); etype = basic.tint; epos = field_expr.epos } ]
-		in
+			print w "public enum %s" (change_clname (snd e.e_path));
+			begin_block w;
+			write w (String.concat ", " (List.map (change_id) e.e_names));
+			end_block w;
 
-		let arr_call = if args <> [] then
-			{ eexpr = TArrayDecl args; etype = basic.tarray t_dynamic; epos = ecall.epos }
-		else
-			null (basic.tarray t_dynamic) ecall.epos
+			if should_close then end_block w
 		in
 
-		let call_args =
-			[field_expr; { field_expr with eexpr = TConst(TString field); etype = basic.tstring } ]
-				@ hash_arg
-				@ [ arr_call ]
+		let module_type_gen w md_tp =
+			let file_start = len w = 0 in
+			let requires_root = no_root && file_start in
+			if file_start then
+				Codegen.map_source_header gen.gcon (fun s -> print w "// %s\n" s);
+			reset_temps();
+			match md_tp with
+				| TClassDecl cl ->
+					if not cl.cl_extern then begin
+						(if requires_root then write w "using haxe.root;\n"; newline w;);
+						gen_class w cl;
+						newline w;
+						newline w
+					end;
+					(not cl.cl_extern)
+				| TEnumDecl e ->
+					if not e.e_extern && not (Meta.has Meta.Class e.e_meta) then begin
+						(if requires_root then write w "using haxe.root;\n"; newline w;);
+						gen_enum w e;
+						newline w;
+						newline w
+					end;
+					(not e.e_extern)
+				| TAbstractDecl _
+				| TTypeDecl _ ->
+					false
 		in
 
-		mk_cast ecall.etype { ecall with eexpr = TCall(infer, call_args) }
-	in
-
-	add_cast_handler gen;
-	if not erase_generics then
-		TypeParams.RealTypeParams.configure gen (fun e t -> gen.gcon.warning ("Cannot cast to " ^ (debug_type t)) e.epos; mk_cast t e) ifaces (get_cl (get_type gen (["haxe";"lang"], "IGenericObject")))
-	else
-		TypeParams.RealTypeParams.RealTypeParamsModf.configure gen (TypeParams.RealTypeParams.RealTypeParamsModf.set_only_hxgeneric gen);
+		(* generate source code *)
+		init_ctx gen;
+
+		Hashtbl.add gen.gspecial_vars "__rethrow__" true;
+		Hashtbl.add gen.gspecial_vars "__typeof__" true;
+		Hashtbl.add gen.gspecial_vars "__is__" true;
+		Hashtbl.add gen.gspecial_vars "__as__" true;
+		Hashtbl.add gen.gspecial_vars "__cs__" true;
+
+		Hashtbl.add gen.gspecial_vars "__checked__" true;
+		Hashtbl.add gen.gspecial_vars "__lock__" true;
+		Hashtbl.add gen.gspecial_vars "__fixed__" true;
+		Hashtbl.add gen.gspecial_vars "__unsafe__" true;
+		Hashtbl.add gen.gspecial_vars "__addressOf__" true;
+		Hashtbl.add gen.gspecial_vars "__valueOf__" true;
+		Hashtbl.add gen.gspecial_vars "__sizeof__" true;
+		Hashtbl.add gen.gspecial_vars "__stackalloc__" true;
+
+		Hashtbl.add gen.gspecial_vars "__delegate__" true;
+		Hashtbl.add gen.gspecial_vars "__array__" true;
+		Hashtbl.add gen.gspecial_vars "__ptr__" true;
+
+		Hashtbl.add gen.gsupported_conversions (["haxe"; "lang"], "Null") (fun t1 t2 -> true);
+		let last_needs_box = gen.gneeds_box in
+		gen.gneeds_box <- (fun t -> match (gen.greal_type t) with
+			| TAbstract( ( { a_path = ["cs"], "Pointer" }, _ ) )
+			| TInst( { cl_path = ["cs"], "Pointer" }, _ )
+			| TInst( { cl_path = (["haxe"; "lang"], "Null") }, _ ) -> true
+			| _ -> last_needs_box t);
+
+		gen.greal_type <- real_type;
+		gen.greal_type_param <- change_param_type;
+
+		(* before running the filters, follow all possible types *)
+		(* this is needed so our module transformations don't break some core features *)
+		(* like multitype selection *)
+		let run_follow_gen = run_follow gen in
+		let rec type_map e = Type.map_expr_type (fun e->type_map e) (run_follow_gen)	(fun tvar-> tvar.v_type <- (run_follow_gen tvar.v_type); tvar) e in
+		let super_map (cl,tl) = (cl, List.map run_follow_gen tl) in
+		List.iter (function
+			| TClassDecl cl ->
+					let all_fields = (Option.map_default (fun cf -> [cf]) [] cl.cl_constructor) @ cl.cl_ordered_fields @ cl.cl_ordered_statics in
+					List.iter (fun cf ->
+						cf.cf_type <- run_follow_gen cf.cf_type;
+						cf.cf_expr <- Option.map type_map cf.cf_expr;
+
+						(* add @:skipReflection to @:event vars *)
+						match cf.cf_kind with
+						| Var _ when (Meta.has Meta.Event cf.cf_meta) && not (Meta.has Meta.SkipReflection cf.cf_meta) ->
+							cf.cf_meta <- (Meta.SkipReflection, [], null_pos) :: cf.cf_meta;
+						| _ -> ()
+
+					) all_fields;
+				cl.cl_dynamic <- Option.map run_follow_gen cl.cl_dynamic;
+				cl.cl_array_access <- Option.map run_follow_gen cl.cl_array_access;
+				cl.cl_init <- Option.map type_map cl.cl_init;
+				cl.cl_super <- Option.map super_map cl.cl_super;
+				cl.cl_implements <- List.map super_map cl.cl_implements
+			| _ -> ()
+			) gen.gtypes_list;
 
-	let flookup_cl = get_cl (get_type gen (["haxe";"lang"], "FieldLookup")) in
+		let mk_tp t pos = { eexpr = TIdent "$type_param"; etype = t; epos = pos } in
+		gen.gparam_func_call <- (fun ecall efield params elist ->
+			match efield.eexpr with
+			| TField(_, FEnum _) ->
+				{ ecall with eexpr = TCall(efield, elist) }
+			| _ ->
+				{ ecall with eexpr = TCall(efield, (List.map (fun t -> mk_tp t ecall.epos) params) @ elist) }
+		);
 
-	let rcf_ctx =
-		ReflectionCFs.new_ctx
-			gen
-			closure_t
-			object_iface
-			true
-			rcf_on_getset_field
-			rcf_on_call_field
-			(fun hash hash_array length -> { hash with eexpr = TCall(rcf_static_find, [hash; hash_array; length]); etype=basic.tint })
-			(fun hash -> { hash with eexpr = TCall(rcf_static_lookup, [hash]); etype = gen.gcon.basic.tstring })
-			(fun hash_array length pos value ->
-				let ecall = mk (TCall(rcf_static_insert value.etype, [hash_array; length; pos; value])) (if erase_generics then hash_array.etype else basic.tvoid) hash_array.epos in
-				if erase_generics then { ecall with eexpr = TBinop(OpAssign, hash_array, ecall) } else ecall
+		if not erase_generics then HardNullableSynf.configure gen
+			(fun e ->
+				match e.eexpr, real_type e.etype with
+					| TConst TThis, _ when gen.gcurrent_path = (["haxe";"lang"], "Null") ->
+						e
+					| _, TInst({ cl_path = (["haxe";"lang"], "Null") }, [t]) ->
+						let e = { e with eexpr = TParenthesis(e) } in
+						{ (mk_field_access gen e "value" e.epos) with etype = t }
+					| _ ->
+						trace (debug_type e.etype); gen.gcon.error "This expression is not a Nullable expression" e.epos; assert false
 			)
-			(fun hash_array length pos ->
-				let t = gen.gclasses.nativearray_type hash_array.etype in
-				{ hash_array with eexpr = TCall(rcf_static_remove t, [hash_array; length; pos]); etype = gen.gcon.basic.tvoid }
+			(fun v t has_value ->
+				match has_value, real_type v.etype with
+					| true, TDynamic _ | true, TAnon _ | true, TMono _ ->
+						{
+							eexpr = TCall(mk_static_field_access_infer null_t "ofDynamic" v.epos [t], [mk_tp t v.epos; v]);
+							etype = TInst(null_t, [t]);
+							epos = v.epos
+						}
+					| _ ->
+						{ eexpr = TNew(null_t, [t], [gen.ghandle_cast t v.etype v; { eexpr = TConst(TBool has_value); etype = gen.gcon.basic.tbool; epos = v.epos } ]); etype = TInst(null_t, [t]); epos = v.epos }
 			)
-			(
-				let delete = mk_static_field_access_infer flookup_cl "deleteHashConflict" null_pos [] in
-				let get = mk_static_field_access_infer flookup_cl "getHashConflict" null_pos [] in
-				let set = mk_static_field_access_infer flookup_cl "setHashConflict" null_pos [] in
-				let add = mk_static_field_access_infer flookup_cl "addHashConflictNames" null_pos [] in
-				let conflict_t = TInst (get_cl (get_type gen (["haxe"; "lang"], "FieldHashConflict")), []) in
-				Some {
-					t = conflict_t;
-					get_conflict = (fun ehead ehash ename -> mk (TCall (get, [ehead; ehash; ename])) conflict_t ehead.epos);
-					set = (fun ehead ehash ename evalue -> mk (TCall (set, [ehead; ehash; ename; evalue])) basic.tvoid ehead.epos);
-					delete = (fun ehead ehash ename -> mk (TCall (delete, [ehead; ehash; ename])) basic.tbool ehead.epos);
-					add_names = (fun ehead earr -> mk (TCall (add, [ehead; earr])) basic.tvoid ehead.epos);
+			(fun e ->
+				{
+					eexpr = TCall(
+						{ (mk_field_access gen { (mk_paren e) with etype = real_type e.etype } "toDynamic" e.epos) with etype = TFun([], t_dynamic) },
+						[]);
+					etype = t_dynamic;
+					epos = e.epos
 				}
 			)
-	in
+			(fun e ->
+				mk_field_access gen { e with etype = real_type e.etype } "hasValue" e.epos
+			)
+			(fun e1 e2 ->
+				mk (TCall(mk_field_access gen e1 "Equals" e1.epos, [e2])) basic.tbool e1.epos
+			);
+
+
+		let explicit_fn_name c tl fname =
+			path_param_s (TClassDecl c) c.cl_path tl ^ "." ^ fname
+		in
+
+		FixOverrides.configure ~explicit_fn_name:explicit_fn_name ~get_vmtype:real_type gen;
+
+		let allowed_meta = Hashtbl.create 1 in
+		Hashtbl.add allowed_meta Meta.LoopLabel true;
+		Normalize.configure gen ~allowed_metas:allowed_meta;
+
+		AbstractImplementationFix.configure gen;
 
-	ReflectionCFs.UniversalBaseClass.configure gen (get_cl (get_type gen (["haxe";"lang"],"HxObject")) ) object_iface dynamic_object;
-
-	ReflectionCFs.configure_dynamic_field_access rcf_ctx;
-
-	let closure_cl = get_cl (get_type gen (["haxe";"lang"],"Closure")) in
-	let varargs_cl = get_cl (get_type gen (["haxe";"lang"],"VarArgsFunction")) in
-	let dynamic_name = gen.gmk_internal_name "hx" "invokeDynamic" in
-
-	List.iter (fun cl ->
-		List.iter (fun cf ->
-			if cf.cf_name = dynamic_name then cl.cl_overrides <- cf :: cl.cl_overrides
-		) cl.cl_ordered_fields
-	) [closure_cl; varargs_cl];
-
-	ReflectionCFs.implement_varargs_cl rcf_ctx ( get_cl (get_type gen (["haxe";"lang"], "VarArgsBase")) );
-
-	let slow_invoke = mk_static_field_access_infer (runtime_cl) "slowCallField" null_pos [] in
-	ReflectionCFs.configure rcf_ctx ~slow_invoke:(fun ethis efield eargs -> {
-		eexpr = TCall(slow_invoke, [ethis; efield; eargs]);
-		etype = t_dynamic;
-		epos = ethis.epos;
-	} ) object_iface;
-
-	ObjectDeclMap.configure gen (ReflectionCFs.implement_dynamic_object_ctor rcf_ctx dynamic_object);
-
-	InitFunction.configure gen;
-	TArrayTransform.configure gen (
-	fun e binop ->
-		match e.eexpr with
-			| TArray(e1, e2) ->
-				(match follow e1.etype with
-					| TDynamic _ | TAnon _ | TMono _ -> true
-					| TInst({ cl_kind = KTypeParameter _ }, _) -> true
-					| TInst(c,p) when erase_generics && is_hxgeneric (TClassDecl c) && is_hxgen (TClassDecl c) -> (match c.cl_path with
-						| [],"String"
-						| ["cs"],"NativeArray" -> false
-						| _ ->
-							true)
-					| _ -> match binop, change_param_type (t_to_md e1.etype) [e.etype] with
-						| Some(Ast.OpAssignOp _), ([TDynamic _] | [TAnon _]) ->
-							true
-						| _ -> false)
-			| _ -> assert false
-	) "__get" "__set";
+		let cl_arg_exc = get_cl (get_type gen (["System"],"ArgumentException")) in
+		let cl_arg_exc_t = TInst (cl_arg_exc, []) in
+		let mk_arg_exception msg pos = mk (TNew (cl_arg_exc, [], [ExprBuilder.make_string gen.gcon msg pos])) cl_arg_exc_t pos in
+		let closure_t = ClosuresToClass.DoubleAndDynamicClosureImpl.get_ctx gen (get_cl (get_type gen (["haxe";"lang"],"Function"))) 6 mk_arg_exception in
+		ClosuresToClass.configure gen closure_t;
 
-	let field_is_dynamic t field =
-		match field_access_esp gen (gen.greal_type t) field with
-			| FEnumField _ -> false
-			| FClassField (cl,p,_,_,_,t,_) ->
-				if not erase_generics then
-					false
-				else
-					let p = change_param_type (TClassDecl cl) p in
-					is_dynamic (apply_params cl.cl_params p t)
-			| _ -> true
-	in
+		let enum_base = (get_cl (get_type gen (["haxe";"lang"],"Enum")) ) in
+		let type_enumindex = mk_static_field_access_infer gen.gclasses.cl_type "enumIndex" null_pos [] in
+		let mk_enum_index_call e p =
+			mk (TCall (type_enumindex, [e])) gen.gcon.basic.tint p
+		in
+		EnumToClass2.configure gen enum_base mk_enum_index_call;
 
-	let is_dynamic_expr e = is_dynamic e.etype || match e.eexpr with
-		| TField(tf, f) -> field_is_dynamic tf.etype (f)
-		| _ -> false
-	in
+		InterfaceVarsDeleteModf.configure gen;
 
-	let may_nullable t = match gen.gfollow#run_f t with
-		| TType({ t_path = ([], "Null") }, [t]) ->
-			(match follow t with
-				| TInst({ cl_path = ([], "String") }, [])
-				| TAbstract ({ a_path = ([], "Float") },[])
-				| TInst({ cl_path = (["haxe"], "Int32")}, [] )
-				| TInst({ cl_path = (["haxe"], "Int64")}, [] )
-				| TAbstract ({ a_path = ([], "Int") },[])
-				| TAbstract ({ a_path = ([], "Bool") },[]) -> Some t
-				| TAbstract _ when like_float t -> Some t
-				| t when is_cs_basic_type t -> Some t
-				| _ -> None )
-		| _ -> None
-	in
+		let dynamic_object = (get_cl (get_type gen (["haxe";"lang"],"DynamicObject")) ) in
 
-	let is_double t = like_float t && not (like_int t) in
-	let is_int t = like_int t in
+		let object_iface = get_cl (get_type gen (["haxe";"lang"],"IHxObject")) in
 
-	let is_null t = match real_type t with
-		| TInst( { cl_path = (["haxe";"lang"], "Null") }, _ ) -> true
-		| _ -> false
-	in
+		let empty_en = match get_type gen (["haxe";"lang"], "EmptyObject") with TEnumDecl e -> e | _ -> assert false in
+		let empty_ctor_type = TEnum(empty_en, []) in
+		let empty_en_expr = mk (TTypeExpr (TEnumDecl empty_en)) (TAnon { a_fields = PMap.empty; a_status = ref (EnumStatics empty_en) }) null_pos in
+		let empty_ctor_expr = mk (TField (empty_en_expr, FEnum(empty_en, PMap.find "EMPTY" empty_en.e_constrs))) empty_ctor_type null_pos in
+		OverloadingConstructor.configure ~empty_ctor_type:empty_ctor_type ~empty_ctor_expr:empty_ctor_expr gen;
 
-	let is_null_expr e = is_null e.etype || match e.eexpr with
-		| TField(tf, f) -> (match field_access_esp gen (real_type tf.etype) (f) with
-			| FClassField(_,_,_,_,_,actual_t,_) -> is_null actual_t
-			| _ -> false)
-		| _ -> false
-	in
+		let rcf_static_find = mk_static_field_access_infer (get_cl (get_type gen (["haxe";"lang"], "FieldLookup"))) "findHash" null_pos [] in
+		let rcf_static_lookup = mk_static_field_access_infer (get_cl (get_type gen (["haxe";"lang"], "FieldLookup"))) "lookupHash" null_pos [] in
 
-	let should_handle_opeq t =
-		match real_type t with
-			| TDynamic _ | TAnon _ | TMono _
-			| TInst( { cl_kind = KTypeParameter _ }, _ )
-			| TInst( { cl_path = (["haxe";"lang"], "Null") }, _ ) -> true
-			| _ -> false
-	in
+		let rcf_static_insert, rcf_static_remove =
+			if erase_generics then begin
+				let get_specialized_postfix t = match t with
+					| TAbstract({a_path = [],("Float" | "Int" as name)}, _) -> name
+					| TAnon _ | TDynamic _ -> "Dynamic"
+					| _ -> print_endline (debug_type t); assert false
+				in
+				(fun t -> mk_static_field_access_infer (get_cl (get_type gen (["haxe";"lang"], "FieldLookup"))) ("insert" ^ get_specialized_postfix t) null_pos []),
+				(fun t -> mk_static_field_access_infer (get_cl (get_type gen (["haxe";"lang"], "FieldLookup"))) ("remove" ^ get_specialized_postfix t) null_pos [])
+			end else
+				(fun t -> mk_static_field_access_infer (get_cl (get_type gen (["haxe";"lang"], "FieldLookup"))) "insert" null_pos [t]),
+				(fun t -> mk_static_field_access_infer (get_cl (get_type gen (["haxe";"lang"], "FieldLookup"))) "remove" null_pos [t])
+		in
 
-	let string_cl = match gen.gcon.basic.tstring with
-		| TInst(c,[]) -> c
-		| _ -> assert false
-	in
+		let can_be_float = like_float in
 
-	let is_undefined e = match e.eexpr with
-		| TLocal { v_name = "__undefined__" } | TField(_,FStatic({cl_path=["haxe";"lang"],"Runtime"},{cf_name="undefined"})) -> true
-		| _ -> false
-	in
+		let rcf_on_getset_field main_expr field_expr field may_hash may_set is_unsafe =
+			let is_float = can_be_float (real_type main_expr.etype) in
+			let fn_name = if is_some may_set then "setField" else "getField" in
+			let fn_name = if is_float then fn_name ^ "_f" else fn_name in
+			let pos = field_expr.epos in
 
-	DynamicOperators.configure gen
-		(fun e -> match e.eexpr with
-			| TBinop (Ast.OpEq, e1, e2)
-			| TBinop (Ast.OpNotEq, e1, e2) ->
-				(
-					(* dont touch (v == null) and (null == v) comparisons because they are handled by HardNullableSynf later *)
-					match e1.eexpr, e2.eexpr with
-					| TConst(TNull), _ when (not (is_tparam e2.etype) && is_dynamic e2.etype) || is_null_expr e2 ->
-						false
-					| _, TConst(TNull) when (not (is_tparam e1.etype) && is_dynamic e1.etype) || is_null_expr e1 ->
-						false
-					| _ when is_undefined e1 || is_undefined e2 ->
-						false
-					| _ ->
-						should_handle_opeq e1.etype || should_handle_opeq e2.etype
-				)
-			| TBinop (Ast.OpAssignOp Ast.OpAdd, e1, e2) ->
-				is_dynamic_expr e1 || is_null_expr e1 || is_string e.etype
-			| TBinop (Ast.OpAdd, e1, e2) -> is_dynamic e1.etype || is_dynamic e2.etype || is_tparam e1.etype || is_tparam e2.etype || is_string e1.etype || is_string e2.etype || is_string e.etype
-			| TBinop (Ast.OpLt, e1, e2)
-			| TBinop (Ast.OpLte, e1, e2)
-			| TBinop (Ast.OpGte, e1, e2)
-			| TBinop (Ast.OpGt, e1, e2) -> is_dynamic e.etype || is_dynamic_expr e1 || is_dynamic_expr e2 || is_string e1.etype || is_string e2.etype
-			| TBinop (_, e1, e2) -> is_dynamic e.etype || is_dynamic_expr e1 || is_dynamic_expr e2
-			| TUnop (_, _, e1) -> is_dynamic_expr e1 || is_null_expr e1 (* we will see if the expression is Null<T> also, as the unwrap from Unop will be the same *)
-			| _ -> false)
-		(fun e1 e2 ->
-			let is_basic = is_cs_basic_type (follow e1.etype) || is_cs_basic_type (follow e2.etype) in
-			let is_ref = if is_basic then false else match follow e1.etype, follow e2.etype with
-				| TDynamic _, _
-				| _, TDynamic _
-				| TInst( { cl_path = ([], "String") }, [] ), _
-				| _, TInst( { cl_path = ([], "String") }, [] )
-				| TInst( { cl_kind = KTypeParameter _ }, [] ), _
-				| _, TInst( { cl_kind = KTypeParameter _ }, [] ) -> false
-				| _, _ -> true
-			in
+			let is_unsafe = { eexpr = TConst(TBool is_unsafe); etype = basic.tbool; epos = pos } in
 
-			let static = mk_static_field_access_infer (runtime_cl) (if is_ref then "refEq" else "eq") e1.epos [] in
-			{ eexpr = TCall(static, [e1; e2]); etype = gen.gcon.basic.tbool; epos=e1.epos }
-		)
-		(fun e e1 e2 ->
-			match may_nullable e1.etype, may_nullable e2.etype with
-				| Some t1, Some t2 ->
-					let t1, t2 = if is_string t1 || is_string t2 then
-						basic.tstring, basic.tstring
-					else if is_double t1 || is_double t2 then
-						basic.tfloat, basic.tfloat
-					else if is_int t1 || is_int t2 then
-						basic.tint, basic.tint
-					else t1, t2 in
-					{ eexpr = TBinop(Ast.OpAdd, mk_cast t1 e1, mk_cast t2 e2); etype = e.etype; epos = e1.epos }
-				| _ when is_string e.etype || is_string e1.etype || is_string e2.etype ->
-					{
-						eexpr = TCall(
-							mk_static_field_access_infer runtime_cl "concat" e.epos [],
-							[ e1; e2 ]
-						);
-						etype = basic.tstring;
-						epos = e.epos
-					}
+			let should_cast = match main_expr.etype with | TAbstract({ a_path = ([], "Float") }, []) -> false | _ -> true in
+			let infer = mk_static_field_access_infer runtime_cl fn_name field_expr.epos [] in
+			let first_args =
+				[ field_expr; { eexpr = TConst(TString field); etype = basic.tstring; epos = pos } ]
+				@ if is_some may_hash then [ { eexpr = TConst(TInt (get may_hash)); etype = basic.tint; epos = pos } ] else []
+			in
+			let args = first_args @ match is_float, may_set with
+				| true, Some(set) ->
+					[ if should_cast then mk_cast basic.tfloat set else set ]
+				| false, Some(set) ->
+					[ set ]
 				| _ ->
-					let static = mk_static_field_access_infer (runtime_cl) "plus"  e1.epos [] in
-					mk_cast e.etype { eexpr = TCall(static, [e1; e2]); etype = t_dynamic; epos=e1.epos })
-		(fun e1 e2 ->
-			if is_string e1.etype then begin
-				{ e1 with eexpr = TCall(mk_static_field_access_infer string_cl "Compare" e1.epos [], [ e1; e2 ]); etype = gen.gcon.basic.tint }
-			end else begin
-				let static = mk_static_field_access_infer (runtime_cl) "compare" e1.epos [] in
-				{ eexpr = TCall(static, [e1; e2]); etype = gen.gcon.basic.tint; epos=e1.epos }
-			end)
-		~handle_strings:false;
+					[ is_unsafe ]
+			in
 
-	FilterClosures.configure gen (fun e1 s -> true) (ReflectionCFs.get_closure_func rcf_ctx closure_cl);
+			let call = { main_expr with eexpr = TCall(infer,args) } in
+			let call = if is_float && should_cast then mk_cast main_expr.etype call else call in
+			call
+		in
 
-	let base_exception = get_cl (get_type gen (["System"], "Exception")) in
-	let base_exception_t = TInst(base_exception, []) in
+		let rcf_on_call_field ecall field_expr field may_hash args =
+			let infer = mk_static_field_access_infer runtime_cl "callField" field_expr.epos [] in
 
-	let hx_exception = get_cl (get_type gen (["haxe";"lang"], "HaxeException")) in
-	let hx_exception_t = TInst(hx_exception, []) in
+			let hash_arg = match may_hash with
+				| None -> []
+				| Some h -> [ { eexpr = TConst(TInt h); etype = basic.tint; epos = field_expr.epos } ]
+			in
 
-	let rec is_exception t =
-		match follow t with
-			| TInst(cl,_) ->
-				if cl == base_exception then
-					true
-				else
-					(match cl.cl_super with | None -> false | Some (cl,arg) -> is_exception (TInst(cl,arg)))
-			| _ -> false
-	in
+			let arr_call = if args <> [] then
+				mk_nativearray_decl gen t_dynamic args ecall.epos
+			else
+				null (gen.gclasses.nativearray t_dynamic) ecall.epos
+			in
 
-	TryCatchWrapper.configure gen
-		(fun t -> not (is_exception (real_type t)))
-		(fun throwexpr expr ->
-			let wrap_static = mk_static_field_access (hx_exception) "wrap" (TFun([("obj",false,t_dynamic)], base_exception_t)) expr.epos in
-			{ throwexpr with eexpr = TThrow { expr with eexpr = TCall(wrap_static, [expr]); etype = hx_exception_t }; etype = gen.gcon.basic.tvoid }
-		)
-		(fun v_to_unwrap pos ->
-			let local = mk_cast hx_exception_t { eexpr = TLocal(v_to_unwrap); etype = v_to_unwrap.v_type; epos = pos } in
-			mk_field_access gen local "obj" pos
-		)
-		(fun rethrow ->
-			{ rethrow with eexpr = TCall(mk_local (alloc_var "__rethrow__" t_dynamic) rethrow.epos, [rethrow]); etype = gen.gcon.basic.tvoid }
-		)
-		(base_exception_t)
-		(hx_exception_t)
-		(fun v e ->
-
-			let exc_cl = get_cl (get_type gen (["haxe";"lang"],"Exceptions")) in
-			let exc_field = mk_static_field_access_infer exc_cl "exception" e.epos [] in
-			let esetstack = mk (TBinop(Ast.OpAssign, exc_field, mk_local v e.epos)) v.v_type e.epos in
-
-			Type.concat esetstack e;
-		);
+			let call_args =
+				[field_expr; { field_expr with eexpr = TConst(TString field); etype = basic.tstring } ]
+					@ hash_arg
+					@ [ arr_call ]
+			in
 
-	ClassInstance.configure gen (fun e _ -> { e with eexpr = TCall({ eexpr = TLocal(alloc_var "__typeof__" t_dynamic); etype = t_dynamic; epos = e.epos }, [e]) });
+			mk_cast ecall.etype { ecall with eexpr = TCall(infer, call_args) }
+		in
 
-	CastDetect.configure gen (Some empty_ctor_type) (not erase_generics) ~overloads_cast_to_base:true;
+		add_cast_handler gen;
+		if not erase_generics then
+			RealTypeParams.configure gen (fun e t -> gen.gcon.warning ("Cannot cast to " ^ (debug_type t)) e.epos; mk_cast t e) ifaces (get_cl (get_type gen (["haxe";"lang"], "IGenericObject")))
+		else
+			RealTypeParams.RealTypeParamsModf.configure gen (RealTypeParams.RealTypeParamsModf.set_only_hxgeneric gen);
 
-	SwitchToIf.configure gen (fun e ->
-		match e.eexpr with
-			| TSwitch(cond, cases, def) ->
-				(match gen.gfollow#run_f cond.etype with
-					| TAbstract ({ a_path = ([], "Int") },[])
-					| TInst({ cl_path = ([], "String") },[]) ->
-						(List.exists (fun (c,_) ->
-							List.exists (fun expr -> match expr.eexpr with | TConst _ -> false | _ -> true ) c
-						) cases)
-					| _ -> true
+		let flookup_cl = get_cl (get_type gen (["haxe";"lang"], "FieldLookup")) in
+
+		let cl_field_exc = get_cl (get_type gen (["System"],"MemberAccessException")) in
+		let cl_field_exc_t = TInst (cl_field_exc, []) in
+		let mk_field_exception msg pos = mk (TNew (cl_field_exc, [], [ExprBuilder.make_string gen.gcon msg pos])) cl_field_exc_t pos in
+
+		let rcf_ctx =
+			ReflectionCFs.new_ctx
+				gen
+				closure_t
+				object_iface
+				true
+				rcf_on_getset_field
+				rcf_on_call_field
+				(fun hash hash_array length -> { hash with eexpr = TCall(rcf_static_find, [hash; hash_array; length]); etype=basic.tint })
+				(fun hash -> { hash with eexpr = TCall(rcf_static_lookup, [hash]); etype = gen.gcon.basic.tstring })
+				(fun hash_array length pos value ->
+					let ecall = mk (TCall(rcf_static_insert value.etype, [hash_array; length; pos; value])) (if erase_generics then hash_array.etype else basic.tvoid) hash_array.epos in
+					if erase_generics then { ecall with eexpr = TBinop(OpAssign, hash_array, ecall) } else ecall
 				)
-			| _ -> assert false
-	);
+				(fun hash_array length pos ->
+					let t = gen.gclasses.nativearray_type hash_array.etype in
+					{ hash_array with eexpr = TCall(rcf_static_remove t, [hash_array; length; pos]); etype = gen.gcon.basic.tvoid }
+				)
+				(
+					let delete = mk_static_field_access_infer flookup_cl "deleteHashConflict" null_pos [] in
+					let get = mk_static_field_access_infer flookup_cl "getHashConflict" null_pos [] in
+					let set = mk_static_field_access_infer flookup_cl "setHashConflict" null_pos [] in
+					let add = mk_static_field_access_infer flookup_cl "addHashConflictNames" null_pos [] in
+					let conflict_t = TInst (get_cl (get_type gen (["haxe"; "lang"], "FieldHashConflict")), []) in
+					Some {
+						t = conflict_t;
+						get_conflict = (fun ehead ehash ename -> mk (TCall (get, [ehead; ehash; ename])) conflict_t ehead.epos);
+						set = (fun ehead ehash ename evalue -> mk (TCall (set, [ehead; ehash; ename; evalue])) basic.tvoid ehead.epos);
+						delete = (fun ehead ehash ename -> mk (TCall (delete, [ehead; ehash; ename])) basic.tbool ehead.epos);
+						add_names = (fun ehead earr -> mk (TCall (add, [ehead; earr])) basic.tvoid ehead.epos);
+					}
+				)
+				mk_field_exception
+		in
 
-	ExpressionUnwrap.configure gen (fun e -> Some { eexpr = TVar(mk_temp gen "expr" e.etype, Some e); etype = gen.gcon.basic.tvoid; epos = e.epos });
+		ReflectionCFs.UniversalBaseClass.configure gen (get_cl (get_type gen (["haxe";"lang"],"HxObject")) ) object_iface dynamic_object;
 
-	UnnecessaryCastsRemoval.configure gen;
+		ReflectionCFs.configure_dynamic_field_access rcf_ctx;
 
-	IntDivisionSynf.configure gen;
+		let closure_cl = get_cl (get_type gen (["haxe";"lang"],"Closure")) in
+		let varargs_cl = get_cl (get_type gen (["haxe";"lang"],"VarArgsFunction")) in
+		let dynamic_name = mk_internal_name "hx" "invokeDynamic" in
 
-	UnreachableCodeEliminationSynf.configure gen false;
+		List.iter (fun cl ->
+			List.iter (fun cf ->
+				if cf.cf_name = dynamic_name then cl.cl_overrides <- cf :: cl.cl_overrides
+			) cl.cl_ordered_fields
+		) [closure_cl; varargs_cl];
 
-	ArrayDeclSynf.configure gen native_arr_cl;
+		ReflectionCFs.implement_varargs_cl rcf_ctx ( get_cl (get_type gen (["haxe";"lang"], "VarArgsBase")) );
 
-	let goto_special = alloc_var "__goto__" t_dynamic in
-	let label_special = alloc_var "__label__" t_dynamic in
-	SwitchBreakSynf.configure gen
-		(fun e_loop n api ->
-			api ({ eexpr = TCall( mk_local label_special e_loop.epos, [ ExprBuilder.make_int gen.gcon n e_loop.epos ] ); etype = t_dynamic; epos = e_loop.epos }) false;
-			e_loop
-		)
-		(fun e_break n api ->
-			{ eexpr = TCall( mk_local goto_special e_break.epos, [ ExprBuilder.make_int gen.gcon n e_break.epos ] ); etype = t_dynamic; epos = e_break.epos }
-		);
+		let slow_invoke_field = mk_static_field_access_infer runtime_cl "slowCallField" null_pos [] in
+		let slow_invoke ethis efield eargs =
+			mk (TCall (slow_invoke_field, [ethis; efield; eargs])) t_dynamic ethis.epos
+		in
+		ReflectionCFs.configure rcf_ctx object_iface ~slow_invoke:slow_invoke;
 
-	DefaultArguments.configure gen;
-	InterfaceMetas.configure gen;
+		ObjectDeclMap.configure gen (ReflectionCFs.implement_dynamic_object_ctor rcf_ctx dynamic_object);
 
-	CSharpSpecificSynf.configure gen runtime_cl;
-	CSharpSpecificESynf.configure gen runtime_cl;
+		InitFunction.configure gen;
+		TArrayTransform.configure gen (
+		fun e binop ->
+			match e.eexpr with
+				| TArray(e1, e2) ->
+					(match follow e1.etype with
+						| TDynamic _ | TAnon _ | TMono _ -> true
+						| TInst({ cl_kind = KTypeParameter _ }, _) -> true
+						| TInst(c,p) when erase_generics && is_hxgeneric (TClassDecl c) && is_hxgen (TClassDecl c) -> (match c.cl_path with
+							| [],"String"
+							| ["cs"],"NativeArray" -> false
+							| _ ->
+								true)
+						| _ -> match binop, change_param_type (t_to_md e1.etype) [e.etype] with
+							| Some(Ast.OpAssignOp _), ([TDynamic _] | [TAnon _]) ->
+								true
+							| _ -> false)
+				| _ -> assert false
+		) "__get" "__set";
 
-	let out_files = ref [] in
+		let field_is_dynamic t field =
+			match field_access_esp gen (gen.greal_type t) field with
+				| FEnumField _ -> false
+				| FClassField (cl,p,_,_,_,t,_) ->
+					if not erase_generics then
+						false
+					else
+						let p = change_param_type (TClassDecl cl) p in
+						is_dynamic (apply_params cl.cl_params p t)
+				| _ -> true
+		in
 
-	(* copy resource files *)
-	if Hashtbl.length gen.gcon.resources > 0 then begin
-		let src =
-				gen.gcon.file ^ "/src/Resources"
+		let is_dynamic_expr e = is_dynamic e.etype || match e.eexpr with
+			| TField(tf, f) -> field_is_dynamic tf.etype (f)
+			| _ -> false
 		in
-		Hashtbl.iter (fun name v ->
-			let name = Codegen.escape_res_name name true in
-			let full_path = src ^ "/" ^ name in
-			mkdir_from_path full_path;
-
-			let f = open_out_bin full_path in
-			output_string f v;
-			close_out f;
-
-			out_files := (Path.unique_full_path full_path) :: !out_files
-		) gen.gcon.resources;
-	end;
-	(* add resources array *)
-	(try
-		let res = get_cl (Hashtbl.find gen.gtypes (["haxe"], "Resource")) in
-		let cf = PMap.find "content" res.cl_statics in
-		let res = ref [] in
-		Hashtbl.iter (fun name v ->
-			res := { eexpr = TConst(TString name); etype = gen.gcon.basic.tstring; epos = null_pos } :: !res;
-		) gen.gcon.resources;
-		cf.cf_expr <- Some ({ eexpr = TArrayDecl(!res); etype = gen.gcon.basic.tarray gen.gcon.basic.tstring; epos = null_pos })
-	with | Not_found -> ());
-
-	run_filters gen;
-	(* after the filters have been run, add all hashed fields to FieldLookup *)
-
-	let normalize_i i =
-		let i = Int32.of_int (i) in
-		if i < Int32.zero then
-			Int32.logor (Int32.logand i (Int32.of_int 0x3FFFFFFF)) (Int32.shift_left Int32.one 30)
-		else i
-	in
 
-	let nhash = ref 0 in
-	let hashes = Hashtbl.fold (fun i s acc -> incr nhash; (normalize_i i,s) :: acc) rcf_ctx.rcf_hash_fields [] in
-	let hashes = List.sort (fun (i,s) (i2,s2) -> compare i i2) hashes in
+		let may_nullable t = match gen.gfollow#run_f t with
+			| TAbstract({ a_path = ([], "Null") }, [t]) ->
+				(match follow t with
+					| TInst({ cl_path = ([], "String") }, [])
+					| TAbstract ({ a_path = ([], "Float") },[])
+					| TInst({ cl_path = (["haxe"], "Int32")}, [] )
+					| TInst({ cl_path = (["haxe"], "Int64")}, [] )
+					| TAbstract ({ a_path = ([], "Int") },[])
+					| TAbstract ({ a_path = ([], "Bool") },[]) -> Some t
+					| TAbstract _ when like_float t -> Some t
+					| t when is_cs_basic_type t -> Some t
+					| _ -> None )
+			| _ -> None
+		in
 
-	let haxe_libs = List.filter (function (_,_,_,lookup) -> is_some (lookup (["haxe";"lang"], "DceNo"))) gen.gcon.net_libs in
-	(try
-		(* first let's see if we're adding a -net-lib that has already a haxe.lang.FieldLookup *)
-		let name,_,_,_ = List.find (function (_,_,_,lookup) -> is_some (lookup (["haxe";"lang"], "FieldLookup"))) gen.gcon.net_libs in
-		if not (Common.defined gen.gcon Define.DllImport) then begin
-			gen.gcon.warning ("The -net-lib with path " ^ name ^ " contains a Haxe-generated assembly. Please define `-D dll_import` to handle Haxe-generated dll import correctly") null_pos;
-			raise Not_found
-		end;
-		if not (List.exists (function (n,_,_,_) -> n = name) haxe_libs) then
-			gen.gcon.warning ("The -net-lib with path " ^ name ^ " contains a Haxe-generated assembly, however it wasn't compiled with `-dce no`. Recompilation with `-dce no` is recommended") null_pos;
-		(* it has; in this case, we need to add the used fields on each __init__ *)
-		flookup_cl.cl_extern <- true;
-		let hashs_by_path = Hashtbl.create !nhash in
-		Hashtbl.iter (fun (path,i) s -> Hashtbl.add hashs_by_path path (i,s)) rcf_ctx.rcf_hash_paths;
-		Hashtbl.iter (fun _ md -> match md with
-			| TClassDecl ({ cl_extern = false; cl_interface = false } as c) -> (try
-				let all = Hashtbl.find_all hashs_by_path c.cl_path in
-				let all = List.map (fun (i,s) -> normalize_i i, s) all in
-				let all = List.sort (fun (i,s) (i2,s2) -> compare i i2) all in
-
-				if all <> [] then begin
-					let add = mk_static_field_access_infer flookup_cl "addFields" c.cl_pos [] in
-					let expr = { eexpr = TCall(add, [
-						mk_nativearray_decl gen basic.tint (List.map (fun (i,s) -> { eexpr = TConst(TInt (i)); etype = basic.tint; epos = c.cl_pos }) all) c.cl_pos;
-						mk_nativearray_decl gen basic.tstring (List.map (fun (i,s) -> { eexpr = TConst(TString (s)); etype = basic.tstring; epos = c.cl_pos }) all) c.cl_pos;
-					]); etype = basic.tvoid; epos = c.cl_pos } in
-					match c.cl_init with
-						| None -> c.cl_init <- Some expr
-						| Some e ->
-							c.cl_init <- Some { eexpr = TBlock([expr;e]); etype = basic.tvoid; epos = e.epos }
-				end
-			with | Not_found -> ())
-			| _ -> ()) gen.gtypes;
+		let is_double t = like_float t && not (like_int t) in
+		let is_int t = like_int t in
 
-	with | Not_found -> try
-		let basic = gen.gcon.basic in
-		let cl = flookup_cl in
-		let field_ids = PMap.find "fieldIds" cl.cl_statics in
-		let fields = PMap.find "fields" cl.cl_statics in
-
-		field_ids.cf_expr <- Some (mk_nativearray_decl gen basic.tint (List.map (fun (i,s) -> { eexpr = TConst(TInt (i)); etype = basic.tint; epos = field_ids.cf_pos }) hashes) field_ids.cf_pos);
-		fields.cf_expr <- Some (mk_nativearray_decl gen basic.tstring (List.map (fun (i,s) -> { eexpr = TConst(TString s); etype = basic.tstring; epos = fields.cf_pos }) hashes) fields.cf_pos);
-
-	with | Not_found ->
-		gen.gcon.error "Fields 'fieldIds' and 'fields' were not found in class haxe.lang.FieldLookup" flookup_cl.cl_pos
-	);
-
-	if Common.defined gen.gcon Define.DllImport then begin
-		Hashtbl.iter (fun _ md -> match md with
-			| TClassDecl ({ cl_extern = false } as c) -> (try
-				let extra = match c.cl_params with
-					| _ :: _ when not erase_generics -> "_" ^ string_of_int (List.length c.cl_params)
-					| _ -> ""
-				in
-				let pack = match c.cl_path with
-					| ([], _) when no_root && is_hxgen (TClassDecl c) ->
-						["haxe";"root"]
-					| (p,_) -> p
-				in
-				let path = (pack, snd c.cl_path ^ extra) in
-				ignore (List.find (function (_,_,_,lookup) ->
-					is_some (lookup path)) haxe_libs);
-				c.cl_extern <- true;
-			with | Not_found -> ())
-			| _ -> ()) gen.gtypes
-	end;
-
-	TypeParams.RenameTypeParameters.run gen;
-
-	let parts = Str.split_delim (Str.regexp "[\\/]+") gen.gcon.file in
-	mkdir_recursive "" parts;
-
-	List.iter (fun md_def ->
-		let source_dir = gen.gcon.file ^ "/src/" ^ (String.concat "/" (fst (path_of_md_def md_def))) in
-		let w = SourceWriter.new_source_writer() in
-		let should_write = List.fold_left (fun should md -> module_type_gen w md || should) false md_def.m_types in
-		if should_write then begin
-			let path = path_of_md_def md_def in
-			write_file gen w source_dir path "cs" out_files
-		end
-	) gen.gmodules;
+		let is_null t = match real_type t with
+			| TInst( { cl_path = (["haxe";"lang"], "Null") }, _ ) -> true
+			| _ -> false
+		in
 
-	if not (Common.defined gen.gcon Define.KeepOldOutput) then
-		clean_files (gen.gcon.file ^ "/src") !out_files gen.gcon.verbose;
+		let is_null_expr e = is_null e.etype || match e.eexpr with
+			| TField(tf, f) -> (match field_access_esp gen (real_type tf.etype) (f) with
+				| FClassField(_,_,_,_,_,actual_t,_) -> is_null actual_t
+				| _ -> false)
+			| _ -> false
+		in
 
-	dump_descriptor gen ("hxcs_build.txt") s_type_path module_s;
-	if ( not (Common.defined gen.gcon Define.NoCompilation) ) then begin
-		let old_dir = Sys.getcwd() in
-		Sys.chdir gen.gcon.file;
-		let cmd = "haxelib run hxcs hxcs_build.txt --haxe-version " ^ (string_of_int gen.gcon.version) ^ " --feature-level 1" in
-		print_endline cmd;
-		if gen.gcon.run_command cmd <> 0 then failwith "Build failed";
-		Sys.chdir old_dir;
-	end
+		let should_handle_opeq t =
+			match real_type t with
+				| TDynamic _ | TAnon _ | TMono _
+				| TInst( { cl_kind = KTypeParameter _ }, _ )
+				| TInst( { cl_path = (["haxe";"lang"], "Null") }, _ ) -> true
+				| _ -> false
+		in
 
-(* end of configure function *)
+		let string_cl = match gen.gcon.basic.tstring with
+			| TInst(c,[]) -> c
+			| _ -> assert false
+		in
 
-let generate con =
-	(try
+		let is_undefined e = match e.eexpr with
+			| TIdent "__undefined__" | TField(_,FStatic({cl_path=["haxe";"lang"],"Runtime"},{cf_name="undefined"})) -> true
+			| _ -> false
+		in
 
-		let gen = new_ctx con in
-		let basic = con.basic in
+		let nullable_basic t = match gen.gfollow#run_f t with
+			| TType({ t_path = ([],"Null") }, [t])
+			| TAbstract({ a_path = ([],"Null") }, [t]) when is_cs_basic_type t ->
+				Some(t)
+			| _ ->
+				None
+		in
 
-		if Common.defined_value con Define.Dce = "no" then begin
-			let m = { null_module with m_id = alloc_mid(); m_path = ["haxe";"lang"],"DceNo" } in
-			let cl = mk_class m (["haxe";"lang"],"DceNo") null_pos in
-			gen.gtypes_list <- (TClassDecl cl) :: gen.gtypes_list;
-			Hashtbl.add gen.gtypes cl.cl_path (TClassDecl cl)
-		end;
+		DynamicOperators.configure gen
+			~handle_strings:false
+			(fun e -> match e.eexpr with
+				| TBinop (Ast.OpEq, e1, e2)
+				| TBinop (Ast.OpNotEq, e1, e2) ->
+					(
+						(* dont touch (v == null) and (null == v) comparisons because they are handled by HardNullableSynf later *)
+						match e1.eexpr, e2.eexpr with
+						| TConst(TNull), _ when (not (is_tparam e2.etype) && is_dynamic e2.etype) || is_null_expr e2 ->
+							false
+						| _, TConst(TNull) when (not (is_tparam e1.etype) && is_dynamic e1.etype) || is_null_expr e1 ->
+							false
+						| _ when is_undefined e1 || is_undefined e2 ->
+							false
+						| _ ->
+							should_handle_opeq e1.etype || should_handle_opeq e2.etype
+					)
+				| TBinop (Ast.OpAssignOp Ast.OpAdd, e1, e2) ->
+					is_dynamic_expr e1 || is_null_expr e1 || is_string e.etype
+				| TBinop (Ast.OpAdd, e1, e2) -> is_dynamic e1.etype || is_dynamic e2.etype || is_tparam e1.etype || is_tparam e2.etype || is_string e1.etype || is_string e2.etype || is_string e.etype
+				| TBinop (Ast.OpLt, e1, e2)
+				| TBinop (Ast.OpLte, e1, e2)
+				| TBinop (Ast.OpGte, e1, e2)
+				| TBinop (Ast.OpGt, e1, e2) -> is_dynamic e.etype || is_dynamic e1.etype || is_dynamic e2.etype || is_string e1.etype || is_string e2.etype
+				| TBinop (_, e1, e2) -> is_dynamic e.etype || is_dynamic_expr e1 || is_dynamic_expr e2
+				| TUnop (_, _, e1) -> is_dynamic_expr e1 || is_null_expr e1 (* we will see if the expression is Null<T> also, as the unwrap from Unop will be the same *)
+				| _ -> false)
+			(fun e1 e2 ->
+				let is_basic = is_cs_basic_type (follow e1.etype) || is_cs_basic_type (follow e2.etype) in
+				let is_ref = if is_basic then false else match follow e1.etype, follow e2.etype with
+					| TDynamic _, _
+					| _, TDynamic _
+					| TInst( { cl_path = ([], "String") }, [] ), _
+					| _, TInst( { cl_path = ([], "String") }, [] )
+					| TInst( { cl_kind = KTypeParameter _ }, [] ), _
+					| _, TInst( { cl_kind = KTypeParameter _ }, [] ) -> false
+					| _, _ -> true
+				in
 
-		(* make the basic functions in C# *)
-		let type_cl = get_cl ( get_type gen (["System"], "Type")) in
-		let basic_fns =
-		[
-			mk_class_field "Equals" (TFun(["obj",false,t_dynamic], basic.tbool)) true null_pos (Method MethNormal) [];
-			mk_class_field "ToString" (TFun([], basic.tstring)) true null_pos (Method MethNormal) [];
-			mk_class_field "GetHashCode" (TFun([], basic.tint)) true null_pos (Method MethNormal) [];
-			mk_class_field "GetType" (TFun([], TInst(type_cl, []))) true null_pos (Method MethNormal) [];
-		] in
-		List.iter (fun cf -> gen.gbase_class_fields <- PMap.add cf.cf_name cf gen.gbase_class_fields) basic_fns;
-		configure gen
-	with | TypeNotFound path ->
-		con.error ("Error. Module '" ^ (s_type_path path) ^ "' is required and was not included in build.")	null_pos);
-	debug_mode := false
+				let static = mk_static_field_access_infer (runtime_cl) (if is_ref then "refEq" else "eq") e1.epos [] in
+				{ eexpr = TCall(static, [e1; e2]); etype = gen.gcon.basic.tbool; epos=e1.epos }
+			)
+			(fun e e1 e2 ->
+				match may_nullable e1.etype, may_nullable e2.etype with
+					| Some t1, Some t2 ->
+						let t1, t2 = if is_string t1 || is_string t2 then
+							basic.tstring, basic.tstring
+						else if is_double t1 || is_double t2 then
+							basic.tfloat, basic.tfloat
+						else if is_int t1 || is_int t2 then
+							basic.tint, basic.tint
+						else t1, t2 in
+						{ eexpr = TBinop(Ast.OpAdd, mk_cast t1 e1, mk_cast t2 e2); etype = e.etype; epos = e1.epos }
+					| _ when is_string e.etype || is_string e1.etype || is_string e2.etype ->
+						{
+							eexpr = TCall(
+								mk_static_field_access_infer runtime_cl "concat" e.epos [],
+								[ e1; e2 ]
+							);
+							etype = basic.tstring;
+							epos = e.epos
+						}
+					| _ ->
+						let static = mk_static_field_access_infer (runtime_cl) "plus"  e1.epos [] in
+						mk_cast e.etype { eexpr = TCall(static, [e1; e2]); etype = t_dynamic; epos=e1.epos })
+		(fun op e e1 e2 ->
+				match nullable_basic e1.etype, nullable_basic e2.etype with
+					| Some(t1), None when is_cs_basic_type e2.etype ->
+						{ e with eexpr = TBinop(op, mk_cast t1 e1, e2) }
+					| None, Some(t2) when is_cs_basic_type e1.etype ->
+						{ e with eexpr = TBinop(op, e1, mk_cast t2 e2) }
+					| _ ->
+							let handler = if is_string e1.etype then begin
+									{ e1 with eexpr = TCall(mk_static_field_access_infer string_cl "Compare" e1.epos [], [ e1; e2 ]); etype = gen.gcon.basic.tint }
+								end else begin
+									let static = mk_static_field_access_infer (runtime_cl) "compare" e1.epos [] in
+									{ eexpr = TCall(static, [e1; e2]); etype = gen.gcon.basic.tint; epos=e1.epos }
+								end
+							in
+							let zero = ExprBuilder.make_int gen.gcon 0 e.epos in
+							{ e with eexpr = TBinop(op, handler, zero) }
+		);
 
-(* -net-lib implementation *)
-open IlData
-open IlMeta
+		FilterClosures.configure gen (fun e1 s -> true) (ReflectionCFs.get_closure_func rcf_ctx closure_cl);
 
-type net_lib_ctx = {
-	nstd : bool;
-	ncom : Common.context;
-	nil : IlData.ilctx;
-}
+		ClassInstance.configure gen;
 
-let is_haxe_keyword = function
-	| "callback" | "cast" | "extern" | "function" | "in" | "typedef" | "using" | "var" | "untyped" | "inline" -> true
-	| _ -> false
+		CastDetect.configure gen (Some empty_ctor_type) (not erase_generics) ~overloads_cast_to_base:true;
 
-let hxpath_to_net ctx path =
-	try
-		Hashtbl.find ctx.ncom.net_path_map path
-	with
-	 | Not_found ->
-			[],[],"Not_found"
-
-let add_cs = function
-	| "haxe" :: ns -> "haxe" :: ns
-	| "std" :: ns -> "std" :: ns
-	| "cs" :: ns -> "cs" :: ns
-	| "system" :: ns -> "cs" :: "system" :: ns
-	| ns -> ns
-
-let escape_chars =
-	String.replace_chars (fun chr ->
-		if (chr >= 'a' && chr <= 'z') || (chr >= 'A' && chr <= 'Z') || (chr >= '0' && chr <= '9') || chr = '_' then
-			Char.escaped chr
-		else
-			"_x" ^ (string_of_int (Char.code chr)) ^ "_")
+		SwitchToIf.configure gen (fun e ->
+			match e.eexpr with
+				| TSwitch(cond, cases, def) ->
+					(match gen.gfollow#run_f cond.etype with
+						| TAbstract ({ a_path = ([], "Int") },[])
+						| TInst({ cl_path = ([], "String") },[]) ->
+							(List.exists (fun (c,_) ->
+								List.exists (fun expr -> match expr.eexpr with | TConst _ -> false | _ -> true ) c
+							) cases)
+						| _ -> true
+					)
+				| _ -> assert false
+		);
 
-let netcl_to_hx cl =
-	let cl = if String.length cl > 0 && String.get cl 0 >= 'a' && String.get cl 0 <= 'z' then
-			Char.escaped (Char.uppercase (String.get cl 0)) ^ (String.sub cl 1 (String.length cl - 1))
-		else
-			cl
-	in
-	try
-		let cl, nargs = String.split cl "`" in
-		(escape_chars cl) ^ "_" ^ nargs
-	with | Invalid_string ->
-		escape_chars cl
-
-let netpath_to_hx std = function
-	| [],[], cl -> [], netcl_to_hx cl
-	| ns,[], cl ->
-		let ns = (List.map (fun s -> String.lowercase (escape_chars s)) ns) in
-		add_cs ns, netcl_to_hx cl
-	| ns,(nhd :: ntl as nested), cl ->
-		let nested = List.map (netcl_to_hx) nested in
-		let ns = (List.map (fun s -> String.lowercase (escape_chars s)) ns) @ [nhd] in
-		add_cs ns, String.concat "_" nested ^ "_" ^ netcl_to_hx cl
-
-let lookup_ilclass std com ilpath =
-	let path = netpath_to_hx std ilpath in
-	List.fold_right (fun (_,_,_,get_raw_class) acc ->
-		match acc with
-		| None -> get_raw_class path
-		| Some p -> acc
-	) com.net_libs None
-
-let discard_nested = function
-	| (ns,_),cl -> (ns,[]),cl
-
-let mk_type_path ctx path params =
-	let pack, sub, name = match path with
-		| ns,[], cl ->
-			ns, None, netcl_to_hx cl
-		| ns, (nhd :: ntl as nested), cl ->
-			let nhd = netcl_to_hx nhd in
-			let nested = List.map (netcl_to_hx) nested in
-			ns, Some (String.concat "_" nested ^ "_" ^ netcl_to_hx cl), nhd
-	in
-	CTPath {
-		tpackage = fst (netpath_to_hx ctx.nstd (pack,[],""));
-		Ast.tname = name;
-		tparams = params;
-		tsub = sub;
-	}
-
-let raw_type_path ctx path params =
-	{
-		tpackage = fst path;
-		Ast.tname = snd path;
-		tparams = params;
-		tsub = None;
-	}
-
-let rec convert_signature ctx p = function
-	| LVoid ->
-		mk_type_path ctx ([],[],"Void") []
-	| LBool ->
-		mk_type_path ctx ([],[],"Bool") []
-	| LChar ->
-		mk_type_path ctx (["cs";"types"],[],"Char16") []
-	| LInt8 ->
-		mk_type_path ctx (["cs";"types"],[],"Int8") []
-	| LUInt8 ->
-		mk_type_path ctx (["cs";"types"],[],"UInt8") []
-	| LInt16 ->
-		mk_type_path ctx (["cs";"types"],[],"Int16") []
-	| LUInt16 ->
-		mk_type_path ctx (["cs";"types"],[],"UInt16") []
-	| LInt32 ->
-		mk_type_path ctx ([],[],"Int") []
-	| LUInt32 ->
-		mk_type_path ctx ([],[],"UInt") []
-	| LInt64 ->
-		mk_type_path ctx (["haxe"],[],"Int64") []
-	| LUInt64 ->
-		mk_type_path ctx (["cs";"types"],[],"UInt64") []
-	| LFloat32 ->
-		mk_type_path ctx ([],[],"Single") []
-	| LFloat64 ->
-		mk_type_path ctx ([],[],"Float") []
-	| LString ->
-		mk_type_path ctx (["std"],[],"String") []
-	| LObject ->
-		mk_type_path ctx ([],[],"Dynamic") []
-	| LPointer s | LManagedPointer s ->
-		mk_type_path ctx (["cs"],[],"Pointer") [ TPType (convert_signature ctx p s,null_pos) ]
-	| LTypedReference ->
-		mk_type_path ctx (["cs";"system"],[],"TypedReference") []
-	| LIntPtr ->
-		mk_type_path ctx (["cs";"system"],[],"IntPtr") []
-	| LUIntPtr ->
-		mk_type_path ctx (["cs";"system"],[],"UIntPtr") []
-	| LValueType (s,args) | LClass (s,args) ->
-		mk_type_path ctx s (List.map (fun s -> TPType (convert_signature ctx p s,null_pos)) args)
-	| LTypeParam i ->
-		mk_type_path ctx ([],[],"T" ^ string_of_int i) []
-	| LMethodTypeParam i ->
-		mk_type_path ctx ([],[],"M" ^ string_of_int i) []
-	| LVector s ->
-		mk_type_path ctx (["cs"],[],"NativeArray") [TPType (convert_signature ctx p s,null_pos)]
-	(* | LArray of ilsig_norm * (int option * int option) array *)
-	| LMethod (_,ret,args) ->
-		CTFunction (List.map (fun v -> convert_signature ctx p v,null_pos) args, (convert_signature ctx p ret,null_pos))
-	| _ -> mk_type_path ctx ([],[], "Dynamic") []
-
-let ilpath_s = function
-	| ns,[], name -> s_type_path (ns,name)
-	| [],nested,name -> String.concat "." nested ^ "." ^ name
-	| ns, nested, name -> String.concat "." ns ^ "." ^ String.concat "." nested ^ "." ^ name
-
-let get_cls = function
-	| _,_,c -> c
-
-(* TODO: When possible on Haxe, use this to detect flag enums, and make an abstract with @:op() *)
-(* that behaves like an enum, and with an enum as its underlying type *)
-let enum_is_flag ilcls =
-	let check_flag name ns = name = "FlagsAttribute" && ns = ["System"] in
-	List.exists (fun a ->
-		match a.ca_type with
-			| TypeRef r ->
-				check_flag r.tr_name r.tr_namespace
-			| TypeDef d ->
-				check_flag d.td_name d.td_namespace
-			| Method m ->
-				(match m.m_declaring with
-					| Some d ->
-						check_flag d.td_name d.td_namespace
-					| _ -> false)
-			| MemberRef r ->
-				(match r.memr_class with
-					| TypeRef r ->
-						check_flag r.tr_name r.tr_namespace
-					| TypeDef d ->
-						check_flag d.td_name d.td_namespace
-					| _ -> false)
-			| _ ->
-				false
-	) ilcls.cattrs
-
-let convert_ilenum ctx p ?(is_flag=false) ilcls =
-	let meta = ref [
-		Meta.Native, [EConst (String (ilpath_s ilcls.cpath) ), p], p;
-		Meta.CsNative, [], p;
-	] in
-
-	let data = ref [] in
-	List.iter (fun f -> match f.fname with
-		| "value__" -> ()
-		| _ when not (List.mem CStatic f.fflags.ff_contract) -> ()
-		| _ ->
-			let meta, const = match f.fconstant with
-				| Some IChar i
-				| Some IByte i
-				| Some IShort i ->
-					[Meta.CsNative, [EConst (Int (string_of_int i) ), p], p ], Int64.of_int i
-				| Some IInt i ->
-					[Meta.CsNative, [EConst (Int (Int32.to_string i) ), p], p ], Int64.of_int32 i
-				| Some IFloat32 f | Some IFloat64 f ->
-					[], Int64.of_float f
-				| Some IInt64 i ->
-					[], i
-				| _ ->
-					[], Int64.zero
-			in
-			data := ( { ec_name = f.fname,null_pos; ec_doc = None; ec_meta = meta; ec_args = []; ec_pos = p; ec_params = []; ec_type = None; }, const) :: !data;
-	) ilcls.cfields;
-	let data = List.stable_sort (fun (_,i1) (_,i2) -> Int64.compare i1 i2) (List.rev !data) in
-
-	let _, c = netpath_to_hx ctx.nstd ilcls.cpath in
-	let name = netname_to_hx c in
-	EEnum {
-		d_name = (if is_flag then name ^ "_FlagsEnum" else name),null_pos;
-		d_doc = None;
-		d_params = []; (* enums never have type parameters *)
-		d_meta = !meta;
-		d_flags = [EExtern];
-		d_data = List.map fst data;
-	}
-
-let rec has_unmanaged = function
-	| LPointer _ -> true
-	| LManagedPointer s -> has_unmanaged s
-	| LValueType (p,pl) -> List.exists (has_unmanaged) pl
-	| LClass (p,pl) -> List.exists (has_unmanaged) pl
-	| LVector s -> has_unmanaged s
-	| LArray (s,a) -> has_unmanaged s
-	| LMethod (c,r,args) -> has_unmanaged r || List.exists (has_unmanaged) args
-	| _ -> false
+		ExpressionUnwrap.configure gen;
 
-let convert_ilfield ctx p field =
-	if not (Common.defined ctx.ncom Define.Unsafe) && has_unmanaged field.fsig.snorm then raise Exit;
-	let p = { p with pfile =	p.pfile ^" (" ^field.fname ^")" } in
-	let cff_doc = None in
-	let cff_pos = p in
-	let cff_meta = ref [] in
-	let cff_name = match field.fname with
-		| name when String.length name > 5 ->
-				(match String.sub name 0 5 with
-				| "__hx_" -> raise Exit
-				| _ -> name)
-		| name -> name
-	in
-	let cff_access = match field.fflags.ff_access with
-		| FAFamily | FAFamOrAssem -> APrivate
-		| FAPublic -> APublic
-		| _ -> raise Exit (* private instances aren't useful on externs *)
-	in
-	let readonly, acc = List.fold_left (fun (readonly,acc) -> function
-		| CStatic -> readonly, AStatic :: acc
-		| CInitOnly | CLiteral -> true, acc
-		| _ -> readonly,acc
-	) (false,[cff_access]) field.fflags.ff_contract in
-	if PMap.mem "net_loader_debug" ctx.ncom.defines then
-		Printf.printf "\t%sfield %s : %s\n" (if List.mem AStatic acc then "static " else "") cff_name (IlMetaDebug.ilsig_s field.fsig.ssig);
-	let kind = match readonly with
-		| true ->
-			cff_meta := (Meta.ReadOnly, [], cff_pos) :: !cff_meta;
-			FProp (("default",null_pos), ("never",null_pos), Some (convert_signature ctx p field.fsig.snorm,null_pos), None)
-		| false ->
-			FVar (Some (convert_signature ctx p field.fsig.snorm,null_pos), None)
-	in
-	let cff_name, cff_meta =
-		if String.get cff_name 0 = '%' then
-			let name = (String.sub cff_name 1 (String.length cff_name - 1)) in
-			"_" ^ name,
-			(Meta.Native, [EConst (String (name) ), cff_pos], cff_pos) :: !cff_meta
-		else
-			cff_name, !cff_meta
-	in
-	{
-		cff_name = cff_name,null_pos;
-		cff_doc = cff_doc;
-		cff_pos = cff_pos;
-		cff_meta = cff_meta;
-		cff_access = acc;
-		cff_kind = kind;
-	}
-
-let convert_ilevent ctx p ev =
-	let p = { p with pfile =	p.pfile ^" (" ^ev.ename ^")" } in
-	let name = ev.ename in
-	let kind = FVar (Some (convert_signature ctx p ev.esig.snorm,null_pos), None) in
-	let meta = [Meta.Event, [], p; Meta.Keep,[],p; Meta.SkipReflection,[],p] in
-	let acc = [APrivate] in
-	let add_m acc m = match m with
-		| None -> acc
-		| Some (name,flags) ->
-			if List.mem (CMStatic) flags.mf_contract then
-				AStatic :: acc
-			else
-				acc
-	in
-	if PMap.mem "net_loader_debug" ctx.ncom.defines then
-		Printf.printf "\tevent %s : %s\n" name (IlMetaDebug.ilsig_s ev.esig.ssig);
-	let acc = add_m acc ev.eadd in
-	let acc = add_m acc ev.eremove in
-	let acc = add_m acc ev.eraise in
-	{
-		cff_name = name,null_pos;
-		cff_doc = None;
-		cff_pos = p;
-		cff_meta = meta;
-		cff_access = acc;
-		cff_kind = kind;
-	}
-
-let convert_ilmethod ctx p m is_explicit_impl =
-	if not (Common.defined ctx.ncom Define.Unsafe) && has_unmanaged m.msig.snorm then raise Exit;
-	let force_check = Common.defined ctx.ncom Define.ForceLibCheck in
-	let p = { p with pfile =	p.pfile ^" (" ^m.mname ^")" } in
-	let cff_doc = None in
-	let cff_pos = p in
-	let cff_name = match m.mname with
-		| ".ctor" -> "new"
-		| ".cctor"-> raise Exit (* __init__ field *)
-		| "Equals" | "GetHashCode" -> raise Exit
-		| name when String.length name > 5 ->
-				(match String.sub name 0 5 with
-				| "__hx_" -> raise Exit
-				| _ -> name)
-		| name -> name
-	in
-	let acc = match m.mflags.mf_access with
-		| FAFamily | FAFamOrAssem -> APrivate
-		(* | FAPrivate -> APrivate *)
-		| FAPublic when List.mem SGetter m.msemantics || List.mem SSetter m.msemantics ->
-			APrivate
-		| FAPublic -> APublic
-		| _ ->
-			if PMap.mem "net_loader_debug" ctx.ncom.defines then
-				Printf.printf "\tmethod %s (skipped) : %s\n" cff_name (IlMetaDebug.ilsig_s m.msig.ssig);
-			raise Exit
-	in
-	let is_static = ref false in
-	let acc, is_final = List.fold_left (fun (acc,is_final) -> function
-		| CMStatic when cff_name <> "new" -> is_static := true; AStatic :: acc, is_final
-		| CMVirtual when is_final = None -> acc, Some false
-		| CMFinal -> acc, Some true
-		| _ -> acc, is_final
-	) ([acc],None) m.mflags.mf_contract in
-	if PMap.mem "net_loader_debug" ctx.ncom.defines then
-		Printf.printf "\t%smethod %s : %s\n" (if !is_static then "static " else "") cff_name (IlMetaDebug.ilsig_s m.msig.ssig);
-
-	let meta = [Meta.Overload, [], p] in
-	let meta = match is_final with
-		| None | Some true when not force_check ->
-			(Meta.Final,[],p) :: meta
-		| _ ->
-			meta
-	in
-	let meta = if is_explicit_impl then
-			(Meta.NoCompletion,[],p) :: (Meta.SkipReflection,[],p) :: meta
-		else
-			meta
-	in
-	(* let meta = if List.mem OSynchronized m.mflags.mf_interop then *)
-	(*	(Meta.Synchronized,[],p) :: meta *)
-	(* else *)
-	(*	meta *)
-	(* in *)
-
-	let rec change_sig = function
-		| LManagedPointer s -> LManagedPointer (change_sig s)
-		| LPointer s -> LPointer (change_sig s)
-		| LValueType (p,pl) -> LValueType(p, List.map change_sig pl)
-		| LClass (p,pl) -> LClass(p, List.map change_sig pl)
-		| LTypeParam i -> LObject
-		| LVector s -> LVector (change_sig s)
-		| LArray (s,a) -> LArray (change_sig s, a)
-		| LMethod (c,r,args) -> LMethod (c, change_sig r, List.map change_sig args)
-		| p -> p
-	in
-	let change_sig = if !is_static then change_sig else (fun s -> s) in
-
-	let ret =
-		if String.length cff_name > 4 && String.sub cff_name 0 4 = "set_" then
-			match m.mret.snorm, m.margs with
-			| LVoid, [_,_,s] ->
-				s.snorm
-			| _ -> m.mret.snorm
-		else
-			m.mret.snorm
-	in
+		UnnecessaryCastsRemoval.configure gen;
 
-	let kind =
-		let args = List.map (fun (name,flag,s) ->
-			let t = match s.snorm with
-				| LManagedPointer s ->
-					let is_out = List.mem POut flag.pf_io && not (List.mem PIn flag.pf_io) in
-					let name = if is_out then "Out" else "Ref" in
-					mk_type_path ctx (["cs"],[],name) [ TPType (convert_signature ctx p s,null_pos) ]
-				| _ ->
-					convert_signature ctx p (change_sig s.snorm)
-			in
-			(name,null_pos),false,[],Some (t,null_pos),None) m.margs
-		in
-		let ret = convert_signature ctx p (change_sig ret) in
-		let types = List.map (fun t ->
-			{
-				tp_name = "M" ^ string_of_int t.tnumber,null_pos;
-				tp_params = [];
-				tp_constraints = [];
-				tp_meta = [];
-			}
-		) m.mtypes in
-		FFun {
-			f_params = types;
-			f_args = args;
-			f_type = Some (ret,null_pos);
-			f_expr = None;
-		}
-	in
-	let cff_name, cff_meta =
-		if String.get cff_name 0 = '%' then
-			let name = (String.sub cff_name 1 (String.length cff_name - 1)) in
-			"_" ^ name,
-			(Meta.Native, [EConst (String (name) ), cff_pos], cff_pos) :: meta
-		else
-			cff_name, meta
-	in
-	let acc = match m.moverride with
-		| None -> acc
-		| _ when cff_name = "new" -> acc
-		| Some (path,s) -> match lookup_ilclass ctx.nstd ctx.ncom path with
-			| Some ilcls when not (List.mem SInterface ilcls.cflags.tdf_semantics) ->
-				AOverride :: acc
-			| None when ctx.ncom.verbose ->
-				prerr_endline ("(net-lib) A referenced assembly for path " ^ ilpath_s path ^ " was not found");
-				acc
-			| _ -> acc
-	in
-	{
-		cff_name = cff_name,null_pos;
-		cff_doc = cff_doc;
-		cff_pos = cff_pos;
-		cff_meta = cff_meta;
-		cff_access = acc;
-		cff_kind = kind;
-	}
-
-let convert_ilprop ctx p prop is_explicit_impl =
-	if not (Common.defined ctx.ncom Define.Unsafe) && has_unmanaged prop.psig.snorm then raise Exit;
-	let p = { p with pfile =	p.pfile ^" (" ^prop.pname ^")" } in
-	let pmflags = match prop.pget, prop.pset with
-		| Some(_,fl1), _ -> Some fl1
-		| _, Some(_,fl2) -> Some fl2
-		| _ -> None
-	in
-	let cff_access = match pmflags with
-		| Some { mf_access = FAFamily | FAFamOrAssem } -> APrivate
-		| Some { mf_access = FAPublic } -> APublic
-		| _ -> raise Exit (* non-public / protected fields don't interest us *)
-	in
-	let access acc = acc.mf_access in
-	let cff_access = match pmflags with
-		| Some m when List.mem CMStatic m.mf_contract ->
-			[AStatic;cff_access]
-		| _ -> [cff_access]
-	in
-	let get = match prop.pget with
-		| None -> "never"
-		| Some(s,_) when String.length s <= 4 || String.sub s 0 4 <> "get_" ->
-			raise Exit (* special (?) getter; not used *)
-		| Some(_,m) when access m <> FAPublic -> (match access m with
-			| FAFamily
-			| FAFamOrAssem -> "null"
-			| _ -> "never")
-		| Some _ -> "get"
-	in
-	let set = match prop.pset with
-		| None -> "never"
-		| Some(s,_) when String.length s <= 4 || String.sub s 0 4 <> "set_" ->
-			raise Exit (* special (?) getter; not used *)
-		| Some(_,m) when access m <> FAPublic -> (match access m with
-			| FAFamily
-			| FAFamOrAssem -> "never"
-			| _ -> "never");
-		| Some _ -> "set"
-	in
-	if PMap.mem "net_loader_debug" ctx.ncom.defines then
-		Printf.printf "\tproperty %s (%s,%s) : %s\n" prop.pname get set (IlMetaDebug.ilsig_s prop.psig.ssig);
-	let ilsig = match prop.psig.snorm with
-		| LMethod (_,ret,[]) -> ret
-		| s -> raise Exit
-	in
+		IntDivisionSynf.configure gen;
 
-	let meta = if is_explicit_impl then
-			[ Meta.NoCompletion,[],p; Meta.SkipReflection,[],p ]
-		else
-			[]
-	in
+		UnreachableCodeEliminationSynf.configure gen false;
 
-	let kind =
-		FProp ((get,null_pos), (set,null_pos), Some(convert_signature ctx p ilsig,null_pos), None)
-	in
-	{
-		cff_name = prop.pname,null_pos;
-		cff_doc = None;
-		cff_pos = p;
-		cff_meta = meta;
-		cff_access = cff_access;
-		cff_kind = kind;
-	}
-
-let get_type_path ctx ct = match ct with | CTPath p -> p | _ -> assert false
-
-let is_explicit ctx ilcls i =
-	let s = match i with
-		| LClass(path,_) | LValueType(path,_) -> ilpath_s path
-		| _ -> assert false
-	in
-	let len = String.length s in
-	List.exists (fun m ->
-		String.length m.mname > len && String.sub m.mname 0 len = s
-	) ilcls.cmethods
-
-let mke e p = (e,p)
-
-let mk_special_call name p args =
-	mke (ECast( mke (EUntyped( mke (ECall( mke (EConst(Ident name)) p, args )) p )) p , None)) p
-
-let mk_this_call name p args =
-	mke (ECall( mke (EField(mke (EConst(Ident "this")) p ,name)) p, args )) p
-
-let mk_metas metas p =
-	List.map (fun m -> m,[],p) metas
-
-let mk_abstract_fun name p kind metas acc =
-	let metas = mk_metas metas p in
-	{
-		cff_name = name,null_pos;
-		cff_doc = None;
-		cff_pos = p;
-		cff_meta = metas;
-		cff_access = acc;
-		cff_kind = kind;
-	}
-
-let convert_fun_arg ctx p = function
-	| LManagedPointer s ->
-		mk_type_path ctx (["cs"],[],"Ref") [ TPType (convert_signature ctx p s,null_pos) ],p
-	| s ->
-		convert_signature ctx p s,p
-
-let convert_fun ctx p ret args =
-	let args = List.map (convert_fun_arg ctx p) args in
-	CTFunction(args, (convert_signature ctx p ret,null_pos))
-
-let get_clsname ctx cpath =
-	match netpath_to_hx ctx.nstd cpath with
-		| (_,n) -> n
-
-let convert_delegate ctx p ilcls =
-	let p = { p with pfile =	p.pfile ^" (abstract delegate)" } in
-	(* will have the following methods: *)
-	(* - new (haxeType:Func) *)
-	(* - FromHaxeFunction(haxeType) *)
-	(* - Invoke() *)
-	(* - AsDelegate():Super *)
-	(* - @:op(A+B) Add(d:absType) *)
-	(* - @:op(A-B) Remove(d:absType) *)
-	let abs_type = mk_type_path ctx (ilcls.cpath) (List.map (fun t -> TPType (mk_type_path ctx ([],[],"T" ^ string_of_int t.tnumber) [],null_pos)) ilcls.ctypes) in
-	let invoke = List.find (fun m -> m.mname = "Invoke") ilcls.cmethods in
-	let ret = invoke.mret.snorm in
-	let args = List.map (fun (_,_,s) -> s.snorm) invoke.margs in
-	let haxe_type = convert_fun ctx p ret args in
-	let types = List.map (fun t ->
-		{
-			tp_name = ("T" ^ string_of_int t.tnumber),null_pos;
-			tp_params = [];
-			tp_constraints = [];
-			tp_meta = [];
-		}
-	) ilcls.ctypes in
-	let mk_op_fn op name p =
-		let fn_name = List.assoc op cs_binops in
-		let clsname = match ilcls.cpath with
-			| (ns,inner,n) -> get_clsname ctx (ns,inner,"Delegate_"^n)
-		in
-		let expr = (ECall( (EField( (EConst(Ident (clsname)),p), fn_name ),p), [(EConst(Ident"arg1"),p);(EConst(Ident"arg2"),p)]),p) in
-		FFun {
-			f_params = types;
-			f_args = [("arg1",null_pos),false,[],Some (abs_type,null_pos),None;("arg2",null_pos),false,[],Some (abs_type,null_pos),None];
-			f_type = Some (abs_type,null_pos);
-			f_expr = Some ( (EReturn (Some expr), p) );
-		}
-	in
-	let mk_op op name =
-		let p = { p with pfile = p.pfile ^" (op " ^ name ^ ")" } in
-		{
-			cff_name = name,null_pos;
-			cff_doc = None;
-			cff_pos = p;
-			cff_meta = [ Meta.Extern,[],p ; Meta.Op, [ (EBinop(op, (EConst(Ident"A"),p), (EConst(Ident"B"),p)),p) ], p ];
-			cff_access = [APublic;AInline;AStatic];
-			cff_kind = mk_op_fn op name p;
-		}
-	in
-	let params = (List.map (fun s ->
-		TPType (mk_type_path ctx ([],[],fst s.tp_name) [],null_pos)
-	) types) in
-	let underlying_type = match ilcls.cpath with
-		| ns,inner,name ->
-			mk_type_path ctx (ns,inner,"Delegate_" ^ name) params
-	in
+		ArrayDeclSynf.configure gen native_arr_cl change_param_type;
 
-	let fn_new = FFun {
-		f_params = [];
-		f_args = [("hxfunc",null_pos),false,[],Some (haxe_type,null_pos),None];
-		f_type = None;
-		f_expr = Some ( EBinop(Ast.OpAssign, (EConst(Ident "this"),p), (mk_special_call "__delegate__" p [EConst(Ident "hxfunc"),p]) ), p );
-	} in
-	let fn_from_hx = FFun {
-		f_params = types;
-		f_args = [("hxfunc",null_pos),false,[],Some (haxe_type,null_pos),None];
-		f_type = Some( mk_type_path ctx ilcls.cpath params,null_pos );
-		f_expr = Some( EReturn( Some (mk_special_call "__delegate__" p [EConst(Ident "hxfunc"),p] )), p);
-	} in
-	let fn_asdel = FFun {
-		f_params = [];
-		f_args = [];
-		f_type = None;
-		f_expr = Some(
-			EReturn( Some ( EConst(Ident "this"), p ) ), p
-		);
-	} in
-	let fn_new = mk_abstract_fun "new" p fn_new [Meta.Extern] [APublic;AInline] in
-	let fn_from_hx = mk_abstract_fun "FromHaxeFunction" p fn_from_hx [Meta.Extern;Meta.From] [APublic;AInline;AStatic] in
-	let fn_asdel = mk_abstract_fun "AsDelegate" p fn_asdel [Meta.Extern] [APublic;AInline] in
-	let _, c = netpath_to_hx ctx.nstd ilcls.cpath in
-	EAbstract {
-		d_name = netname_to_hx c,null_pos;
-		d_doc = None;
-		d_params = types;
-		d_meta = mk_metas [Meta.Delegate; Meta.Forward] p;
-		d_flags = [AIsType (underlying_type,null_pos)];
-		d_data = [fn_new;fn_from_hx;fn_asdel;mk_op Ast.OpAdd "Add";mk_op Ast.OpSub "Remove"];
-	}
-
-let convert_ilclass ctx p ?(delegate=false) ilcls = match ilcls.csuper with
-	| Some { snorm = LClass ((["System"],[],"Enum"),[]) } ->
-		convert_ilenum ctx p ilcls
-	| _ ->
-		let flags = ref [HExtern] in
-		(* todo: instead of CsNative, use more specific definitions *)
-		if PMap.mem "net_loader_debug" ctx.ncom.defines then begin
-			let sup = match ilcls.csuper with | None -> [] | Some c -> [IlMetaDebug.ilsig_s c.ssig] in
-			let sup = sup @ List.map (fun i -> IlMetaDebug.ilsig_s i.ssig) ilcls.cimplements in
-			print_endline ("converting " ^ ilpath_s ilcls.cpath ^ " : " ^ (String.concat ", " sup))
-		end;
-		let meta = ref [Meta.CsNative, [], p; Meta.Native, [EConst (String (ilpath_s ilcls.cpath) ), p], p] in
-		let force_check = Common.defined ctx.ncom Define.ForceLibCheck in
-		if not force_check then
-			meta := (Meta.LibType,[],p) :: !meta;
-
-		let is_interface = ref false in
-		List.iter (fun f -> match f with
-			| SSealed -> meta := (Meta.Final, [], p) :: !meta
-			| SInterface ->
-				is_interface := true;
-				flags := HInterface :: !flags
-			| SAbstract -> meta := (Meta.Abstract, [], p) :: !meta
-			| _ -> ()
-		) ilcls.cflags.tdf_semantics;
-
-		(* (match ilcls.cflags.tdf_vis with *)
-		(*	| VPublic | VNestedFamOrAssem | VNestedFamily -> () *)
-		(*	| _ -> raise Exit); *)
-		(match ilcls.csuper with
-			| Some { snorm = LClass ( (["System"],[],"Object"), [] ) } -> ()
-			| Some ({ snorm = LClass ( (["System"],[],"ValueType"), [] ) } as s) ->
-				flags := HExtends (get_type_path ctx (convert_signature ctx p s.snorm),null_pos) :: !flags;
-				meta := (Meta.Struct,[],p) :: !meta
-			| Some { snorm = LClass ( (["haxe";"lang"],[],"HxObject"), [] ) } ->
-				meta := (Meta.HxGen,[],p) :: !meta
-			| Some s ->
-				flags := HExtends (get_type_path ctx (convert_signature ctx p s.snorm),null_pos) :: !flags
-			| _ -> ());
-
-			let has_explicit_ifaces = ref false in
-			List.iter (fun i ->
-				match i.snorm with
-				| LClass ( (["haxe";"lang"],[], "IHxObject"), _ ) ->
-					meta := (Meta.HxGen,[],p) :: !meta
-				(* | i when is_explicit ctx ilcls i -> () *)
-				| i ->
-					if is_explicit ctx ilcls i then has_explicit_ifaces := true;
-					flags := if !is_interface then
-						HExtends (get_type_path ctx (convert_signature ctx p i),null_pos) :: !flags
-					else
-						HImplements (get_type_path ctx (convert_signature ctx p i),null_pos) :: !flags
-			) ilcls.cimplements;
-			(* this is needed because of explicit interfaces. see http://msdn.microsoft.com/en-us/library/aa288461(v=vs.71).aspx *)
-			(* explicit interfaces can't be mapped into Haxe in any way - since their fields can't be accessed directly, but they still implement that interface *)
-			if !has_explicit_ifaces && force_check then (* do not check on this specific case *)
-				meta := (Meta.LibType,[],p) :: !meta;
-
-			(* ArrayAccess *)
-			ignore (List.exists (function
-			| { psig = { snorm = LMethod(_,ret,[v]) } } ->
-				flags := if !is_interface then
-					(HExtends( raw_type_path ctx ([],"ArrayAccess") [ TPType (convert_signature ctx p ret,null_pos) ],null_pos) :: !flags)
-				else
-					(HImplements( raw_type_path ctx ([],"ArrayAccess") [ TPType (convert_signature ctx p ret,null_pos) ],null_pos) :: !flags);
-				true
-			| _ -> false) ilcls.cprops);
-
-			let fields = ref [] in
-			let run_fields fn f =
-				List.iter (fun f ->
-					try
-						fields := fn f :: !fields
-					with
-						| Exit -> ()
-				) f
-			in
-			let meths = if !is_interface then
-					List.filter (fun m -> m.moverride = None) ilcls.cmethods
-				else
-					ilcls.cmethods
-			in
-			run_fields (fun m ->
-				convert_ilmethod ctx p m (List.exists (fun m2 -> m != m2 && String.get m2.mname 0 <> '.' && String.ends_with m2.mname ("." ^ m.mname)) meths)
-			) meths;
-			run_fields (convert_ilfield ctx p) ilcls.cfields;
-			run_fields (fun prop ->
-				convert_ilprop ctx p prop (List.exists (fun p2 -> prop != p2 && String.get p2.pname 0 <> '.' && String.ends_with p2.pname ("." ^ prop.pname)) ilcls.cprops)
-			) ilcls.cprops;
-			run_fields (convert_ilevent ctx p) ilcls.cevents;
-
-			let params = List.map (fun p ->
-				{
-					tp_name = "T" ^ string_of_int p.tnumber,null_pos;
-					tp_params = [];
-					tp_constraints = [];
-					tp_meta = [];
-				}) ilcls.ctypes
-			in
+		CSharpSpecificSynf.configure gen runtime_cl;
+		CSharpSpecificESynf.configure gen runtime_cl;
 
-			if delegate then begin
-				(* add op_Addition and op_Subtraction *)
-				let path = ilcls.cpath in
-				let thist = mk_type_path ctx path (List.map (fun t -> TPType (mk_type_path ctx ([],[],"T" ^ string_of_int t.tnumber) [],null_pos)) ilcls.ctypes) in
-				let op name =
-					{
-						cff_name = name,null_pos;
-						cff_doc = None;
-						cff_pos = p;
-						cff_meta = [];
-						cff_access = [APublic;AStatic];
-						cff_kind = FFun {
-							f_params = params;
-							f_args = [("arg1",null_pos),false,[],Some (thist,null_pos),None;("arg2",null_pos),false,[],Some (thist,null_pos),None];
-							f_type = Some (thist,null_pos);
-							f_expr = None;
-						};
-					}
-				in
-				fields := op "op_Addition" :: op "op_Subtraction" :: !fields;
-			end;
-			let path = match ilcls.cpath with
-				| ns,inner,name when delegate ->
-					ns,inner,"Delegate_"^name
-				| _ -> ilcls.cpath
-			in
-			let _, c = netpath_to_hx ctx.nstd path in
-			EClass {
-				d_name = netname_to_hx c,null_pos;
-				d_doc = None;
-				d_params = params;
-				d_meta = !meta;
-				d_flags = !flags;
-				d_data = !fields;
-			}
-
-type il_any_field =
-	| IlField of ilfield
-	| IlMethod of ilmethod
-	| IlProp of ilprop
-
-let get_fname = function
-	| IlField f -> f.fname
-	| IlMethod m -> m.mname
-	| IlProp p -> p.pname
-
-let is_static = function
-	| IlField f ->
-		List.mem CStatic f.fflags.ff_contract
-	| IlMethod m ->
-		List.mem CMStatic m.mflags.mf_contract
-	| IlProp p ->
-		List.exists (function
-		 | None -> false
-		 | Some (_,m) -> List.mem CMStatic m.mf_contract
-		) [p.pget;p.pset]
-	(* | _ -> false *)
-
-let change_name name = function
-	| IlField f -> IlField { f with fname = name }
-	| IlMethod m -> IlMethod { m with mname = name }
-	| IlProp p -> IlProp { p with pname = name }
-
-let compatible_methods m1 m2 = match m1,m2 with
-	| IlMethod { msig = { snorm = LMethod(_,ret1,args1) } }, IlMethod { msig = { snorm = LMethod(_,ret2,args2) } } ->
-		ret1 = ret2 && args1 = args2
-	| _ -> false
+		let out_files = ref [] in
 
-let ilcls_from_ilsig ctx ilsig =
-	let path, params = match ilsig with
-		| LClass(path, params) | LValueType(path, params) ->
-			path, params
-		| LObject ->
-			(["System"],[],"Object"),[]
-		| _ -> raise Not_found (* all other types won't appear as superclass *)
-	in
-	match lookup_ilclass ctx.nstd ctx.ncom path with
-	| None -> raise Not_found
-	| Some c ->
-		c, params
-
-let rec ilapply_params params = function
-	| LManagedPointer s -> LManagedPointer (ilapply_params params s)
-	| LPointer s -> LPointer (ilapply_params params s)
-	| LValueType (p,pl) -> LValueType(p, List.map (ilapply_params params) pl)
-	| LClass (p,pl) -> LClass(p, List.map (ilapply_params params) pl)
-	| LTypeParam i ->
-		List.nth params i (* TODO: maybe i - 1? *)
-	| LVector s -> LVector (ilapply_params params s)
-	| LArray (s,a) -> LArray (ilapply_params params s, a)
-	| LMethod (c,r,args) -> LMethod (c, ilapply_params params r, List.map (ilapply_params params) args)
-	| p -> p
-
-let ilcls_with_params ctx cls params =
-	match cls.ctypes with
-	| [] -> cls
-	| _ ->
-		{ cls with
-			cfields = List.map (fun f -> { f with fsig = { f.fsig with snorm = ilapply_params params f.fsig.snorm } }) cls.cfields;
-			cmethods = List.map (fun m -> { m with
-				msig = { m.msig with snorm = ilapply_params params m.msig.snorm };
-				margs = List.map (fun (n,f,s) -> (n,f,{ s with snorm = ilapply_params params s.snorm })) m.margs;
-				mret = { m.mret with snorm = ilapply_params params m.mret.snorm };
-			}) cls.cmethods;
-			cprops = List.map (fun p -> { p with psig = { p.psig with snorm = ilapply_params params p.psig.snorm } }) cls.cprops;
-			csuper = Option.map (fun s -> { s with snorm = ilapply_params params s.snorm } ) cls.csuper;
-			cimplements = List.map (fun s -> { s with snorm = ilapply_params params s.snorm } ) cls.cimplements;
-		}
+		(* copy resource files *)
+		if Hashtbl.length gen.gcon.resources > 0 then begin
+			let src =
+					gen.gcon.file ^ "/src/Resources"
+			in
+			Hashtbl.iter (fun name v ->
+				let name = Codegen.escape_res_name name true in
+				let full_path = src ^ "/" ^ name in
+				mkdir_from_path full_path;
 
-let rec compatible_params t1 t2 = match t1,t2 with
-	| LManagedPointer(s1), LManagedPointer(s2) -> compatible_params s1 s2
-	| LManagedPointer(s1), s2 | s1, LManagedPointer(s2) ->
-		compatible_params s1 s2
-	| _ -> t1 = t2
-
-let compatible_methods m1 m2 = match m1, m2 with
-	| LMethod(_,r1,a1), LMethod(_,r2,a2) -> (try
-		List.for_all2 (fun a1 a2 -> compatible_params a1 a2) a1 a2
-	with | Invalid_argument _ ->
-		false)
-	| _ -> false
+				let f = open_out_bin full_path in
+				output_string f v;
+				close_out f;
 
-let compatible_field f1 f2 = match f1, f2 with
-	| IlMethod { msig = { snorm = LMethod(_,_,a1) } },
-		IlMethod { msig = { snorm = LMethod(_,_,a2) } } ->
-			a1 = a2
-	| IlProp p1, IlProp p2 ->
-			(* p1.psig.snorm = p2.psig.snorm *)
-			true
-	| IlField f1, IlField f2 ->
-			(* f1.fsig.snorm = f2.fsig.snorm *)
-			true
-	| _ -> false
+				out_files := (Path.unique_full_path full_path) :: !out_files
+			) gen.gcon.resources;
+		end;
+		(* add resources array *)
+		(try
+			let res = get_cl (Hashtbl.find gen.gtypes (["haxe"], "Resource")) in
+			let cf = PMap.find "content" res.cl_statics in
+			let res = ref [] in
+			Hashtbl.iter (fun name v ->
+				res := { eexpr = TConst(TString name); etype = gen.gcon.basic.tstring; epos = null_pos } :: !res;
+			) gen.gcon.resources;
+			cf.cf_expr <- Some ({ eexpr = TArrayDecl(!res); etype = gen.gcon.basic.tarray gen.gcon.basic.tstring; epos = null_pos })
+		with | Not_found -> ());
 
-let get_all_fields cls =
-	let all_fields = List.map (fun f -> IlField f, cls.cpath, f.fname, List.mem CStatic f.fflags.ff_contract) cls.cfields in
-	let all_fields = all_fields @ List.map (fun m -> IlMethod m, cls.cpath, m.mname, List.mem CMStatic m.mflags.mf_contract) cls.cmethods in
-	let all_fields = all_fields @ List.map (fun p -> IlProp p, cls.cpath, p.pname, is_static (IlProp p)) cls.cprops in
-	all_fields
-
-let normalize_ilcls ctx cls =
-	let force_check = Common.defined ctx.ncom Define.ForceLibCheck in
-	(* first filter out overloaded fields of same signature *)
-	let rec loop acc = function
-		| [] -> acc
-		| m :: cmeths ->
-			let static = List.mem CMStatic m.mflags.mf_contract in
-			if List.exists (fun m2 -> m.mname = m2.mname && List.mem CMStatic m2.mflags.mf_contract = static && compatible_methods m.msig.snorm m2.msig.snorm) cmeths then
-				loop acc cmeths
-			else
-				loop (m :: acc) cmeths
-	in
-	let meths = loop [] cls.cmethods in
-	(* fix overrides *)
-	(* get only the methods that aren't declared as override, but may be *)
-	let meths = List.map (fun v -> ref v) meths in
-	let no_overrides = List.filter (fun m ->
-		let m = !m in
-		not (List.mem CMStatic m.mflags.mf_contract)
-	) meths in
-	let no_overrides = ref no_overrides in
-
-	let all_fields = ref [] in
-	let all_events_name = Hashtbl.create 0 in
-	(* avoid naming collision between events and functions *)
-	let add_cls_events_collision cls =
-		List.iter (fun m -> if not (List.mem CMStatic m.mflags.mf_contract) then Hashtbl.replace all_events_name m.mname true) cls.cmethods;
-		List.iter (fun p -> if not (is_static (IlProp p)) then Hashtbl.replace all_events_name p.pname true) cls.cprops;
-	in
+		run_filters gen;
+		(* after the filters have been run, add all hashed fields to FieldLookup *)
 
-	let rec loop cls = try
-		match cls.csuper with
-		| Some { snorm = LClass((["System"],[],"Object"),_) }
-		| Some { snorm = LObject } | None -> ()
-		| Some s ->
-			let cls, params = ilcls_from_ilsig ctx s.snorm in
-			let cls = ilcls_with_params ctx cls params in
-			if force_check then no_overrides := List.filter (fun v ->
-				let m = !v in
-				let is_override_here = List.exists (fun m2 ->
-					m2.mname = m.mname && not (List.mem CMStatic m2.mflags.mf_contract) && compatible_methods m.msig.snorm m2.msig.snorm
-				) cls.cmethods in
-				if is_override_here then v := { m with moverride = Some(cls.cpath, m.mname) };
-				not is_override_here
-			) !no_overrides;
-			all_fields := get_all_fields cls @ !all_fields;
-
-			add_cls_events_collision cls;
-			List.iter (fun ev -> Hashtbl.replace all_events_name ev.ename true) cls.cevents;
-
-			loop cls
-		with | Not_found -> ()
-	in
-	loop cls;
-
-	add_cls_events_collision cls;
-	if force_check then List.iter (fun v -> v := { !v with moverride = None }) !no_overrides;
-	let added = ref [] in
-
-	let current_all = ref (get_all_fields cls @ !all_fields) in
-	(* look for interfaces and add missing implementations (some methods' implementation is optional) *)
-	let rec loop_interface cls iface = try
-		match iface.snorm with
-		| LClass((["System"],[],"Object"),_) | LObject -> ()
-		| LClass(path,_) when path = cls.cpath -> ()
-		| s ->
-			let cif, params = ilcls_from_ilsig ctx s in
-			let cif = ilcls_with_params ctx cif params in
-			List.iter (function
-				| (f,_,name,false) as ff ->
-					(* look for compatible fields *)
-					if not (List.exists (function
-						| (f2,_,name2,false) when (name = name2 || String.ends_with name2 ("." ^ name)) -> (* consider explicit implementations as implementations *)
-							compatible_field f f2
-						| _ -> false
-					) !current_all) then begin
-						current_all := ff :: !current_all;
-						added := ff :: !added
-					end else
-						(* ensure it's public *)
-						List.iter (fun mref -> match !mref with
-							| m when m.mname = name && compatible_field f (IlMethod m) ->
-								mref := { m with mflags = { m.mflags with mf_access = FAPublic } }
-							| _ -> ()
-						) meths
-				| _ -> ()
-			) (get_all_fields cif);
-			List.iter (loop_interface cif) cif.cimplements
-		with | Not_found -> ()
-	in
-	List.iter (loop_interface cls) cls.cimplements;
-	let added = List.map (function
-		| (IlMethod m,a,name,b) when m.mflags.mf_access <> FAPublic ->
-			(IlMethod { m with mflags = { m.mflags with mf_access = FAPublic } },a,name,b)
-		| (IlField f,a,name,b) when f.fflags.ff_access <> FAPublic ->
-			(IlField { f with fflags = { f.fflags with ff_access = FAPublic } },a,name,b)
-		| s -> s
-	) !added in
-
-	(* filter out properties that were already declared *)
-	let props = if force_check then List.filter (function
-			| p ->
-				let static = is_static (IlProp p) in
-				let name = p.pname in
-				not (List.exists (function (IlProp _,_,n,s) -> s = static && name = n | _ -> false) !all_fields)
-			(* | _ -> false *)
-		) cls.cprops
-		else
-			cls.cprops
-	in
-	let cls = { cls with cmethods = List.map (fun v -> !v) meths; cprops = props } in
-
-	let clsfields = (get_all_fields cls) @ added in
-	let super_fields = !all_fields in
-	all_fields := clsfields @ !all_fields;
-	let refclsfields = (List.map (fun v -> ref v) clsfields) in
-	(* search static / non-static name clash *)
-	(* change field name to not collide with haxe keywords *)
-	let fold_field acc v =
-		let f, p, name, is_static = !v in
-		let change, copy = match name with
-		| _ when is_haxe_keyword name ->
-			true, false
-		| _ ->
-			((is_static && List.exists (function | (f,_,n,false) -> name = n | _ -> false) !all_fields) ||
-			(not is_static && match f with (* filter methods that have the same name as fields *)
-			| IlMethod _ ->
-				List.exists (function | ( (IlProp _ | IlField _),_,n,false) -> name = n | _ -> false) super_fields ||
-				List.exists (function | ( (IlProp _ | IlField _),_,n,s) -> name = n | _ -> false) clsfields
-			| _ -> false)), true
+		let normalize_i i =
+			let i = Int32.of_int (i) in
+			if i < Int32.zero then
+				Int32.logor (Int32.logand i (Int32.of_int 0x3FFFFFFF)) (Int32.shift_left Int32.one 30)
+			else i
 		in
-		if change then begin
-			let name = "%" ^ name in
-			let changed = change_name name f, p, name, is_static in
-			if not copy then
-				v := changed;
-			if copy then
-				v :: ref changed :: acc
-			else
-				v :: acc
-		end else
-			v :: acc
-	in
-	let refclsfields = List.fold_left fold_field [] refclsfields in
 
-	let rec fold (fields,methods,props) f = match !f with
-		| IlField f,_,_,_ -> f :: fields,methods,props
-		| IlMethod m,_,_,_ -> fields,m :: methods,props
-		| IlProp p,_,_,_ -> fields,methods,p :: props
-	in
-	let fields, methods, props = List.fold_left fold ([],[],[]) refclsfields in
-	{ cls with
-		cfields = fields;
-		cprops = props;
-		cmethods = methods;
-		cevents = List.filter (fun ev -> not (Hashtbl.mem all_events_name ev.ename)) cls.cevents;
-	}
-
-let add_net_std com file =
-	com.net_std <- file :: com.net_std
-
-let add_net_lib com file std =
-	let ilctx = ref None in
-	let netpath_to_hx = netpath_to_hx std in
-	let real_file = ref file in
-	let get_ctx () =
-		match !ilctx with
-		| Some c ->
-			c
-		| None ->
-			let file = if Sys.file_exists file then
-				file
-			else try Common.find_file com file with
-				| Not_found -> try Common.find_file com (file ^ ".dll") with
-				| Not_found ->
-					failwith (".NET lib " ^ file ^ " not found")
-			in
-			real_file := file;
-			let r = PeReader.create_r (open_in_bin file) com.defines in
-			let ctx = PeReader.read r in
-			let clr_header = PeReader.read_clr_header ctx in
-			let cache = IlMetaReader.create_cache () in
-			let meta = IlMetaReader.read_meta_tables ctx clr_header cache in
-			close_in (r.PeReader.ch);
-			if PMap.mem "net_loader_debug" com.defines then
-				print_endline ("for lib " ^ file);
-			let il_typedefs = Hashtbl.copy meta.il_typedefs in
-			Hashtbl.clear meta.il_typedefs;
-
-			Hashtbl.iter (fun _ td ->
-				let path = IlMetaTools.get_path (TypeDef td) in
-				if PMap.mem "net_loader_debug" com.defines then
-					Printf.printf "found %s\n" (s_type_path (netpath_to_hx path));
-				Hashtbl.replace com.net_path_map (netpath_to_hx path) path;
-				Hashtbl.replace meta.il_typedefs path td
-			) il_typedefs;
-			let meta = { nstd = std; ncom = com; nil = meta } in
-			ilctx := Some meta;
-			meta
-	in
+		let nhash = ref 0 in
+		let hashes = Hashtbl.fold (fun i s acc -> incr nhash; (normalize_i i,s) :: acc) rcf_ctx.rcf_hash_fields [] in
+		let hashes = List.sort (fun (i,s) (i2,s2) -> compare i i2) hashes in
 
-	let cache = Hashtbl.create 0 in
-	let lookup path =
-		try
-			Hashtbl.find cache path
-		with | Not_found -> try
-			let ctx = get_ctx() in
-			let ns, n, cl = hxpath_to_net ctx path in
-			let cls = IlMetaTools.convert_class ctx.nil (ns,n,cl) in
-			let cls = normalize_ilcls ctx cls in
-			Hashtbl.add cache path (Some cls);
-			Some cls
-		with | Not_found ->
-			Hashtbl.add cache path None;
-			None
-	in
+		let haxe_libs = List.filter (function (_,_,_,lookup) -> is_some (lookup (["haxe";"lang"], "DceNo"))) gen.gcon.net_libs in
+		(try
+			(* first let's see if we're adding a -net-lib that has already a haxe.lang.FieldLookup *)
+			let name,_,_,_ = List.find (function (_,_,_,lookup) -> is_some (lookup (["haxe";"lang"], "FieldLookup"))) gen.gcon.net_libs in
+			if not (Common.defined gen.gcon Define.DllImport) then begin
+				gen.gcon.warning ("The -net-lib with path " ^ name ^ " contains a Haxe-generated assembly. Please define `-D dll_import` to handle Haxe-generated dll import correctly") null_pos;
+				raise Not_found
+			end;
+			if not (List.exists (function (n,_,_,_) -> n = name) haxe_libs) then
+				gen.gcon.warning ("The -net-lib with path " ^ name ^ " contains a Haxe-generated assembly, however it wasn't compiled with `-dce no`. Recompilation with `-dce no` is recommended") null_pos;
+			(* it has; in this case, we need to add the used fields on each __init__ *)
+			flookup_cl.cl_extern <- true;
+			let hashs_by_path = Hashtbl.create !nhash in
+			Hashtbl.iter (fun (path,i) s -> Hashtbl.add hashs_by_path path (i,s)) rcf_ctx.rcf_hash_paths;
+			Hashtbl.iter (fun _ md -> match md with
+				| TClassDecl ({ cl_extern = false; cl_interface = false } as c) -> (try
+					let all = Hashtbl.find_all hashs_by_path c.cl_path in
+					let all = List.map (fun (i,s) -> normalize_i i, s) all in
+					let all = List.sort (fun (i,s) (i2,s2) -> compare i i2) all in
+
+					if all <> [] then begin
+						let add = mk_static_field_access_infer flookup_cl "addFields" c.cl_pos [] in
+						let expr = { eexpr = TCall(add, [
+							mk_nativearray_decl gen basic.tint (List.map (fun (i,s) -> { eexpr = TConst(TInt (i)); etype = basic.tint; epos = c.cl_pos }) all) c.cl_pos;
+							mk_nativearray_decl gen basic.tstring (List.map (fun (i,s) -> { eexpr = TConst(TString (s)); etype = basic.tstring; epos = c.cl_pos }) all) c.cl_pos;
+						]); etype = basic.tvoid; epos = c.cl_pos } in
+						match c.cl_init with
+							| None -> c.cl_init <- Some expr
+							| Some e ->
+								c.cl_init <- Some { eexpr = TBlock([expr;e]); etype = basic.tvoid; epos = e.epos }
+					end
+				with | Not_found -> ())
+				| _ -> ()) gen.gtypes;
 
-	let all_files () =
-		Hashtbl.fold (fun path _ acc -> match path with
-			| _,_ :: _, _ -> acc
-			| _ -> netpath_to_hx path :: acc) (get_ctx()).nil.il_typedefs []
-	in
+		with | Not_found -> try
+			let basic = gen.gcon.basic in
+			let cl = flookup_cl in
+			let field_ids = PMap.find "fieldIds" cl.cl_statics in
+			let fields = PMap.find "fields" cl.cl_statics in
 
-	let build path =
-		let p = { pfile = !real_file ^ " @ " ^ s_type_path path; pmin = 0; pmax = 0; } in
-		let pack = match fst path with | ["haxe";"root"] -> [] | p -> p in
-		let cp = ref [] in
-		let rec build path = try
-			if PMap.mem "net_loader_debug" com.defines then
-				Printf.printf "looking up %s\n" (s_type_path path);
-			match lookup path with
-			| Some({csuper = Some{snorm = LClass( (["System"],[],("Delegate"|"MulticastDelegate")),_)}} as cls)
-				when List.mem SSealed cls.cflags.tdf_semantics ->
-				let ctx = get_ctx() in
-				let hxcls = convert_ilclass ctx p ~delegate:true cls in
-				let delegate = convert_delegate ctx p cls in
-				cp := (hxcls,p) :: (delegate,p) :: !cp;
-				List.iter (fun ilpath ->
-					let path = netpath_to_hx ilpath in
-					build path
-				) cls.cnested
-			| Some cls ->
-				let ctx = get_ctx() in
-				let hxcls = convert_ilclass ctx p cls in
-				cp := (hxcls,p) :: !cp;
-				List.iter (fun ilpath ->
-					let path = netpath_to_hx ilpath in
-					build path
-				) cls.cnested
-			| _ -> ()
-		with | Not_found | Exit ->
-			()
-		in
-		build path;
-		match !cp with
-			| [] -> None
-			| cp -> Some (!real_file, (pack,cp))
-	in
-	let build path p =
-		build path
-	in
-	com.load_extern_type <- com.load_extern_type @ [build];
-	com.net_libs <- (file, std, all_files, lookup) :: com.net_libs
+			field_ids.cf_expr <- Some (mk_nativearray_decl gen basic.tint (List.map (fun (i,s) -> { eexpr = TConst(TInt (i)); etype = basic.tint; epos = field_ids.cf_pos }) hashes) field_ids.cf_pos);
+			fields.cf_expr <- Some (mk_nativearray_decl gen basic.tstring (List.map (fun (i,s) -> { eexpr = TConst(TString s); etype = basic.tstring; epos = fields.cf_pos }) hashes) fields.cf_pos);
 
-let before_generate com =
-	(* net version *)
-	let net_ver = try
-			int_of_string (PMap.find "net_ver" com.defines)
 		with | Not_found ->
-			Common.define_value com Define.NetVer "20";
-			20
-	in
-	if net_ver < 20 then
-		failwith (
-			".NET version is defined to target .NET "
-			^ string_of_int net_ver
-			^ ", but the compiler can only output code to versions equal or superior to .NET 2.0 (defined as 20)"
+			gen.gcon.error "Fields 'fieldIds' and 'fields' were not found in class haxe.lang.FieldLookup" flookup_cl.cl_pos
 		);
-	let rec loop = function
-		| v :: acc when v <= net_ver ->
-			Common.raw_define com ("NET_" ^ string_of_int v);
-			loop acc
-		| _ -> ()
-	in
-	loop [20;21;30;35;40;45];
 
-	(* net target *)
-	let net_target = try
-			String.lowercase (PMap.find "net_target" com.defines)
-		with | Not_found ->
-			"net"
-	in
-	Common.define_value com Define.NetTarget net_target;
-	Common.raw_define com net_target;
+		if Common.defined gen.gcon Define.DllImport then begin
+			Hashtbl.iter (fun _ md -> match md with
+				| TClassDecl ({ cl_extern = false } as c) -> (try
+					let extra = match c.cl_params with
+						| _ :: _ when not erase_generics -> "_" ^ string_of_int (List.length c.cl_params)
+						| _ -> ""
+					in
+					let pack = match c.cl_path with
+						| ([], _) when no_root && is_hxgen (TClassDecl c) ->
+							["haxe";"root"]
+						| (p,_) -> p
+					in
+					let path = (pack, snd c.cl_path ^ extra) in
+					ignore (List.find (function (_,_,_,lookup) ->
+						is_some (lookup path)) haxe_libs);
+					c.cl_extern <- true;
+				with | Not_found -> ())
+				| _ -> ()) gen.gtypes
+		end;
 
-	(* std dirs *)
-	let stds = match com.net_std with
-		| [] -> ["netlib"]
-		| s -> s
-	in
-	(* look for all dirs that have the digraph NET_TARGET-NET_VER *)
-	let digraph = net_target ^ "-" ^ string_of_int net_ver in
-	let matched = ref [] in
-	List.iter (fun f -> try
-		let f = Common.find_file com (f ^ "/" ^ digraph) in
-		matched := (f, Unix.opendir f) :: !matched
-	with | _ -> ()) stds;
-
-	if !matched = [] then failwith (
-		"No .NET std lib directory with the pattern '" ^ digraph ^ "' was found in the -net-std search path. " ^
-		"Try updating the hxcs lib to the latest version, or specifying another -net-std path.");
-	List.iter (fun (path,f) ->
-		let rec loop () =
-			try
-				let f = Unix.readdir f in
-				let finsens = String.lowercase f in
-				if String.ends_with finsens ".dll" then
-					add_net_lib com (path ^ "/" ^ f) true;
-				loop()
-			with | End_of_file ->
-				Unix.closedir f
-		in
-		loop()
-	) !matched;
+		RenameTypeParameters.run gen.gtypes_list;
+
+		mkdir_from_path gen.gcon.file;
+
+		List.iter (fun md_def ->
+			let source_dir = gen.gcon.file ^ "/src/" ^ (String.concat "/" (fst (path_of_md_def md_def))) in
+			let w = SourceWriter.new_source_writer() in
+			let should_write = List.fold_left (fun should md -> module_type_gen w md || should) false md_def.m_types in
+			if should_write then begin
+				let path = path_of_md_def md_def in
+				write_file gen w source_dir path "cs" out_files
+			end
+		) gen.gmodules;
+
+		if not (Common.defined gen.gcon Define.KeepOldOutput) then
+			clean_files (gen.gcon.file ^ "/src") !out_files gen.gcon.verbose;
+
+		dump_descriptor gen ("hxcs_build.txt") s_type_path module_s;
+		if ( not (Common.defined gen.gcon Define.NoCompilation) ) then begin
+			let old_dir = Sys.getcwd() in
+			Sys.chdir gen.gcon.file;
+			let cmd = "haxelib run hxcs hxcs_build.txt --haxe-version " ^ (string_of_int gen.gcon.version) ^ " --feature-level 1" in
+			print_endline cmd;
+			if gen.gcon.run_command cmd <> 0 then failwith "Build failed";
+			Sys.chdir old_dir;
+		end
 
-	(* now force all libraries to initialize *)
-	List.iter (function (_,_,_,lookup) -> ignore (lookup ([],""))) com.net_libs
+	with TypeNotFound path ->
+		con.error ("Error. Module '" ^ (s_type_path path) ^ "' is required and was not included in build.") null_pos);
+	debug_mode := false

+ 240 - 102
src/generators/genhl.ml

@@ -135,7 +135,7 @@ let is_to_string t =
 	| _ -> false
 
 let is_extern_field f =
-	Type.is_extern_field f || (match f.cf_kind with Method MethNormal -> List.exists (fun (m,_,_) -> m = Meta.Custom ":hlNative") f.cf_meta | _ -> false) || Meta.has Meta.Extern f.cf_meta
+	not (Type.is_physical_field f) || (match f.cf_kind with Method MethNormal -> List.exists (fun (m,_,_) -> m = Meta.Custom ":hlNative") f.cf_meta | _ -> false) || Meta.has Meta.Extern f.cf_meta
 
 let is_array_class name =
 	match name with
@@ -184,7 +184,7 @@ let type_size_bits = function
 	| HUI8 | HBool -> 0
 	| HUI16 -> 1
 	| HI32 | HF32 -> 2
-	| HF64 -> 3
+	| HI64 | HF64 -> 3
 	| _ -> assert false
 
 let new_lookup() =
@@ -334,6 +334,12 @@ let make_debug ctx arr =
 	done;
 	out
 
+let fake_tnull =
+	{null_abstract with
+		a_path = [],"Null";
+		a_params = ["T",t_dynamic];
+	}
+
 let rec to_type ?tref ctx t =
 	match t with
 	| TMono r ->
@@ -353,11 +359,10 @@ let rec to_type ?tref ctx t =
 			t
 		) in
 		(match td.t_path with
-		| [], "Null" when not (is_nullable t) -> HNull t
 		| ["haxe";"macro"], name -> Hashtbl.replace ctx.macro_typedefs name t; t
 		| _ -> t)
 	| TLazy f ->
-		to_type ?tref ctx (!f())
+		to_type ?tref ctx (lazy_type f)
 	| TFun (args, ret) ->
 		HFun (List.map (fun (_,o,t) ->
 			let pt = to_type ctx t in
@@ -425,6 +430,9 @@ let rec to_type ?tref ctx t =
 			in
 			loop tl
 		| _ -> class_type ~tref ctx c pl false)
+	| TAbstract ({a_path = [],"Null"},[t1]) ->
+		let t = to_type ?tref ctx t1 in
+		if not (is_nullable t) then HNull t else t
 	| TAbstract (a,pl) ->
 		if Meta.has Meta.CoreType a.a_meta then
 			(match a.a_path with
@@ -444,6 +452,7 @@ let rec to_type ?tref ctx t =
 			| ["hl"], "Type" -> HType
 			| ["hl"], "UI16" -> HUI16
 			| ["hl"], "UI8" -> HUI8
+			| ["hl"], "I64" -> HI64
 			| ["hl"], "NativeArray" -> HArray
 			| ["haxe";"macro"], "Position" -> HAbstract ("macro_pos", alloc_string ctx "macro_pos")
 			| _ -> failwith ("Unknown core type " ^ s_type_path a.a_path))
@@ -486,14 +495,24 @@ and real_type ctx e =
 		match e.eexpr with
 		| TField (_,f) ->
 			let ft = field_type ctx f e.epos in
-			(*
-				Handle function variance:
-				If we have type parameters which are function types, we need to keep the functions
-				because we might need to insert a cast to coerce Void->Bool to Void->Dynamic for instance.
-			*)
 			(match ft, e.etype with
 			| TFun (args,ret), TFun (args2,_) ->
-				TFun (List.map2 (fun ((_,_,t) as a) ((_,_,t2) as a2) -> match t, t2 with TInst ({cl_kind=KTypeParameter _},_), TFun _ -> a2 | _ -> a) args args2, ret)
+				TFun (List.map2 (fun ((name,opt,t) as a) ((_,_,t2) as a2) ->
+					match t, t2 with
+					(*
+						Handle function variance:
+						If we have type parameters which are function types, we need to keep the functions
+						because we might need to insert a cast to coerce Void->Bool to Void->Dynamic for instance.
+					*)
+					| TInst ({cl_kind=KTypeParameter _},_), TFun _ -> a2
+					(*
+						If we have a number, it is more accurate to cast it to the type parameter before wrapping it as dynamic
+					*)
+					| TInst ({cl_kind=KTypeParameter _},_), t when is_number (to_type ctx t) ->
+						(name, opt, TAbstract (fake_tnull,[t]))
+					| _ ->
+						a
+				) args args2, ret)
 			| _ -> ft)
 		| TLocal v -> v.v_type
 		| TParenthesis e -> loop e
@@ -837,7 +856,7 @@ let shl ctx idx v =
 
 let set_default ctx r =
 	match rtype ctx r with
-	| HUI8 | HUI16 | HI32 ->
+	| HUI8 | HUI16 | HI32 | HI64 ->
 		op ctx (OInt (r,alloc_i32 ctx 0l))
 	| HF32 | HF64 ->
 		op ctx (OFloat (r,alloc_float ctx 0.))
@@ -854,12 +873,8 @@ let read_mem ctx rdst bytes index t =
 		op ctx (OGetUI8 (rdst,bytes,index))
 	| HUI16 ->
 		op ctx (OGetUI16 (rdst,bytes,index))
-	| HI32 ->
-		op ctx (OGetI32 (rdst,bytes,index))
-	| HF32 ->
-		op ctx (OGetF32 (rdst,bytes,index))
-	| HF64 ->
-		op ctx (OGetF64 (rdst,bytes,index))
+	| HI32 | HI64 | HF32 | HF64 ->
+		op ctx (OGetMem (rdst,bytes,index))
 	| _ ->
 		assert false
 
@@ -869,12 +884,8 @@ let write_mem ctx bytes index t r =
 		op ctx (OSetUI8 (bytes,index,r))
 	| HUI16 ->
 		op ctx (OSetUI16 (bytes,index,r))
-	| HI32 ->
-		op ctx (OSetI32 (bytes,index,r))
-	| HF32 ->
-		op ctx (OSetF32 (bytes,index,r))
-	| HF64 ->
-		op ctx (OSetF64 (bytes,index,r))
+	| HI32 | HI64 | HF32 | HF64 ->
+		op ctx (OSetMem (bytes,index,r))
 	| _ ->
 		assert false
 
@@ -884,16 +895,16 @@ let common_type ctx e1 e2 for_eq p =
 	let rec loop t1 t2 =
 		if t1 == t2 then t1 else
 		match t1, t2 with
-		| HUI8, (HUI16 | HI32 | HF32 | HF64) -> t2
-		| HUI16, (HI32 | HF32 | HF64) -> t2
-		| HI32, HF32 -> t2 (* possible loss of precision *)
-		| (HI32 | HF32), HF64 -> t2
-		| (HUI8|HUI16|HI32|HF32|HF64), (HUI8|HUI16|HI32|HF32|HF64) -> t1
-		| (HUI8|HUI16|HI32|HF32|HF64), (HNull t2) -> if for_eq then HNull (loop t1 t2) else loop t1 t2
-		| (HNull t1), (HUI8|HUI16|HI32|HF32|HF64) -> if for_eq then HNull (loop t1 t2) else loop t1 t2
+		| HUI8, (HUI16 | HI32 | HI64 | HF32 | HF64) -> t2
+		| HUI16, (HI32 | HI64 | HF32 | HF64) -> t2
+		| (HI32 | HI64), HF32 -> t2 (* possible loss of precision *)
+		| (HI32 | HI64 | HF32), HF64 -> t2
+		| (HUI8|HUI16|HI32|HI64|HF32|HF64), (HUI8|HUI16|HI32|HI64|HF32|HF64) -> t1
+		| (HUI8|HUI16|HI32|HI64|HF32|HF64), (HNull t2) -> if for_eq then HNull (loop t1 t2) else loop t1 t2
+		| (HNull t1), (HUI8|HUI16|HI32|HI64|HF32|HF64) -> if for_eq then HNull (loop t1 t2) else loop t1 t2
 		| (HNull t1), (HNull t2) -> if for_eq then HNull (loop t1 t2) else loop t1 t2
-		| HDyn, (HUI8|HUI16|HI32|HF32|HF64) -> HF64
-		| (HUI8|HUI16|HI32|HF32|HF64), HDyn -> HF64
+		| HDyn, (HUI8|HUI16|HI32|HI64|HF32|HF64) -> HF64
+		| (HUI8|HUI16|HI32|HI64|HF32|HF64), HDyn -> HF64
 		| HDyn, _ -> HDyn
 		| _, HDyn -> HDyn
 		| _ when for_eq && safe_cast t1 t2 -> t2
@@ -985,11 +996,11 @@ and cast_to ?(force=false) ctx (r:reg) (t:ttype) p =
 		let tmp = alloc_tmp ctx HDyn in
 		op ctx (OMov (tmp,r));
 		cast_to ctx tmp t p
-	| (HUI8 | HUI16 | HI32 | HF32 | HF64), (HF32 | HF64) ->
+	| (HUI8 | HUI16 | HI32 | HI64 | HF32 | HF64), (HF32 | HF64) ->
 		let tmp = alloc_tmp ctx t in
 		op ctx (OToSFloat (tmp, r));
 		tmp
-	| (HUI8 | HUI16 | HI32 | HF32 | HF64), (HUI8 | HUI16 | HI32) ->
+	| (HUI8 | HUI16 | HI32 | HI64 | HF32 | HF64), (HUI8 | HUI16 | HI32 | HI64) ->
 		let tmp = alloc_tmp ctx t in
 		op ctx (OToInt (tmp, r));
 		tmp
@@ -1081,25 +1092,25 @@ and cast_to ?(force=false) ctx (r:reg) (t:ttype) p =
 		j();
 		op ctx (ONull out);
 		out
-	| (HUI8 | HUI16 | HI32 | HF32 | HF64), HNull ((HF32 | HF64) as t) ->
+	| (HUI8 | HUI16 | HI32 | HI64 | HF32 | HF64), HNull ((HF32 | HF64) as t) ->
 		let tmp = alloc_tmp ctx t in
 		op ctx (OToSFloat (tmp, r));
 		let r = alloc_tmp ctx (HNull t) in
 		op ctx (OToDyn (r,tmp));
 		r
-	| (HUI8 | HUI16 | HI32 | HF32 | HF64), HNull ((HUI8 | HUI16 | HI32) as t) ->
+	| (HUI8 | HUI16 | HI32 | HI64 | HF32 | HF64), HNull ((HUI8 | HUI16 | HI32) as t) ->
 		let tmp = alloc_tmp ctx t in
 		op ctx (OToInt (tmp, r));
 		let r = alloc_tmp ctx (HNull t) in
 		op ctx (OToDyn (r,tmp));
 		r
-	| HNull ((HUI8 | HUI16 | HI32) as it), (HF32 | HF64) ->
+	| HNull ((HUI8 | HUI16 | HI32 | HI64) as it), (HF32 | HF64) ->
 		let i = alloc_tmp ctx it in
 		op ctx (OSafeCast (i,r));
 		let tmp = alloc_tmp ctx t in
 		op ctx (OToSFloat (tmp, i));
 		tmp
-	| HNull ((HF32 | HF64) as it), (HUI8 | HUI16 | HI32) ->
+	| HNull ((HF32 | HF64) as it), (HUI8 | HUI16 | HI32 | HI64) ->
 		let i = alloc_tmp ctx it in
 		op ctx (OSafeCast (i,r));
 		let tmp = alloc_tmp ctx t in
@@ -1153,6 +1164,14 @@ and unsafe_cast_to ctx (r:reg) (t:ttype) p =
 		if is_dynamic (rtype ctx r) && is_dynamic t then
 			let r2 = alloc_tmp ctx t in
 			op ctx (OUnsafeCast (r2,r));
+			if ctx.com.debug then begin
+				hold ctx r2;
+				let r3 = cast_to ~force:true ctx r t p in
+				let j = jump ctx (fun n -> OJEq (r2,r3,n)) in
+				op ctx (OAssert 0);
+				j();
+				free ctx r2;
+			end;
 			r2
 		else
 			cast_to ~force:true ctx r t p
@@ -1326,8 +1345,8 @@ and jump_expr ctx e jcond =
 		free ctx r1;
 		let unsigned = unsigned_op e1 e2 in
 		jump ctx (fun i ->
-			let lt a b = if unsigned then OJULt (a,b,i) else OJSLt (a,b,i) in
-			let gte a b = if unsigned then OJUGte (a,b,i) else OJSGte (a,b,i) in
+			let lt a b = if unsigned then OJULt (a,b,i) else if not jcond && is_float t then OJNotGte (a,b,i) else OJSLt (a,b,i) in
+			let gte a b = if unsigned then OJUGte (a,b,i) else if not jcond && is_float t then OJNotLt (a,b,i) else OJSGte (a,b,i) in
 			match jop with
 			| OpEq -> if jcond then OJEq (r1,r2,i) else OJNotEq (r1,r2,i)
 			| OpNotEq -> if jcond then OJNotEq (r1,r2,i) else OJEq (r1,r2,i)
@@ -1385,6 +1404,13 @@ and make_string ctx s p =
 	op ctx (OSetField (s,1,reg_int ctx len));
 	s
 
+and get_enum_index ctx v =
+	let r = alloc_tmp ctx HI32 in
+	let re = eval_expr ctx v in
+	op ctx (ONullCheck re);
+	op ctx (OEnumIndex (r,re));
+	r
+
 and eval_var ctx v =
 	match captured_index ctx v with
 	| None -> alloc_var ctx v false
@@ -1489,9 +1515,9 @@ and eval_expr ctx e =
 				r
 			)
 		| _ -> assert false);
-	| TCall ({ eexpr = TLocal v }, el) when v.v_name.[0] = '$' ->
+	| TCall ({ eexpr = TIdent s }, el) when s.[0] = '$' ->
 		let invalid() = abort "Invalid native call" e.epos in
-		(match v.v_name, el with
+		(match s, el with
 		| "$new", [{ eexpr = TTypeExpr (TClassDecl _) }] ->
 			(match follow e.etype with
 			| TInst (c,pl) ->
@@ -1540,7 +1566,17 @@ and eval_expr ctx e =
 			let r = eval_to ctx v HI32 in
 			free ctx pos;
 			free ctx b;
-			op ctx (OSetI32 (b, pos, r));
+			op ctx (OSetMem (b, pos, r));
+			r
+		| "$bseti64", [b;pos;v] ->
+			let b = eval_to ctx b HBytes in
+			hold ctx b;
+			let pos = eval_to ctx pos HI32 in
+			hold ctx pos;
+			let r = eval_to ctx v HI64 in
+			free ctx pos;
+			free ctx b;
+			op ctx (OSetMem (b, pos, r));
 			r
 		| "$bsetf32", [b;pos;v] ->
 			let b = eval_to ctx b HBytes in
@@ -1550,7 +1586,7 @@ and eval_expr ctx e =
 			let r = eval_to ctx v HF32 in
 			free ctx pos;
 			free ctx b;
-			op ctx (OSetF32 (b, pos, r));
+			op ctx (OSetMem (b, pos, r));
 			r
 		| "$bsetf64", [b;pos;v] ->
 			let b = eval_to ctx b HBytes in
@@ -1560,7 +1596,7 @@ and eval_expr ctx e =
 			let r = eval_to ctx v HF64 in
 			free ctx pos;
 			free ctx b;
-			op ctx (OSetF64 (b, pos, r));
+			op ctx (OSetMem (b, pos, r));
 			r
 		| "$bytes_sizebits", [eb] ->
 			(match follow eb.etype with
@@ -1568,9 +1604,8 @@ and eval_expr ctx e =
 				reg_int ctx (match to_type ctx t with
 				| HUI8 -> 0
 				| HUI16 -> 1
-				| HI32 -> 2
-				| HF32 -> 2
-				| HF64 -> 3
+				| HI32 | HF32 -> 2
+				| HI64 | HF64 -> 3
 				| t -> abort ("Unsupported basic type " ^ tstr t) e.epos)
 			| _ ->
 				abort "Invalid BytesAccess" eb.epos);
@@ -1580,7 +1615,7 @@ and eval_expr ctx e =
 				let t = to_type ctx t in
 				let r = alloc_tmp ctx t in
 				(match t with
-				| HUI8 | HUI16 | HI32 ->
+				| HUI8 | HUI16 | HI32 | HI64 ->
 					op ctx (OInt (r,alloc_i32 ctx 0l))
 				| HF32 | HF64 ->
 					op ctx (OFloat (r, alloc_float ctx 0.))
@@ -1608,15 +1643,19 @@ and eval_expr ctx e =
 					r
 				| HI32 ->
 					let r = alloc_tmp ctx HI32 in
-					op ctx (OGetI32 (r, b, shl ctx pos 2));
+					op ctx (OGetMem (r, b, shl ctx pos 2));
+					r
+				| HI64 ->
+					let r = alloc_tmp ctx HI64 in
+					op ctx (OGetMem (r, b, shl ctx pos 3));
 					r
 				| HF32 ->
 					let r = alloc_tmp ctx HF32 in
-					op ctx (OGetF32 (r, b, shl ctx pos 2));
+					op ctx (OGetMem (r, b, shl ctx pos 2));
 					r
 				| HF64 ->
 					let r = alloc_tmp ctx HF64 in
-					op ctx (OGetF64 (r, b, shl ctx pos 3));
+					op ctx (OGetMem (r, b, shl ctx pos 3));
 					r
 				| _ ->
 					abort ("Unsupported basic type " ^ tstr t) e.epos)
@@ -1644,19 +1683,25 @@ and eval_expr ctx e =
 				| HI32 ->
 					let v = eval_to ctx value HI32 in
 					hold ctx v;
-					op ctx (OSetI32 (b, shl ctx pos 2, v));
+					op ctx (OSetMem (b, shl ctx pos 2, v));
+					free ctx v;
+					v
+				| HI64 ->
+					let v = eval_to ctx value HI64 in
+					hold ctx v;
+					op ctx (OSetMem (b, shl ctx pos 3, v));
 					free ctx v;
 					v
 				| HF32 ->
 					let v = eval_to ctx value HF32 in
 					hold ctx v;
-					op ctx (OSetF32 (b, shl ctx pos 2, v));
+					op ctx (OSetMem (b, shl ctx pos 2, v));
 					free ctx v;
 					v
 				| HF64 ->
 					let v = eval_to ctx value HF64 in
 					hold ctx v;
-					op ctx (OSetF64 (b, shl ctx pos 3, v));
+					op ctx (OSetMem (b, shl ctx pos 3, v));
 					free ctx v;
 					v
 				| _ ->
@@ -1689,7 +1734,15 @@ and eval_expr ctx e =
 			let pos = eval_to ctx pos HI32 in
 			free ctx b;
 			let r = alloc_tmp ctx HI32 in
-			op ctx (OGetI32 (r, b, pos));
+			op ctx (OGetMem (r, b, pos));
+			r
+		| "$bgeti64", [b;pos] ->
+			let b = eval_to ctx b HBytes in
+			hold ctx b;
+			let pos = eval_to ctx pos HI32 in
+			free ctx b;
+			let r = alloc_tmp ctx HI64 in
+			op ctx (OGetMem (r, b, pos));
 			r
 		| "$bgetf32", [b;pos] ->
 			let b = eval_to ctx b HBytes in
@@ -1697,7 +1750,7 @@ and eval_expr ctx e =
 			let pos = eval_to ctx pos HI32 in
 			free ctx b;
 			let r = alloc_tmp ctx HF32 in
-			op ctx (OGetF32 (r, b, pos));
+			op ctx (OGetMem (r, b, pos));
 			r
 		| "$bgetf64", [b;pos] ->
 			let b = eval_to ctx b HBytes in
@@ -1705,7 +1758,7 @@ and eval_expr ctx e =
 			let pos = eval_to ctx pos HI32 in
 			free ctx b;
 			let r = alloc_tmp ctx HF64 in
-			op ctx (OGetF64 (r, b, pos));
+			op ctx (OGetMem (r, b, pos));
 			r
 		| "$asize", [e] ->
 			let r = alloc_tmp ctx HI32 in
@@ -1734,8 +1787,12 @@ and eval_expr ctx e =
 		| "$aset", [a; pos; value] ->
 			let et = (match follow a.etype with TAbstract ({ a_path = ["hl"],"NativeArray" },[t]) -> to_type ctx t | _ -> invalid()) in
 			let arr = eval_to ctx a HArray in
+			hold ctx arr;
 			let pos = eval_to ctx pos HI32 in
+			hold ctx pos;
 			let r = eval_to ctx value et in
+			free ctx pos;
+			free ctx arr;
 			op ctx (OSetArray (arr, pos, r));
 			r
 		| "$abytes", [a] ->
@@ -1780,6 +1837,17 @@ and eval_expr ctx e =
 			let out = alloc_tmp ctx (match rtype ctx r with HRef t -> t | _ -> invalid()) in
 			op ctx (OUnref (out,r));
 			out
+		| "$refdata", [e1] ->
+			let v = eval_expr ctx e1 in
+			let r = alloc_tmp ctx (match to_type ctx e.etype with HRef _ as t -> t | _ -> invalid()) in
+			op ctx (ORefData (r,v));
+			r
+		| "$refoffset", [r;e1] ->
+			let r = eval_expr ctx r in
+			let e = eval_to ctx e1 HI32 in
+			let r2 = alloc_tmp ctx (match rtype ctx r with HRef _ as t -> t | _ -> invalid()) in
+			op ctx (ORefOffset (r2,r,e));
+			r2
 		| "$ttype", [v] ->
 			let r = alloc_tmp ctx HType in
 			op ctx (OType (r,to_type ctx v.etype));
@@ -1838,11 +1906,7 @@ and eval_expr ctx e =
 				r
 			| _ -> abort "Constant string required" v.epos)
 		| "$enumIndex", [v] ->
-			let r = alloc_tmp ctx HI32 in
-			let re = eval_expr ctx v in
-			op ctx (ONullCheck re);
-			op ctx (OEnumIndex (r,re));
-			r
+			get_enum_index ctx v
 		| "$__mk_pos__", [{ eexpr = TConst (TString file) };min;max] ->
 			(* macros only - generated by reification *)
 			let rt = HAbstract ("macro_pos",alloc_string ctx "macro_pos") in
@@ -1857,7 +1921,13 @@ and eval_expr ctx e =
 			free ctx min;
 			r
 		| _ ->
-			abort ("Unknown native call " ^ v.v_name) e.epos)
+			abort ("Unknown native call " ^ s) e.epos)
+	| TEnumIndex v ->
+		get_enum_index ctx v
+	| TCall ({ eexpr = TField (_,FStatic ({ cl_path = [],"Type" },{ cf_name = "enumIndex" })) },[{ eexpr = TCast(v,_) }]) when (match follow v.etype with TEnum _ -> true | _ -> false) ->
+		get_enum_index ctx v
+	| TCall ({ eexpr = TField (_,FStatic ({ cl_path = [],"Type" },{ cf_name = "enumIndex" })) },[v]) when (match follow v.etype with TEnum _ -> true | _ -> false) ->
+		get_enum_index ctx v
 	| TCall (ec,args) ->
 		let tfun = real_type ctx ec in
 		let el() = eval_args ctx args tfun e.epos in
@@ -1910,7 +1980,28 @@ and eval_expr ctx e =
 			op ctx (OCallClosure (ret, r, el)); (* if it's a value, it's a closure *)
 			def_ret := Some (cast_to ~force:true ctx ret (to_type ctx e.etype) e.epos);
 		);
-		(match !def_ret with None -> cast_to ~force:true ctx ret (to_type ctx e.etype) e.epos | Some r -> r)
+		(match !def_ret with
+		| None ->
+			let rt = to_type ctx e.etype in
+			let is_valid_method t =
+				match follow t with
+				| TFun (_,rt) ->
+					(match follow rt with
+					| TInst({ cl_kind = KTypeParameter tl },_) ->
+						(* don't allow if we have a constraint virtual, see hxbit.Serializer.getRef *)
+						not (List.exists (fun t -> match to_type ctx t with HVirtual _ -> true | _ -> false) tl)
+					| _ -> false)
+				| _ ->
+					false
+			in
+			(match ec.eexpr with
+			| TField (_, FInstance(_,_,{ cf_kind = Method (MethNormal|MethInline); cf_type = t })) when is_valid_method t ->
+				(* let's trust the compiler when it comes to casting the return value from a type parameter *)
+				unsafe_cast_to ctx ret rt e.epos
+			| _ ->
+				cast_to ~force:true ctx ret rt e.epos)
+		| Some r ->
+			r)
 	| TField (ec,FInstance({ cl_path = [],"Array" },[t],{ cf_name = "length" })) when to_type ctx t = HDyn ->
 		let r = alloc_tmp ctx HI32 in
 		op ctx (OCall1 (r,alloc_fun_path ctx (["hl";"types"],"ArrayDyn") "get_length", eval_null_check ctx ec));
@@ -2044,7 +2135,7 @@ and eval_expr ctx e =
 				| OpNotEq -> boolop r (fun d -> OJNotEq (a,b,d))
 				| OpAdd ->
 					(match rtype ctx r with
-					| HUI8 | HUI16 | HI32 | HF32 | HF64 ->
+					| HUI8 | HUI16 | HI32 | HI64 | HF32 | HF64 ->
 						op ctx (OAdd (r,a,b))
 					| HObj { pname = "String" } ->
 						op ctx (OCall2 (r,alloc_fun_path ctx ([],"String") "__add__",a,b))
@@ -2054,18 +2145,20 @@ and eval_expr ctx e =
 						abort ("Cannot add " ^ tstr t) e.epos)
 				| OpSub | OpMult | OpMod | OpDiv ->
 					(match rtype ctx r with
-					| HUI8 | HUI16 | HI32 | HF32 | HF64 ->
+					| HUI8 | HUI16 | HI32 | HI64 | HF32 | HF64 ->
 						(match bop with
 						| OpSub -> op ctx (OSub (r,a,b))
 						| OpMult -> op ctx (OMul (r,a,b))
 						| OpMod -> op ctx (if unsigned e1.etype then OUMod (r,a,b) else OSMod (r,a,b))
 						| OpDiv -> op ctx (OSDiv (r,a,b)) (* don't use UDiv since both operands are float already *)
 						| _ -> assert false)
+					| HDyn ->
+						op ctx (OCall3 (r, alloc_std ctx "dyn_op" [HI32;HDyn;HDyn] HDyn, reg_int ctx (match bop with OpSub -> 1 | OpMult -> 2 | OpMod -> 3 | OpDiv -> 4 | _ -> assert false), a, b))
 					| _ ->
 						assert false)
 				| OpShl | OpShr | OpUShr | OpAnd | OpOr | OpXor ->
 					(match rtype ctx r with
-					| HUI8 | HUI16 | HI32 ->
+					| HUI8 | HUI16 | HI32 | HI64 ->
 						(match bop with
 						| OpShl -> op ctx (OShl (r,a,b))
 						| OpShr -> op ctx (if unsigned e1.etype then OUShr (r,a,b) else OSShr (r,a,b))
@@ -2074,6 +2167,8 @@ and eval_expr ctx e =
 						| OpOr -> op ctx (OOr (r,a,b))
 						| OpXor -> op ctx (OXor (r,a,b))
 						| _ -> ())
+					| HDyn ->
+						op ctx (OCall3 (r, alloc_std ctx "dyn_op" [HI32;HDyn;HDyn] HDyn, reg_int ctx (match bop with OpShl -> 5 | OpShr -> 6 | OpUShr -> 7 | OpAnd -> 8 | OpOr -> 9 | OpXor -> 10 | _ -> assert false), a, b))
 					| _ ->
 						assert false)
 				| OpAssignOp bop ->
@@ -2221,7 +2316,7 @@ and eval_expr ctx e =
 					free ctx r;
 					binop r r b;
 					r))
-		| OpInterval | OpArrow ->
+		| OpInterval | OpArrow | OpIn ->
 			assert false)
 	| TUnop (Not,_,v) ->
 		let tmp = alloc_tmp ctx HBool in
@@ -2253,7 +2348,7 @@ and eval_expr ctx e =
 	| TUnop (Increment|Decrement as uop,fix,v) ->
 		let rec unop r =
 			match rtype ctx r with
-			| HUI8 | HUI16 | HI32 ->
+			| HUI8 | HUI16 | HI32 | HI64 ->
 				if uop = Increment then op ctx (OIncr r) else op ctx (ODecr r)
 			| HF32 | HF64 as t ->
 				hold ctx r;
@@ -2261,7 +2356,7 @@ and eval_expr ctx e =
 				free ctx r;
 				op ctx (OFloat (tmp,alloc_float ctx 1.));
 				if uop = Increment then op ctx (OAdd (r,r,tmp)) else op ctx (OSub (r,r,tmp))
-			| HNull (HUI8 | HUI16 | HI32 | HF32 | HF64 as t) ->
+			| HNull (HUI8 | HUI16 | HI32 | HI64 | HF32 | HF64 as t) ->
 				hold ctx r;
 				let tmp = alloc_tmp ctx t in
 				free ctx r;
@@ -2358,6 +2453,9 @@ and eval_expr ctx e =
 		ctx.m.mcontinues <- oldc;
 		ctx.m.mloop_trys <- oldtrys;
 		alloc_tmp ctx HVoid
+	| TCast ({ eexpr = TCast (v,None) },None) when not (is_number (to_type ctx e.etype)) ->
+        (* coalesce double casts into a single runtime check - temp fix for Map accesses *)
+        eval_expr ctx { e with eexpr = TCast(v,None) }
 	| TCast (v,None) ->
 		let t = to_type ctx e.etype in
 		let rv = eval_expr ctx v in
@@ -2396,13 +2494,13 @@ and eval_expr ctx e =
 		in
 		(match et with
 		| HI32 ->
-			array_bytes 2 HI32 "I32" (fun b i r -> OSetI32 (b,i,r))
+			array_bytes 2 HI32 "I32" (fun b i r -> OSetMem (b,i,r))
 		| HUI16 ->
 			array_bytes 1 HI32 "UI16" (fun b i r -> OSetUI16 (b,i,r))
 		| HF32 ->
-			array_bytes 2 HF32 "F32" (fun b i r -> OSetF32 (b,i,r))
+			array_bytes 2 HF32 "F32" (fun b i r -> OSetMem (b,i,r))
 		| HF64 ->
-			array_bytes 3 HF64 "F64" (fun b i r -> OSetF64 (b,i,r))
+			array_bytes 3 HF64 "F64" (fun b i r -> OSetMem (b,i,r))
 		| _ ->
 			let at = if is_dynamic et then et else HDyn in
 			let a = alloc_tmp ctx HArray in
@@ -2484,12 +2582,47 @@ and eval_expr ctx e =
 			let jends = ref [] in
 			let rvalue = eval_expr ctx en in
 			let loop (cases,e) =
+				hold ctx rvalue;
 				let ok = List.map (fun c ->
-					hold ctx rvalue;
-					let r = eval_to ctx c (common_type ctx en c true c.epos) in
-					free ctx rvalue;
-					jump ctx (fun n -> OJEq (r,rvalue,n))
+					let ct = common_type ctx en c true c.epos in
+					match c.eexpr, ct with
+					| TConst (TString str), HObj { pname = "String" } ->
+						let jnull = jump ctx (fun n -> OJNull (rvalue,n)) in
+
+						(* compare len *)
+						let rlen = alloc_tmp ctx HI32 in
+						op ctx (OField (rlen, rvalue, 1));
+						hold ctx rlen;
+						let str, len = to_utf8 str c.epos in
+						let rlen2 = reg_int ctx len in
+						let jdiff = jump ctx (fun n -> OJNotEq (rlen, rlen2, n)) in
+						free ctx rlen;
+
+						(* compare data *)
+						let rbytes = alloc_tmp ctx HBytes in
+						op ctx (OField (rbytes, rvalue, 0));
+						hold ctx rbytes;
+						let rbytes2 = alloc_tmp ctx HBytes in
+						op ctx (OString (rbytes2,alloc_string ctx str));
+						let result = alloc_tmp ctx HI32 in
+						op ctx (OCall3 (result, alloc_std ctx "string_compare" [HBytes;HBytes;HI32] HI32,rbytes,rbytes2,rlen));
+						free ctx rbytes;
+
+						hold ctx result;
+						let zero = reg_int ctx 0 in
+						let jok = jump ctx (fun n -> OJEq (result, zero, n)) in
+						free ctx result;
+
+						jnull();
+						jdiff();
+						jok
+
+
+					| _ ->
+						let r = eval_to ctx c ct in
+						jump ctx (fun n -> OJEq (r,rvalue,n))
 				) cases in
+				free ctx rvalue;
 				(fun() ->
 					List.iter (fun f -> f()) ok;
 					let re = eval_to ctx e rt in
@@ -2584,6 +2717,8 @@ and eval_expr ctx e =
 		else
 			op ctx (OSafeCast (r,re));
 		r
+	| TIdent s ->
+		assert false
 
 and gen_assign_op ctx acc e1 f =
 	let f r =
@@ -2837,7 +2972,7 @@ and make_fun ?gen_content ctx name fidx f cthis cparent =
 			let j = jump ctx (fun n -> OJNotNull (r,n)) in
 			let t = alloc_tmp ctx vt in
 			(match vt with
-			| HUI8 | HUI16 | HI32 ->
+			| HUI8 | HUI16 | HI32 | HI64 ->
 				(match c with
 				| TInt i -> op ctx (OInt (t,alloc_i32 ctx i))
 				| TFloat s -> op ctx (OInt (t,alloc_i32 ctx  (Int32.of_float (float_of_string s))))
@@ -2866,11 +3001,11 @@ and make_fun ?gen_content ctx name fidx f cthis cparent =
 			let j = jump ctx (fun n -> OJNotNull (r,n)) in
 			(match c with
 			| TNull | TThis | TSuper -> assert false
-			| TInt i when (match to_type ctx (follow v.v_type) with HUI8 | HUI16 | HI32 | HDyn -> true | _ -> false) ->
+			| TInt i when (match to_type ctx (follow v.v_type) with HUI8 | HUI16 | HI32 | HI64 | HDyn -> true | _ -> false) ->
 				let tmp = alloc_tmp ctx HI32 in
 				op ctx (OInt (tmp, alloc_i32 ctx i));
 				op ctx (OToDyn (r, tmp));
-			| TFloat s when (match to_type ctx (follow v.v_type) with HUI8 | HUI16 | HI32 -> true | _ -> false) ->
+			| TFloat s when (match to_type ctx (follow v.v_type) with HUI8 | HUI16 | HI32 | HI64 -> true | _ -> false) ->
 				let tmp = alloc_tmp ctx HI32 in
 				op ctx (OInt (tmp, alloc_i32 ctx (Int32.of_float (float_of_string s))));
 				op ctx (OToDyn (r, tmp));
@@ -2917,7 +3052,7 @@ and make_fun ?gen_content ctx name fidx f cthis cparent =
 	else if has_final_jump f.tf_expr then begin
 		let r = alloc_tmp ctx tret in
 		(match tret with
-		| HI32 | HUI8 | HUI16 -> op ctx (OInt (r,alloc_i32 ctx 0l))
+		| HI32 | HUI8 | HUI16 | HI64 -> op ctx (OInt (r,alloc_i32 ctx 0l))
 		| HF32 | HF64 -> op ctx (OFloat (r,alloc_float ctx 0.))
 		| HBool -> op ctx (OBool (r,false))
 		| _ -> op ctx (ONull r));
@@ -3282,7 +3417,7 @@ let write_code ch code debug =
 		let oid = Obj.tag o in
 
 		match op with
-		| OLabel _ | ONop _ ->
+		| OLabel _ | ONop _ | OAssert _ ->
 			byte oid
 		| OCall2 (r,g,a,b) ->
 			byte oid;
@@ -3354,7 +3489,7 @@ let write_code ch code debug =
 				assert false
 	in
 
-	IO.nwrite ch "HLB";
+	IO.nwrite_string ch "HLB";
 	byte code.version;
 
 	let flags = ref 0 in
@@ -3393,20 +3528,21 @@ let write_code ch code debug =
 		| HUI8 -> byte 1
 		| HUI16 -> byte 2
 		| HI32 -> byte 3
-		| HF32 -> byte 4
-		| HF64 -> byte 5
-		| HBool -> byte 6
-		| HBytes -> byte 7
-		| HDyn -> byte 8
+		| HI64 -> byte 4
+		| HF32 -> byte 5
+		| HF64 -> byte 6
+		| HBool -> byte 7
+		| HBytes -> byte 8
+		| HDyn -> byte 9
 		| HFun (args,ret) ->
 			let n = List.length args in
 			if n > 0xFF then assert false;
-			byte 9;
+			byte 10;
 			byte n;
 			List.iter write_type args;
 			write_type ret
 		| HObj p ->
-			byte 10;
+			byte 11;
 			write_index p.pid;
 			(match p.psuper with
 			| None -> write_index (-1)
@@ -3421,23 +3557,23 @@ let write_code ch code debug =
 			Array.iter (fun f -> write_index f.fid; write_index f.fmethod; write_index (match f.fvirtual with None -> -1 | Some i -> i)) p.pproto;
 			List.iter (fun (fid,fidx) -> write_index fid; write_index fidx) p.pbindings;
 		| HArray ->
-			byte 11
-		| HType ->
 			byte 12
+		| HType ->
+			byte 13
 		| HRef t ->
-			byte 13;
+			byte 14;
 			write_type t
 		| HVirtual v ->
-			byte 14;
+			byte 15;
 			write_index (Array.length v.vfields);
 			Array.iter (fun (_,sid,t) -> write_index sid; write_type t) v.vfields
 		| HDynObj ->
-			byte 15
+			byte 16
 		| HAbstract (_,i) ->
-			byte 16;
+			byte 17;
 			write_index i
 		| HEnum e ->
-			byte 17;
+			byte 18;
 			write_index e.eid;
 			(match e.eglobal with
 			| None -> write_index 0
@@ -3450,7 +3586,7 @@ let write_code ch code debug =
 				Array.iter write_type tl;
 			) e.efields
 		| HNull t ->
-			byte 18;
+			byte 19;
 			write_type t
 	) all_types;
 
@@ -3621,7 +3757,7 @@ let add_types ctx types =
 let build_code ctx types main =
 	let ep = generate_static_init ctx types main in
 	{
-		version = 1;
+		version = 2;
 		entrypoint = ep;
 		strings = DynArray.to_array ctx.cstrings.arr;
 		ints = DynArray.to_array ctx.cints.arr;
@@ -3649,7 +3785,7 @@ let generate com =
 		Hlcode.dump (fun s -> output_string ch (s ^ "\n")) code;
 		close_out ch;
 	end;
-	if Common.raw_defined com "hl-dump-spec" then begin
+	(*if Common.raw_defined com "hl-dump-spec" then begin
 		let ch = open_out_bin "dump/hlspec.txt" in
 		let write s = output_string ch (s ^ "\n") in
 		Array.iter (fun f ->
@@ -3659,7 +3795,7 @@ let generate com =
 			write "";
 		) code.functions;
 		close_out ch;
-	end;
+	end;*)
 	if Common.raw_defined com "hl-check" then begin
 		check ctx;
 		Hlinterp.check code false;
@@ -3713,7 +3849,9 @@ let generate com =
 	end;
 	if Common.defined com Define.Interp then
 		try
+			let t = Common.timer ["generate";"hl";"interp"] in
 			let ctx = Hlinterp.create true in
 			Hlinterp.add_code ctx code;
+			t();
 		with
 			Failure msg -> abort msg null_pos

Tiedoston diff-näkymää rajattu, sillä se on liian suuri
+ 390 - 194
src/generators/genjava.ml


+ 123 - 95
src/generators/genjs.ml

@@ -52,7 +52,6 @@ type ctx = {
 	mutable tabs : string;
 	mutable in_value : tvar option;
 	mutable in_loop : bool;
-	mutable handle_break : bool;
 	mutable id_counter : int;
 	mutable type_accessor : module_type -> string;
 	mutable separator : bool;
@@ -75,27 +74,30 @@ let get_exposed ctx path meta =
 
 let dot_path = s_type_path
 
-let flat_path (p,s) =
-	(* Replace _ with _$ in paths to prevent name collisions. *)
-	let escape str = String.concat "_$" (ExtString.String.nsplit str "_") in
+let s_path ctx = if ctx.js_flatten then Path.flat_path else dot_path
 
-	match p with
-	| [] -> escape s
-	| _ -> String.concat "_" (List.map escape p) ^ "_" ^ (escape s)
+let kwds = Hashtbl.create 0
 
-let s_path ctx = if ctx.js_flatten then flat_path else dot_path
+let setup_kwds lst =
+	List.iter (fun s -> Hashtbl.add kwds s ()) lst
 
-let kwds =
-	let h = Hashtbl.create 0 in
-	List.iter (fun s -> Hashtbl.add h s ()) [
-		"abstract"; "as"; "boolean"; "break"; "byte"; "case"; "catch"; "char"; "class"; "continue"; "const";
-		"debugger"; "default"; "delete"; "do"; "double"; "else"; "enum"; "export"; "extends"; "false"; "final";
-		"finally"; "float"; "for"; "function"; "goto"; "if"; "implements"; "import"; "in"; "instanceof"; "int";
-		"interface"; "is"; "let"; "long"; "namespace"; "native"; "new"; "null"; "package"; "private"; "protected";
-		"public"; "return"; "short"; "static"; "super"; "switch"; "synchronized"; "this"; "throw"; "throws";
-		"transient"; "true"; "try"; "typeof"; "use"; "var"; "void"; "volatile"; "while"; "with"; "yield"
-	];
-	h
+let es3kwds = [
+	"abstract"; "boolean"; "break"; "byte"; "case"; "catch"; "char"; "class"; "const"; "continue";
+	"debugger"; "default"; "delete"; "do"; "double"; "else"; "enum"; "export"; "extends"; "false"; "final";
+	"finally"; "float"; "for"; "function"; "goto"; "if"; "implements"; "import"; "in"; "instanceof"; "int";
+	"interface"; "long"; "native"; "new"; "null"; "package"; "private"; "protected";
+	"public"; "return"; "short"; "static"; "super"; "switch"; "synchronized"; "this"; "throw"; "throws";
+	"transient"; "true"; "try"; "typeof"; "var"; "void"; "volatile"; "while"; "with"
+]
+
+let es5kwds = [
+	"arguments"; "break"; "case"; "catch"; "class"; "const"; "continue";
+	"debugger"; "default"; "delete"; "do"; "else"; "enum"; "eval"; "export"; "extends"; "false";
+	"finally"; "for"; "function"; "if"; "implements"; "import"; "in"; "instanceof";
+	"interface"; "let"; "new"; "null"; "package"; "private"; "protected";
+	"public"; "return"; "static"; "super"; "switch"; "this"; "throw";
+	"true"; "try"; "typeof"; "var"; "void"; "while"; "with"; "yield"
+]
 
 (* Identifiers Haxe reserves to make the JS output cleaner. These can still be used in untyped code (TLocal),
    but are escaped upon declaration. *)
@@ -295,43 +297,13 @@ let open_block ctx =
 	ctx.tabs <- "\t" ^ ctx.tabs;
 	(fun() -> ctx.tabs <- oldt)
 
-let rec has_return e =
-	match e.eexpr with
-	| TBlock [] -> false
-	| TBlock el -> has_return (List.hd (List.rev el))
-	| TReturn _ -> true
-	| _ -> false
-
-let rec iter_switch_break in_switch e =
+let rec needs_switch_break e =
 	match e.eexpr with
-	| TFunction _ | TWhile _ | TFor _ -> ()
-	| TSwitch _ when not in_switch -> iter_switch_break true e
-	| TBreak when in_switch -> raise Exit
-	| _ -> iter (iter_switch_break in_switch) e
-
-let handle_break ctx e =
-	let old = ctx.in_loop, ctx.handle_break in
-	ctx.in_loop <- true;
-	try
-		iter_switch_break false e;
-		ctx.handle_break <- false;
-		(fun() ->
-			ctx.in_loop <- fst old;
-			ctx.handle_break <- snd old;
-		)
-	with
-		Exit ->
-			spr ctx "try {";
-			let b = open_block ctx in
-			newline ctx;
-			ctx.handle_break <- true;
-			(fun() ->
-				b();
-				ctx.in_loop <- fst old;
-				ctx.handle_break <- snd old;
-				newline ctx;
-				spr ctx "} catch( e ) { if( e != \"__break__\" ) throw e; }";
-			)
+	| TBlock [] -> true
+	| TBlock el -> needs_switch_break (List.hd (List.rev el))
+	| TMeta ((Meta.LoopLabel,_,_), {eexpr = TBreak})
+	| TContinue | TReturn _ | TThrow _ -> false
+	| _ -> true
 
 let this ctx = match ctx.in_value with None -> "this" | Some _ -> "$this"
 
@@ -383,64 +355,64 @@ let rec gen_call ctx e el in_value =
 			List.iter (fun p -> print ctx ","; gen_value ctx p) params;
 			spr ctx ")";
 		);
-	| TCall (x,_) , el when (match x.eexpr with TLocal { v_name = "__js__" } -> false | _ -> true) ->
+	| TCall (x,_) , el when (match x.eexpr with TIdent "__js__" -> false | _ -> true) ->
 		spr ctx "(";
 		gen_value ctx e;
 		spr ctx ")";
 		spr ctx "(";
 		concat ctx "," (gen_value ctx) el;
 		spr ctx ")";
-	| TLocal { v_name = "__new__" }, { eexpr = TConst (TString cl) } :: params ->
+	| TIdent "__new__", { eexpr = TConst (TString cl) } :: params ->
 		print ctx "new %s(" cl;
 		concat ctx "," (gen_value ctx) params;
 		spr ctx ")";
-	| TLocal { v_name = "__new__" }, e :: params ->
+	| TIdent "__new__", e :: params ->
 		spr ctx "new ";
 		gen_value ctx e;
 		spr ctx "(";
 		concat ctx "," (gen_value ctx) params;
 		spr ctx ")";
-	| TLocal { v_name = "__js__" }, [{ eexpr = TConst (TString "this") }] ->
+	| TIdent "__js__", [{ eexpr = TConst (TString "this") }] ->
 		spr ctx (this ctx)
-	| TLocal { v_name = "__js__" }, [{ eexpr = TConst (TString code) }] ->
+	| TIdent "__js__", [{ eexpr = TConst (TString code) }] ->
 		spr ctx (String.concat "\n" (ExtString.String.nsplit code "\r\n"))
-	| TLocal { v_name = "__js__" }, { eexpr = TConst (TString code); epos = p } :: tl ->
+	| TIdent "__js__", { eexpr = TConst (TString code); epos = p } :: tl ->
 		Codegen.interpolate_code ctx.com code tl (spr ctx) (gen_expr ctx) p
-	| TLocal { v_name = "__instanceof__" },  [o;t] ->
+	| TIdent "__instanceof__",  [o;t] ->
 		spr ctx "(";
 		gen_value ctx o;
 		print ctx " instanceof ";
 		gen_value ctx t;
 		spr ctx ")";
-	| TLocal { v_name = "__typeof__" },  [o] ->
+	| TIdent "__typeof__",  [o] ->
 		spr ctx "typeof(";
 		gen_value ctx o;
 		spr ctx ")";
-	| TLocal { v_name = "__strict_eq__" } , [x;y] ->
+	| TIdent "__strict_eq__" , [x;y] ->
 		(* add extra parenthesis here because of operator precedence *)
 		spr ctx "((";
 		gen_value ctx x;
 		spr ctx ") === ";
 		gen_value ctx y;
 		spr ctx ")";
-	| TLocal { v_name = "__strict_neq__" } , [x;y] ->
+	| TIdent "__strict_neq__" , [x;y] ->
 		(* add extra parenthesis here because of operator precedence *)
 		spr ctx "((";
 		gen_value ctx x;
 		spr ctx ") !== ";
 		gen_value ctx y;
 		spr ctx ")";
-	| TLocal ({v_name = "__define_feature__"}), [_;e] ->
+	| TIdent "__define_feature__", [_;e] ->
 		gen_expr ctx e
-	| TLocal { v_name = "__feature__" }, { eexpr = TConst (TString f) } :: eif :: eelse ->
+	| TIdent "__feature__", { eexpr = TConst (TString f) } :: eif :: eelse ->
 		(if has_feature ctx f then
 			gen_value ctx eif
 		else match eelse with
 			| [] -> ()
 			| e :: _ -> gen_value ctx e)
-	| TLocal { v_name = "__rethrow__" }, [] ->
+	| TIdent "__rethrow__", [] ->
 		spr ctx "throw $hx_rethrow";
-	| TLocal { v_name = "__resources__" }, [] ->
+	| TIdent "__resources__", [] ->
 		spr ctx "[";
 		concat ctx "," (fun (name,data) ->
 			spr ctx "{ ";
@@ -451,7 +423,7 @@ let rec gen_call ctx e el in_value =
 			spr ctx "}"
 		) (Hashtbl.fold (fun name data acc -> (name,data) :: acc) ctx.com.resources []);
 		spr ctx "]";
-	| TLocal { v_name = "`trace" }, [e;infos] ->
+	| TIdent "`trace", [e;infos] ->
 		if has_feature ctx "haxe.Log.trace" then begin
 			let t = (try List.find (fun t -> t_path t = (["haxe"],"Log")) ctx.com.types with _ -> assert false) in
 			spr ctx (ctx.type_accessor t);
@@ -462,6 +434,13 @@ let rec gen_call ctx e el in_value =
 			spr ctx ")";
 		end else begin
 			spr ctx "console.log(";
+			(match infos.eexpr with
+			| TObjectDecl (
+				("fileName" , { eexpr = (TConst (TString file)) }) ::
+				("lineNumber" , { eexpr = (TConst (TInt line)) }) :: _) ->
+					print ctx "\"%s:%i:\"," file (Int32.to_int line)
+			| _ ->
+				());
 			gen_value ctx e;
 			spr ctx ")";
 		end
@@ -531,9 +510,21 @@ and gen_expr ctx e =
 			print ctx "($_=";
 			gen_value ctx x;
 			print ctx ",$bind($_,$_%s))" (if Meta.has Meta.SelfCall f.cf_meta then "" else (field f.cf_name)))
-	| TEnumParameter (x,_,i) ->
+	| TEnumIndex x ->
 		gen_value ctx x;
-		print ctx "[%i]" (i + 2)
+		if Common.defined ctx.com Define.JsEnumsAsObjects then
+		print ctx "._hx_index"
+		else
+		print ctx "[1]"
+	| TEnumParameter (x,f,i) ->
+		gen_value ctx x;
+		if Common.defined ctx.com Define.JsEnumsAsObjects then
+			let fname = (match f.ef_type with TFun((args,_)) -> let fname,_,_ = List.nth args i in  fname | _ -> assert false ) in
+			print ctx ".%s" (fname)
+		else
+			print ctx "[%i]" (i + 2)
+	| TField (_, FStatic ({cl_path = [],""},f)) ->
+		spr ctx f.cf_name;
 	| TField (x, (FInstance(_,_,f) | FStatic(_,f) | FAnon(f))) when Meta.has Meta.SelfCall f.cf_meta ->
 		gen_value ctx x;
 	| TField (x,f) ->
@@ -552,6 +543,14 @@ and gen_expr ctx e =
 		spr ctx "(";
 		gen_value ctx e;
 		spr ctx ")";
+	| TMeta ((Meta.LoopLabel,[(EConst(Int n),_)],_), e) ->
+		(match e.eexpr with
+		| TWhile _ | TFor _ ->
+			print ctx "_hx_loop%s: " n;
+			gen_expr ctx e
+		| TBreak ->
+			print ctx "break _hx_loop%s" n;
+		| _ -> assert false)
 	| TMeta (_,e) ->
 		gen_expr ctx e
 	| TReturn eo ->
@@ -564,7 +563,7 @@ and gen_expr ctx e =
 			gen_value ctx e);
 	| TBreak ->
 		if not ctx.in_loop then unsupported e.epos;
-		if ctx.handle_break then spr ctx "throw \"__break__\"" else spr ctx "break"
+		spr ctx "break"
 	| TContinue ->
 		if not ctx.in_loop then unsupported e.epos;
 		spr ctx "continue"
@@ -579,7 +578,11 @@ and gen_expr ctx e =
 		let old = ctx.in_value, ctx.in_loop in
 		ctx.in_value <- None;
 		ctx.in_loop <- false;
-		print ctx "function(%s) " (String.concat "," (List.map ident (List.map arg_name f.tf_args)));
+		let args = List.map (fun (v,_) ->
+			check_var_declaration v;
+			ident v.v_name
+		) f.tf_args in
+		print ctx "function(%s) " (String.concat "," args);
 		gen_expr ctx (fun_block ctx f e.epos);
 		ctx.in_value <- fst old;
 		ctx.in_loop <- snd old;
@@ -633,20 +636,22 @@ and gen_expr ctx e =
 		gen_value ctx e;
 		spr ctx (Ast.s_unop op)
 	| TWhile (cond,e,Ast.NormalWhile) ->
-		let handle_break = handle_break ctx e in
+		let old_in_loop = ctx.in_loop in
+		ctx.in_loop <- true;
 		spr ctx "while";
 		gen_value ctx cond;
 		spr ctx " ";
 		gen_expr ctx e;
-		handle_break();
+		ctx.in_loop <- old_in_loop
 	| TWhile (cond,e,Ast.DoWhile) ->
-		let handle_break = handle_break ctx e in
+		let old_in_loop = ctx.in_loop in
+		ctx.in_loop <- true;
 		spr ctx "do ";
 		gen_expr ctx e;
 		semicolon ctx;
 		spr ctx " while";
 		gen_value ctx cond;
-		handle_break();
+		ctx.in_loop <- old_in_loop
 	| TObjectDecl fields ->
 		spr ctx "{ ";
 		concat ctx ", " (fun (f,e) -> (match e.eexpr with
@@ -658,7 +663,8 @@ and gen_expr ctx e =
 		ctx.separator <- true
 	| TFor (v,it,e) ->
 		check_var_declaration v;
-		let handle_break = handle_break ctx e in
+		let old_in_loop = ctx.in_loop in
+		ctx.in_loop <- true;
 		let it = ident (match it.eexpr with
 			| TLocal v -> v.v_name
 			| _ ->
@@ -678,7 +684,7 @@ and gen_expr ctx e =
 		bend();
 		newline ctx;
 		spr ctx "}";
-		handle_break();
+		ctx.in_loop <- old_in_loop
 	| TTry (e,catchs) ->
 		spr ctx "try ";
 		gen_expr ctx e;
@@ -700,7 +706,7 @@ and gen_expr ctx e =
 		if (has_feature ctx "js.Lib.rethrow") then begin
 			let has_rethrow (_,e) =
 				let rec loop e = match e.eexpr with
-				| TCall({eexpr = TLocal {v_name = "__rethrow__"}}, []) -> raise Exit
+				| TCall({eexpr = TIdent "__rethrow__"}, []) -> raise Exit
 				| _ -> Type.iter loop e
 				in
 				try (loop e; false) with Exit -> true
@@ -800,7 +806,7 @@ and gen_expr ctx e =
 			) el;
 			let bend = open_block ctx in
 			gen_block_element ctx e2;
-			if not (has_return e2) then begin
+			if needs_switch_break e2 then begin
 				newline ctx;
 				print ctx "break";
 			end;
@@ -824,7 +830,10 @@ and gen_expr ctx e =
 		gen_expr ctx e1;
 		spr ctx " , ";
 		spr ctx (ctx.type_accessor t);
-		spr ctx ")");
+		spr ctx ")"
+	| TIdent s ->
+		spr ctx s
+	);
 	Option.may (fun smap -> smap.current_expr <- None) ctx.smap
 
 
@@ -832,7 +841,7 @@ and gen_block_element ?(after=false) ctx e =
 	match e.eexpr with
 	| TBlock el ->
 		List.iter (gen_block_element ~after ctx) el
-	| TCall ({ eexpr = TLocal { v_name = "__feature__" } }, { eexpr = TConst (TString f) } :: eif :: eelse) ->
+	| TCall ({ eexpr = TIdent "__feature__" }, { eexpr = TConst (TString f) } :: eif :: eelse) ->
 		if has_feature ctx f then
 			gen_block_element ~after ctx eif
 		else (match eelse with
@@ -885,13 +894,15 @@ and gen_value ctx e =
 	| TBinop _
 	| TField _
 	| TEnumParameter _
+	| TEnumIndex _
 	| TTypeExpr _
 	| TParenthesis _
 	| TObjectDecl _
 	| TArrayDecl _
 	| TNew _
 	| TUnop _
-	| TFunction _ ->
+	| TFunction _
+	| TIdent _ ->
 		gen_expr ctx e
 	| TMeta (_,e1) ->
 		gen_value ctx e1
@@ -1006,7 +1017,7 @@ let gen_class_static_field ctx c f =
 	match f.cf_expr with
 	| None | Some { eexpr = TConst TNull } when not (has_feature ctx "Type.getClassFields") ->
 		()
-	| None when is_extern_field f ->
+	| None when not (is_physical_field f) ->
 		()
 	| None ->
 		print ctx "%s%s = null" (s_path ctx c.cl_path) (static_field c f.cf_name);
@@ -1028,7 +1039,7 @@ let can_gen_class_field ctx = function
 	| { cf_expr = (None | Some { eexpr = TConst TNull }) } when not (has_feature ctx "Type.getInstanceFields") ->
 		false
 	| f ->
-		not (is_extern_field f)
+		is_physical_field f
 
 let gen_class_field ctx c f =
 	check_field_name c f;
@@ -1162,26 +1173,44 @@ let generate_enum ctx e =
 	if has_feature ctx "js.Boot.isEnum" then print ctx " __ename__ : %s," (if has_feature ctx "Type.getEnumName" then "[" ^ String.concat "," ename ^ "]" else "true");
 	print ctx " __constructs__ : [%s] }" (String.concat "," (List.map (fun s -> Printf.sprintf "\"%s\"" s) e.e_names));
 	ctx.separator <- true;
+	let as_objects = Common.defined ctx.com Define.JsEnumsAsObjects in
+	if as_objects then begin
+		newline ctx;
+		print ctx "$hxEnums[\"%s\"] = %s" p p
+	end;
 	newline ctx;
 	List.iter (fun n ->
 		let f = PMap.find n e.e_constrs in
 		print ctx "%s%s = " p (field f.ef_name);
 		(match f.ef_type with
 		| TFun (args,_) ->
-			let sargs = String.concat "," (List.map (fun (n,_,_) -> ident n) args) in
-			print ctx "function(%s) { var $x = [\"%s\",%d,%s]; $x.__enum__ = %s;" sargs f.ef_name f.ef_index sargs p;
+			let sargs = String.concat "," (List.map (fun (n,_,_) -> ident n) args) in begin
+			if as_objects then
+				let sfields = String.concat "," (List.map (fun (n,_,_) -> (ident n) ^ ":" ^ (ident n) ) args) in
+				print ctx "function(%s) { var $x = {_hx_index:%d,%s,__enum__:\"%s\"};" sargs f.ef_index sfields p;
+			else
+				print ctx "function(%s) { var $x = [\"%s\",%d,%s]; $x.__enum__ = %s;" sargs f.ef_name f.ef_index sargs p;
+			end;
 			if has_feature ctx "has_enum" then
 				spr ctx " $x.toString = $estr;";
 			spr ctx " return $x; }";
+			if as_objects then begin
+				let sparams = String.concat "," (List.map (fun (n,_,_) -> "\"" ^ (ident n) ^ "\"" ) args) in
+				newline ctx;
+				print ctx "%s%s.__params__ = [%s];" p (field f.ef_name) sparams
+			end;
 			ctx.separator <- true;
 		| _ ->
-			print ctx "[\"%s\",%d]" f.ef_name f.ef_index;
+			if as_objects then
+				print ctx "{_hx_index:%d};" f.ef_index
+			else
+				print ctx "[\"%s\",%d]" f.ef_name f.ef_index;
 			newline ctx;
 			if has_feature ctx "has_enum" then begin
 				print ctx "%s%s.toString = $estr" p (field f.ef_name);
 				newline ctx;
 			end;
-			print ctx "%s%s.__enum__ = %s" p (field f.ef_name) p;
+			print ctx "%s%s.__enum__ = %s" p (field f.ef_name) (if as_objects then "\"" ^ p ^"\"" else p);
 		);
 		newline ctx
 	) e.e_names;
@@ -1291,7 +1320,6 @@ let alloc_ctx com =
 		tabs = "";
 		in_value = None;
 		in_loop = false;
-		handle_break = false;
 		id_counter = 0;
 		type_accessor = (fun _ -> assert false);
 		separator = false;
@@ -1325,6 +1353,8 @@ let generate com =
 
 	let nodejs = Common.raw_defined com "nodejs" in
 
+	setup_kwds (if ctx.es_version >= 5 then es5kwds else es3kwds);
+
 	let exposed = List.concat (List.map (fun t ->
 		match t with
 			| TClassDecl c ->
@@ -1413,9 +1443,6 @@ let generate com =
 	);
 
 	if ctx.js_modern then begin
-		(* Additional ES5 strict mode keywords. *)
-		List.iter (fun s -> Hashtbl.replace kwds s ()) [ "arguments"; "eval" ];
-
 		(* Wrap output in a closure *)
 		print ctx "(function (%s) { \"use strict\"" (String.concat ", " (List.map fst closureArgs));
 		newline ctx;
@@ -1456,6 +1483,7 @@ let generate com =
 	let vars = if has_feature ctx "has_enum"
 		then ("$estr = function() { return " ^ (ctx.type_accessor (TClassDecl { null_class with cl_path = ["js"],"Boot" })) ^ ".__string_rec(this,''); }") :: vars
 		else vars in
+	let vars = if Common.defined com Define.JsEnumsAsObjects then "$hxEnums = {}" :: vars else vars in
 	(match List.rev vars with
 	| [] -> ()
 	| vl ->

Tiedoston diff-näkymää rajattu, sillä se on liian suuri
+ 1229 - 1188
src/generators/genlua.ml


+ 8 - 4
src/generators/genneko.ml

@@ -199,7 +199,7 @@ and gen_call ctx p e el =
 			this p;
 			array p (List.map (gen_expr ctx) el)
 		]
-	| TLocal { v_name = "__resources__" }, [] ->
+	| TIdent "__resources__", [] ->
 		call p (builtin p "array") (Hashtbl.fold (fun name data acc ->
 			(EObject [("name",gen_constant ctx e.epos (TString name));("data",gen_big_string ctx p data)],p) :: acc
 		) ctx.com.resources [])
@@ -219,8 +219,8 @@ and gen_expr ctx e =
 	match e.eexpr with
 	| TConst c ->
 		gen_constant ctx e.epos c
-	| TLocal v when v.v_name.[0] = '$' ->
-		(EConst (Builtin (String.sub v.v_name 1 (String.length v.v_name - 1))),p)
+	| TIdent s when s.[0] = '$' ->
+		(EConst (Builtin (String.sub s 1 (String.length s - 1))),p)
 	| TLocal v ->
 		if v.v_capture then
 			(EArray (ident p v.v_name,int p 0),p)
@@ -248,6 +248,8 @@ and gen_expr ctx e =
 		| _ -> assert false)
 	| TEnumParameter (e,_,i) ->
 		EArray (field p (gen_expr ctx e) "args",int p i),p
+	| TEnumIndex e ->
+		field p (gen_expr ctx e) "index"
 	| TField (e,f) ->
 		field p (gen_expr ctx e) (field_name f)
 	| TTypeExpr t ->
@@ -368,6 +370,8 @@ and gen_expr ctx e =
 		gen_expr ctx e
 	| TCast (e1,Some t) ->
 		gen_expr ctx (Codegen.default_cast ~vtmp:"@tmp" ctx.com e1 t e.etype e.epos)
+	| TIdent s ->
+		ident p s
 	| TSwitch (e,cases,eo) ->
 		let e = gen_expr ctx e in
 		let eo = (match eo with None -> None | Some e -> Some (gen_expr ctx e)) in
@@ -399,7 +403,7 @@ and gen_expr ctx e =
 
 let gen_method ctx p c acc =
 	ctx.curmethod <- c.cf_name;
-	if is_extern_field c then acc else
+	if not (is_physical_field c) then acc else
 	match c.cf_expr with
 	| None ->
 		((c.cf_name, null p) :: acc)

+ 67 - 36
src/generators/genphp.ml

@@ -126,20 +126,17 @@ and type_string_suff suffix haxe_type =
 	| TAbstract ({ a_path = [],"Float" },[]) -> "double"
 	| TAbstract ({ a_path = [],"Bool" },[]) -> "bool"
 	| TAbstract ({ a_path = [],"Void" },[]) -> "Void"
+	| TAbstract ({ a_path = [],"Null"},[t]) ->
+		(match follow t with
+		| TInst ({ cl_path = [],"Int" },_)
+		| TInst ({ cl_path = [],"Float" },_)
+		| TEnum ({ e_path = [],"Bool" },_) -> "Dynamic"
+		| _ -> type_string_suff suffix t)
 	| TEnum (enum,params) ->  (join_class_path enum.e_path "::") ^ suffix
 	| TInst (klass,params) ->  (class_string klass suffix params)
 	| TAbstract (abs,params) ->  (join_class_path abs.a_path "::") ^ suffix
 	| TType (type_def,params) ->
 		(match type_def.t_path with
-		| [] , "Null" ->
-			(match params with
-			| [t] ->
-				(match follow t with
-				| TInst ({ cl_path = [],"Int" },_)
-				| TInst ({ cl_path = [],"Float" },_)
-				| TEnum ({ e_path = [],"Bool" },_) -> "Dynamic"
-				| _ -> type_string_suff suffix t)
-			| _ -> assert false);
 		| [] , "Array" ->
 			(match params with
 			| [t] -> "Array<" ^ (type_string (follow t) ) ^ " >"
@@ -149,7 +146,7 @@ and type_string_suff suffix haxe_type =
 	| TFun (args,haxe_type) -> "Dynamic"
 	| TAnon anon -> "Dynamic"
 	| TDynamic haxe_type -> "Dynamic"
-	| TLazy func -> type_string_suff suffix ((!func)())
+	| TLazy func -> type_string_suff suffix (lazy_type func)
 	)
 and type_string haxe_type =
 	type_string_suff "" haxe_type;;
@@ -225,8 +222,7 @@ let rec is_string_type t =
 let is_string_expr e = is_string_type e.etype
 
 let to_string ctx e =
-	let v = alloc_var "__call__" t_dynamic e.epos in
-	let f = mk (TLocal v) t_dynamic e.epos in
+	let f = mk (TIdent "__call__") t_dynamic e.epos in
 	mk (TCall (f, [ ExprBuilder.make_string ctx.com "_hx_string_rec" e.epos; e; ExprBuilder.make_string ctx.com "" e.epos])) ctx.com.basic.tstring e.epos
 
 let as_string_expr ctx e =
@@ -238,8 +234,7 @@ let as_string_expr ctx e =
 	| _ -> e
 (* for known String type that could have null value *)
 let to_string_null ctx e =
-	let v = alloc_var "__call__" t_dynamic e.epos in
-	let f = mk (TLocal v) t_dynamic e.epos in
+	let f = mk (TIdent "__call__") t_dynamic e.epos in
 	mk (TCall (f, [ ExprBuilder.make_string ctx.com "_hx_string_or_null" e.epos; e])) ctx.com.basic.tstring e.epos
 
 
@@ -344,8 +339,7 @@ let s_ident_local n =
 	| _ -> n
 
 let create_directory com ldir =
- 	let atm_path = ref (String.create 0) in
- 	atm_path := com.file;
+ 	let atm_path = ref com.file in
  	if not (Sys.file_exists com.file) then (Unix.mkdir com.file 0o755);
  	(List.iter (fun p -> atm_path := !atm_path ^ "/" ^ p; if not (Sys.file_exists !atm_path) then (Unix.mkdir !atm_path 0o755);) ldir)
 
@@ -568,54 +562,62 @@ and gen_call ctx e el =
 			concat ctx "," (gen_value ctx) params;
 			spr ctx ")";
 		);
-	| TLocal { v_name = "__set__" }, { eexpr = TConst (TString code) } :: el ->
+	| TField ({ eexpr = TTypeExpr _ }, FStatic (_, {cf_type = TDynamic _; cf_kind = Var _})) , params ->
+		spr ctx "call_user_func(";
+		ctx.is_call <- true;
+		gen_value ctx e;
+		ctx.is_call <- false;
+		spr ctx ", ";
+		concat ctx ", " (gen_value ctx) el;
+		spr ctx ")";
+	| TIdent "__set__", { eexpr = TConst (TString code) } :: el ->
 		print ctx "$%s" code;
 		genargs el;
-	| TLocal { v_name = "__set__" }, e :: el ->
+	| TIdent "__set__", e :: el ->
 		gen_value ctx e;
 		genargs el;
-	| TLocal { v_name = "__setfield__" }, e :: (f :: el) ->
+	| TIdent "__setfield__", e :: (f :: el) ->
 		gen_value ctx e;
 		spr ctx "->{";
 		gen_value ctx f;
 		spr ctx "}";
 		genargs el;
-	| TLocal { v_name = "__field__" }, e :: ({ eexpr = TConst (TString code) } :: el) ->
+	| TIdent "__field__", e :: ({ eexpr = TConst (TString code) } :: el) ->
 		gen_value ctx e;
 		spr ctx "->";
 		spr ctx code;
 		gen_array_args ctx el;
-	| TLocal { v_name = "__field__" }, e :: (f :: el) ->
+	| TIdent "__field__", e :: (f :: el) ->
 		gen_value ctx e;
 		spr ctx "->";
 		gen_value ctx f;
 		gen_array_args ctx el;
-	| TLocal { v_name = "__prefix__" }, [] ->
+	| TIdent "__prefix__", [] ->
 		(match ctx.com.php_prefix with
 		| Some prefix ->
 			print ctx "\"%s\"" prefix
 		| None ->
 			spr ctx "null")
-	| TLocal { v_name = "__var__" }, { eexpr = TConst (TString code) } :: el ->
+	| TIdent "__var__", { eexpr = TConst (TString code) } :: el ->
 		print ctx "$%s" code;
 		gen_array_args ctx el;
-	| TLocal { v_name = "__var__" }, e :: el ->
+	| TIdent "__var__", e :: el ->
 		gen_value ctx e;
 		gen_array_args ctx el;
-	| TLocal { v_name = "__call__" }, { eexpr = TConst (TString code) } :: el ->
+	| TIdent "__call__", { eexpr = TConst (TString code) } :: el ->
 		spr ctx code;
 		spr ctx "(";
 		concat ctx ", " (gen_value ctx) el;
 		spr ctx ")";
-	| TLocal { v_name = "__php__" }, [{ eexpr = TConst (TString code) }] ->
+	| TIdent "__php__", [{ eexpr = TConst (TString code) }] ->
 		(*--php-prefix*)
 		spr ctx (prefix_init_replace ctx.com code)
-	| TLocal { v_name = "__php__" }, { eexpr = TConst (TString code); epos = p } :: tl ->
+	| TIdent "__php__", { eexpr = TConst (TString code); epos = p } :: tl ->
 		Codegen.interpolate_code ctx.com code tl (spr ctx) (gen_expr ctx) p
-	| TLocal { v_name = "__instanceof__" },  [e1;{ eexpr = TConst (TString t) }] ->
+	| TIdent "__instanceof__",  [e1;{ eexpr = TConst (TString t) }] ->
 		gen_value ctx e1;
 		print ctx " instanceof %s" t;
-	| TLocal { v_name = "__physeq__" },  [e1;e2] ->
+	| TIdent "__physeq__",  [e1;e2] ->
 		spr ctx "(";
 		gen_value ctx e1;
 		spr ctx " === ";
@@ -803,7 +805,12 @@ and gen_member_access ctx isvar e s =
 	| TAnon a ->
 		(match !(a.a_status) with
 		| EnumStatics _ ->
-			print ctx "::%s%s" (if isvar then "$" else "") (s_ident s)
+			let (isvar, access_operator) =
+				match e.eexpr with
+					| TField _ -> (false, "->")
+					| _ -> (isvar, "::")
+			in
+			print ctx "%s%s" (access_operator ^ (if isvar then "$" else "")) (s_ident s)
 		| Statics sta ->
 			let (sep, no_dollar) = if Meta.has Meta.PhpGlobal sta.cl_meta then
 					("", false)
@@ -820,7 +827,7 @@ and gen_member_access ctx isvar e s =
 	| _ -> print ctx "->%s" (if isvar then s_ident_field s else s_ident s)
 
 and gen_field_access ctx isvar e s =
-	match e.eexpr with
+	match (reveal_expr e).eexpr with
 	| TTypeExpr t ->
 		let isglobal = match t with
 		| TClassDecl(c) -> Meta.has Meta.PhpGlobal c.cl_meta && c.cl_extern
@@ -1304,6 +1311,11 @@ and gen_expr ctx e =
 		gen_value ctx e1;
 		spr ctx ")";
 		print ctx "->params[%d]" i;
+	| TEnumIndex e1 ->
+		spr ctx "_hx_deref(";
+		gen_value ctx e1;
+		spr ctx ")";
+		print ctx "->index";
 	| TField (e1,s) ->
 		gen_tfield ctx e e1 s
 	| TTypeExpr t ->
@@ -1702,6 +1714,8 @@ and gen_expr ctx e =
 		spr ctx ", ";
 		gen_expr ctx (mk (TTypeExpr t) (mk_texpr t) e1.epos);
 		spr ctx ")"
+	| TIdent s ->
+		spr ctx s
 
 and argument_list_from_locals include_this in_var l =
 	let lst = ref [] in
@@ -1792,6 +1806,7 @@ and gen_value ctx e =
 	| TArray _
 	| TBinop _
 	| TEnumParameter _
+	| TEnumIndex _
 	| TField _
 	| TParenthesis _
 	| TObjectDecl _
@@ -1799,7 +1814,8 @@ and gen_value ctx e =
 	| TCall _
 	| TUnop _
 	| TNew _
-	| TFunction _ ->
+	| TFunction _
+	| TIdent _ ->
 		gen_expr ctx e
 	| TMeta (_,e1) ->
 		gen_value ctx e1
@@ -1889,7 +1905,7 @@ let gen_assigned_value ctx eo =	match eo with
 		()
 
 let generate_field ctx static f =
-	if not (is_extern_field f) then
+	if is_physical_field f then
 		newline ctx;
 	ctx.locals <- PMap.empty;
 	ctx.inv_locals <- PMap.empty;
@@ -1908,7 +1924,7 @@ let generate_field ctx static f =
 		else
 			gen_function ctx (s_ident f.cf_name) fd f.cf_params p
 	| _ ->
-		if (is_extern_field f) then
+		if not (is_physical_field f) then
 			()
 		else if ctx.curclass.cl_interface then
 			match follow f.cf_type, f.cf_kind with
@@ -1997,7 +2013,7 @@ let generate_static_field_assign ctx path f =
 					print ctx "%s::$%s = " (s_path ctx path false p) (s_ident f.cf_name);
 					gen_value ctx e
 				| _ -> ())
-			| _ when is_extern_field f ->
+			| _ when not (is_physical_field f) ->
 				()
 			| _ ->
 				newline ctx;
@@ -2066,7 +2082,22 @@ let generate_class ctx c =
 	| Some (csup,_) ->
 		requires_constructor := false;
 		print ctx "extends %s " (s_path ctx csup.cl_path csup.cl_extern c.cl_pos));
-	let implements = ExtList.List.unique ~cmp:(fun a b -> (fst a).cl_path = (fst b).cl_path) c.cl_implements in
+	(* Do not add interfaces which are implemented through other interfaces inheritance *)
+	let unique = List.filter
+		(fun (iface, _) ->
+			not (List.exists
+				(fun (probably_descendant, _) ->
+					if probably_descendant == iface then
+						false
+					else
+						is_parent iface probably_descendant
+				)
+				c.cl_implements
+			)
+		)
+		c.cl_implements
+	in
+	let implements = ExtList.List.unique ~cmp:(fun a b -> (fst a).cl_path = (fst b).cl_path) unique in
 	(match implements with
 	| [] -> ()
 	| l ->

+ 193 - 124
src/generators/genphp7.ml

@@ -240,7 +240,7 @@ let error_message pos message = (stringify_pos pos) ^ ": " ^ message
 *)
 let fail hxpos mlpos =
 	match mlpos with
-		| (file, line, _) ->
+		| (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
@@ -332,7 +332,7 @@ let get_void ctx : Type.t =
 			List.iter find ctx.types;
 			match !void with
 				| Some value -> value
-				| None -> fail dummy_pos (try assert false with Assert_failure mlpos -> mlpos)
+				| None -> fail dummy_pos __POS__
 
 (**
 	@return `tclass` instance for `php.Boot`
@@ -350,7 +350,7 @@ let get_boot ctx : tclass =
 			List.iter find ctx.types;
 			match !boot with
 				| Some value -> value
-				| None -> fail dummy_pos (try assert false with Assert_failure mlpos -> mlpos)
+				| None -> fail dummy_pos __POS__
 
 (**
 	@return `expr` wrapped in parenthesis
@@ -382,13 +382,14 @@ let need_parenthesis_for_binop current parent =
 *)
 let needs_dereferencing for_assignment expr =
 	let rec is_create target_expr =
-		match target_expr.eexpr with
+		match (reveal_expr target_expr).eexpr with
 			| TParenthesis e -> is_create e
 			| TCast (e, _) -> is_create e
 			| TNew _ -> for_assignment
 			| TArrayDecl _ -> for_assignment
 			| TObjectDecl _ -> for_assignment
 			| TConst TNull -> true
+			| TIf _ -> true
 			(* some of `php.Syntax` methods *)
 			| TCall ({ eexpr = TField (_, FStatic ({ cl_path = syntax_type_path }, { cf_name = name })) }, _) ->
 				(match name with
@@ -397,7 +398,7 @@ let needs_dereferencing for_assignment expr =
 				)
 			| _ -> false
 	in
-	match expr.eexpr with
+	match (reveal_expr expr).eexpr with
 		| TField (target_expr, _) -> is_create target_expr
 		| TArray (target_expr, _) -> is_create target_expr
 		| _ -> false
@@ -408,7 +409,7 @@ let needs_dereferencing for_assignment expr =
 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)
+		| _ -> fail field.cf_pos __POS__
 
 (**
 	Check if `target` is 100% guaranteed to be a scalar type in PHP.
@@ -600,6 +601,13 @@ let is_var_with_nonconstant_expr (field:tclass_field) =
 				| Some _ -> true
 			)
 		| Method _ -> false
+(**
+	Check if specified field is an `inline var` field.
+*)
+let is_inline_var (field:tclass_field) =
+	match field.cf_kind with
+		| Var { v_read = AccInline; v_write = AccNever } -> true
+		| _ -> false
 
 (**
 	@return New TBlock expression which is composed of setting default values for optional arguments and function body.
@@ -693,7 +701,7 @@ let is_access expr =
 *)
 let is_magic expr =
 	match expr.eexpr with
-	| TCall ({ eexpr = TLocal { v_name = name }}, _) ->
+	| TCall ({ eexpr = TIdent name}, _) ->
 		(match name with
 			| "__php__" -> true
 			| "__call__" -> true
@@ -720,14 +728,14 @@ let need_boot_equal expr1 expr2 =
 *)
 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 [] -> fail block_expr.epos __POS__
 		| 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)
+		| _ -> fail block_expr.epos __POS__
 
 (**
 	If `expr` is a block, then return list of expressions in that block.
@@ -746,7 +754,7 @@ let unpack_block expr =
 let unpack_single_expr_block expr =
 		match expr.eexpr with
 			| TBlock [ e ] -> e
-			| TBlock _ -> fail expr.epos (try assert false with Assert_failure mlpos -> mlpos)
+			| TBlock _ -> fail expr.epos __POS__
 			| _ -> expr
 
 (**
@@ -757,17 +765,6 @@ let has_rtti_meta ctx mtype =
 		| 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.
 *)
@@ -825,7 +822,7 @@ let instanceof_compatible (subject_arg:texpr) (type_arg:texpr) : bool =
 		| TTypeExpr (TClassDecl { cl_path = path }) when path <> ([], "String") && path <> ([], "Class") ->
 			let subject_arg = reveal_expr_with_parenthesis subject_arg in
 			(match subject_arg.eexpr with
-				| TLocal _ | TField _ | TCall _ | TArray _ -> not (is_magic subject_arg)
+				| TLocal _ | TField _ | TCall _ | TArray _ | TConst TThis -> not (is_magic subject_arg)
 				| _ -> false
 			)
 		| _ -> false
@@ -904,10 +901,14 @@ class class_wrapper (cls) =
 						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
+								(* Skip `inline var` fields *)
+								if not (is_inline_var field) then begin
+									if not !needs then needs := is_var_with_nonconstant_expr 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
+								end
 							)
 							cls.cl_statics;
 						!needs
@@ -1327,7 +1328,7 @@ class code_writer (ctx:Common.context) hx_type_path php_name =
 								| Not_found ->
 									Hashtbl.add use_table !alias type_path;
 									added := true
-								| _ -> fail self#pos (try assert false with Assert_failure mlpos -> mlpos)
+								| _ -> fail self#pos __POS__
 						done;
 						!alias
 		(**
@@ -1493,7 +1494,7 @@ class code_writer (ctx:Common.context) hx_type_path php_name =
 							access_expr
 						)
 					}
-				| _ -> fail self#pos (try assert false with Assert_failure mlpos -> mlpos)
+				| _ -> fail self#pos __POS__
 		(**
 			Writes specified string to output buffer
 		*)
@@ -1574,7 +1575,7 @@ class code_writer (ctx:Common.context) hx_type_path php_name =
 				| 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 (fexpr, access) when needs_dereferencing false expr -> self#write_expr (self#dereference expr)
+				| TField (fexpr, access) when needs_dereferencing (self#is_in_write_context) expr -> self#write_expr (self#dereference expr)
 				| TField (fexpr, access) -> self#write_expr_field fexpr access
 				| TTypeExpr mtype -> self#write_expr_type mtype
 				| TParenthesis expr ->
@@ -1585,7 +1586,7 @@ class code_writer (ctx:Common.context) hx_type_path php_name =
 				| TArrayDecl exprs -> self#write_expr_array_decl exprs
 				| TCall (target, [arg1; arg2]) when is_std_is target && instanceof_compatible arg1 arg2 -> self#write_expr_lang_instanceof [arg1; arg2]
 				| TCall (_, [arg]) when is_native_struct_array_cast expr && is_object_declaration arg -> self#write_assoc_array_decl arg
-				| TCall ({ eexpr = TLocal { v_name = name }}, args) when is_magic expr -> self#write_expr_magic name args
+				| TCall ({ eexpr = TIdent 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
@@ -1598,7 +1599,7 @@ class code_writer (ctx:Common.context) hx_type_path php_name =
 				| 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)
+				| TFor (var, iterator, body) -> fail self#pos __POS__
 				| 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
@@ -1610,6 +1611,8 @@ class code_writer (ctx:Common.context) hx_type_path php_name =
 				| 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
+				| TEnumIndex expr -> self#write_expr_enum_index expr
+				| TIdent s -> self#write s
 			);
 			expr_hierarchy <- List.tl expr_hierarchy
 		(**
@@ -1697,7 +1700,7 @@ class code_writer (ctx:Common.context) hx_type_path php_name =
 						self#write_indentation;
 						self#write "]";
 					end
-				| _ -> fail object_decl.epos (try assert false with Assert_failure mlpos -> mlpos)
+				| _ -> fail object_decl.epos __POS__
 		(**
 			Writes TArray to output buffer
 		*)
@@ -1986,7 +1989,7 @@ class code_writer (ctx:Common.context) hx_type_path php_name =
 							(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)
+								| _ -> fail self#pos __POS__
 							)
 						| "__call__" ->
 							self#write (code ^ "(");
@@ -2159,7 +2162,7 @@ class code_writer (ctx:Common.context) hx_type_path php_name =
 					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)
+				| _ -> fail self#pos __POS__
 		(**
 			Writes TUnOp to output buffer
 		*)
@@ -2179,72 +2182,76 @@ class code_writer (ctx:Common.context) hx_type_path php_name =
 				| Postfix ->
 					self#write_expr expr;
 					write_unop operation
+		method private write_expr_for_field_access expr access_str field_str =
+			let access_str = ref access_str in
+			(match (reveal_expr expr).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)
 		(**
 			Writes TField to output buffer
 		*)
 		method 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 access with
 				| FInstance ({ cl_path = [], "String"}, _, { cf_name = "length"; cf_kind = Var _ }) ->
 					self#write "strlen(";
 					self#write_expr expr;
 					self#write ")"
-				| FInstance (_, _, field) -> write_access "->" (field_name field)
+				| FInstance (_, _, field) -> self#write_expr_for_field_access expr "->" (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)
+						| TTypeExpr _ -> self#write_expr_for_field_access expr "::" ("$" ^ (field_name field))
+						| _ -> self#write_expr_for_field_access expr "->" (field_name field)
 					)
 				| FStatic (_, ({ cf_kind = Method MethDynamic } as field)) ->
 					(match self#parent_expr with
 						| Some { eexpr = TCall ({ eexpr = TField (e, a) }, _) } when a == access ->
 							self#write "(";
-							write_access "::" ("$" ^ (field_name field));
+							self#write_expr_for_field_access expr "::" ("$" ^ (field_name field));
 							self#write ")"
 						| _ ->
-							write_access "::" ("$" ^ (field_name field))
+							self#write_expr_for_field_access expr "::" ("$" ^ (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
+					if not written_as_probable_string then self#write_expr_for_field_access expr "->" (field_name field)
+				| FDynamic field_name -> self#write_expr_field_dynamic expr 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
-
+					self#write_expr_field_enum expr field
+		(**
+			Generate EField for enum constructor.
+		*)
+		method write_expr_field_enum expr field =
+			let write_field () =
+				self#write_expr expr;
+				let access_operator =
+					match (reveal_expr expr).eexpr with
+						| TTypeExpr _ -> "::"
+						| _ -> "->"
+				in
+				self#write (access_operator ^ field.ef_name)
+			in
+			if is_enum_constructor_with_args field then
+				match self#parent_expr with
+					(* Invoking this enum field *)
+					| Some { eexpr = TCall ({ eexpr = TField (target_expr, FEnum (_, target_field)) }, _) } when target_expr == expr && target_field == field ->
+						write_field ()
+					(* Passing this enum field somewhere *)
+					| _ ->
+						self#write_static_method_closure expr field.ef_name
+			else
+				begin
+					write_field ();
+					self#write "()"
+				end
 		(**
 			Writes field access on Dynamic expression to output buffer
 		*)
@@ -2277,7 +2284,7 @@ class code_writer (ctx:Common.context) hx_type_path php_name =
 					self#write ((self#use hxstring_type_path) ^ "::" ^ (field_name field) ^ "(");
 					write_args self#write self#write_expr (expr :: args);
 					self#write ")"
-				| _ -> fail self#pos (try assert false with Assert_failure mlpos -> mlpos)
+				| _ -> fail self#pos __POS__
 		(**
 			Writes FStatic field access for methods to output buffer
 		*)
@@ -2296,14 +2303,24 @@ class code_writer (ctx:Common.context) hx_type_path php_name =
 					write_expr ();
 					self#write (operator ^ (field_name field))
 				| _ ->
-					let (args, return_type) = get_function_signature field  in
-					self#write "function(";
-					write_args self#write (self#write_arg true) args;
-					self#write ") { return ";
-					write_expr ();
-					self#write (operator ^ (field_name field) ^ "(");
-					write_args self#write (self#write_arg false) args;
-					self#write "); }"
+					self#write_static_method_closure expr field.cf_name
+		(**
+			Generates a closure of a static method. `expr` should contain a `HxClass` instance or a string name of a class.
+		*)
+		method write_static_method_closure expr field_name =
+			let expr = reveal_expr expr in
+			self#write ("new " ^ (self#use hxclosure_type_path) ^ "(");
+			(match (reveal_expr expr).eexpr with
+				| TTypeExpr (TClassDecl { cl_path = ([], "String") }) ->
+					self#write ((self#use hxstring_type_path) ^ "::class")
+				| TTypeExpr _ ->
+					self#write_expr expr;
+					self#write "::class"
+				| _ ->
+					self#write_expr expr;
+					self#write "->phpClassName"
+			);
+			self#write (", '" ^ field_name ^ "')")
 		(**
 			Writes FClosure field access to output buffer
 		*)
@@ -2323,16 +2340,16 @@ class code_writer (ctx:Common.context) hx_type_path php_name =
 						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 ("(new " ^ (self#use hxdynamicstr_type_path) ^ "(");
 								self#write_expr expr;
-								self#write ")"
+								self#write ("))->" ^ (field_name field))
 							| _ ->
-								self#write_expr expr
+								self#write (new_closure ^ "(");
+								self#write_expr expr;
+								self#write (", '" ^ (field_name field) ^ "')")
 						);
-						self#write (", '" ^ (field_name field) ^ "')")
 		(**
 			Write anonymous object declaration to output buffer
 		*)
@@ -2368,7 +2385,7 @@ class code_writer (ctx:Common.context) hx_type_path php_name =
 		method 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)
+				| _ -> fail self#pos __POS__
 			in
 			match name with
 				| "int" | "float"
@@ -2388,7 +2405,7 @@ class code_writer (ctx:Common.context) hx_type_path php_name =
 				| "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)
+				| _ -> fail self#pos __POS__
 		(**
 			Writes splat operator (for `php.Syntax.splat()`)
 		*)
@@ -2397,7 +2414,7 @@ class code_writer (ctx:Common.context) hx_type_path php_name =
 				| [ args_expr ] ->
 					self#write "...";
 					self#write_expr args_expr
-				| _ -> fail self#pos (try assert false with Assert_failure mlpos -> mlpos)
+				| _ -> fail self#pos __POS__
 		(**
 			Writes error suppression operator (for `php.Syntax.suppress()`)
 		*)
@@ -2406,7 +2423,7 @@ class code_writer (ctx:Common.context) hx_type_path php_name =
 				| [ args_expr ] ->
 					self#write "@";
 					self#write_expr args_expr
-				| _ -> fail self#pos (try assert false with Assert_failure mlpos -> mlpos)
+				| _ -> fail self#pos __POS__
 		(**
 			Writes native array declaration (for `php.Syntax.arrayDecl()`)
 		*)
@@ -2426,7 +2443,7 @@ class code_writer (ctx:Common.context) hx_type_path php_name =
 					self#write "}(";
 					write_args self#write (fun e -> self#write_expr e) args;
 					self#write ")"
-				| _ -> fail self#pos (try assert false with Assert_failure mlpos -> mlpos)
+				| _ -> fail self#pos __POS__
 		(**
 			Writes a call to a static method (for `php.Syntax.staticCall()`)
 		*)
@@ -2439,7 +2456,7 @@ class code_writer (ctx:Common.context) hx_type_path php_name =
 					self#write "}(";
 					write_args self#write (fun e -> self#write_expr e) args;
 					self#write ")"
-				| _ -> fail self#pos (try assert false with Assert_failure mlpos -> mlpos)
+				| _ -> fail self#pos __POS__
 		(**
 			Writes field access for reading (for `php.Syntax.getField()`)
 		*)
@@ -2450,7 +2467,7 @@ class code_writer (ctx:Common.context) hx_type_path php_name =
 					self#write "->{";
 					self#write_expr field_expr;
 					self#write "}"
-				| _ -> fail self#pos (try assert false with Assert_failure mlpos -> mlpos)
+				| _ -> fail self#pos __POS__
 		(**
 			Writes field access for writing (for `php.Syntax.setField()`)
 		*)
@@ -2463,7 +2480,7 @@ class code_writer (ctx:Common.context) hx_type_path php_name =
 					self#write "}";
 					self#write " = ";
 					self#write_expr value_expr
-				| _ -> fail self#pos (try assert false with Assert_failure mlpos -> mlpos)
+				| _ -> fail self#pos __POS__
 		(**
 			Writes static field access for reading (for `php.Syntax.getStaticField()`)
 		*)
@@ -2474,7 +2491,7 @@ class code_writer (ctx:Common.context) hx_type_path php_name =
 					self#write "::${";
 					self#write_expr field_expr;
 					self#write "}"
-				| _ -> fail self#pos (try assert false with Assert_failure mlpos -> mlpos)
+				| _ -> fail self#pos __POS__
 		(**
 			Writes static field access for writing (for `php.Syntax.setField()`)
 		*)
@@ -2487,14 +2504,14 @@ class code_writer (ctx:Common.context) hx_type_path php_name =
 					self#write "}";
 					self#write " = ";
 					self#write_expr value_expr
-				| _ -> fail self#pos (try assert false with Assert_failure mlpos -> mlpos)
+				| _ -> fail self#pos __POS__
 		(**
 			Writes `new` expression with class name taken local variable (for `php.Syntax.construct()`)
 		*)
 		method 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)
+				| _ -> fail self#pos __POS__
 			in
 			self#write "new ";
 			self#write_expr class_expr;
@@ -2516,7 +2533,7 @@ class code_writer (ctx:Common.context) hx_type_path php_name =
 					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)
+				| _ -> fail self#pos __POS__
 		(**
 			Generates binary operation to output buffer (for `php.Syntax.binop()`)
 		*)
@@ -2532,7 +2549,7 @@ class code_writer (ctx:Common.context) hx_type_path php_name =
 					self#write (" " ^ operator ^ " ");
 					self#write_expr val_expr2;
 					self#write ")"
-				| _ -> fail self#pos (try assert false with Assert_failure mlpos -> mlpos)
+				| _ -> fail self#pos __POS__
 		(**
 			Writes `instanceof` expression to output buffer (for `php.Syntax.instanceof()`)
 		*)
@@ -2550,7 +2567,7 @@ class code_writer (ctx:Common.context) hx_type_path php_name =
 							if not (is_string type_expr) then self#write "->phpClassName"
 					);
 					self#write ")"
-				| _ -> fail self#pos (try assert false with Assert_failure mlpos -> mlpos)
+				| _ -> fail self#pos __POS__
 		(**
 			Writes `foreach` expression to output buffer (for `php.Syntax.foreach()`)
 		*)
@@ -2559,7 +2576,7 @@ class code_writer (ctx:Common.context) hx_type_path php_name =
 				| 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)
+						| _ -> fail self#pos __POS__
 					and add_parentheses =
 						match collection_expr.eexpr with
 							| TLocal _ -> false
@@ -2598,7 +2615,7 @@ class code_writer (ctx:Common.context) hx_type_path php_name =
 		method 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)
+				| _ -> fail self#pos __POS__
 		(**
 			Writes access to PHP class constant
 		*)
@@ -2606,7 +2623,7 @@ class code_writer (ctx:Common.context) hx_type_path php_name =
 			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)
+				| _ -> fail self#pos __POS__
 		(**
 			Writes TNew to output buffer
 		*)
@@ -2647,7 +2664,7 @@ class code_writer (ctx:Common.context) hx_type_path php_name =
 			in
 			if is_ternary then
 				match else_expr with
-					| None -> fail self#pos (try assert false with Assert_failure mlpos -> mlpos)
+					| None -> fail self#pos __POS__
 					| Some expr ->
 						self#write_expr_ternary condition if_expr expr self#pos
 			else begin
@@ -2725,6 +2742,15 @@ class code_writer (ctx:Common.context) hx_type_path php_name =
 				| _ -> self#write_expr expr
 			);
 			self#write ("->params[" ^ (string_of_int index) ^ "]")
+		(**
+			Write TEnumIndex expression to output buffer
+		*)
+		method write_expr_enum_index expr =
+			(match expr.eexpr with
+				| TConst TNull -> self#write "(null)"
+				| _ -> self#write_expr expr
+			);
+			self#write "->index"
 		(**
 			Writes argument for function declarations or calls
 		*)
@@ -2744,6 +2770,29 @@ class code_writer (ctx:Common.context) hx_type_path php_name =
 						| Some const ->
 							self#write " = ";
 							self#write_expr_const const
+		(**
+			Write an access to a field of dynamic value
+		*)
+		method private write_expr_field_dynamic expr field_name =
+			let write_direct_access () =
+				let written_as_probable_string = self#write_expr_field_if_string expr field_name in
+				if not written_as_probable_string then self#write_expr_for_field_access expr "->" field_name
+			in
+			let write_wrapped_access () =
+				self#write ((self#use boot_type_path) ^ "::dynamicField(");
+				self#write_expr expr;
+				self#write (", '" ^ field_name ^ "')");
+			in
+			let check_and_write checked_expr =
+				match (reveal_expr checked_expr).eexpr with
+					| TField (target, _) when target == expr -> write_direct_access()
+					| _ -> write_wrapped_access()
+			in
+			match self#parent_expr with
+				| Some { eexpr = TCall (callee, _) } -> check_and_write callee
+				| Some { eexpr = TUnop (op, _, target) } when is_modifying_unop op -> check_and_write target
+				| Some { eexpr = TBinop (op, left, _) } when is_assignment_binop op -> check_and_write left
+				| _ -> write_wrapped_access()
 	end
 
 (**
@@ -3044,7 +3093,7 @@ class enum_builder ctx (enm:tenum) =
 				match follow field.ef_type with
 					| TFun (args, _) -> args
 					| TEnum _ -> []
-					| _ -> fail field.ef_pos (try assert false with Assert_failure mlpos -> mlpos)
+					| _ -> fail field.ef_pos __POS__
 			in
 			writer#indent 1;
 			self#write_doc (DocMethod (args, TEnum (enm, []), field.ef_doc));
@@ -3108,7 +3157,7 @@ class enum_builder ctx (enm:tenum) =
 					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)
+						| _ -> fail field.ef_pos __POS__
 					in
 					writer#write_line ("'" ^ name ^ "' => " ^ (string_of_int count) ^ ",")
 				)
@@ -3218,7 +3267,22 @@ class class_builder ctx (cls:tclass) =
 					match iface with
 						| (i, params) -> writer#use_t (TInst (i, params))
 				in
-				let interfaces = List.map use_interface cls.cl_implements in
+				(* Do not add interfaces which are implemented through other interfaces inheritance *)
+				let unique = List.filter
+					(fun (iface, _) ->
+						not (List.exists
+							(fun (probably_descendant, _) ->
+								if probably_descendant == iface then
+									false
+								else
+									is_parent iface probably_descendant
+							)
+							cls.cl_implements
+						)
+					)
+					cls.cl_implements
+				in
+				let interfaces = List.map use_interface unique in
 				writer#write (String.concat ", " interfaces);
 			end;
 		(**
@@ -3360,7 +3424,7 @@ class class_builder ctx (cls:tclass) =
 				writer#write (field_access ^ " = ");
 				(match field.cf_expr with
 					| Some expr -> writer#write_expr expr
-					| None -> fail field.cf_pos (try assert false with Assert_failure mlpos -> mlpos)
+					| None -> fail field.cf_pos __POS__
 				);
 				writer#write ";\n"
 			in
@@ -3378,9 +3442,12 @@ class class_builder ctx (cls:tclass) =
 					writer#write ("self::$" ^ (field_name field) ^ " = ");
 					writer#write_expr expr
 				in
-				(* Do not generate fields for RTTI meta, because this generator uses another way to store it *)
+				(*
+					Do not generate fields for RTTI meta, because this generator uses another way to store it.
+					Also skip initialization for `inline var` fields as those are generated as PHP class constants.
+				*)
 				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
+				if (is_var_with_nonconstant_expr field) && (not is_auto_meta_var) && (not (is_inline_var field)) then begin
 					(match field.cf_expr with
 						| None -> ()
 						(* There can be not-inlined blocks when compiling with `-debug` *)
@@ -3408,7 +3475,7 @@ class class_builder ctx (cls:tclass) =
 		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 ->
+				| Var _ when is_physical_field 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;
@@ -3440,13 +3507,15 @@ class class_builder ctx (cls:tclass) =
 			Writes "inline var" to output buffer as constant
 		*)
 		method private write_const field =
-			writer#indent 1;
-			self#write_doc (DocVar (writer#use_t field.cf_type, field.cf_doc));
-			writer#write_indentation;
-			writer#write ("const " ^ (field_name field) ^ " = ");
 			match field.cf_expr with
-				| None -> fail writer#pos (try assert false with Assert_failure mlpos -> mlpos)
+				| None -> fail writer#pos __POS__
+				(* Do not generate a PHP constant of `inline var` field if expression is not compatible with PHP const *)
+				| Some expr when not (is_constant expr) -> ()
 				| Some expr ->
+					writer#indent 1;
+					self#write_doc (DocVar (writer#use_t field.cf_type, field.cf_doc));
+					writer#write_indentation;
+					writer#write ("const " ^ (field_name field) ^ " = ");
 					writer#write_expr expr;
 					writer#write ";\n"
 		(**
@@ -3472,7 +3541,7 @@ class class_builder ctx (cls:tclass) =
 					let name = if field.cf_name = "new" then "__construct" else (field_name field) in
 					self#write_method name fn;
 					writer#write "\n"
-				| _ -> fail field.cf_pos (try assert false with Assert_failure mlpos -> mlpos)
+				| _ -> fail field.cf_pos __POS__
 		(**
 			Writes dynamic method to output buffer.
 			Only for non-static methods. Static methods are created as static vars in `__hx__init`.
@@ -3505,7 +3574,7 @@ class class_builder ctx (cls:tclass) =
 					writer#write_line "}";
 					(* Don't forget to create a field for default value *)
 					writer#write_statement ("protected $__hx__default__" ^ (field_name field))
-				| _ -> fail field.cf_pos (try assert false with Assert_failure mlpos -> mlpos)
+				| _ -> fail field.cf_pos __POS__
 			);
 		(**
 			Writes initialization code for instances of this class
@@ -3610,7 +3679,7 @@ class generator (ctx:context) =
 				| [] -> ()
 				| _ ->
 					match boot with
-						| None -> fail dummy_pos (try assert false with Assert_failure mlpos -> mlpos)
+						| None -> fail dummy_pos __POS__
 						| Some (_, filename) ->
 							let channel = open_out_gen [Open_creat; Open_text; Open_append] 0o644 filename in
 							List.iter
@@ -3638,7 +3707,7 @@ class generator (ctx:context) =
 					output_string channel "	}\n";
 					output_string channel ");\n";
 					(match boot with
-						| None -> fail dummy_pos (try assert false with Assert_failure mlpos -> mlpos)
+						| None -> fail dummy_pos __POS__
 						| Some (builder, filename) ->
 							let boot_class = get_full_type_name (add_php_prefix ctx builder#get_type_path) in
 							output_string channel (boot_class ^ "::__hx__init();\n")

+ 37 - 80
src/generators/genpy.ml

@@ -27,7 +27,7 @@ module Utils = struct
 	let class_of_module_type mt = match mt with
 		| TClassDecl c -> c
 		| _ -> failwith ("Not a class: " ^ (s_type_path (t_infos mt).mt_path))
-	
+
 	let find_type com path =
 		try
 			List.find (fun mt -> match mt with
@@ -713,6 +713,10 @@ module Transformer = struct
 			let e1 = trans true [] e1 in
 			let p = { ae.a_expr with eexpr = TEnumParameter(e1.a_expr,ef,i)} in
 			lift true e1.a_blocks p
+		| (_, TEnumIndex e1) ->
+			let e1 = trans true [] e1 in
+			let p = { ae.a_expr with eexpr = TEnumIndex e1.a_expr } in
+			lift true e1.a_blocks p
 		| (true, TIf(econd, eif, eelse)) ->
 			(let econd1 = trans true [] econd in
 			let eif1 = trans true [] eif in
@@ -846,13 +850,6 @@ module Transformer = struct
 		| (is_value, TBinop(OpAssignOp op,{eexpr = TField(e1,FDynamic s); etype = t},e2)) ->
 			let e = dynamic_field_read_write ae.a_next_id e1 s op e2 t in
 			transform_expr ~is_value:is_value e
-		(*
-		| (is_value, TField(e1, FClosure(Some ({cl_path = [],("str")},_),cf))) ->
-
-		| (is_value, TField(e1, FClosure(Some ({cl_path = [],("list")},_),cf))) ->
-			let e = dynamic_field_read e1 cf.cf_name ae.a_expr.etype in
-			transform_expr ~is_value:is_value e
-		*)
 		| (is_value, TBinop(OpAssign, left, right))->
 			(let left = trans true [] left in
 			let right = trans true [] right in
@@ -954,7 +951,7 @@ module Transformer = struct
 			let f = exprs_to_func (new_expr.a_blocks @ [new_expr.a_expr]) (ae.a_next_id()) ae in
 			lift_expr ~is_value:true ~blocks:f.a_blocks f.a_expr
 
-		| ( _, TBreak ) | ( _, TContinue ) ->
+		| ( _, TBreak ) | ( _, TContinue ) | ( _, TIdent _) ->
 			lift_expr a_expr
 
 	and transform e =
@@ -1056,7 +1053,7 @@ module Printer = struct
 		| OpShr -> ">>"
 		| OpUShr -> ">>"
 		| OpMod -> "%"
-		| OpInterval | OpArrow | OpAssignOp _ -> assert false
+		| OpInterval | OpArrow | OpIn | OpAssignOp _ -> assert false
 
 	let print_string s =
 		Printf.sprintf "\"%s\"" (Ast.s_escape s)
@@ -1196,6 +1193,8 @@ module Printer = struct
 				handle_keywords v.v_name
 			| TEnumParameter(e1,_,index) ->
 				Printf.sprintf "%s.params[%i]" (print_expr pctx e1) index
+			| TEnumIndex e1 ->
+				Printf.sprintf "%s.index" (print_expr pctx e1)
 			| TArray(e1,e2) when (is_type1 "" "list")(e1.etype) || is_underlying_array e1.etype ->
 				print_tarray_list pctx e1 e2
 			| TArray({etype = t} as e1,e2) when is_anon_or_dynamic t ->
@@ -1214,7 +1213,7 @@ module Printer = struct
 				Printf.sprintf "%s = %s" (print_expr pctx e1) (print_expr pctx (remove_outer_parens e2))
 			| TBinop(op,e1,({eexpr = TBinop(_,_,_)} as e2)) ->
 				print_expr pctx { e with eexpr = TBinop(op, e1, { e2 with eexpr = TParenthesis(e2) })}
-			| TBinop(OpEq,{eexpr = TCall({eexpr = TLocal {v_name = "__typeof__"}},[e1])},e2) ->
+			| TBinop(OpEq,{eexpr = TCall({eexpr = TIdent "__typeof__"},[e1])},e2) ->
 				begin match e2.eexpr with
 					| TConst(TString s) ->
 						begin match s with
@@ -1264,7 +1263,9 @@ module Printer = struct
 					Printf.sprintf "(%s %s %s)" (print_expr pctx e1) (fst ops) (print_expr pctx e2)
 				| TDynamic _, TDynamic _ ->
 					Printf.sprintf "%s(%s,%s)" (third ops) (print_expr pctx e1) (print_expr pctx e2)
-				| TDynamic _, x | x, TDynamic _ when is_list_or_anon x ->
+				| TDynamic _, x when is_list_or_anon x ->
+					Printf.sprintf "%s(%s,%s)" (third ops) (print_expr pctx e1) (print_expr pctx e2)
+				| x, TDynamic _ when is_list_or_anon x ->
 					Printf.sprintf "%s(%s,%s)" (third ops) (print_expr pctx e1) (print_expr pctx e2)
 				| _,_ -> Printf.sprintf "(%s %s %s)" (print_expr pctx e1) (snd ops) (print_expr pctx e2))
 			| TBinop(OpMod,e1,e2) when (is_type1 "" "Int")(e1.etype) && (is_type1 "" "Int")(e2.etype) ->
@@ -1380,6 +1381,8 @@ module Printer = struct
 				Printf.sprintf "(%s if %s else %s)" (print_expr pctx eif) (print_expr pctx econd) (print_expr pctx eelse)
 			| TMeta(_,e1) ->
 				print_expr pctx e1
+			| TIdent s ->
+				s
 			| TSwitch _ | TCast(_, Some _) | TFor _ | TUnop(_,Postfix,_) ->
 				assert false
 
@@ -1427,14 +1430,14 @@ module Printer = struct
 				Printf.sprintf "HxString.fromCharCode"
 			| FStatic({cl_path = ["python";"internal"],"UBuiltins"},{cf_name = s}) ->
 				s
-			| FClosure (Some(c,cf),_)  when call_override(name) && ((is_type "" "list")(TClassDecl c)) ->
-				Printf.sprintf "_hx_partial(python_internal_ArrayImpl.%s, %s)" name obj
-			| FInstance (c,_,cf) when call_override(name) && ((is_type "" "list")(TClassDecl c)) ->
-				Printf.sprintf "_hx_partial(python_internal_ArrayImpl.%s, %s)" name obj
-			| FClosure (Some(c,cf),_)  when call_override(name) && ((is_type "" "str")(TClassDecl c)) ->
-				Printf.sprintf "_hx_partial(HxString.%s, %s)" name obj
-			| FInstance (c,_,cf) when call_override(name) && ((is_type "" "str")(TClassDecl c)) ->
-				Printf.sprintf "_hx_partial(HxString.%s, %s)" name obj
+			| FClosure (Some(c,cf),_) when ((is_type "" "list")(TClassDecl c)) ->
+				Printf.sprintf "python_Boot.createClosure(%s, python_internal_ArrayImpl.%s)" obj name
+			| FClosure (Some(c,cf),_) when ((is_type "" "str")(TClassDecl c)) ->
+				Printf.sprintf "python_Boot.createClosure(%s, HxString.%s)" obj name
+			| FInstance (c,_,cf) when ((is_type "" "list")(TClassDecl c)) ->
+				Printf.sprintf "python_Boot.createClosure(%s, python_internal_ArrayImpl.%s)" obj name
+			| FInstance (c,_,cf) when ((is_type "" "str")(TClassDecl c)) ->
+				Printf.sprintf "python_Boot.createClosure(%s, HxString.%s)" obj name
 			| FInstance _ | FStatic _ ->
 				do_default ()
 			| FAnon cf when is_assign && call_override(name) ->
@@ -1519,35 +1522,14 @@ module Printer = struct
 				let s_el = (print_call_args pctx e1 el) in
 				Printf.sprintf "super().__init__(%s)" s_el
 			| ("python_Syntax._pythonCode"),[({ eexpr = TConst (TString code) } as ecode); {eexpr = TArrayDecl tl}] ->
-				let exprs = Array.of_list tl in
-				let i = ref 0 in
-				let err msg =
-					let pos = { ecode.epos with pmin = ecode.epos.pmin + !i } in
-					abort msg pos
+				let buf = Buffer.create 0 in
+				let interpolate () =
+					Codegen.interpolate_code pctx.pc_com code tl (Buffer.add_string buf) (fun e -> Buffer.add_string buf (print_expr pctx e)) ecode.epos
 				in
-				let regex = Str.regexp "[{}]" in
-				let rec loop m = match m with
-					| [] -> ""
-					| Str.Text txt :: tl ->
-						i := !i + String.length txt;
-						txt ^ (loop tl)
-					| Str.Delim a :: Str.Delim b :: tl when a = b ->
-						i := !i + 2;
-						a ^ (loop tl)
-					| Str.Delim "{" :: Str.Text n :: Str.Delim "}" :: tl ->
-						(try
-						let expr = Array.get exprs (int_of_string n) in
-						let txt = print_expr pctx expr in
-						i := !i + 2 + String.length n;
-						txt ^ (loop tl)
-					with | Failure "int_of_string" ->
-						err ("Index expected. Got " ^ n)
-					| Invalid_argument _ ->
-						err ("Out-of-bounds pythonCode special parameter: " ^ n))
-					| Str.Delim x :: _ ->
-						err ("Unexpected " ^ x)
-				in
-				loop (Str.full_split regex code)
+				let old = pctx.pc_com.error in
+				pctx.pc_com.error <- abort;
+				Std.finally (fun() -> pctx.pc_com.error <- old) interpolate ();
+				Buffer.contents buf
 			| ("python_Syntax._pythonCode"), [e] ->
 				print_expr pctx e
 			| "python_Syntax._callNamedUntyped",el ->
@@ -1627,7 +1609,7 @@ module Printer = struct
 			PMap.foldi fold_dict native_fields ""
 		in
 		match e1.eexpr, el with
-			| TLocal { v_name = "`trace" }, [e;infos] ->
+			| TIdent "`trace", [e;infos] ->
 				if has_feature pctx "haxe.Log.trace" then begin
 					"haxe_Log.trace(" ^ (print_expr pctx e) ^ "," ^ (print_expr pctx infos) ^ ")"
 				end else if is_safe_string pctx e then
@@ -1784,7 +1766,7 @@ module Generator = struct
 			match cf.cf_kind with
 				| Var({v_read = AccResolve}) ->
 					()
-				| Var _ when is_extern_field cf ->
+				| Var _ when not (is_physical_field cf) ->
 					()
 				| Var({v_read = AccCall}) ->
 					if Meta.has Meta.IsVar cf.cf_meta then
@@ -1805,7 +1787,7 @@ module Generator = struct
 	let collect_class_statics_data cfl =
 		let fields = DynArray.create () in
 		List.iter (fun cf ->
-			if not (is_extern_field cf) then
+			if is_physical_field cf then
 				DynArray.add fields cf.cf_name
 		) cfl;
 		DynArray.to_list fields
@@ -1815,7 +1797,7 @@ module Generator = struct
 
 	let get_members_with_init_expr c =
 		List.filter (fun cf -> match cf.cf_kind with
-			| Var _ when is_extern_field cf -> false
+			| Var _ when not (is_physical_field cf) -> false
 			| Var _ when cf.cf_expr = None -> true
 			| _ -> false
 		) c.cl_ordered_fields
@@ -2110,7 +2092,7 @@ module Generator = struct
 					let real_fields =
 						List.filter (fun f -> match f.cf_kind with
 							| Method MethDynamic -> raise Exit (* if a class has dynamic method, we can't use __slots__ because python will complain *)
-							| Var _ -> not (is_extern_field f)
+							| Var _ -> is_physical_field f
 							| _ -> false
 						) c.cl_ordered_fields
 					in
@@ -2255,32 +2237,7 @@ module Generator = struct
 		newline ctx;
 		let mt = (t_infos (TAbstractDecl a)) in
 		let p = get_path mt in
-		let p_name = get_full_name mt in
-		print ctx "class %s:" p;
-
-		let use_pass = ref true in
-
-		if has_feature ctx "python._hx_class_name" then begin
-			use_pass := false;
-			print ctx "\n    _hx_class_name = \"%s\"" p_name
-		end;
-
-		(match a.a_impl with
-		| Some c ->
-			List.iter (fun cf ->
-				use_pass := false;
-				if cf.cf_name = "_new" then
-					gen_class_constructor ctx c cf
-				else
-					gen_class_field ctx c p cf
-			) c.cl_ordered_statics
-		| None -> ());
-
-		if !use_pass then spr ctx "\n    pass";
-
-		if has_feature ctx "python._hx_class" then print ctx "\n%s._hx_class = %s" p p;
-		if has_feature ctx "python._hx_classes" then print ctx "\n_hx_classes[\"%s\"] = %s" p_name p
-
+		print ctx "class %s: pass" p
 
 	let gen_type ctx mt = match mt with
 		| TClassDecl c -> gen_class ctx c
@@ -2295,7 +2252,7 @@ module Generator = struct
 		| TAbstractDecl {a_path = [],"Dynamic"} when not (has_feature ctx "Dynamic.*") -> ()
 		| TAbstractDecl {a_path = [],"Bool"} when not (has_feature ctx "Bool.*") -> ()
 
-		| TAbstractDecl a when Meta.has Meta.CoreType a.a_meta -> gen_abstract ctx a
+		| TAbstractDecl a when Meta.has Meta.RuntimeValue a.a_meta -> gen_abstract ctx a
 		| _ -> ()
 
 	(* Generator parts *)

+ 15 - 542
src/generators/genswf.ml

@@ -15,509 +15,16 @@
 	You should have received a copy of the GNU General Public License
 	along with this program; if not, write to the Free Software
 	Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- *)
-
+*)
 open Swf
-open As3
 open As3hl
 open Genswf9
+open ExtString
 open Type
 open Common
 open Ast
 open Globals
 
-let rec make_tpath = function
-	| HMPath (pack,name) ->
-		let pdyn = ref false in
-		let pack, name = match pack, name with
-			| [], "void" -> [], "Void"
-			| [], "int" -> [], "Int"
-			| [], "uint" -> [], "UInt"
-			| [], "Number" -> [], "Float"
-			| [], "Boolean" -> [], "Bool"
-			| [], "Object" -> ["flash";"utils"], "Object"
-			| [], "Function" -> ["flash";"utils"], "Function"
-			| [], "Class" | [],"Array" -> pdyn := true; pack, name
-			| [], "Error" -> ["flash";"errors"], "Error"
-			| [] , "XML" -> ["flash";"xml"], "XML"
-			| [] , "XMLList" -> ["flash";"xml"], "XMLList"
-			| [] , "QName" -> ["flash";"utils"], "QName"
-			| [] , "Namespace" -> ["flash";"utils"], "Namespace"
-			| [] , "RegExp" -> ["flash";"utils"], "RegExp"
-			| ["__AS3__";"vec"] , "Vector" -> ["flash"], "Vector"
-			| _ -> pack, name
-		in
-		{
-			tpackage = pack;
-			tname = name;
-			tparams = if !pdyn then [TPType (CTPath { tpackage = []; tname = "Dynamic"; tparams = []; tsub = None; },null_pos)] else[];
-			tsub = None;
-		}
-	| HMName (id,ns) ->
-		{
-			tpackage = (match ns with
-				| HNInternal (Some ns) -> ExtString.String.nsplit ns "."
-				| HNPrivate (Some ns) ->
-					(try
-						let file, line = ExtString.String.split ns ".as$" in
-						[file ^ "_" ^ line]
-					with _ ->
-						[])
-				| _ -> []);
-			tname = id;
-			tparams = [];
-			tsub = None;
-		}
-	| HMNSAny (id) ->
-		{
-			tpackage = [];
-			tname = id;
-			tparams = [];
-			tsub = None;
-		}
-	| HMMultiName _ ->
-		assert false
-	| HMRuntimeName _ ->
-		assert false
-	| HMRuntimeNameLate ->
-		assert false
-	| HMMultiNameLate _ ->
-		assert false
-	| HMAttrib _ ->
-		assert false
-	| HMAny ->
-		assert false
-	| HMParams (t,params) ->
-		let params = List.map (fun t -> TPType (CTPath (make_tpath t),null_pos)) params in
-		{ (make_tpath t) with tparams = params }
-
-let make_param cl p =
-	{ tpackage = fst cl; tname = snd cl; tparams = [TPType (CTPath { tpackage = fst p; tname = snd p; tparams = []; tsub = None },null_pos)]; tsub = None }
-
-let make_topt = function
-	| None -> { tpackage = []; tname = "Dynamic"; tparams = []; tsub = None }
-	| Some t -> make_tpath t
-
-let make_type t = CTPath (make_topt t)
-
-let make_dyn_type t =
-	match make_topt t with
-	| { tpackage = ["flash";"utils"]; tname = ("Object"|"Function") } -> make_type None
-	| o -> CTPath o
-
-let is_valid_path com pack name =
-	let rec loop = function
-		| [] ->
-			false
-		| load :: l ->
-			match load (pack,name) null_pos with
-			| None -> loop l
-			| Some (file,(_,a)) -> true
-	in
-	let file = Printf.sprintf "%s/%s.hx" (String.concat "/" pack) name in
-	loop com.load_extern_type || (try ignore(Common.find_file com file); true with Not_found -> false)
-
-let build_class com c file =
-	let path = make_tpath c.hlc_name in
-	let pos = { pfile = file ^ "@" ^ s_type_path (path.tpackage,path.tname); pmin = 0; pmax = 0 } in
-	match path with
-	| { tpackage = ["flash";"utils"]; tname = ("Object"|"Function") } ->
-		let inf = {
-			d_name = path.tname,null_pos;
-			d_doc = None;
-			d_params = [];
-			d_meta = [];
-			d_flags = [];
-			d_data = CTPath { tpackage = []; tname = "Dynamic"; tparams = []; tsub = None; },null_pos;
-		} in
-		(path.tpackage, [(ETypedef inf,pos)])
-	| _ ->
-	(* make flags *)
-	let flags = [HExtern] in
-	let flags = if c.hlc_interface then HInterface :: flags else flags in
-	let flags = (match c.hlc_super with
-		| None | Some (HMPath ([],"Object")) -> flags
-		| Some (HMPath ([],"Function")) -> flags (* found in AIR SDK *)
-		| Some s -> HExtends (make_tpath s,null_pos) :: flags
-	) in
-	let flags = List.map (fun i ->
-		let i = (match i with
-			| HMMultiName (Some id,ns) ->
-				let rec loop = function
-					| [] -> HMPath ([],id)
-					| HNPublic (Some ns) :: _ when is_valid_path com (ExtString.String.nsplit ns ".") id -> HMPath (ExtString.String.nsplit ns ".",id)
-					| _ :: l -> loop l
-				in
-				loop ns
-			| HMPath _ -> i
-			| _ -> assert false
-		) in
-		if c.hlc_interface then HExtends (make_tpath i,null_pos) else HImplements (make_tpath i,null_pos)
-	) (Array.to_list c.hlc_implements) @ flags in
-	let flags = if c.hlc_sealed || Common.defined com Define.FlashStrict then flags else HImplements (make_tpath (HMPath ([],"Dynamic")),null_pos) :: flags in
-	(* make fields *)
-	let getters = Hashtbl.create 0 in
-	let setters = Hashtbl.create 0 in
-	let override = Hashtbl.create 0 in
-	let is_xml = (match path.tpackage, path.tname with
-		| ["flash";"xml"], ("XML" | "XMLList") -> true
-		| _ -> false
-	) in
-	let make_field stat acc f =
-		let meta = ref [] in
-		let flags = (match f.hlf_name with
-			| HMPath _ -> [APublic]
-			| HMName (_,ns) ->
-				(match ns with
-				| HNPrivate _ | HNNamespace "http://www.adobe.com/2006/flex/mx/internal" -> []
-				| HNNamespace ns ->
-					if not (c.hlc_interface || is_xml) then meta := (Meta.Ns,[String ns]) :: !meta;
-					[APublic]
-				| HNExplicit _ | HNInternal _ | HNPublic _ ->
-					[APublic]
-				| HNStaticProtected _ | HNProtected _ ->
-					meta := (Meta.Protected,[]) :: !meta;
-					[APrivate])
-			| _ -> []
-		) in
-		if flags = [] then acc else
-		let flags = if stat then AStatic :: flags else flags in
-		let name = (make_tpath f.hlf_name).tname in
-		let mk_meta() =
-			List.map (fun (s,cl) -> s, List.map (fun c -> EConst c,pos) cl, pos) (!meta)
-		in
-		let cf = {
-			cff_name = name,null_pos;
-			cff_doc = None;
-			cff_pos = pos;
-			cff_meta = mk_meta();
-			cff_access = flags;
-			cff_kind = FVar (None,None);
-		} in
-		match f.hlf_kind with
-		| HFVar v ->
-			if v.hlv_const then
-				cf.cff_kind <- FProp (("default",null_pos),("never",null_pos),Some (make_type v.hlv_type,null_pos),None)
-			else
-				cf.cff_kind <- FVar (Some (make_dyn_type v.hlv_type,null_pos),None);
-			cf :: acc
-		| HFMethod m when m.hlm_override ->
-			Hashtbl.add override (name,stat) ();
-			acc
-		| HFMethod m ->
-			(match m.hlm_kind with
-			| MK3Normal ->
-				let t = m.hlm_type in
-				let p = ref 0 and pn = ref 0 in
-				let make_type = if stat || name = "new" then make_dyn_type else make_type in
-				let args = List.map (fun at ->
-					let aname = (match t.hlmt_pnames with
-						| None -> incr pn; "p" ^ string_of_int !pn
-						| Some l ->
-							match List.nth l !p with
-							| None -> incr pn; "p" ^ string_of_int !pn
-							| Some i -> i
-					) in
-					let opt_val = (match t.hlmt_dparams with
-						| None -> None
-						| Some l ->
-							try
-								Some (List.nth l (!p - List.length t.hlmt_args + List.length l))
-							with
-								_ -> None
-					) in
-					incr p;
-					let t = make_type at in
-					let is_opt = ref false in
-					let def_val = match opt_val with
-						| None -> None
-						| Some v ->
-							let v = (match v with
-							| HVNone | HVNull | HVNamespace _ | HVString _ ->
-								is_opt := true;
-								None
-							| HVBool b ->
-								Some (Ident (if b then "true" else "false"))
-							| HVInt i | HVUInt i ->
-								Some (Int (Int32.to_string i))
-							| HVFloat f ->
-								Some (Float (float_repres f))
-							) in
-							match v with
-							| None -> None
-							| Some v ->
-								(* add for --gen-hx-classes generation *)
-								meta := (Meta.DefParam,[String aname;v]) :: !meta;
-								Some (EConst v,pos)
-					in
-					((aname,null_pos),!is_opt,[],Some (t,null_pos),def_val)
-				) t.hlmt_args in
-				let args = if t.hlmt_var_args then
-					args @ List.map (fun _ -> incr pn; (("p" ^ string_of_int !pn,null_pos),true,[],Some (make_type None,null_pos),None)) [1;2;3;4;5]
-				else args in
-				let f = {
-					f_params = [];
-					f_args = args;
-					f_type = Some (make_type t.hlmt_ret,null_pos);
-					f_expr = None;
-				} in
-				cf.cff_meta <- mk_meta();
-				cf.cff_kind <- FFun f;
-				cf :: acc
-			| MK3Getter ->
-				Hashtbl.add getters (name,stat) m.hlm_type.hlmt_ret;
-				acc
-			| MK3Setter ->
-				Hashtbl.add setters (name,stat) (match m.hlm_type.hlmt_args with [t] -> t | _ -> assert false);
-				acc
-			)
-		| _ -> acc
-	in
-	let fields = if c.hlc_interface then [] else make_field false [] {
-		hlf_name = HMPath ([],"new");
-		hlf_slot = 0;
-		hlf_metas = None;
-		hlf_kind = HFMethod {
-			hlm_type = { c.hlc_construct with hlmt_ret = Some (HMPath ([],"void")) };
-			hlm_final = false;
-			hlm_override = false;
-			hlm_kind = MK3Normal
-		}
-	} in
-	let fields = Array.fold_left (make_field false) fields c.hlc_fields in
-	let fields = Array.fold_left (make_field true) fields c.hlc_static_fields in
-	let make_get_set name stat tget tset =
-		let get, set, t = (match tget, tset with
-			| None, None -> assert false
-			| Some t, None -> true, false, t
-			| None, Some t -> false, true, t
-			| Some t1, Some t2 -> true, true, (if t1 <> t2 then None else t1)
-		) in
-		let t = if name = "endian" then Some (HMPath (["flash";"utils"],"Endian")) else t in
-		let flags = [APublic] in
-		let flags = if stat then AStatic :: flags else flags in
-		{
-			cff_name = name,null_pos;
-			cff_pos = pos;
-			cff_doc = None;
-			cff_access = flags;
-			cff_meta = [];
-			cff_kind = if get && set then FVar (Some (make_dyn_type t,null_pos), None) else FProp (((if get then "default" else "never"),null_pos),((if set then "default" else "never"),null_pos),Some (make_dyn_type t,null_pos),None);
-		}
-	in
-	let fields = Hashtbl.fold (fun (name,stat) t acc ->
-		if Hashtbl.mem override (name,stat) then acc else
-		make_get_set name stat (Some t) (try Some (Hashtbl.find setters (name,stat)) with Not_found -> None) :: acc
-	) getters fields in
-	let fields = Hashtbl.fold (fun (name,stat) t acc ->
-		if Hashtbl.mem getters (name,stat) || Hashtbl.mem override (name,stat) then
-			acc
-		else
-			make_get_set name stat None (Some t) :: acc
-	) setters fields in
-	try
-		(*
-			If the class only contains static String constants, make it an enum
-		*)
-		let real_type = ref "" in
-		let rec loop = function
-			| [] -> []
-			| f :: l ->
-				match f.cff_kind with
-				| FVar (Some (CTPath { tpackage = []; tname = ("String" | "Int" | "UInt") as tname },null_pos),None)
-				| FProp (("default",_),("never",_),Some (CTPath { tpackage = []; tname = ("String" | "Int" | "UInt") as tname },null_pos),None) when List.mem AStatic f.cff_access ->
-					if !real_type = "" then real_type := tname else if !real_type <> tname then raise Exit;
-					{
-						ec_name = f.cff_name;
-						ec_pos = pos;
-						ec_args = [];
-						ec_params = [];
-						ec_meta = [];
-						ec_doc = None;
-						ec_type = None;
-					} :: loop l
-				| FFun { f_args = [] } when fst f.cff_name = "new" -> loop l
-				| _ -> raise Exit
-		in
-		List.iter (function HExtends _ | HImplements _ -> raise Exit | _ -> ()) flags;
-		let constr = loop fields in
-		let name = "fakeEnum:" ^ String.concat "." (path.tpackage @ [path.tname]) in
-		if not (Common.raw_defined com name) then raise Exit;
-		let enum_data = {
-			d_name = path.tname,null_pos;
-			d_doc = None;
-			d_params = [];
-			d_meta = [(Meta.FakeEnum,[EConst (Ident !real_type),pos],pos)];
-			d_flags = [EExtern];
-			d_data = constr;
-		} in
-		(path.tpackage, [(EEnum enum_data,pos)])
-	with Exit ->
-	let class_data = {
-		d_name = path.tname,null_pos;
-		d_doc = None;
-		d_params = [];
-		d_meta = if c.hlc_final && List.exists (fun f -> fst f.cff_name <> "new" && not (List.mem AStatic f.cff_access)) fields then [Meta.Final,[],pos] else [];
-		d_flags = flags;
-		d_data = fields;
-	} in
-	(path.tpackage, [(EClass class_data,pos)])
-
-let extract_data (_,tags) =
-	let t = Common.timer ["read";"swf"] in
-	let h = Hashtbl.create 0 in
-	let rec loop_field f =
-		match f.hlf_kind with
-		| HFClass c ->
-			let path = make_tpath f.hlf_name in
-			(match path with
-			| { tpackage = []; tname = "Float" | "Bool" | "Int" | "UInt" | "Dynamic" } -> ()
-			| { tpackage = _; tname = "MethodClosure" } -> ()
-			| _ -> Hashtbl.add h (path.tpackage,path.tname) c)
-		| _ -> ()
-	in
-	List.iter (fun t ->
-		match t.tdata with
-		| TActionScript3 (_,as3) ->
-			List.iter (fun i -> Array.iter loop_field i.hls_fields) (As3hlparse.parse as3)
-		| _ -> ()
-	) tags;
-	t();
-	h
-
-let remove_debug_infos as3 =
-	let hl = As3hlparse.parse as3 in
-	let methods = Hashtbl.create 0 in
-	let rec loop_field f =
-		{ f with hlf_kind = (match f.hlf_kind with
-			| HFMethod m -> HFMethod { m with hlm_type = loop_method m.hlm_type }
-			| HFFunction f -> HFFunction (loop_method f)
-			| HFVar v -> HFVar v
-			| HFClass c -> HFClass (loop_class c))
-		}
-	and loop_class c =
-		(* mutate in order to preserve sharing *)
-		c.hlc_construct <- loop_method c.hlc_construct;
-		c.hlc_fields <- Array.map loop_field c.hlc_fields;
-		c.hlc_static_construct <- loop_method c.hlc_static_construct;
-		c.hlc_static_fields <- Array.map loop_field c.hlc_static_fields;
-		c
-	and loop_static s =
-		{
-			hls_method = loop_method s.hls_method;
-			hls_fields = Array.map loop_field s.hls_fields;
-		}
-	and loop_method m =
-		try
-			Hashtbl.find methods m.hlmt_index
-		with Not_found ->
-			let m2 = { m with hlmt_debug_name = None; hlmt_pnames = None } in
-			Hashtbl.add methods m.hlmt_index m2;
-			m2.hlmt_function <- (match m.hlmt_function with None -> None | Some f -> Some (loop_function f));
-			m2
-	and loop_function f =
-		let cur = ref 0 in
-		let positions = MultiArray.map (fun op ->
-			let p = !cur in
-			(match op with
-			| HDebugReg _ | HDebugLine _ | HDebugFile _ | HBreakPointLine _ | HTimestamp -> ()
-			| _ -> incr cur);
-			p
-		) f.hlf_code in
-		MultiArray.add positions (!cur);
-		let code = MultiArray.create() in
-		MultiArray.iteri (fun pos op ->
-			match op with
-			| HDebugReg _ | HDebugLine _ | HDebugFile _ | HBreakPointLine _ | HTimestamp -> ()
-			| _ ->
-				let p delta =
-					MultiArray.get positions (pos + delta) - MultiArray.length code
-				in
-				let op = (match op with
-				| HJump (j,delta) -> HJump (j, p delta)
-				| HSwitch (d,deltas) -> HSwitch (p d,List.map p deltas)
-				| HFunction m -> HFunction (loop_method m)
-				| HCallStatic (m,args) -> HCallStatic (loop_method m,args)
-				| HClassDef c -> HClassDef c (* mutated *)
-				| _ -> op) in
-				MultiArray.add code op
-		) f.hlf_code;
-		f.hlf_code <- code;
-		f.hlf_trys <- Array.map (fun t ->
-			{
-				t with
-				hltc_start = MultiArray.get positions t.hltc_start;
-				hltc_end = MultiArray.get positions t.hltc_end;
-				hltc_handle = MultiArray.get positions t.hltc_handle;
-			}
-		) f.hlf_trys;
-		f
-	in
-	As3hlparse.flatten (List.map loop_static hl)
-
-let parse_swf com file =
-	let t = Common.timer ["read";"swf"] in
-	let is_swc = file_extension file = "swc" || file_extension file = "ane" in
-	let file = (try Common.find_file com file with Not_found -> failwith ((if is_swc then "SWC" else "SWF") ^ " Library not found : " ^ file)) in
-	let ch = if is_swc then begin
-		let zip = Zip.open_in file in
-		try
-			let entry = Zip.find_entry zip "library.swf" in
-			let ch = IO.input_string (Zip.read_entry zip entry) in
-			Zip.close_in zip;
-			ch
-		with _ ->
-			Zip.close_in zip;
-			failwith ("The input swc " ^ file ^ " is corrupted")
-	end else
-		IO.input_channel (open_in_bin file)
-	in
-	let h, tags = try
-		Swf.parse ch
-	with Out_of_memory ->
-		failwith ("Out of memory while parsing " ^ file)
-	| _ ->
-		failwith ("The input swf " ^ file ^ " is corrupted")
-	in
-	IO.close_in ch;
-	List.iter (fun t ->
-		match t.tdata with
-		| TActionScript3 (id,as3) when not com.debug && not com.display.DisplayMode.dms_display ->
-			t.tdata <- TActionScript3 (id,remove_debug_infos as3)
-		| _ -> ()
-	) tags;
-	t();
-	(h,tags)
-
-let add_swf_lib com file extern =
-	let swf_data = ref None in
-	let swf_classes = ref None in
-	let getSWF = (fun() ->
-		match !swf_data with
-		| None ->
-			let d = parse_swf com file in
-			swf_data := Some d;
-			d
-		| Some d -> d
-	) in
-	let extract = (fun() ->
-		match !swf_classes with
-		| None ->
-			let d = extract_data (getSWF()) in
-			swf_classes := Some d;
-			d
-		| Some d -> d
-	) in
-	let build cl p =
-		match (try Some (Hashtbl.find (extract()) cl) with Not_found -> None) with
-		| None -> None
-		| Some c -> Some (file, build_class com c file)
-	in
-	com.load_extern_type <- com.load_extern_type @ [build];
-	if not extern then com.swf_libs <- (file,getSWF,extract) :: com.swf_libs
-
-(* ------------------------------- *)
-
 let tag ?(ext=false) d = {
 	tid = 0;
 	textended = ext;
@@ -578,7 +85,7 @@ let build_dependencies t =
 		| TDynamic t2 ->
 			add_type_rec (t::l) t2;
 		| TLazy f ->
-			add_type_rec l ((!f)())
+			add_type_rec l (lazy_type f)
 		| TMono r ->
 			(match !r with
 			| None -> ()
@@ -695,40 +202,6 @@ let build_swc_catalog com types =
 	] in
 	"<?xml version=\"1.0\" encoding =\"utf-8\"?>\n" ^ Xml.to_string_fmt x
 
-let remove_classes toremove lib hcl =
-	let lib = lib() in
-	match !toremove with
-	| [] -> lib
-	| _ ->
-		let hcl = hcl() in
-		match List.filter (fun c -> Hashtbl.mem hcl c) (!toremove) with
-		| [] -> lib
-		| classes ->
-			let rec tags = function
-				| [] -> []
-				| t :: l ->
-					match t.tdata with
-					| TActionScript3 (h,data) ->
-						let data = As3hlparse.parse data in
-						let rec loop f =
-							match f.hlf_kind with
-							| HFClass _ ->
-								let path = make_tpath f.hlf_name in
-								not (List.mem (path.tpackage,path.tname) classes)
-							| _ -> true
-						in
-						let data = List.map (fun s -> { s with hls_fields = Array.of_list (List.filter loop (Array.to_list s.hls_fields)) }) data in
-						let data = List.filter (fun s -> Array.length s.hls_fields > 0) data in
-						(if data = [] then
-							tags l
-						else
-							{ t with tdata = TActionScript3 (h,As3hlparse.flatten data) } :: tags l)
-					| _ ->
-						t :: tags l
-			in
-			toremove := List.filter (fun p -> not (List.mem p classes)) !toremove;
-			fst lib, tags (snd lib)
-
 type file_format =
 	| BJPG
 	| BPNG
@@ -787,7 +260,7 @@ let build_swf9 com file swc =
 		if String.length file > 5 && String.sub file 0 5 = "data:" then
 			String.sub file 5 (String.length file - 5)
 		else
-			(try Std.input_file ~bin:true file with Invalid_argument("String.create") -> abort "File is too big (max 16MB allowed)" p | _  -> abort "File not found" p)
+			(try Std.input_file ~bin:true file with Invalid_argument _ -> abort "File is too big (max 16MB allowed)" p | _  -> abort "File not found" p)
 	in
 	let bmp = List.fold_left (fun acc t ->
 		match t with
@@ -885,11 +358,11 @@ let build_swf9 com file swc =
 						| Png.ClTrueColor (Png.TBits8,Png.HaveAlpha) ->
 							let data = Extc.unzip (Png.data png) in
 							let raw_data = Png.filter png data in
-							let alpha = String.make (h.Png.png_width * h.Png.png_height) '\000' in
-							for i = 0 to String.length alpha do
-								String.unsafe_set alpha i (String.unsafe_get raw_data (i lsl 2));
+							let alpha = Bytes.make (h.Png.png_width * h.Png.png_height) '\000' in
+							for i = 0 to Bytes.length alpha do
+								Bytes.unsafe_set alpha i (String.unsafe_get raw_data (i lsl 2));
 							done;
-							Extc.zip alpha
+							Extc.zip (Bytes.unsafe_to_string alpha)
 						| _ -> abort "PNG file must contain 8 bit alpha channel" p2
 					) in
 					incr cid;
@@ -911,9 +384,9 @@ let build_swf9 com file swc =
 						| SWAV ->
 							(try
 								let i = IO.input_string data in
-								if IO.nread i 4 <> "RIFF" then raise Exit;
+								if IO.nread_string i 4 <> "RIFF" then raise Exit;
 								ignore(IO.nread i 4); (* size *)
-								if IO.nread i 4 <> "WAVE" || IO.nread i 4 <> "fmt " then raise Exit;
+								if IO.nread_string i 4 <> "WAVE" || IO.nread_string i 4 <> "fmt " then raise Exit;
 								let chunk_size = IO.read_i32 i in
 								if not (chunk_size = 0x10 || chunk_size = 0x12 || chunk_size = 0x40) then failwith ("Unsupported chunk size " ^ string_of_int chunk_size);
 								if IO.read_ui16 i <> 1 then failwith "Not a PCM file";
@@ -924,9 +397,9 @@ let build_swf9 com file swc =
 								ignore(IO.read_i16 i);
 								let bits = IO.read_ui16 i in
 								if chunk_size <> 0x10 then ignore(IO.nread i (chunk_size - 0x10));
-								if IO.nread i 4 <> "data" then raise Exit;
+								if IO.nread_string i 4 <> "data" then raise Exit;
 								let data_size = IO.read_i32 i in
-								let data = IO.nread i data_size in
+								let data = IO.nread_string i data_size in
 								make_flags 0 (chan = 1) freq bits, (data_size * 8 / (chan * bits)), data
 							with Exit | IO.No_more_input | IO.Overflow _ ->
 								abort "Invalid WAV file" p
@@ -945,7 +418,7 @@ let build_swf9 com file swc =
 										()
 									| 0x49 ->
 										(* ID3 *)
-										if IO.nread i 2 <> "D3" then raise Exit;
+										if IO.nread_string i 2 <> "D3" then raise Exit;
 										ignore(IO.read_ui16 i); (* version *)
 										ignore(IO.read_byte i); (* flags *)
 										let size = IO.read_byte i land 0x7F in
@@ -956,7 +429,7 @@ let build_swf9 com file swc =
 										read_frame()
 									| 0x54 ->
 										(* TAG and TAG+ *)
-										if IO.nread i 3 = "AG+" then ignore(IO.nread i 223) else ignore(IO.nread i 124);
+										if IO.nread_string i 3 = "AG+" then ignore(IO.nread i 223) else ignore(IO.nread i 124);
 										read_frame()
 									| 0xFF ->
 										let infos = IO.read_byte i in
@@ -1150,7 +623,7 @@ let generate swf_header com =
 	(* merge swf libraries *)
 	let priority = ref (swf_header = None) in
 	let swf = List.fold_left (fun swf (file,lib,cl) ->
-		let swf = merge com file !priority swf (remove_classes toremove lib cl) in
+		let swf = merge com file !priority swf (SwfLoader.remove_classes toremove lib cl) in
 		priority := false;
 		swf
 	) swf com.swf_libs in

+ 46 - 29
src/generators/genswf9.ml

@@ -188,8 +188,8 @@ let rec follow_basic t =
 		| Some t -> follow_basic t
 		| _ -> t)
 	| TLazy f ->
-		follow_basic (!f())
-	| TType ({ t_path = [],"Null" },[tp]) ->
+		follow_basic (lazy_type f)
+	| TAbstract ({ a_path = [],"Null" },[tp]) ->
 		(match follow_basic tp with
 		| TMono _
 		| TFun _
@@ -228,7 +228,9 @@ let rec type_id ctx t =
 			| _ -> type_path ctx ([],"Object"))
 		| _ ->
 			type_path ctx c.cl_path)
-	| TAbstract (a,_) ->
+	| TAbstract ({ a_path = [],"Null"},_) ->
+		HMPath ([],"Object")
+	| TAbstract (a,_) when Meta.has Meta.CoreType a.a_meta ->
 		type_path ctx a.a_path
 	| TFun _ | TType ({ t_path = ["flash";"utils"],"Function" },[]) ->
 		type_path ctx ([],"Function")
@@ -882,6 +884,9 @@ let rec gen_access ctx e (forset : 'a) : 'a access =
 		write ctx (HGetProp (ident "params"));
 		write ctx (HSmallInt i);
 		VArray
+	| TEnumIndex e1 ->
+		gen_expr ctx true e1;
+		VId (ident "index")
 	| TField (e1,fa) ->
 		let f = field_name fa in
 		let id, k, closure = property ctx f e1.etype in
@@ -921,7 +926,7 @@ let rec gen_access ctx e (forset : 'a) : 'a access =
 			else
 				VCast (id,classify ctx e.etype)
 		)
-	| TArray ({ eexpr = TLocal { v_name = "__global__" } },{ eexpr = TConst (TString s) }) ->
+	| TArray ({ eexpr = TIdent "__global__" },{ eexpr = TConst (TString s) }) ->
 		let path = parse_path s in
 		let id = type_path ctx path in
 		if is_set forset then write ctx HGetGlobalScope;
@@ -1060,6 +1065,7 @@ let rec gen_expr_content ctx retval e =
 		no_value ctx retval
 	| TField _
 	| TLocal _
+	| TEnumIndex _
 	| TTypeExpr _ ->
 		getvar ctx (gen_access ctx e Read)
 	(* both accesses return dynamic so let's cast them to the real type *)
@@ -1307,6 +1313,15 @@ let rec gen_expr_content ctx retval e =
 		);
 		List.iter (fun j -> j()) jend;
 		branch());
+	| TCast (e1,Some t) when not retval ->
+		let p = e.epos in
+		let e2 = mk (TTypeExpr t) t_dynamic p in
+		let eis = mk (TIdent "__is__") t_dynamic p in
+		let ecall = mk (TCall(eis,[e1;e2])) ctx.com.basic.tbool p in
+		let enot = {ecall with eexpr = TUnop(Not,Prefix,ecall)} in
+		let exc = mk (TThrow (mk (TConst (TString "Class cast error")) ctx.com.basic.tstring p)) ctx.com.basic.tvoid p in
+		let eif = mk (TIf(enot,exc,None)) ctx.com.basic.tvoid p in
+		gen_expr ctx retval eif
 	| TCast (e1,t) ->
 		gen_expr ctx retval e1;
 		if retval then begin
@@ -1345,10 +1360,12 @@ let rec gen_expr_content ctx retval e =
 					j();
 					write ctx (HCast tid)
 		end
+	| TIdent s ->
+		abort ("Unbound variable " ^ s) e.epos
 
 and gen_call ctx retval e el r =
 	match e.eexpr , el with
-	| TLocal { v_name = "__is__" }, [e;t] ->
+	| TIdent "__is__", [e;t] ->
 		gen_expr ctx true e;
 		gen_expr ctx true t;
 		write ctx (HOp A3OIs)
@@ -1357,31 +1374,31 @@ and gen_call ctx retval e el r =
 		gen_expr ctx true e;
 		gen_expr ctx true t;
 		write ctx (HOp A3OIs)
-	| TLocal { v_name = "__as__" }, [e;t] ->
+	| TIdent "__as__", [e;t] ->
 		gen_expr ctx true e;
 		gen_expr ctx true t;
 		write ctx (HOp A3OAs)
-	| TLocal { v_name = "__int__" }, [e] ->
+	| TIdent "__int__", [e] ->
 		gen_expr ctx true e;
 		write ctx HToInt
-	| TLocal { v_name = "__float__" }, [e] ->
+	| TIdent "__float__", [e] ->
 		gen_expr ctx true e;
 		write ctx HToNumber
-	| TLocal { v_name = "__foreach__" }, [obj;counter] ->
+	| TIdent "__foreach__", [obj;counter] ->
 		gen_expr ctx true obj;
 		gen_expr ctx true counter;
 		write ctx HForEach
-	| TLocal { v_name = "__forin__" }, [obj;counter] ->
+	| TIdent "__forin__", [obj;counter] ->
 		gen_expr ctx true obj;
 		gen_expr ctx true counter;
 		write ctx HForIn
-	| TLocal { v_name = "__has_next__" }, [obj;counter] ->
+	| TIdent "__has_next__", [obj;counter] ->
 		let oreg = match gen_access ctx obj Read with VReg r -> r | _ -> abort "Must be a local variable" obj.epos in
 		let creg = match gen_access ctx counter Read with VReg r -> r | _ -> abort "Must be a local variable" obj.epos in
 		write ctx (HNext (oreg.rid,creg.rid))
-	| TLocal { v_name = "__hkeys__" }, [e2]
-	| TLocal { v_name = "__foreach__" }, [e2]
-	| TLocal { v_name = "__keys__" }, [e2] ->
+	| TIdent "__hkeys__", [e2]
+	| TIdent "__foreach__", [e2]
+	| TIdent "__keys__", [e2] ->
 		let racc = alloc_reg ctx (KType (type_path ctx ([],"Array"))) in
 		let rcounter = alloc_reg ctx KInt in
 		let rtmp = alloc_reg ctx KDynamic in
@@ -1397,9 +1414,9 @@ and gen_call ctx retval e el r =
 		write ctx (HReg rtmp.rid);
 		write ctx (HReg rcounter.rid);
 		(match e.eexpr with
-		| TLocal { v_name = "__foreach__" } ->
+		| TIdent "__foreach__" ->
 			write ctx HForEach
-		| TLocal { v_name = "__hkeys__" } ->
+		| TIdent "__hkeys__" ->
 			write ctx HForIn;
 			write ctx (HSmallInt 1);
 			write ctx (HCallProperty (as3 "substr",1));
@@ -1413,26 +1430,26 @@ and gen_call ctx retval e el r =
 		free_reg ctx rtmp;
 		free_reg ctx rcounter;
 		free_reg ctx racc;
-	| TLocal { v_name = "__new__" }, e :: el ->
+	| TIdent "__new__", e :: el ->
 		gen_expr ctx true e;
 		List.iter (gen_expr ctx true) el;
 		write ctx (HConstruct (List.length el))
-	| TLocal { v_name = "__delete__" }, [o;f] ->
+	| TIdent "__delete__", [o;f] ->
 		gen_expr ctx true o;
 		gen_expr ctx true f;
 		write ctx (HDeleteProp dynamic_prop);
-	| TLocal { v_name = "__unprotect__" }, [e] ->
+	| TIdent "__unprotect__", [e] ->
 		write ctx (HGetLex (type_path ctx (["flash"],"Boot")));
 		gen_expr ctx true e;
 		write ctx (HCallProperty (ident "__unprotect__",1));
-	| TLocal { v_name = "__typeof__" }, [e] ->
+	| TIdent "__typeof__", [e] ->
 		gen_expr ctx true e;
 		write ctx HTypeof
-	| TLocal { v_name = "__in__" }, [e; f] ->
+	| TIdent "__in__", [e; f] ->
 		gen_expr ctx true e;
 		gen_expr ctx true f;
 		write ctx (HOp A3OIn)
-	| TLocal { v_name = "__resources__" }, [] ->
+	| TIdent "__resources__", [] ->
 		let count = ref 0 in
 		Hashtbl.iter (fun name data ->
 			incr count;
@@ -1441,7 +1458,7 @@ and gen_call ctx retval e el r =
 			write ctx (HObject 1);
 		) ctx.com.resources;
 		write ctx (HArray !count)
-	| TLocal { v_name = "__vmem_set__" }, [{ eexpr = TConst (TInt code) };e1;e2] ->
+	| TIdent "__vmem_set__", [{ eexpr = TConst (TInt code) };e1;e2] ->
 		gen_expr ctx true e2;
 		gen_expr ctx true e1;
 		write ctx (HOp (match code with
@@ -1452,7 +1469,7 @@ and gen_call ctx retval e el r =
 			| 4l -> A3OMemSetDouble
 			| _ -> assert false
 		))
-	| TLocal { v_name = "__vmem_get__" }, [{ eexpr = TConst (TInt code) };e] ->
+	| TIdent "__vmem_get__", [{ eexpr = TConst (TInt code) };e] ->
 		gen_expr ctx true e;
 		write ctx (HOp (match code with
 			| 0l -> A3OMemGet8
@@ -1462,7 +1479,7 @@ and gen_call ctx retval e el r =
 			| 4l -> A3OMemGetDouble
 			| _ -> assert false
 		))
-	| TLocal { v_name = "__vmem_sign__" }, [{ eexpr = TConst (TInt code) };e] ->
+	| TIdent "__vmem_sign__", [{ eexpr = TConst (TInt code) };e] ->
 		gen_expr ctx true e;
 		write ctx (HOp (match code with
 			| 0l -> A3OSign1
@@ -1470,12 +1487,12 @@ and gen_call ctx retval e el r =
 			| 2l -> A3OSign16
 			| _ -> assert false
 		))
-	| TLocal { v_name = "__vector__" }, [ep] ->
+	| TIdent "__vector__", [ep] ->
 		gen_type ctx (type_id ctx r);
 		write ctx HGetGlobalScope;
 		gen_expr ctx true ep;
 		write ctx (HCallStack 1)
-	| TArray ({ eexpr = TLocal { v_name = "__global__" } },{ eexpr = TConst (TString s) }), _ ->
+	| TArray ({ eexpr = TIdent "__global__" },{ eexpr = TConst (TString s) }), _ ->
 		(match gen_access ctx e Read with
 		| VGlobal id ->
 			write ctx (HFindPropStrict id);
@@ -1674,7 +1691,7 @@ and gen_binop ctx retval op e1 e2 t p =
 		gen_op A3OLt
 	| OpLte ->
 		gen_op A3OLte
-	| OpInterval | OpArrow ->
+	| OpInterval | OpArrow | OpIn ->
 		assert false
 
 and gen_expr ctx retval e =
@@ -1951,7 +1968,7 @@ let generate_field_kind ctx f c stat =
 		in
 		loop f.cf_meta
 	in
-	if is_extern_field f then None else
+	if not (is_physical_field f) then None else
 	match f.cf_expr with
 	| Some { eexpr = TFunction fdata } ->
 		let rec loop c name =

+ 8 - 7
src/generators/genxml.ml

@@ -75,8 +75,8 @@ let rec follow_param t =
 		(match !r with
 		| Some t -> follow_param t
 		| _ -> t)
-	| TType ({ t_path = [],"Null" } as t,tl) ->
-		follow_param (apply_params t.t_params tl t.t_type)
+	| TAbstract ({ a_path = [],"Null" },[t]) ->
+		follow_param t
 	| _ ->
 		t
 
@@ -123,7 +123,7 @@ let rec gen_type ?(values=None) t =
 		node "f" (("a",names) :: values) (List.map gen_type (args @ [r]))
 	| TAnon a -> node "a" [] (pmap (fun f -> gen_field [] { f with cf_public = false }) a.a_fields)
 	| TDynamic t2 -> node "d" [] (if t == t2 then [] else [gen_type t2])
-	| TLazy f -> gen_type (!f())
+	| TLazy f -> gen_type (lazy_type f)
 
 and gen_type_decl n t pl =
 	let i = t_infos t in
@@ -291,6 +291,7 @@ let generate com file =
 	t();
 	let t = Common.timer ["write";"xml"] in
 	let ch = IO.output_channel (open_out_bin file) in
+	IO.printf ch "<!-- This file can be parsed by haxe.rtti.XmlParser -->\n";
 	write_xml ch "" x;
 	IO.close_out ch;
 	t()
@@ -355,8 +356,8 @@ let generate_type com t =
 			| None -> t
 			| Some t -> notnull t)
 		| TLazy f ->
-			notnull ((!f)())
-		| TType ({ t_path = [],"Null" },[t]) ->
+			notnull (lazy_type f)
+		| TAbstract ({ a_path = [],"Null" },[t]) ->
 			t
 		| _ ->
 			t
@@ -384,7 +385,7 @@ let generate_type com t =
 			let fields = PMap.fold (fun f acc -> (f.cf_name ^ " : " ^ stype f.cf_type) :: acc) a.a_fields [] in
 			"{" ^ String.concat ", " fields ^ "}"
 		| TLazy f ->
-			stype ((!f)())
+			stype (lazy_type f)
 		| TDynamic t2 ->
 			if t == t2 then "Dynamic" else "Dynamic<" ^ stype t2 ^ ">"
 		| TFun ([],ret) ->
@@ -398,7 +399,7 @@ let generate_type com t =
 			| None -> stype t
 			| Some t -> ftype t)
 		| TLazy f ->
-			ftype ((!f)())
+			ftype (lazy_type f)
 		| TFun _ ->
 			"(" ^ stype t ^ ")"
 		| _ ->

+ 36 - 38
src/generators/hl2c.ml

@@ -70,6 +70,8 @@ let keywords =
 	"thread2";"__declspec";"__finally";"__int64";"__try";"dllexport2";"__inline";"__leave";"asm";
 	(* reserved by HLC *)
 	"t";
+	(* GCC *)
+	"typeof";
 	(* C11 *)
 	"_Alignas";"_Alignof";"_Atomic";"_Bool";"_Complex";"_Generic";"_Imaginary";"_Noreturn";"_Static_assert";"_Thread_local";"_Pragma";
 	"inline";"restrict"
@@ -91,18 +93,18 @@ let s_comp = function
 let core_types =
 	let vp = { vfields = [||]; vindex = PMap.empty } in
 	let ep = { ename = ""; eid = 0; eglobal = None; efields = [||] } in
-	[HVoid;HUI8;HUI16;HI32;HF32;HF64;HBool;HBytes;HDyn;HFun ([],HVoid);HObj null_proto;HArray;HType;HRef HVoid;HVirtual vp;HDynObj;HAbstract ("",0);HEnum ep;HNull HVoid]
+	[HVoid;HUI8;HUI16;HI32;HI64;HF32;HF64;HBool;HBytes;HDyn;HFun ([],HVoid);HObj null_proto;HArray;HType;HRef HVoid;HVirtual vp;HDynObj;HAbstract ("",0);HEnum ep;HNull HVoid]
 
 let tname str =
 	let n = String.concat "__" (ExtString.String.nsplit str ".") in
 	if Hashtbl.mem keywords ("_" ^ n) then "__" ^ n else n
 
 let is_gc_ptr = function
-	| HVoid | HUI8 | HUI16 | HI32 | HF32 | HF64 | HBool | HType | HRef _ -> false
+	| HVoid | HUI8 | HUI16 | HI32 | HI64 | HF32 | HF64 | HBool | HType | HRef _ -> false
 	| HBytes | HDyn | HFun _ | HObj _ | HArray | HVirtual _ | HDynObj | HAbstract _ | HEnum _ | HNull _ -> true
 
 let is_ptr = function
-	| HVoid | HUI8 | HUI16 | HI32 | HF32 | HF64 | HBool -> false
+	| HVoid | HUI8 | HUI16 | HI32 | HI64 | HF32 | HF64 | HBool -> false
 	| _ -> true
 
 let rec ctype_no_ptr = function
@@ -110,6 +112,7 @@ let rec ctype_no_ptr = function
 	| HUI8 -> "unsigned char",0
 	| HUI16 -> "unsigned short",0
 	| HI32 -> "int",0
+	| HI64 -> "int64",0
 	| HF32 -> "float",0
 	| HF64 -> "double",0
 	| HBool -> "bool",0
@@ -149,6 +152,7 @@ let type_id t =
 	| HUI8 -> "HUI8"
 	| HUI16 -> "HUI16"
 	| HI32 -> "HI32"
+	| HI64 -> "HI64"
 	| HF32 -> "HF32"
 	| HF64 -> "HF64"
 	| HBool -> "HBOOL"
@@ -261,7 +265,7 @@ let generate_reflection ctx =
 	let funByArgs = Hashtbl.create 0 in
 	let type_kind t =
 		match t with
-		| HVoid | HF32 | HF64 -> t
+		| HVoid | HF32 | HF64 | HI64 -> t
 		| HBool | HUI8 | HUI16 | HI32 -> HI32
 		| HBytes | HDyn | HFun _ | HObj _ | HArray | HType | HRef _ | HVirtual _ | HDynObj | HAbstract _ | HEnum _ | HNull _ -> HDyn
 	in
@@ -271,7 +275,8 @@ let generate_reflection ctx =
 		| HBool | HUI8 | HUI16 | HI32 -> 1 (* same int representation *)
 		| HF32 -> 2
 		| HF64 -> 3
-		| _ -> 4
+		| HI64 -> 4
+		| _ -> 5
 	in
 	let add_fun args t =
 		let nargs = List.length args in
@@ -655,7 +660,7 @@ let generate_function ctx f =
 					sexpr "if( %s != %s && (!%s || !%s || !%s->value || !%s->value || %s->value != %s->value) ) goto %s" (reg a) (reg b) (reg a) (reg b) (reg a) (reg b) (reg a) (reg b) (label d)
 				else
 					assert false
-			| HEnum _, HEnum _ | HDynObj, HDynObj ->
+			| HEnum _, HEnum _ | HDynObj, HDynObj | HFun _, HFun _ | HAbstract _, HAbstract _ ->
 				phys_compare()
 			| HVirtual _, HObj _->
 				if op = CEq then
@@ -666,8 +671,6 @@ let generate_function ctx f =
 					assert false
 			| HObj _, HVirtual _ ->
 				compare_op op b a d
-			| HFun _, HFun _ ->
-				phys_compare()
 			| ta, tb ->
 				failwith ("Don't know how to compare " ^ tstr ta ^ " and " ^ tstr tb ^ " (hlc)")
 		in
@@ -680,7 +683,8 @@ let generate_function ctx f =
 			else
 				sexpr "%s = %ld" (reg r) code.ints.(idx)
 		| OFloat (r,idx) ->
-			sexpr "%s = %.19g" (reg r) code.floats.(idx)
+			let fstr = sprintf "%.19g" code.floats.(idx) in
+			sexpr "%s = %s" (reg r) (if String.contains fstr '.' || String.contains fstr 'e' then fstr else fstr ^ ".")
 		| OBool (r,b) ->
 			sexpr "%s = %s" (reg r) (if b then "true" else "false")
 		| OBytes (r,idx) ->
@@ -795,6 +799,10 @@ let generate_function ctx f =
 			sexpr "if( ((unsigned)%s) < ((unsigned)%s) ) goto %s" (reg a) (reg b) (label d)
 		| OJUGte (a,b,d) ->
 			sexpr "if( ((unsigned)%s) >= ((unsigned)%s) ) goto %s" (reg a) (reg b) (label d)
+		| OJNotLt (a,b,d) ->
+			sexpr "if( !(%s < %s) ) goto %s" (reg a) (reg b) (label d)
+		| OJNotGte (a,b,d) ->
+			sexpr "if( !(%s >= %s) ) goto %s" (reg a) (reg b) (label d)
 		| OJEq (a,b,d) ->
 			compare_op CEq a b d
 		| OJNotEq (a,b,d) ->
@@ -850,24 +858,16 @@ let generate_function ctx f =
 			sexpr "%s = *(unsigned char*)(%s + %s)" (reg r) (reg b) (reg idx)
 		| OGetUI16 (r,b,idx) ->
 			sexpr "%s = *(unsigned short*)(%s + %s)" (reg r) (reg b) (reg idx)
-		| OGetI32 (r,b,idx) ->
-			sexpr "%s = *(int*)(%s + %s)" (reg r) (reg b) (reg idx)
-		| OGetF32 (r,b,idx) ->
-			sexpr "%s = *(float*)(%s + %s)" (reg r) (reg b) (reg idx)
-		| OGetF64 (r,b,idx) ->
-			sexpr "%s = *(double*)(%s + %s)" (reg r) (reg b) (reg idx)
+		| OGetMem (r,b,idx) ->
+			sexpr "%s = *(%s*)(%s + %s)" (reg r) (ctype (rtype r)) (reg b) (reg idx)
 		| OGetArray (r, arr, idx) ->
 			sexpr "%s = ((%s*)(%s + 1))[%s]" (reg r) (ctype (rtype r)) (reg arr) (reg idx)
 		| OSetUI8 (b,idx,r) ->
 			sexpr "*(unsigned char*)(%s + %s) = (unsigned char)%s" (reg b) (reg idx) (reg r)
 		| OSetUI16 (b,idx,r) ->
 			sexpr "*(unsigned short*)(%s + %s) = (unsigned short)%s" (reg b) (reg idx) (reg r)
-		| OSetI32 (b,idx,r) ->
-			sexpr "*(int*)(%s + %s) = %s" (reg b) (reg idx) (reg r)
-		| OSetF32 (b,idx,r) ->
-			sexpr "*(float*)(%s + %s) = (float)%s" (reg b) (reg idx) (reg r)
-		| OSetF64 (b,idx,r) ->
-			sexpr "*(double*)(%s + %s) = %s" (reg b) (reg idx) (reg r)
+		| OSetMem (b,idx,r) ->
+			sexpr "*(%s*)(%s + %s) = %s" (ctype (rtype r)) (reg b) (reg idx) (reg r)
 		| OSetArray (arr,idx,v) ->
 			sexpr "((%s*)(%s + 1))[%s] = %s" (ctype (rtype v)) (reg arr) (reg idx) (reg v)
 		| OSafeCast (r,v) ->
@@ -904,33 +904,21 @@ let generate_function ctx f =
 			sexpr "hl_dyn_set%s((vdynamic*)%s,%ld/*%s*/%s,%s)" (dyn_prefix (rtype v)) (reg o) h code.strings.(sid) (type_value_opt (rtype v)) (reg v)
 		| OMakeEnum (r,cid,rl) ->
 			let e, et = (match rtype r with HEnum e -> e, enum_constr_type ctx e cid | _ -> assert false) in
-			let has_ptr = List.exists (fun r -> is_gc_ptr (rtype r)) rl in
 			let need_tmp = List.mem r rl in
 			let tmp = if not need_tmp then reg r else begin
 				sexpr "{ venum *tmp";
 				"tmp"
 			end in
-			sexpr "%s = (venum*)hl_gc_alloc%s(sizeof(%s))" tmp (if has_ptr then "" else "_noptr") et;
-			sexpr "%s->index = %d" tmp cid;
+			sexpr "%s = hl_alloc_enum(%s,%d)" tmp (type_value (rtype r)) cid;
 			let _,_,tl = e.efields.(cid) in
 			list_iteri (fun i v ->
 				sexpr "((%s*)%s)->p%d = %s" et tmp i (rcast v tl.(i))
 			) rl;
 			if need_tmp then sexpr "%s = tmp; }" (reg r)
 		| OEnumAlloc (r,cid) ->
-			let et, (_,_,tl) = (match rtype r with HEnum e -> enum_constr_type ctx e cid, e.efields.(cid) | _ -> assert false) in
-			let has_ptr = List.exists is_gc_ptr (Array.to_list tl) in
-			sexpr "%s = (venum*)hl_gc_alloc%s(sizeof(%s))" (reg r) (if has_ptr then "" else "_noptr") et;
-			sexpr "memset(%s,0,sizeof(%s))" (reg r) et;
-			if cid <> 0 then sexpr "%s->index = %d" (reg r) cid
+			sexpr "%s = hl_alloc_enum(%s,%d)" (reg r) (type_value (rtype r)) cid
 		| OEnumIndex (r,v) ->
-			(match rtype v with
-			| HEnum _ ->
-				sexpr "%s = %s->index" (reg r) (reg v)
-			| HDyn ->
-				sexpr "%s = ((venum*)%s->v.ptr)->index" (reg r) (reg v)
-			| _ ->
-				assert false)
+			sexpr "%s = HL__ENUM_INDEX__(%s)" (reg r) (reg v)
 		| OEnumField (r,e,cid,pid) ->
 			let tname,(_,_,tl) = (match rtype e with HEnum e -> enum_constr_type ctx e cid, e.efields.(cid) | _ -> assert false) in
 			sexpr "%s((%s*)%s)->p%d" (rassign r tl.(pid)) tname (reg e) pid
@@ -955,6 +943,16 @@ let generate_function ctx f =
 		| OEndTrap b ->
 			sexpr "hl_endtrap(trap$%d)" (!trap_depth - 1);
 			if b then decr trap_depth;
+		| OAssert _ ->
+			sexpr "hl_assert()"
+		| ORefData (r,d) ->
+			(match rtype d with
+			| HArray ->
+				sexpr "%s = (%s)hl_aptr(%s,void*)" (reg r) (ctype (rtype r)) (reg d)
+			| _ ->
+				assert false)
+		| ORefOffset (r,r2,off) ->
+			sexpr "%s = %s + %s" (reg r) (reg r2) (reg off)
 		| ONop _ ->
 			()
 	) f.code;
@@ -1054,7 +1052,7 @@ let write_c com file (code:code) =
 				if Array.length pl <> 0 then begin
 					line ("typedef struct {");
 					block ctx;
-					expr "int index";
+					line "HL__ENUM_CONSTRUCT__";
 					Array.iteri (fun i t ->
 						expr (var_type ("p" ^ string_of_int i) t)
 					) pl;
@@ -1281,7 +1279,7 @@ let write_c com file (code:code) =
 		| HEnum e ->
 			sexpr "type$%d.tenum = &enum$%d" i i;
 			(match e.eglobal with None -> () | Some g -> sexpr "enum$%d.global_value = (void**)&global$%d" i g);
-			sexpr "hl_init_enum(&type$%d)" i;
+			sexpr "hl_init_enum(&type$%d,ctx)" i;
 		| HVirtual _ ->
 			sexpr "type$%d.virt = &virt$%d" i i;
 			sexpr "hl_init_virtual(&type$%d,ctx)" i;

+ 21 - 17
src/generators/hlcode.ml

@@ -30,6 +30,7 @@ type ttype =
 	| HUI8
 	| HUI16
 	| HI32
+	| HI64
 	| HF32
 	| HF64
 	| HBool
@@ -145,6 +146,8 @@ type opcode =
 	| OJSLte of reg * reg * int
 	| OJULt of reg * reg * int
 	| OJUGte of reg * reg * int
+	| OJNotLt of reg * reg * int
+	| OJNotGte of reg * reg * int
 	| OJEq of reg * reg * int
 	| OJNotEq of reg * reg * int
 	| OJAlways of int
@@ -168,15 +171,11 @@ type opcode =
 	(* memory access *)
 	| OGetUI8 of reg * reg * reg
 	| OGetUI16 of reg * reg * reg
-	| OGetI32 of reg * reg * reg
-	| OGetF32 of reg * reg * reg
-	| OGetF64 of reg * reg * reg
+	| OGetMem of reg * reg * reg
 	| OGetArray of reg * reg * reg
 	| OSetUI8 of reg * reg * reg
 	| OSetUI16 of reg * reg * reg
-	| OSetI32 of reg * reg * reg
-	| OSetF32 of reg * reg * reg
-	| OSetF64 of reg * reg * reg
+	| OSetMem of reg * reg * reg
 	| OSetArray of reg * reg * reg
 	(* type operations *)
 	| ONew of reg
@@ -195,6 +194,9 @@ type opcode =
 	| OEnumField of reg * reg * field index * int
 	| OSetEnumField of reg * int * reg
 	(* misc *)
+	| OAssert of unused
+	| ORefData of reg * reg
+	| ORefOffset of reg * reg * reg
 	| ONop of string
 
 type fundecl = {
@@ -249,11 +251,11 @@ let list_mapi f l =
 let is_nullable t =
 	match t with
 	| HBytes | HDyn | HFun _ | HObj _ | HArray | HVirtual _ | HDynObj | HAbstract _ | HEnum _ | HNull _ | HRef _ -> true
-	| HUI8 | HUI16 | HI32 | HF32 | HF64 | HBool | HVoid | HType -> false
+	| HUI8 | HUI16 | HI32 | HI64 | HF32 | HF64 | HBool | HVoid | HType -> false
 
 
 let is_int = function
-	| HUI8 | HUI16 | HI32 -> true
+	| HUI8 | HUI16 | HI32 | HI64 -> true
 	| _ -> false
 
 let is_float = function
@@ -261,7 +263,7 @@ let is_float = function
 	| _ -> false
 
 let is_number = function
-	| HUI8 | HUI16 | HI32 | HF32 | HF64 -> true
+	| HUI8 | HUI16 | HI32 | HI64 | HF32 | HF64 -> true
 	| _ -> false
 
 (*
@@ -269,7 +271,7 @@ let is_number = function
 *)
 let is_dynamic t =
 	match t with
-	| HDyn | HFun _ | HObj _ | HArray | HVirtual _ | HDynObj | HNull _ -> true
+	| HDyn | HFun _ | HObj _ | HArray | HVirtual _ | HDynObj | HNull _ | HEnum _ -> true
 	| _ -> false
 
 let rec tsame t1 t2 =
@@ -405,7 +407,7 @@ let gather_types (code:code) =
 		| _ ->
 			()
 	in
-	List.iter (fun t -> get_type t) [HVoid; HUI8; HUI16; HI32; HF32; HF64; HBool; HType; HDyn]; (* make sure all basic types get lower indexes *)
+	List.iter (fun t -> get_type t) [HVoid; HUI8; HUI16; HI32; HI64; HF32; HF64; HBool; HType; HDyn]; (* make sure all basic types get lower indexes *)
 	Array.iter (fun g -> get_type g) code.globals;
 	Array.iter (fun (_,_,t,_) -> get_type t) code.natives;
 	Array.iter (fun f ->
@@ -430,6 +432,7 @@ let rec tstr ?(stack=[]) ?(detailed=false) t =
 	| HUI8 -> "ui8"
 	| HUI16 -> "ui16"
 	| HI32 -> "i32"
+	| HI64 -> "i64"
 	| HF32 -> "f32"
 	| HF64 -> "f64"
 	| HBool -> "bool"
@@ -513,6 +516,8 @@ let ostr fstr o =
 	| OJSLte (r,a,b) -> Printf.sprintf "jslte %d,%d,%d" r a b
 	| OJULt (a,b,i) -> Printf.sprintf "jult %d,%d,%d" a b i
 	| OJUGte (a,b,i) -> Printf.sprintf "jugte %d,%d,%d" a b i
+	| OJNotLt (a,b,i) -> Printf.sprintf "jnotlt %d,%d,%d" a b i
+	| OJNotGte (a,b,i) -> Printf.sprintf "jnotgte %d,%d,%d" a b i
 	| OJEq (a,b,i) -> Printf.sprintf "jeq %d,%d,%d" a b i
 	| OJNotEq (a,b,i) -> Printf.sprintf "jnoteq %d,%d,%d" a b i
 	| OJAlways d -> Printf.sprintf "jalways %d" d
@@ -531,15 +536,11 @@ let ostr fstr o =
 	| ORethrow r -> Printf.sprintf "rethrow %d" r
 	| OGetUI8 (r,b,p) -> Printf.sprintf "getui8 %d,%d[%d]" r b p
 	| OGetUI16 (r,b,p) -> Printf.sprintf "getui16 %d,%d[%d]" r b p
-	| OGetI32 (r,b,p) -> Printf.sprintf "geti32 %d,%d[%d]" r b p
-	| OGetF32 (r,b,p) -> Printf.sprintf "getf32 %d,%d[%d]" r b p
-	| OGetF64 (r,b,p) -> Printf.sprintf "getf64 %d,%d[%d]" r b p
+	| OGetMem (r,b,p) -> Printf.sprintf "getmem %d,%d[%d]" r b p
 	| OGetArray (r,a,i) -> Printf.sprintf "getarray %d,%d[%d]" r a i
 	| OSetUI8 (r,p,v) -> Printf.sprintf "setui8 %d,%d,%d" r p v
 	| OSetUI16 (r,p,v) -> Printf.sprintf "setui16 %d,%d,%d" r p v
-	| OSetI32 (r,p,v) -> Printf.sprintf "seti32 %d,%d,%d" r p v
-	| OSetF32 (r,p,v) -> Printf.sprintf "setf32 %d,%d,%d" r p v
-	| OSetF64 (r,p,v) -> Printf.sprintf "setf64 %d,%d,%d" r p v
+	| OSetMem (r,p,v) -> Printf.sprintf "setmem %d,%d,%d" r p v
 	| OSetArray (a,i,v) -> Printf.sprintf "setarray %d[%d],%d" a i v
 	| OSafeCast (r,v) -> Printf.sprintf "safecast %d,%d" r v
 	| OUnsafeCast (r,v) -> Printf.sprintf "unsafecast %d,%d" r v
@@ -562,6 +563,9 @@ let ostr fstr o =
 	| ONullCheck r -> Printf.sprintf "nullcheck %d" r
 	| OTrap (r,i) -> Printf.sprintf "trap %d, %d" r i
 	| OEndTrap b -> Printf.sprintf "endtrap %b" b
+	| OAssert _ -> "assert"
+	| ORefData (r,d) -> Printf.sprintf "refdata %d, %d" r d
+	| ORefOffset (r,r2,off) -> Printf.sprintf "refoffset %d, %d, %d" r r2 off
 	| ONop s -> if s = "" then "nop" else "nop " ^ s
 
 let fundecl_name f = if snd f.fpath = "" then "fun$" ^ (string_of_int f.findex) else (fst f.fpath) ^ "." ^ (snd f.fpath)

+ 187 - 109
src/generators/hlinterp.ml

@@ -25,6 +25,7 @@ open Hlcode
 type value =
 	| VNull
 	| VInt of int32
+	| VInt64 of int64
 	| VFloat of float
 	| VBool of bool
 	| VDyn of value * ttype
@@ -37,13 +38,14 @@ type value =
 	| VRef of ref_value * ttype
 	| VVirtual of vvirtual
 	| VDynObj of vdynobj
-	| VEnum of int * value array
+	| VEnum of enum_proto * int * value array
 	| VAbstract of vabstract
 	| VVarArgs of vfunction * value option
 
 and ref_value =
 	| RStack of int
 	| RValue of value ref
+	| RArray of value array * int
 
 and vabstract =
 	| AHashBytes of (string, value) Hashtbl.t
@@ -119,6 +121,7 @@ type context = {
 let default t =
 	match t with
 	| HUI8 | HUI16 | HI32 -> VInt Int32.zero
+	| HI64 -> VInt64 Int64.zero
 	| HF32 | HF64 -> VFloat 0.
 	| HBool -> VBool false
 	| _ -> if is_nullable t then VNull else VUndef
@@ -132,15 +135,17 @@ let get_type = function
 	| VClosure (f,None) -> Some (match f with FFun f -> f.ftype | FNativeFun (_,_,t) -> t)
 	| VClosure (f,Some _) -> Some (match f with FFun { ftype = HFun(_::args,ret) } | FNativeFun (_,_,HFun(_::args,ret)) -> HFun (args,ret) | _ -> assert false)
 	| VVarArgs _ -> Some (HFun ([],HDyn))
+	| VEnum (e,_,_) -> Some (HEnum e)
 	| _ -> None
 
 let v_dynamic = function
-	| VNull	| VDyn _ | VObj _ | VClosure _ | VArray _ | VVirtual _ | VDynObj _ | VVarArgs _ -> true
+	| VNull	| VDyn _ | VObj _ | VClosure _ | VArray _ | VVirtual _ | VDynObj _ | VVarArgs _ | VEnum _ -> true
 	| _ -> false
 
 let rec is_compatible v t =
 	match v, t with
 	| VInt _, (HUI8 | HUI16 | HI32) -> true
+	| VInt64 _, HI64 -> true
 	| VFloat _, (HF32 | HF64) -> true
 	| VBool _, HBool -> true
 	| _, HVoid -> true
@@ -237,13 +242,17 @@ let get_to_string ctx p =
 
 let set_i32 b p v =
 	try
-		String.set b p (char_of_int ((Int32.to_int v) land 0xFF));
-		String.set b (p+1) (char_of_int ((Int32.to_int (Int32.shift_right_logical v 8)) land 0xFF));
-		String.set b (p+2) (char_of_int ((Int32.to_int (Int32.shift_right_logical v 16)) land 0xFF));
-		String.set b (p+3) (char_of_int (Int32.to_int (Int32.shift_right_logical v 24)));
+		Bytes.set (Bytes.unsafe_of_string b) p (char_of_int ((Int32.to_int v) land 0xFF));
+		Bytes.set (Bytes.unsafe_of_string b) (p+1) (char_of_int ((Int32.to_int (Int32.shift_right_logical v 8)) land 0xFF));
+		Bytes.set (Bytes.unsafe_of_string b) (p+2) (char_of_int ((Int32.to_int (Int32.shift_right_logical v 16)) land 0xFF));
+		Bytes.set (Bytes.unsafe_of_string b) (p+3) (char_of_int (Int32.to_int (Int32.shift_right_logical v 24)));
 	with _ ->
 		error "Set outside of bytes bounds"
 
+let set_i64 b p v =
+	set_i32 b p (Int64.to_int32 v);
+	set_i32 b (p + 4) (Int64.to_int32 (Int64.shift_right_logical v 32))
+
 let get_i32 b p =
 	let i = int_of_char (String.get b p) in
 	let j = int_of_char (String.get b (p + 1)) in
@@ -251,6 +260,11 @@ let get_i32 b p =
 	let l = int_of_char (String.get b (p + 3)) in
 	Int32.logor (Int32.of_int (i lor (j lsl 8) lor (k lsl 16))) (Int32.shift_left (Int32.of_int l) 24)
 
+let get_i64 b p =
+	let low = get_i32 b p in
+	let high = get_i32 b (p + 4) in
+	Int64.logor (Int64.logand (Int64.of_int32 low) 0xFFFFFFFFL) (Int64.shift_left (Int64.of_int32 high) 32)
+
 let make_dyn v t =
 	if v = VNull || is_dynamic t then
 		v
@@ -260,11 +274,13 @@ let make_dyn v t =
 let get_ref ctx = function
 	| RStack i -> ctx.stack.(i)
 	| RValue r -> !r
+	| RArray (a,i) -> a.(i)
 
 let set_ref ctx r v =
 	match r with
 	| RStack i -> ctx.stack.(i) <- v
 	| RValue r -> r := v
+	| RArray (a,i) -> a.(i) <- v
 
 let fstr = function
 	| FFun f -> "function@" ^ string_of_int f.findex
@@ -325,6 +341,7 @@ let rec vstr_d ctx v =
 	match v with
 	| VNull -> "null"
 	| VInt i -> Int32.to_string i ^ "i"
+	| VInt64 i -> Int64.to_string i ^ "l"
 	| VFloat f -> string_of_float f ^ "f"
 	| VBool b -> if b then "true" else "false"
 	| VDyn (v,t) -> "dyn(" ^ vstr_d v ^ ":" ^ tstr t ^ ")"
@@ -344,7 +361,7 @@ let rec vstr_d ctx v =
 	| VRef (r,_) -> "ref(" ^ vstr_d (get_ref ctx r) ^ ")"
 	| VVirtual v -> "virtual(" ^ vstr_d v.vvalue ^ ")"
 	| VDynObj d -> "dynobj(" ^ String.concat "," (Hashtbl.fold (fun f i acc -> (f^":"^vstr_d d.dvalues.(i)) :: acc) d.dfields []) ^ ")"
-	| VEnum (i,vals) -> "enum#" ^ string_of_int i  ^ "(" ^ String.concat "," (Array.to_list (Array.map vstr_d vals)) ^ ")"
+	| VEnum (e,i,vals) -> let n, _, _ = e.efields.(i) in if Array.length vals = 0 then n else n ^ "(" ^ String.concat "," (Array.to_list (Array.map vstr_d vals)) ^ ")"
 	| VAbstract _ -> "abstract"
 	| VVarArgs _ -> "varargs"
 
@@ -673,6 +690,7 @@ let rec vstr ctx v t =
 	match v with
 	| VNull -> "null"
 	| VInt i -> Int32.to_string i
+	| VInt64 i -> Int64.to_string i
 	| VFloat f -> float_to_string f
 	| VBool b -> if b then "true" else "false"
 	| VDyn (v,t) ->
@@ -701,20 +719,16 @@ let rec vstr ctx v t =
 		with Not_found ->
 			"{" ^ String.concat ", " (Hashtbl.fold (fun f i acc -> (f^":"^vstr d.dvalues.(i) d.dtypes.(i)) :: acc) d.dfields []) ^ "}")
 	| VAbstract _ -> "abstract"
-	| VEnum (i,vals) ->
-		(match t with
-		| HEnum e ->
-			let n, _, pl = e.efields.(i) in
-			if Array.length pl = 0 then
-				n
-			else
-				let rec loop i =
-					if i = Array.length pl then []
-					else let v = vals.(i) in vstr v pl.(i) :: loop (i + 1)
-				in
-				n ^ "(" ^ String.concat "," (loop 0) ^ ")"
-		| _ ->
-			assert false)
+	| VEnum (e,i,vals) ->
+		let n, _, pl = e.efields.(i) in
+		if Array.length pl = 0 then
+			n
+		else
+			let rec loop i =
+				if i = Array.length pl then []
+				else let v = vals.(i) in vstr v pl.(i) :: loop (i + 1)
+			in
+			n ^ "(" ^ String.concat "," (loop 0) ^ ")"
 	| VVarArgs _ -> "varargs"
 
 let interp ctx f args =
@@ -869,13 +883,15 @@ let interp ctx f args =
 		| OJSLte (a,b,i) -> if vcompare a b (<=) then pos := !pos + i
 		| OJULt (a,b,i) -> if ucompare (get a) (get b) < 0 then pos := !pos + i
 		| OJUGte (a,b,i) -> if ucompare (get a) (get b) >= 0 then pos := !pos + i
+		| OJNotLt (a,b,i) -> if not (vcompare a b (<)) then pos := !pos + i
+		| OJNotGte (a,b,i) -> if not (vcompare a b (>=)) then pos := !pos + i
 		| OJEq (a,b,i) -> if vcompare a b (=) then pos := !pos + i
 		| OJNotEq (a,b,i) -> if not (vcompare a b (=)) then pos := !pos + i
 		| OJAlways i -> pos := !pos + i
 		| OToDyn (r,a) -> set r (make_dyn (get a) f.regs.(a))
 		| OToSFloat (r,a) -> set r (match get a with VInt v -> VFloat (Int32.to_float v) | VFloat _ as v -> v | _ -> assert false)
 		| OToUFloat (r,a) -> set r (match get a with VInt v -> VFloat (ufloat v) | VFloat _ as v -> v | _ -> assert false)
-		| OToInt (r,a) -> set r (match get a with VFloat v -> VInt (Int32.of_float v) | VInt _ as v -> v | _ -> assert false)
+		| OToInt (r,a) -> set r (match get a with VFloat v -> VInt (Int32.of_float v) | VInt i when rtype r = HI64 -> VInt64 (Int64.of_int32 i) | VInt _ as v -> v | _ -> assert false)
 		| OLabel _ -> ()
 		| ONew r ->
 			set r (alloc_obj ctx (rtype r))
@@ -982,56 +998,51 @@ let interp ctx f args =
 				let b = int_of_char (String.get b (Int32.to_int p + 1)) in
 				set r (VInt (Int32.of_int (a lor (b lsl 8))))
 			| _ -> assert false)
-		| OGetI32 (r,b,p) ->
-			(match get b, get p with
-			| VBytes b, VInt p -> set r (VInt (get_i32 b (Int32.to_int p)))
-			| _ -> assert false)
-		| OGetF32 (r,b,p) ->
-			(match get b, get p with
-			| VBytes b, VInt p -> set r (VFloat (Int32.float_of_bits (get_i32 b (Int32.to_int p))))
-			| _ -> assert false)
-		| OGetF64 (r,b,p) ->
+		| OGetMem (r,b,p) ->
 			(match get b, get p with
 			| VBytes b, VInt p ->
 				let p = Int32.to_int p in
-				let i64 = Int64.logor (Int64.logand (Int64.of_int32 (get_i32 b p)) 0xFFFFFFFFL) (Int64.shift_left (Int64.of_int32 (get_i32 b (p + 4))) 32) in
-				set r (VFloat (Int64.float_of_bits i64))
-			| _ -> assert false)
+				set r (match rtype r with
+				| HI32 -> VInt (get_i32 b p)
+				| HI64 -> VInt64 (get_i64 b p)
+				| HF32 -> VFloat (Int32.float_of_bits (get_i32 b p))
+				| HF64 -> VFloat (Int64.float_of_bits (get_i64 b p))
+				| _ -> assert false)
+			| _ ->
+				assert false)
 		| OGetArray (r,a,i) ->
 			(match get a, get i with
 			| VArray (a,_), VInt i -> set r a.(Int32.to_int i)
 			| _ -> assert false);
 		| OSetUI8 (r,p,v) ->
 			(match get r, get p, get v with
-			| VBytes b, VInt p, VInt v -> String.set b (Int32.to_int p) (char_of_int ((Int32.to_int v) land 0xFF))
+			| VBytes b, VInt p, VInt v -> Bytes.set (Bytes.unsafe_of_string b) (Int32.to_int p) (char_of_int ((Int32.to_int v) land 0xFF))
 			| _ -> assert false)
 		| OSetUI16 (r,p,v) ->
 			(match get r, get p, get v with
 			| VBytes b, VInt p, VInt v ->
-				String.set b (Int32.to_int p) (char_of_int ((Int32.to_int v) land 0xFF));
-				String.set b (Int32.to_int p + 1) (char_of_int (((Int32.to_int v) lsr 8) land 0xFF))
-			| _ -> assert false)
-		| OSetI32 (r,p,v) ->
-			(match get r, get p, get v with
-			| VBytes b, VInt p, VInt v -> set_i32 b (Int32.to_int p) v
+				Bytes.set (Bytes.unsafe_of_string b) (Int32.to_int p) (char_of_int ((Int32.to_int v) land 0xFF));
+				Bytes.set (Bytes.unsafe_of_string b) (Int32.to_int p + 1) (char_of_int (((Int32.to_int v) lsr 8) land 0xFF))
 			| _ -> assert false)
-		| OSetF32 (r,p,v) ->
-			(match get r, get p, get v with
-			| VBytes b, VInt p, VFloat v -> set_i32 b (Int32.to_int p) (Int32.bits_of_float v)
-			| _ -> assert false)
-		| OSetF64 (r,p,v) ->
-			(match get r, get p, get v with
-			| VBytes b, VInt p, VFloat v ->
+		| OSetMem (r,p,v) ->
+			(match get r, get p with
+			| VBytes b, VInt p ->
 				let p = Int32.to_int p in
-				let v64 = Int64.bits_of_float v in
-				set_i32 b p (Int64.to_int32 v64);
-				set_i32 b (p + 4) (Int64.to_int32 (Int64.shift_right_logical v64 32));
-			| _ -> assert false)
+				(match rtype v, get v with
+				| HI32, VInt v -> set_i32 b p v
+				| HI64, VInt64 v -> set_i64 b p v
+				| HF32, VFloat f -> set_i32 b p (Int32.bits_of_float f)
+				| HF64, VFloat f -> set_i64 b p (Int64.bits_of_float f)
+				| _ -> assert false)
+			| _ ->
+				assert false)
 		| OSetArray (a,i,v) ->
 			(match get a, get i with
 			| VArray (a,t), VInt i ->
 				let v = get v in
 				check v t (fun() -> "array");
+				let idx = Int32.to_int i in
+				if ctx.checked && (idx < 0 || idx >= Array.length a) then error (Printf.sprintf "Can't set array index %d with %s" idx (vstr_d ctx v));
 				a.(Int32.to_int i) <- v
 			| _ -> assert false);
 		| OSafeCast (r, v) ->
@@ -1056,21 +1067,22 @@ let interp ctx f args =
 					| HUI8 -> 1
 					| HUI16 -> 2
 					| HI32 -> 3
-					| HF32 -> 4
-					| HF64 -> 5
-					| HBool -> 6
-					| HBytes -> 7
-					| HDyn -> 8
-					| HFun _ -> 9
-					| HObj _ -> 10
-					| HArray -> 11
-					| HType -> 12
-					| HRef _ -> 13
-					| HVirtual _ -> 14
-					| HDynObj -> 15
-					| HAbstract _ -> 16
-					| HEnum _ -> 17
-					| HNull _ -> 18)))
+					| HI64 -> 4
+					| HF32 -> 5
+					| HF64 -> 6
+					| HBool -> 7
+					| HBytes -> 8
+					| HDyn -> 9
+					| HFun _ -> 10
+					| HObj _ -> 11
+					| HArray -> 12
+					| HType -> 13
+					| HRef _ -> 14
+					| HVirtual _ -> 15
+					| HDynObj -> 16
+					| HAbstract _ -> 17
+					| HEnum _ -> 18
+					| HNull _ -> 19)))
 				| _ -> assert false);
 		| ORef (r,v) ->
 			set r (VRef (RStack (v + spos),rtype v))
@@ -1092,26 +1104,26 @@ let interp ctx f args =
 		| ODynSet (o,fid,vr) ->
 			dyn_set_field ctx (get o) ctx.code.strings.(fid) (get vr) (rtype vr)
 		| OMakeEnum (r,e,pl) ->
-			set r (VEnum (e,Array.map get (Array.of_list pl)))
+			set r (VEnum ((match rtype r with HEnum e -> e | _ -> assert false),e,Array.map get (Array.of_list pl)))
 		| OEnumAlloc (r,f) ->
 			(match rtype r with
 			| HEnum e ->
 				let _, _, fl = e.efields.(f) in
 				let vl = Array.create (Array.length fl) VUndef in
-				set r (VEnum (f, vl))
+				set r (VEnum (e, f, vl))
 			| _ -> assert false
 			)
 		| OEnumIndex (r,v) ->
 			(match get v with
-			| VEnum (i,_) | VDyn (VEnum (i,_),_) -> set r (VInt (Int32.of_int i))
+			| VEnum (_,i,_) -> set r (VInt (Int32.of_int i))
 			| _ -> assert false)
 		| OEnumField (r, v, _, i) ->
 			(match get v with
-			| VEnum (_,vl) -> set r vl.(i)
+			| VEnum (_,_,vl) -> set r vl.(i)
 			| _ -> assert false)
 		| OSetEnumField (v, i, r) ->
 			(match get v, rtype v with
-			| VEnum (id,vl), HEnum e ->
+			| VEnum (_,id,vl), HEnum e ->
 				let rv = get r in
 				let _, _, fields = e.efields.(id) in
 				check rv fields.(i) (fun() -> "enumfield");
@@ -1130,6 +1142,16 @@ let interp ctx f args =
 			traps := (r,target) :: !traps
 		| OEndTrap _ ->
 			traps := List.tl !traps
+		| OAssert _ ->
+			throw_msg ctx "Assert"
+		| ORefData (r,d) ->
+			(match get d with
+			| VArray (a,t) -> set r (VRef (RArray (a,0),t))
+			| _ -> assert false)
+		| ORefOffset (r,r2,off) ->
+			(match get r2, get off with
+			| VRef (RArray (a,pos),t), VInt i -> set r (VRef (RArray (a,pos + Int32.to_int i),t))
+			| _ -> assert false)
 		| ONop _ ->
 			()
 		);
@@ -1172,6 +1194,8 @@ let call_fun ctx f args =
 			raise (InterpThrow v)
 		| Failure msg ->
 			throw_msg ctx msg
+		| Sys_exit _ as exc ->
+			raise exc
 		| e ->
 			throw_msg ctx (Printexc.to_string e)
 
@@ -1226,7 +1250,7 @@ let load_native ctx lib name t =
 		(match name with
 		| "alloc_bytes" ->
 			(function
-			| [VInt i] -> VBytes (String.create (int i))
+			| [VInt i] -> VBytes (Bytes.unsafe_to_string (Bytes.create (int i)))
 			| _ -> assert false)
 		| "alloc_array" ->
 			(function
@@ -1236,7 +1260,7 @@ let load_native ctx lib name t =
 			(function
 			| [VType t] -> alloc_obj ctx t
 			| _ -> assert false)
-		| "alloc_enum" ->
+		| "alloc_enum_dyn" ->
 			(function
 			| [VType (HEnum e); VInt idx; VArray (vl,vt); VInt len] ->
 				let idx = int idx in
@@ -1245,7 +1269,7 @@ let load_native ctx lib name t =
 				if Array.length args <> len then
 					VNull
 				else
-					VDyn (VEnum (idx,Array.mapi (fun i v -> dyn_cast ctx v vt args.(i)) (Array.sub vl 0 len)),HEnum e)
+					VEnum (e,idx,Array.mapi (fun i v -> dyn_cast ctx v vt args.(i)) (Array.sub vl 0 len))
 			| vl ->
 				assert false)
 		| "array_blit" ->
@@ -1257,7 +1281,7 @@ let load_native ctx lib name t =
 		| "bytes_blit" ->
 			(function
 			| [VBytes dst; VInt dp; VBytes src; VInt sp; VInt len] ->
-				String.blit src (int sp) dst (int dp) (int len);
+				String.blit src (int sp) (Bytes.unsafe_of_string dst) (int dp) (int len);
 				VUndef
 			| [(VBytes _ | VNull); VInt _; (VBytes _ | VNull); VInt _; VInt len] ->
 				if len = 0l then VUndef else error "bytes_blit to NULL bytes";
@@ -1595,7 +1619,7 @@ let load_native ctx lib name t =
 			| _ -> assert false)
 		| "enum_parameters" ->
 			(function
-			| [VDyn (VEnum (idx,pl),HEnum e)] ->
+			| [VEnum (e,idx,pl)] ->
 				let _,_, ptypes = e.efields.(idx) in
 				VArray (Array.mapi (fun i v -> make_dyn v ptypes.(i)) pl,HDyn)
 			| _ ->
@@ -1628,18 +1652,18 @@ let load_native ctx lib name t =
 			| _ -> assert false)
 		| "type_enum_values" ->
 			(function
-			| [VType (HEnum e as t)] ->
-				VArray (Array.mapi (fun i (_,_,args) -> if Array.length args <> 0 then VNull else VDyn (VEnum (i,[||]),t)) e.efields,HDyn)
+			| [VType (HEnum e)] ->
+				VArray (Array.mapi (fun i (_,_,args) -> if Array.length args <> 0 then VNull else VEnum (e,i,[||])) e.efields,HDyn)
 			| _ -> assert false)
 		| "type_enum_eq" ->
 			(function
-			| [VDyn (VEnum _, HEnum _); VNull] | [VNull; VDyn (VEnum _, HEnum _)] -> VBool false
+			| [VEnum _; VNull] | [VNull; VEnum _] -> VBool false
 			| [VNull; VNull] -> VBool true
-			| [VDyn (VEnum _ as v1, HEnum e1); VDyn (VEnum _ as v2, HEnum e2)] ->
+			| [VEnum (e1,_,_) as v1; VEnum (e2,_,_) as v2] ->
 				let rec loop v1 v2 e =
 					match v1, v2 with
-					| VEnum (t1,_), VEnum (t2,_) when t1 <> t2 -> false
-					| VEnum (t,vl1), VEnum (_,vl2) ->
+					| VEnum (_,t1,_), VEnum (_,t2,_) when t1 <> t2 -> false
+					| VEnum (_,t,vl1), VEnum (_,_,vl2) ->
 						let _, _, pl = e.efields.(t) in
 						let rec chk i =
 							if i = Array.length pl then true
@@ -1857,10 +1881,14 @@ let load_native ctx lib name t =
 			(function
 			| [VBytes a; VInt apos; VBytes b; VInt bpos; VInt len] -> to_int (String.compare (String.sub a (int apos) (int len)) (String.sub b (int bpos) (int len)))
 			| _ -> assert false)
+		| "string_compare" ->
+			(function
+			| [VBytes a; VBytes b; VInt len] -> to_int (String.compare (String.sub a 0 ((int len) * 2)) (String.sub b 0 ((int len)*2)))
+			| _ -> assert false)
 		| "bytes_fill" ->
 			(function
 			| [VBytes a; VInt pos; VInt len; VInt v] ->
-				String.fill a (int pos) (int len) (char_of_int ((int v) land 0xFF));
+				Bytes.fill (Bytes.unsafe_of_string a) (int pos) (int len) (char_of_int ((int v) land 0xFF));
 				VUndef
 			| _ -> assert false)
 		| "exception_stack" ->
@@ -2026,6 +2054,54 @@ let load_native ctx lib name t =
 			| [VBytes file;VInt min;VInt max] ->
 				VAbstract (APos { Globals.pfile = String.sub file 0 (String.length file - 1); pmin = Int32.to_int min; pmax = Int32.to_int max })
 			| _ -> assert false)
+		| "dyn_op" ->
+			let op_names = [|"+";"-";"*";"%";"/";"<<";">>";">>>";"&";"|";"^"|] in
+			(function
+			| [VInt op; a; b] ->
+				let op = Int32.to_int op in
+				let is_number v =
+					match v with
+					| VNull -> true
+					| VDyn (_,t) -> is_number t
+					| _ -> false
+				in
+				let error() =
+					failwith ("Can't perform dyn op " ^ vstr ctx a HDyn ^ " " ^ op_names.(op) ^ " " ^ vstr ctx b HDyn)
+				in
+				let fop op =
+					if is_number a && is_number b then begin
+						let a = dyn_cast ctx a HDyn HF64 in
+						let b = dyn_cast ctx b HDyn HF64 in
+						match a, b with
+						| VFloat a, VFloat b -> VDyn (VFloat (op a b),HF64)
+						| _ -> assert false
+					end else
+						error();
+				in
+				let iop op =
+					if is_number a && is_number b then begin
+						let a = dyn_cast ctx a HDyn HI32 in
+						let b = dyn_cast ctx b HDyn HI32 in
+						match a, b with
+						| VInt a, VInt b -> VDyn (VInt (op a b),HI32)
+						| _ -> assert false
+					end else
+						error();
+				in
+				(match op with
+				| 0 -> fop ( +. )
+				| 1 -> fop ( -. )
+				| 2 -> fop ( *. )
+				| 3 -> fop mod_float
+				| 4 -> fop ( /. )
+				| 5 -> iop (fun a b -> Int32.shift_left a (Int32.to_int b))
+				| 6 -> iop (fun a b -> Int32.shift_right a (Int32.to_int b))
+				| 7 -> iop (fun a b -> Int32.shift_right_logical a (Int32.to_int b))
+				| 8 -> iop Int32.logand
+				| 9 -> iop Int32.logor
+				| 10 -> iop Int32.logxor
+				| _ -> assert false)
+			| _ -> assert false)
 		| _ ->
 			unresolved())
 	| "macro" ->
@@ -2138,12 +2214,12 @@ let check code macros =
 		in
 		let numeric r =
 			match rtype r with
-			| HUI8 | HUI16 | HI32 | HF32 | HF64 -> ()
+			| HUI8 | HUI16 | HI32 | HI64 | HF32 | HF64 -> ()
 			| _ -> error (reg_inf r ^ " should be numeric")
 		in
 		let int r =
 			match rtype r with
-			| HUI8 | HUI16 | HI32 -> ()
+			| HUI8 | HUI16 | HI32 | HI64 -> ()
 			| _ -> error (reg_inf r ^ " should be integral")
 		in
 		let float r =
@@ -2273,7 +2349,7 @@ let check code macros =
 			| OJNull (r,delta) | OJNotNull (r,delta) ->
 				ignore(rtype r);
 				can_jump delta
-			| OJUGte (a,b,delta) | OJULt (a,b,delta) | OJSGte (a,b,delta) | OJSLt (a,b,delta) | OJSGt (a,b,delta) | OJSLte (a,b,delta) ->
+			| OJUGte (a,b,delta) | OJULt (a,b,delta) | OJSGte (a,b,delta) | OJSLt (a,b,delta) | OJSGt (a,b,delta) | OJSLte (a,b,delta) | OJNotLt (a,b,delta) | OJNotGte (a,b,delta) ->
 				if not (safe_cast (rtype a) (rtype b)) then reg b (rtype a);
 				can_jump delta
 			| OJEq (a,b,delta) | OJNotEq (a,b,delta) ->
@@ -2339,30 +2415,22 @@ let check code macros =
 				reg a HArray;
 				reg i HI32;
 				ignore(rtype v);
-			| OGetUI8 (r,b,p) | OGetI32 (r,b,p) | OGetUI16(r,b,p) ->
+			| OGetUI8 (r,b,p) | OGetUI16(r,b,p) ->
 				reg r HI32;
 				reg b HBytes;
 				reg p HI32;
-			| OGetF32 (r,b,p) ->
-				reg r HF32;
+			| OGetMem (r,b,p) ->
+				(match rtype r with HI32 | HI64 | HF32 | HF64 -> () | _ -> error (reg_inf r ^ " should be numeric"));
 				reg b HBytes;
 				reg p HI32;
-			| OGetF64 (r,b,p) ->
-				reg r HF64;
-				reg b HBytes;
-				reg p HI32;
-			| OSetUI8 (r,p,v) | OSetI32 (r,p,v) | OSetUI16 (r,p,v) ->
+			| OSetUI8 (r,p,v) | OSetUI16 (r,p,v) ->
 				reg r HBytes;
 				reg p HI32;
 				reg v HI32;
-			| OSetF32 (r,p,v) ->
-				reg r HBytes;
-				reg p HI32;
-				reg v HF32;
-			| OSetF64 (r,p,v) ->
+			| OSetMem (r,p,v) ->
 				reg r HBytes;
 				reg p HI32;
-				reg v HF64;
+				(match rtype v with HI32 | HI64 | HF32 | HF64 -> () | _ -> error (reg_inf r ^ " should be numeric"));
 			| OSetArray (a,i,v) ->
 				reg a HArray;
 				reg i HI32;
@@ -2444,6 +2512,15 @@ let check code macros =
 				can_jump idx
 			| OEndTrap _ ->
 				()
+			| OAssert _ ->
+				()
+			| ORefData (r,d) ->
+				reg d HArray;
+				(match rtype r with HRef _ -> () | _ -> reg r (HRef HDyn))
+			| ORefOffset (r,r2,off) ->
+				(match rtype r2 with HRef _ -> () | _ -> reg r2 (HRef HDyn));
+				reg r (rtype r2);
+				reg off HI32;
 			| ONop _ ->
 				()
 		) f.code
@@ -2464,6 +2541,7 @@ let check code macros =
 	Array.iter check_fun code.functions
 
 (* ------------------------------- SPEC ---------------------------------------------- *)
+(*
 
 open Hlopt
 
@@ -2781,8 +2859,10 @@ let make_spec (code:code) (f:fundecl) =
 			| OJSGte (a,b,_) -> semit (SJComp (">=",args.(a),args.(b)))
 			| OJSGt (a,b,_) -> semit (SJComp (">",args.(a),args.(b)))
 			| OJSLte (a,b,_) -> semit (SJComp ("<=",args.(a),args.(b)))
-			| OJULt (a,b,_) -> semit (SJComp ("<!",args.(a),args.(b)))
-			| OJUGte (a,b,_) -> semit (SJComp (">=!",args.(a),args.(b)))
+			| OJULt (a,b,_) -> semit (SJComp ("<U",args.(a),args.(b)))
+			| OJUGte (a,b,_) -> semit (SJComp (">=U",args.(a),args.(b)))
+			| OJNotLt (a,b,_) -> semit (SJComp ("not<",args.(a),args.(b)))
+			| OJNotGte (a,b,_) -> semit (SJComp ("not>=",args.(a),args.(b)))
 			| OJEq (a,b,_) -> semit (SJComp ("==",args.(a),args.(b)))
 			| OJNotEq (a,b,_) -> semit (SJComp ("!=",args.(a),args.(b)))
 			| OJAlways _ -> semit SJump
@@ -2805,15 +2885,11 @@ let make_spec (code:code) (f:fundecl) =
 			| OTrap _ | OEndTrap _ -> ()
 			| OGetUI8 (d,b,i) -> args.(d) <- SMem (args.(b),args.(i),HUI8)
 			| OGetUI16 (d,b,i) -> args.(d) <- SMem (args.(b),args.(i),HUI16)
-			| OGetI32 (d,b,i) -> args.(d) <- SMem (args.(b),args.(i),HI32)
-			| OGetF32 (d,b,i) -> args.(d) <- SMem (args.(b),args.(i),HF32)
-			| OGetF64 (d,b,i) -> args.(d) <- SMem (args.(b),args.(i),HF64)
+			| OGetMem (d,b,i) -> args.(d) <- SMem (args.(b),args.(i),f.regs.(d))
 			| OGetArray (d,b,i) -> args.(d) <- SMem (args.(b),args.(i),HArray)
 			| OSetUI8 (b,i,v) -> semit (SWriteMem (args.(b),args.(i),args.(v),HUI8))
 			| OSetUI16 (b,i,v) -> semit (SWriteMem (args.(b),args.(i),args.(v),HUI16))
-			| OSetI32 (b,i,v) -> semit (SWriteMem (args.(b),args.(i),args.(v),HI32))
-			| OSetF32 (b,i,v) -> semit (SWriteMem (args.(b),args.(i),args.(v),HF32))
-			| OSetF64 (b,i,v) -> semit (SWriteMem (args.(b),args.(i),args.(v),HF64))
+			| OSetMem (b,i,v) -> semit (SWriteMem (args.(b),args.(i),args.(v),f.regs.(v)))
 			| OSetArray (b,i,v) -> semit (SWriteMem (args.(b),args.(i),args.(v),HArray))
 			| ONew d ->
 				incr alloc_count;
@@ -2837,6 +2913,7 @@ let make_spec (code:code) (f:fundecl) =
 			| OEnumIndex (d,r) -> args.(d) <- SConv ("index",args.(r))
 			| OEnumField (d,r,fid,cid) -> args.(d) <- SEnumField (args.(r),fid,cid)
 			| OSetEnumField (e,fid,r) -> semit (SSetEnumField (args.(e),fid,args.(r)))
+			| OAssert _  -> ()
 			| ONop _ -> ()
 		done;
 		Hashtbl.add block_args b.bstart args
@@ -2851,3 +2928,4 @@ let make_spec (code:code) (f:fundecl) =
 	in
 	loop 0;
 	List.rev !out_spec
+*)

+ 35 - 23
src/generators/hlopt.ml

@@ -61,7 +61,7 @@ type control =
 
 let control = function
 	| OJTrue (_,d) | OJFalse (_,d) | OJNull (_,d) | OJNotNull (_,d)
-	| OJSLt (_,_,d) | OJSGte (_,_,d) | OJSGt (_,_,d) | OJSLte (_,_,d) | OJULt (_,_,d) | OJUGte (_,_,d) | OJEq (_,_,d) | OJNotEq (_,_,d) ->
+	| OJSLt (_,_,d) | OJSGte (_,_,d) | OJSGt (_,_,d) | OJSLte (_,_,d) | OJULt (_,_,d) | OJUGte (_,_,d) | OJEq (_,_,d) | OJNotEq (_,_,d) | OJNotLt (_,_,d) | OJNotGte (_,_,d) ->
 		CJCond d
 	| OJAlways d ->
 		CJAlways d
@@ -121,7 +121,7 @@ let opcode_fx frw op =
 		read a
 	| OJTrue (r,_) | OJFalse (r,_) | OJNull (r,_) | OJNotNull (r,_) ->
 		read r
-	| OJSLt (a,b,_) | OJSGte (a,b,_) | OJSGt (a,b,_) | OJSLte (a,b,_) | OJULt (a,b,_) | OJUGte (a,b,_) | OJEq (a,b,_) | OJNotEq (a,b,_) ->
+	| OJSLt (a,b,_) | OJSGte (a,b,_) | OJSGt (a,b,_) | OJSLte (a,b,_) | OJULt (a,b,_) | OJUGte (a,b,_) | OJNotLt (a,b,_) | OJNotGte (a,b,_) | OJEq (a,b,_) | OJNotEq (a,b,_) ->
 		read a; read b;
 	| OJAlways _ | OLabel _ ->
 		()
@@ -133,9 +133,9 @@ let opcode_fx frw op =
 		write r
 	| OEndTrap _ ->
 		() (* ??? *)
-	| OGetUI8 (d,a,b) | OGetUI16 (d,a,b) | OGetI32 (d,a,b) | OGetF32 (d,a,b) | OGetF64 (d,a,b) | OGetArray (d,a,b) ->
+	| OGetUI8 (d,a,b) | OGetUI16 (d,a,b) | OGetMem (d,a,b) | OGetArray (d,a,b) ->
 		read a; read b; write d
-	| OSetUI8 (a,b,c) | OSetUI16 (a,b,c) | OSetI32 (a,b,c) | OSetF32 (a,b,c) | OSetF64 (a,b,c) | OSetArray (a,b,c) ->
+	| OSetUI8 (a,b,c) | OSetUI16 (a,b,c) | OSetMem (a,b,c) | OSetArray (a,b,c) ->
 		read a; read b; read c
 	| ONew d ->
 		write d
@@ -149,7 +149,16 @@ let opcode_fx frw op =
 		write d
 	| OSetEnumField (a,_,b) ->
 		read a; read b
-	| ONop _ ->
+	| OAssert _ ->
+		()
+	| ORefData (r,d) ->
+		read d;
+		write r;
+	| ORefOffset (r,r2,off) ->
+		read r2;
+		read off;
+		write r;
+	| ONop _  ->
 		()
 
 let opcode_eq a b =
@@ -304,6 +313,10 @@ let opcode_map read write op =
 		OJULt (read a, read b, d)
 	| OJUGte (a,b,d) ->
 		OJUGte (read a, read b, d)
+	| OJNotLt (a,b,d) ->
+		OJNotLt (read a, read b, d)
+	| OJNotGte (a,b,d) ->
+		OJNotGte (read a, read b, d)
 	| OJEq (a,b,d) ->
 		OJEq (read a, read b, d)
 	| OJNotEq (a,b,d) ->
@@ -351,15 +364,9 @@ let opcode_map read write op =
 	| OGetUI16 (d,a,b) ->
 		let a = read a and b = read b in
 		OGetUI16 (write d, a, b)
-	| OGetI32 (d,a,b) ->
-		let a = read a and b = read b in
-		OGetI32 (write d, a, b)
-	| OGetF32 (d,a,b) ->
-		let a = read a and b = read b in
-		OGetF32 (write d, a, b)
-	| OGetF64 (d,a,b) ->
+	| OGetMem (d,a,b) ->
 		let a = read a and b = read b in
-		OGetF64 (write d, a, b)
+		OGetMem (write d, a, b)
 	| OGetArray (d,a,b) ->
 		let a = read a and b = read b in
 		OGetArray (write d, a, b)
@@ -369,15 +376,9 @@ let opcode_map read write op =
 	| OSetUI16 (a,b,c) ->
 		let a = read a and b = read b and c = read c in
 		OSetUI16 (a, b, c)
-	| OSetI32 (a,b,c) ->
+	| OSetMem (a,b,c) ->
 		let a = read a and b = read b and c = read c in
-		OSetI32 (a, b, c)
-	| OSetF32 (a,b,c) ->
-		let a = read a and b = read b and c = read c in
-		OSetF32 (a, b, c)
-	| OSetF64 (a,b,c) ->
-		let a = read a and b = read b and c = read c in
-		OSetF64 (a, b, c)
+		OSetMem (a, b, c)
 	| OSetArray (a,b,c) ->
 		let a = read a and b = read b and c = read c in
 		OSetArray (a, b, c)
@@ -416,6 +417,15 @@ let opcode_map read write op =
 		OMakeEnum (write d, e, rl)
 	| OSetEnumField (a,f,b) ->
 		OSetEnumField (read a, f, read b)
+	| OAssert _ ->
+		op
+	| ORefData (r,d) ->
+		let d = read d in
+		ORefData(write r,d);
+	| ORefOffset (r,r2,off) ->
+		let r2 = read r2 in
+		let off = read off in
+		ORefOffset (write r,r2,off);
 	| ONop _ ->
 		op
 
@@ -485,7 +495,7 @@ let optimize dump (f:fundecl) =
 	let set_op index op = f.code.(index) <- op in
 	let nop_count = ref 0 in
 	let set_nop index r = f.code.(index) <- (ONop r); incr nop_count in
-	let write str = match dump with None -> () | Some ch -> IO.nwrite ch (str ^ "\n") in
+	let write str = match dump with None -> () | Some ch -> IO.nwrite ch (Bytes.unsafe_of_string (str ^ "\n")) in
 
 	let blocks_pos, root = code_graph f in
 
@@ -788,7 +798,7 @@ let optimize dump (f:fundecl) =
 			| ONop _ -> ()
 			| _ ->
 				(match op with
-				| OJTrue _ | OJFalse _ | OJNull _ | OJNotNull _  | OJSLt _ | OJSGte _ | OJSGt _ | OJSLte _ | OJULt _ | OJUGte _ | OJEq _ | OJNotEq _ | OJAlways _ | OSwitch _  | OTrap _ ->
+				| OJTrue _ | OJFalse _ | OJNull _ | OJNotNull _  | OJSLt _ | OJSGte _ | OJSGt _ | OJSLte _ | OJNotLt _ | OJNotGte _ | OJULt _ | OJUGte _ | OJEq _ | OJNotEq _ | OJAlways _ | OSwitch _  | OTrap _ ->
 					jumps := i :: !jumps
 				| _ -> ());
 				let op = if reg_remap then opcode_map (fun r -> reg_map.(r)) (fun r -> reg_map.(r)) op else op in
@@ -812,6 +822,8 @@ let optimize dump (f:fundecl) =
 			| OJSGt (a,b,d) -> OJSGt (a,b,pos d)
 			| OJULt (a,b,d) -> OJULt (a,b,pos d)
 			| OJUGte (a,b,d) -> OJUGte (a,b,pos d)
+			| OJNotLt (a,b,d) -> OJNotLt (a,b,pos d)
+			| OJNotGte (a,b,d) -> OJNotGte (a,b,pos d)
 			| OJEq (a,b,d) -> OJEq (a,b,pos d)
 			| OJNotEq (a,b,d) -> OJNotEq (a,b,pos d)
 			| OJAlways d -> OJAlways (pos d)

+ 0 - 104
src/json.ml

@@ -1,104 +0,0 @@
-(*
-	The Haxe Compiler
-	Copyright (C) 2005-2017  Haxe Foundation
-
-	This program is free software; you can redistribute it and/or
-	modify it under the terms of the GNU General Public License
-	as published by the Free Software Foundation; either version 2
-	of the License, or (at your option) any later version.
-
-	This program is distributed in the hope that it will be useful,
-	but WITHOUT ANY WARRANTY; without even the implied warranty of
-	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-	GNU General Public License for more details.
-
-	You should have received a copy of the GNU General Public License
-	along with this program; if not, write to the Free Software
-	Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- *)
-
-type t =
-	| JString of string
-	| JFloat of float
-	| JInt of int
-	| JObject of (string * t) list
-	| JArray of t list
-	| JBool of bool
-	| JNull
-
-let write_iter f_el f_sep l =
-	let rec rest = function
-		| [] -> ()
-		| v :: l ->
-			f_sep();
-			f_el v;
-			rest l
-	in
-	match l with
-	| [] -> ()
-	| v :: l ->
-		f_el v;
-		rest l
-
-let write_sep w =
-	w ","
-
-let rec write_json w v =
-	match v with
-	| JNull -> write_null w
-	| JBool b -> write_bool w b
-	| JString s -> write_string w s
-	| JFloat f -> write_float w f
-	| JInt i -> write_int w i
-	| JObject o -> write_object w o
-	| JArray a -> write_array w a
-
-and write_null w =
-	w "null"
-
-and write_bool w b =
-	w (if b then "true" else "false")
-
-and write_string w s =
-	w "\"";
-	let b = Buffer.create (String.length s) in
-	for i = 0 to String.length s - 1 do
-		match String.unsafe_get s i with
-		| '"' -> Buffer.add_string b "\\\""
-		| '\t' -> Buffer.add_string b "\\t"
-		| '\r' -> Buffer.add_string b "\\r"
-		| '\b' -> Buffer.add_string b "\\b"
-		| '\n' -> Buffer.add_string b "\\n"
-		| '\012' -> Buffer.add_string b "\\f"
-		| '\\' -> Buffer.add_string b "\\\\"
-		| '\x00'..'\x1F' | '\x7F' as c -> Buffer.add_string b (Printf.sprintf "\\u%04X" (int_of_char c))
-		| c -> Buffer.add_char b c
-	done;
-	w (Buffer.contents b);
-	w "\""
-
-and write_int w i =
-	w (string_of_int i)
-
-and write_float w f =
-	match classify_float f with
-	| FP_nan | FP_infinite -> failwith "NaN and infinity floats are unsupported in JSON"
-	| _ ->
-		let s = Printf.sprintf "%.16g" f in
-		let s = if float_of_string s = f then s else Printf.sprintf "%.17g" f in
-		w s
-
-and write_array w a =
-	w "[";
-	write_iter (write_json w) (fun() -> write_sep w) a;
-	w "]"
-
-and write_object w o =
-	let write_el (k, v) =
-		write_string w k;
-		w ":";
-		write_json w v
-	in
-	w "{";
-	write_iter write_el (fun() -> write_sep w) o;
-	w "}"

+ 193 - 0
src/macro/eval/evalArray.ml

@@ -0,0 +1,193 @@
+(*
+	The Haxe Compiler
+	Copyright (C) 2005-2017  Haxe Foundation
+
+	This program is free software; you can redistribute it and/or
+	modify it under the terms of the GNU General Public License
+	as published by the Free Software Foundation; either version 2
+	of the License, or (at your option) any later version.
+
+	This program is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+
+	You should have received a copy of the GNU General Public License
+	along with this program; if not, write to the Free Software
+	Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ *)
+
+open Globals
+open EvalValue
+
+let create values = {
+	avalues = values;
+	alength = Array.length values;
+}
+
+let array_join a f sep =
+	let buf = Rope.Buffer.create 0 in
+	let last = Array.length a - 1 in
+	Array.iteri (fun i v ->
+		Rope.Buffer.add_rope buf (f v);
+		if i <> last then Rope.Buffer.add_rope buf sep;
+	) a;
+	Rope.Buffer.contents buf
+
+let to_list a = Array.to_list (Array.sub a.avalues 0 a.alength)
+
+let set_length a l =
+	a.alength <- l;
+	if a.alength > Array.length a.avalues then begin
+		let values' = Array.make (a.alength * 2) vnull in
+		Array.blit a.avalues 0 values' 0 (Array.length a.avalues);
+		a.avalues <- values'
+	end
+
+let unsafe_get a i = a.avalues.(i)
+let unsafe_set a i v = a.avalues.(i) <- v
+
+let concat a a2 =
+	let values' = Array.make (a.alength + a2.alength) vnull in
+	Array.blit a.avalues 0 values' 0 a.alength;
+	let values2 = (Obj.magic a2.avalues) in
+	Array.blit values2 0 values' a.alength a2.alength;
+	create values'
+
+let copy a =
+	create (Array.sub a.avalues 0 a.alength)
+
+let filter a f =
+	create (ExtArray.Array.filter f (Array.sub a.avalues 0 a.alength))
+
+let get a i =
+	if i < 0 || i >= a.alength then vnull
+	else Array.unsafe_get a.avalues i
+
+let rec indexOf a equals x fromIndex =
+	if fromIndex >= a.alength then -1
+	else if equals x (Array.get a.avalues fromIndex) then fromIndex
+	else indexOf a equals x (fromIndex + 1)
+
+let insert a pos x =
+	if a.alength + 1 >= Array.length a.avalues then begin
+		let values' = Array.make (Array.length a.avalues * 2 + 5) vnull in
+		Array.blit a.avalues 0 values' 0 a.alength;
+		a.avalues <- values'
+	end;
+	Array.blit a.avalues pos a.avalues (pos + 1) (a.alength - pos);
+	Array.set a.avalues pos x;
+	a.alength <- a.alength + 1
+
+let iterator a =
+	let i = ref 0 in
+	let a = Array.sub a.avalues 0 a.alength in
+	let length = Array.length a in
+	(fun () ->
+		!i < length
+	),
+	(fun () ->
+		if !i >= length then
+			vnull
+		else begin
+			let v = a.(!i) in
+			incr i;
+			v
+		end
+	)
+
+let join a f sep =
+	array_join (Array.sub a.avalues 0 a.alength) f sep
+
+let lastIndexOf a equals x fromIndex =
+	let rec loop i =
+		if i < 0 then -1
+		else if equals x (Array.get a.avalues i) then i
+		else loop (i - 1)
+	in
+	if a.alength = 0 then -1 else loop fromIndex
+
+let map a f =
+	create (Array.map f (Array.sub a.avalues 0 a.alength))
+
+let pop a =
+	if a.alength = 0 then
+		vnull
+	else begin
+		let v = get a (a.alength - 1) in
+		a.alength <- a.alength - 1;
+		v
+	end
+
+let push a v =
+	if a.alength + 1 >= Array.length a.avalues then begin
+		let values' = Array.make (Array.length a.avalues * 2 + 5) vnull in
+		Array.blit a.avalues 0 values' 0 a.alength;
+		Array.set values' a.alength v;
+		a.avalues <- values'
+	end else begin
+		Array.set a.avalues a.alength v;
+	end;
+	a.alength <- a.alength + 1;
+	a.alength
+
+let remove a equals x =
+	let i = indexOf a equals x 0 in
+	if i < 0 then
+		false
+	else begin
+		Array.blit a.avalues (i + 1) a.avalues i (a.alength - i - 1);
+		a.alength <- a.alength - 1;
+		true
+	end
+
+let reverse a =
+	a.avalues <- ExtArray.Array.rev (Array.sub a.avalues 0 a.alength)
+
+let set a i v =
+	if i >= a.alength then begin
+		if i >= Array.length a.avalues then begin
+			let values' = Array.make (i + 5) vnull in
+			Array.blit a.avalues 0 values' 0 a.alength;
+			a.avalues <- values';
+		end;
+		a.alength <- i + 1;
+	end;
+	Array.unsafe_set a.avalues i v
+
+let shift a =
+	if a.alength = 0 then
+		vnull
+	else begin
+		let v = get a 0 in
+		a.alength <- a.alength - 1;
+		Array.blit a.avalues 1 a.avalues 0 a.alength;
+		v
+	end
+
+let slice a pos end' =
+	if pos > a.alength || pos >= end' then
+		create [||]
+	else
+		create (Array.sub a.avalues pos (end' - pos))
+
+let sort a f =
+	a.avalues <- Array.sub a.avalues 0 a.alength;
+	Array.sort f a.avalues
+
+let splice a pos len end' =
+	let values' = Array.init len (fun i -> Array.get a.avalues (pos + i)) in
+	Array.blit a.avalues (pos + len) a.avalues pos (a.alength - end');
+	a.alength <- a.alength - len;
+	create values'
+
+let unshift a v =
+	if a.alength + 1 >= Array.length a.avalues then begin
+		let values' = Array.make (Array.length a.avalues * 2 + 5) vnull in
+		Array.blit a.avalues 0 values' 1 a.alength;
+		a.avalues <- values'
+	end else begin
+		Array.blit a.avalues 0 a.avalues 1 a.alength;
+	end;
+	Array.set a.avalues 0 v;
+	a.alength <- a.alength + 1

+ 369 - 0
src/macro/eval/evalContext.ml

@@ -0,0 +1,369 @@
+(*
+	The Haxe Compiler
+	Copyright (C) 2005-2017  Haxe Foundation
+
+	This program is free software; you can redistribute it and/or
+	modify it under the terms of the GNU General Public License
+	as published by the Free Software Foundation; either version 2
+	of the License, or (at your option) any later version.
+
+	This program is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+
+	You should have received a copy of the GNU General Public License
+	along with this program; if not, write to the Free Software
+	Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ *)
+
+open Globals
+open Type
+open EvalValue
+open EvalHash
+
+type var_info = string
+
+type scope = {
+	pos : pos;
+	(* The local start offset of the current scope. *)
+	local_offset : int;
+	(* The locals declared in the current scope. Maps variable IDs to local slots. *)
+	mutable locals : (int,int) Hashtbl.t;
+	(* The name of local variables. Maps local slots to variable names. Only filled in debug mode. *)
+	mutable local_infos : (int,var_info) Hashtbl.t;
+	(* The IDs of local variables. Maps variable names to variable IDs. *)
+	mutable local_ids : (string,int) Hashtbl.t;
+}
+
+type env_kind =
+	| EKLocalFunction of int
+	| EKMethod of int * int
+	| EKDelayed
+
+type env_info = {
+	static : bool;
+	pfile : int;
+	kind : env_kind;
+	capture_infos : (int,var_info) Hashtbl.t;
+}
+
+type env_debug = {
+	timer : unit -> unit;
+	mutable scopes : scope list;
+	mutable line : int;
+	mutable expr : texpr;
+}
+
+type env = {
+	env_info : env_info;
+	env_debug : env_debug;
+	mutable env_leave_pmin : int;
+	mutable env_leave_pmax : int;
+	mutable env_in_use : bool;
+	env_locals : value array;
+	env_captures : value ref array;
+}
+
+type breakpoint_state =
+	| BPEnabled
+	| BPDisabled
+	| BPHit
+
+type breakpoint_column =
+	| BPAny
+	| BPColumn of int
+
+type breakpoint = {
+	bpid : int;
+	bpfile : int;
+	bpline : int;
+	bpcolumn : breakpoint_column;
+	mutable bpstate : breakpoint_state;
+}
+
+type debug_state =
+	| DbgStart
+	| DbgRunning
+	| DbgWaiting
+	| DbgContinue
+	| DbgNext of int
+	| DbgFinish of int
+
+type builtins = {
+	mutable instance_builtins : (int * value) list IntMap.t;
+	mutable static_builtins : (int * value) list IntMap.t;
+	constructor_builtins : (int,value list -> value) Hashtbl.t;
+	empty_constructor_builtins : (int,unit -> value) Hashtbl.t;
+}
+
+type debug_socket = {
+	addr : Unix.inet_addr;
+	port : int;
+	mutable socket : Unix.file_descr option;
+}
+
+type debug = {
+	debug : bool;
+	breakpoints : (int,(int,breakpoint) Hashtbl.t) Hashtbl.t;
+	mutable support_debugger : bool;
+	mutable debug_state : debug_state;
+	mutable breakpoint : breakpoint;
+	caught_types : (int,bool) Hashtbl.t;
+	mutable environment_offset_delta : int;
+	mutable debug_socket : debug_socket option;
+}
+
+type eval = {
+	environments : env DynArray.t;
+	mutable environment_offset : int;
+}
+
+type context = {
+	ctx_id : int;
+	is_macro : bool;
+	record_stack : bool;
+	detail_times : bool;
+	builtins : builtins;
+	debug : debug;
+	mutable had_error : bool;
+	mutable curapi : value MacroApi.compiler_api;
+	mutable type_cache : Type.module_type IntMap.t;
+	overrides : (Type.path * string,bool) Hashtbl.t;
+	(* prototypes *)
+	mutable string_prototype : vprototype;
+	mutable instance_prototypes : vprototype IntMap.t;
+	mutable static_prototypes : vprototype IntMap.t;
+	mutable constructors : value Lazy.t IntMap.t;
+	get_object_prototype : 'a . context -> (int * 'a) list -> vprototype * (int * 'a) list;
+	(* eval *)
+	eval : eval;
+	mutable exception_stack : (pos * env_kind) list;
+}
+
+let get_ctx_ref : (unit -> context) ref = ref (fun() -> assert false)
+let get_ctx () = (!get_ctx_ref)()
+let select ctx = get_ctx_ref := (fun() -> ctx)
+
+(* Misc *)
+
+let get_eval ctx =
+	ctx.eval
+
+let rec kind_name ctx kind =
+	let rec loop kind env_id = match kind, env_id with
+		| EKLocalFunction i, 0 ->
+			Printf.sprintf "localFunction%i" i
+		| EKLocalFunction i, env_id ->
+			let parent_id = env_id - 1 in
+			let env = DynArray.get ctx.environments parent_id in
+			Printf.sprintf "%s.localFunction%i" (loop env.env_info.kind parent_id) i
+		| EKMethod(i1,i2),_ -> Printf.sprintf "%s.%s" (rev_hash_s i1) (rev_hash_s i2)
+		| EKDelayed,_ -> "delayed"
+	in
+	loop kind ctx.environment_offset
+
+let vstring s =
+	VString (s,lazy (Rope.to_string s))
+
+let vstring_direct (r,s) =
+	VString(r,s)
+
+let call_function f vl = match f,vl with
+	| Fun0 f,_ -> f()
+	| Fun1 f,[] -> f vnull
+	| Fun1 f,(a :: _) -> f a
+	| Fun2 f,[] -> f vnull vnull
+	| Fun2 f,[a] -> f a vnull
+	| Fun2 f,(a :: b :: _) -> f a b
+	| Fun3 f,[] -> f vnull vnull vnull
+	| Fun3 f,[a] -> f a vnull vnull
+	| Fun3 f,[a;b] -> f a b vnull
+	| Fun3 f,(a :: b :: c :: _) -> f a b c
+	| Fun4 f,[] -> f vnull vnull vnull vnull
+	| Fun4 f,[a] -> f a vnull vnull vnull
+	| Fun4 f,[a;b] -> f a b vnull vnull
+	| Fun4 f,[a;b;c] -> f a b c vnull
+	| Fun4 f,(a :: b :: c :: d :: _) -> f a b c d
+	| Fun5 f,[] -> f vnull vnull vnull vnull vnull
+	| Fun5 f,[a] -> f a vnull vnull vnull vnull
+	| Fun5 f,[a;b] -> f a b vnull vnull vnull
+	| Fun5 f,[a;b;c] -> f a b c vnull vnull
+	| Fun5 f,[a;b;c;d] -> f a b c d vnull
+	| Fun5 f,(a :: b :: c :: d :: e :: _) -> f a b c d e
+	| FunN f,_ -> f vl
+
+let object_fields o =
+	let fields = IntMap.fold (fun key vvalue acc -> (key,vvalue) :: acc) o.oextra [] in
+	IntMap.fold (fun key index acc ->
+		if IntMap.mem key o.oremoved then acc
+		else (key,(o.ofields.(index))) :: acc
+	) o.oproto.pinstance_names fields
+
+let instance_fields i =
+	IntMap.fold (fun name key acc ->
+		(name,i.ifields.(key)) :: acc
+	) i.iproto.pinstance_names []
+
+let proto_fields proto =
+	IntMap.fold (fun name key acc ->
+		(name,proto.pfields.(key)) :: acc
+	) proto.pnames []
+
+(* Exceptions *)
+
+exception RunTimeException of value * env list * pos
+
+let call_stack ctx =
+	if not ctx.record_stack then
+		[]
+	else
+		List.rev (DynArray.to_list (DynArray.sub ctx.eval.environments 0 ctx.eval.environment_offset))
+
+let throw v p =
+	let ctx = get_ctx() in
+	let eval = get_eval ctx in
+	if ctx.record_stack && eval.environment_offset > 0 then begin
+		let env = DynArray.get eval.environments (eval.environment_offset - 1) in
+		env.env_leave_pmin <- p.pmin;
+		env.env_leave_pmax <- p.pmax;
+	end;
+	raise (RunTimeException(v,call_stack ctx,p))
+
+let exc v = throw v null_pos
+
+let exc_string str = exc (vstring (Rope.of_string str))
+
+let error_message = exc_string
+
+let flush_core_context f =
+	let ctx = get_ctx() in
+	ctx.curapi.MacroApi.flush_context f
+
+(* Environment handling *)
+
+let no_timer = fun () -> ()
+let empty_array = [||]
+let no_expr = mk (TConst TNull) t_dynamic null_pos
+
+let no_debug = {
+	timer = no_timer;
+	scopes = [];
+	line = 0;
+	expr = no_expr
+}
+
+let create_env_info static pfile kind capture_infos =
+	let info = {
+		static = static;
+		kind = kind;
+		pfile = pfile;
+		capture_infos = capture_infos;
+	} in
+	info
+
+let push_environment_debug ctx info num_locals num_captures =
+	let eval = get_eval ctx in
+	let timer = if ctx.detail_times then
+		Common.timer ["macro";"execution";kind_name eval info.kind]
+	else
+		no_timer
+	in
+	let env = {
+		env_info = info;
+		env_leave_pmin = 0;
+		env_leave_pmax = 0;
+		env_in_use = false;
+		env_debug = {
+			timer = timer;
+			scopes = [];
+			line = 0;
+			expr = no_expr;
+		};
+		env_locals = Array.make num_locals vnull;
+		env_captures = Array.make num_captures (ref vnull);
+	} in
+	if eval.environment_offset = DynArray.length eval.environments then
+		DynArray.add eval.environments env
+	else
+		DynArray.unsafe_set eval.environments eval.environment_offset env;
+	eval.environment_offset <- eval.environment_offset + 1;
+	env
+
+let create_default_environment ctx info num_locals =
+	{
+		env_info = info;
+		env_leave_pmin = 0;
+		env_leave_pmax = 0;
+		env_in_use = false;
+		env_debug = no_debug;
+		env_locals = Array.make num_locals vnull;
+		env_captures = empty_array;
+	}
+
+let pop_environment_debug ctx env =
+	let eval = get_eval ctx in
+	eval.environment_offset <- eval.environment_offset - 1;
+	env.env_debug.timer();
+	()
+
+let push_environment ctx info num_locals num_captures =
+	if ctx.record_stack then
+		push_environment_debug ctx info num_locals num_captures
+	else {
+		env_info = info;
+		env_leave_pmin = 0;
+		env_leave_pmax = 0;
+		env_in_use = false;
+		env_debug = no_debug;
+		env_locals = Array.make num_locals vnull;
+		env_captures = Array.make num_captures (ref vnull);
+	}
+[@@inline]
+
+let pop_environment ctx env =
+	if ctx.record_stack then pop_environment_debug ctx env else ()
+[@@inline]
+
+(* Prototypes *)
+
+let get_static_prototype_raise ctx path =
+	IntMap.find path ctx.static_prototypes
+
+let get_static_prototype ctx path p =
+	try get_static_prototype_raise ctx path
+	with Not_found -> Error.error (Printf.sprintf "[%i] Type not found: %s" ctx.ctx_id (rev_hash_s path)) p
+
+let get_static_prototype_as_value ctx path p =
+	(get_static_prototype ctx path p).pvalue
+
+let get_instance_prototype_raise ctx path =
+	IntMap.find path ctx.instance_prototypes
+
+let get_instance_prototype ctx path p =
+	try get_instance_prototype_raise ctx path
+	with Not_found -> Error.error (Printf.sprintf "[%i] Instance prototype not found: %s" ctx.ctx_id (rev_hash_s path)) p
+
+let get_instance_constructor_raise ctx path =
+	IntMap.find path ctx.constructors
+
+let get_instance_constructor ctx path p =
+	try get_instance_constructor_raise ctx path
+	with Not_found -> Error.error (Printf.sprintf "[%i] Instance constructor not found: %s" ctx.ctx_id (rev_hash_s path)) p
+
+let get_special_instance_constructor_raise ctx path =
+	Hashtbl.find (get_ctx()).builtins.constructor_builtins path
+
+let get_proto_field_index_raise proto name =
+	IntMap.find name proto.pnames
+
+let get_proto_field_index proto name =
+	try get_proto_field_index_raise proto name
+	with Not_found -> Error.error (Printf.sprintf "Field index for %s not found on prototype %s" (rev_hash_s name) (rev_hash_s proto.ppath)) null_pos
+
+let get_instance_field_index_raise proto name =
+	IntMap.find name proto.pinstance_names
+
+let get_instance_field_index proto name p =
+	try get_instance_field_index_raise proto name
+	with Not_found -> Error.error (Printf.sprintf "Field index for %s not found on prototype %s" (rev_hash_s name) (rev_hash_s proto.ppath)) p

+ 99 - 0
src/macro/eval/evalDebug.ml

@@ -0,0 +1,99 @@
+open Gc
+open Globals
+open Ast
+open Type
+open EvalJitContext
+open EvalContext
+open EvalValue
+open EvalExceptions
+open EvalPrinting
+open EvalHash
+open EvalEncode
+open EvalMisc
+open EvalDebugMisc
+open MacroApi
+
+let is_caught ctx v =
+	try
+		Hashtbl.iter (fun path _ -> if is v path then raise Exit) ctx.debug.caught_types;
+		false
+	with Exit ->
+		true
+
+(* Checks debug state and calls what's needed. *)
+let rec run_loop ctx wait run env : value =
+	let check_breakpoint () =
+		if ctx.debug.breakpoint.bpstate = BPHit && env.env_debug.line <> ctx.debug.breakpoint.bpline then ctx.debug.breakpoint.bpstate <- BPEnabled
+	in
+	match ctx.debug.debug_state with
+		| DbgRunning ->
+			check_breakpoint();
+			run env
+		| DbgContinue ->
+			check_breakpoint();
+			run env
+		| DbgNext offset ->
+			if offset < (get_eval ctx).environment_offset then
+				run env
+			else begin
+				ctx.debug.debug_state <- DbgWaiting;
+				run_loop ctx wait run env
+			end
+		| DbgFinish offset ->
+			if offset <= (get_eval ctx).environment_offset then
+				run env
+			else begin
+				ctx.debug.debug_state <- DbgWaiting;
+				run_loop ctx wait run env
+			end
+		| DbgWaiting | DbgStart ->
+			wait ctx run env
+
+let debug_loop jit e f =
+	let ctx = jit.ctx in
+	let scopes = jit.scopes in
+	let line,col1,_,_ = Lexer.get_pos_coords e.epos in
+	let column_matches breakpoint = match breakpoint.bpcolumn with
+		| BPAny -> true
+		| BPColumn i -> i = col1
+	in
+	let conn = match ctx.debug.debug_socket with
+		| Some socket -> EvalDebugSocket.make_connection socket
+		| None -> EvalDebugCLI.connection
+	in
+	(* Checks if we hit a breakpoint, runs the code if not. *)
+	let rec run_check_breakpoint env =
+		try
+			let h = Hashtbl.find ctx.debug.breakpoints env.env_info.pfile in
+			let breakpoint = Hashtbl.find h env.env_debug.line in
+			begin match breakpoint.bpstate with
+				| BPEnabled when column_matches breakpoint ->
+					breakpoint.bpstate <- BPHit;
+					ctx.debug.breakpoint <- breakpoint;
+					conn.bp_stop ctx env;
+					ctx.debug.debug_state <- DbgWaiting;
+					run_loop ctx conn.wait run_check_breakpoint env
+				| _ ->
+					raise Not_found
+			end
+		with Not_found -> try
+			f env
+		with
+		| RunTimeException(v,_,_) when not (is_caught ctx v) ->
+			conn.exc_stop ctx v e.epos;
+			ctx.debug.debug_state <- DbgWaiting;
+			run_loop ctx conn.wait run_check_breakpoint env
+		| BreakHere ->
+			conn.bp_stop ctx env;
+			ctx.debug.debug_state <- DbgWaiting;
+			run_loop ctx conn.wait run_check_breakpoint env
+
+	in
+	(* Sets the environmental debug data, then executes the debug loop. *)
+	let run_set env =
+		env.env_debug.scopes <- scopes;
+		env.env_debug.line <- line;
+		env.env_debug.expr <- e;
+		run_loop ctx conn.wait run_check_breakpoint env;
+	in
+	run_set

+ 389 - 0
src/macro/eval/evalDebugCLI.ml

@@ -0,0 +1,389 @@
+open Gc
+open Ast
+open Type
+open Globals
+open MacroApi
+open EvalContext
+open EvalValue
+open EvalExceptions
+open EvalHash
+open EvalPrinting
+open EvalMisc
+open EvalDebugMisc
+
+let get_call_stack_envs ctx kind p =
+	let envs = match call_stack ctx with
+		| _ :: envs -> envs
+		| [] -> []
+	in
+	let rec loop delta envs = match envs with
+		| _ :: envs when delta < 0 -> loop (delta + 1) envs
+		| _ -> envs
+	in
+	loop ctx.debug.environment_offset_delta envs
+
+(* Printing *)
+
+let value_string value =
+	let rec fields_string depth fields =
+		let tabs = String.make (depth * 2) ' ' in
+		let l = List.map (fun (name,value) ->
+			let s_type,s_value = value_string depth value in
+			Printf.sprintf "%s%s : %s = %s" tabs (rev_hash_s name) s_type s_value
+		) fields in
+		Printf.sprintf "{\n%s\n%s}" (String.concat "\n" l) tabs
+	and instance_fields depth vi =
+		let fields = IntMap.fold (fun name key acc ->
+			(name,vi.ifields.(key)) :: acc
+		) vi.iproto.pinstance_names [] in
+		fields_string (depth + 1) fields
+	and value_string depth v = match v with
+		| VNull -> "NULL","null"
+		| VTrue -> "Bool","true"
+		| VFalse -> "Bool","false"
+		| VInt32 i -> "Int",Int32.to_string i
+		| VFloat f -> "Float",string_of_float f
+		| VEnumValue ev -> rev_hash_s ev.epath,Rope.to_string (s_enum_value 0 ev)
+		| VObject o -> "Anonymous",fields_string (depth + 1) (object_fields o)
+		| VString(_,s) -> "String","\"" ^ (Ast.s_escape (Lazy.force s)) ^ "\""
+		| VArray va -> "Array",Rope.to_string (s_array (depth + 1) va)
+		| VVector vv -> "Vector",Rope.to_string (s_vector (depth + 1) vv)
+		| VInstance vi -> rev_hash_s vi.iproto.ppath,instance_fields (depth + 1) vi
+		| VPrototype proto -> "Anonymous",Rope.to_string (s_proto_kind proto)
+		| VFunction _ | VFieldClosure _ -> "Function","fun"
+	in
+	let s_type,s_value = value_string 0 value in
+	Printf.sprintf "%s = %s" s_type s_value
+
+let send_string s =
+	print_endline s
+
+let output_info = send_string
+let output_error = send_string
+
+let output_exception_stop ctx v pos =
+	output_info (uncaught_exception_string v pos "")
+
+let output_variable_name name =
+	send_string (Printf.sprintf "%s" name)
+
+let output_value name value =
+	send_string (Printf.sprintf "%s : %s" name (value_string value))
+
+let output_call_stack_position ctx i kind p =
+	let line = Lexer.get_error_line p in
+	send_string (Printf.sprintf "%6i : %s at %s:%i" i (kind_name (get_eval ctx) kind) (Path.get_real_path p.pfile) line)
+
+let output_call_stack ctx kind p =
+	let envs = get_call_stack_envs ctx kind p in
+	let i = ref ((get_eval ctx).environment_offset - 1) in
+	output_call_stack_position ctx !i kind {p with pfile = Path.unique_full_path p.Globals.pfile};
+	List.iter (fun env ->
+		if env.env_leave_pmin >= 0 then begin
+			let p = {pmin = env.env_leave_pmin; pmax = env.env_leave_pmax; pfile = rev_file_hash env.env_info.pfile} in
+			decr i;
+			output_call_stack_position ctx !i env.env_info.kind p
+		end
+	) envs
+
+let output_file_path s = send_string (Path.get_real_path s)
+
+let output_type_name = send_string
+let output_breakpoint breakpoint =
+	let flag = match breakpoint.bpstate with
+		| BPHit | BPEnabled -> "E"
+		| BPDisabled -> "d"
+	in
+	send_string (Printf.sprintf "%i %s" breakpoint.bpid flag)
+
+let output_breakpoints ctx =
+	iter_breakpoints ctx (fun breakpoint ->
+		output_breakpoint breakpoint
+	)
+
+let output_breakpoint_set breakpoint =
+	output_info (Printf.sprintf "Breakpoint %i set and enabled" breakpoint.bpid)
+
+let output_breakpoint_stop ctx env =
+	output_info (Printf.sprintf "Thread %i stopped in %s at %s:%i." 0 (kind_name (get_eval ctx) env.env_info.kind) (rev_file_hash env.env_info.pfile) env.env_debug.line)
+
+let output_breakpoint_description breakpoint =
+	let s_col = match breakpoint.bpcolumn with
+		| BPAny -> ""
+		| BPColumn i -> ":" ^ (string_of_int i)
+	in
+	send_string (Printf.sprintf "%s:%i%s" ((Path.get_real_path (rev_file_hash breakpoint.bpfile))) breakpoint.bpline s_col)
+
+let read_line () =
+	input_line Pervasives.stdin
+
+let parse_breakpoint_pattern pattern =
+	(* TODO: more than file:line patterns? *)
+	try
+		let split = ExtString.String.nsplit pattern ":" in
+		let file,line,column = match List.rev split with
+			| first :: rest ->
+				let first = int_of_string first in
+				begin match rest with
+					| second :: file ->
+						begin try
+							file,(int_of_string second),BPColumn first
+						with _ ->
+							(second :: file),first,BPAny
+						end
+					| file ->
+						file,first,BPAny
+				end
+			| [] -> raise Exit
+		in
+		let file = String.concat ":" (List.rev file) in
+		file,line,column
+	with _ ->
+		raise Exit
+
+let print_variables ctx capture_infos scopes env =
+	let rec loop scopes = match scopes with
+		| scope :: scopes ->
+			Hashtbl.iter (fun _ name -> output_variable_name name) scope.local_infos;
+			loop scopes
+		| [] ->
+			()
+	in
+	loop scopes;
+	Hashtbl.iter (fun slot name ->
+		if slot < Array.length env.env_captures then
+			output_variable_name name
+	) capture_infos
+
+
+let set_variable ctx scopes name value env =
+	try
+		let slot = get_var_slot_by_name scopes name in
+		env.env_locals.(slot) <- value;
+		output_value name value;
+	with Not_found ->
+		output_error ("No variable found: " ^ name)
+
+(* Reads input and reacts accordingly. *)
+let rec wait ctx run env =
+	let get_real_env ctx =
+		ctx.debug.environment_offset_delta <- 0;
+		DynArray.get (get_eval ctx).environments ((get_eval ctx).environment_offset - 1);
+	in
+	let rec move_frame offset : value =
+		if offset < 0 || offset >= (get_eval ctx).environment_offset then begin
+			output_error (Printf.sprintf "Frame out of bounds: %i (valid range is %i - %i)" offset 0 ((get_eval ctx).environment_offset - 1));
+			loop()
+		end else begin
+			ctx.debug.environment_offset_delta <- ((get_eval ctx).environment_offset - offset - 1);
+			wait ctx run (DynArray.get (get_eval ctx).environments offset);
+		end
+	and loop () =
+		print_string "1> ";
+		flush stdout;
+		let line = read_line () in
+		match ExtString.String.nsplit line " " with
+		| ["quit" | "exit"] ->
+			(* TODO: Borrowed from interp.ml *)
+			if (get_ctx()).curapi.use_cache() then raise (Error.Fatal_error ("",Globals.null_pos));
+			raise (Interp.Sys_exit 0);
+		| ["detach"] ->
+			Hashtbl.iter (fun _ h ->
+				Hashtbl.clear h
+			) ctx.debug.breakpoints;
+			ctx.debug.debug_state <- DbgRunning;
+			run env
+		(* source | history *)
+		| ["files" | "filespath"] ->
+			Hashtbl.iter (fun i _ ->
+				output_file_path (rev_file_hash i);
+			) ctx.debug.breakpoints;
+			loop()
+		| ["classes"] ->
+			IntMap.iter (fun i _ ->
+				output_type_name (rev_hash_s i)
+			) ctx.type_cache;
+			loop()
+		| ["mem"] ->
+			output_info (Printf.sprintf "%i" (Gc.stat()).live_words);
+			loop()
+		| ["compact"] ->
+			let before = (Gc.stat()).live_words in
+			Gc.compact();
+			let after = (Gc.stat()).live_words in
+			output_info (Printf.sprintf "before: %i\nafter: %i" before after);
+			loop()
+		| ["collect"] ->
+			let before = (Gc.stat()).live_words in
+			Gc.full_major();
+			let after = (Gc.stat()).live_words in
+			output_info (Printf.sprintf "before: %i\nafter: %i" before after);
+			loop()
+		| ["break" | "b";pattern] ->
+			begin try
+				let file,line,column = parse_breakpoint_pattern pattern in
+				begin try
+					let breakpoint = add_breakpoint ctx file line column in
+					output_breakpoint_set breakpoint;
+				with Not_found ->
+					output_error ("Could not find file " ^ file);
+				end;
+			with Exit ->
+				output_error "Unrecognized breakpoint pattern";
+			end;
+			loop()
+		| ["list" | "l"] ->
+			(* TODO: other list syntax *)
+			output_breakpoints ctx;
+			loop()
+		| ["describe" | "desc";bpid] ->
+			(* TODO: range patterns? *)
+			begin try
+				let breakpoint = find_breakpoint ctx bpid in
+				output_breakpoint_description breakpoint;
+			with Not_found ->
+				output_error (Printf.sprintf "Unknown breakpoint: %s" bpid);
+			end;
+			loop()
+		| ["disable" | "dis";bpid] ->
+			(* TODO: range patterns? *)
+			if bpid = "all" then
+				iter_breakpoints ctx (fun breakpoint -> breakpoint.bpstate <- BPDisabled)
+			else begin try
+				let breakpoint = find_breakpoint ctx bpid in
+				breakpoint.bpstate <- BPDisabled;
+				output_info (Printf.sprintf "Breakpoint %s disabled" bpid);
+			with Not_found ->
+				output_error (Printf.sprintf "Unknown breakpoint: %s" bpid);
+			end;
+			loop()
+		| ["enable" | "en";bpid] ->
+			(* TODO: range patterns? *)
+			if bpid = "all" then
+				iter_breakpoints ctx (fun breakpoint -> breakpoint.bpstate <- BPEnabled)
+			else begin try
+				let breakpoint = find_breakpoint ctx bpid in
+				breakpoint.bpstate <- BPEnabled;
+				output_info (Printf.sprintf "Breakpoint %s enabled" bpid);
+			with Not_found ->
+				output_error (Printf.sprintf "Unknown breakpoint: %s" bpid);
+			end;
+			loop()
+		| ["delete" | "d";bpid] ->
+			(* TODO: range patterns? *)
+			if bpid = "all" then
+				Hashtbl.iter (fun _ h ->
+					Hashtbl.clear h
+				) ctx.debug.breakpoints
+			else begin try
+				let id = try int_of_string bpid with _ -> raise Not_found in
+				Hashtbl.iter (fun _ h ->
+					let to_delete = ref [] in
+					Hashtbl.iter (fun k breakpoint -> if breakpoint.bpid = id then to_delete := k :: !to_delete) h;
+					List.iter (fun k -> Hashtbl.remove h k) !to_delete;
+				) ctx.debug.breakpoints;
+				output_info (Printf.sprintf "Breakpoint %s deleted" bpid);
+			with Not_found ->
+				output_error (Printf.sprintf "Unknown breakpoint: %s" bpid);
+			end;
+			loop()
+		| ["clear";pattern] ->
+			(* TODO: range patterns? *)
+			begin try
+				let file,line,column = parse_breakpoint_pattern pattern in
+				begin try
+					delete_breakpoint ctx file line
+				with Not_found ->
+					output_info (Printf.sprintf "Could not find breakpoint %s:%i" file line);
+				end
+			with Exit ->
+				output_error ("Unrecognized breakpoint pattern");
+			end;
+			loop()
+		(* thread | unsafe | safe *)
+		| ["continue" | "c"] ->
+			let env = get_real_env ctx in
+			ctx.debug.debug_state <- (if ctx.debug.debug_state = DbgStart then DbgRunning else DbgContinue);
+			run env
+		| ["step" | "s" | ""] ->
+			let env = get_real_env ctx in
+			run env
+		| ["next" | "n"] ->
+			let env = get_real_env ctx in
+			ctx.debug.debug_state <- DbgNext (get_eval ctx).environment_offset;
+			run env
+		| ["finish" | "f"] ->
+			let env = get_real_env ctx in
+			ctx.debug.debug_state <- DbgFinish (get_eval ctx).environment_offset;
+			run env
+		| ["where" | "w"] ->
+			output_call_stack ctx env.env_info.kind env.env_debug.expr.epos;
+			loop()
+		| ["up"] ->
+			let offset = (get_eval ctx).environment_offset - ctx.debug.environment_offset_delta in
+			move_frame (offset - 2)
+		| ["down"] ->
+			let offset = (get_eval ctx).environment_offset - ctx.debug.environment_offset_delta in
+			move_frame offset
+		| ["frame";sframe] ->
+			let frame = try
+				Some (int_of_string sframe)
+			with _ ->
+				None
+			in
+			begin match frame with
+				| Some frame -> move_frame ((get_eval ctx).environment_offset - frame - 1)
+				| None ->
+					output_error ("Invalid frame format: " ^ sframe);
+					loop()
+			end
+		| ["variables" | "vars"] ->
+			print_variables ctx env.env_info.capture_infos env.env_debug.scopes env;
+			loop()
+		| ["print" | "p";e] ->
+			begin try
+				let e = parse_expr ctx e env.env_debug.expr.epos in
+				begin try
+					let name,v = expr_to_value ctx env e in
+					output_value name v
+				with Exit ->
+					output_error ("Don't know how to handle this expression: " ^ (Ast.s_expr e))
+				end
+			with Parse_expr_error e ->
+				output_error e
+			end;
+			loop()
+		| ["set" | "s";expr_s;"=";value] ->
+			let parse s = parse_expr ctx s env.env_debug.expr.epos in
+			begin try
+				let expr,value = parse expr_s,parse value in
+				begin try
+					let _,value = expr_to_value ctx env value in
+					begin match fst expr with
+						(* TODO: support setting array elements and enum values *)
+						| EField(e1,s) ->
+							let _,v1 = expr_to_value ctx env e1 in
+							set_field v1 (hash_s s) value;
+						| EConst (Ident s) ->
+							set_variable ctx env.env_debug.scopes s value env;
+						| _ ->
+							raise Exit
+					end
+				with Exit ->
+					output_error ("Don't know how to handle this expression")
+				end
+			with Parse_expr_error e ->
+				output_error e
+			end;
+			loop()
+		| s ->
+			output_error (Printf.sprintf "Unknown command: %s" (String.concat " " s));
+			loop()
+	in
+	loop ()
+
+let connection : debug_connection = {
+	wait = wait;
+	bp_stop = output_breakpoint_stop;
+	exc_stop = output_exception_stop;
+}

+ 185 - 0
src/macro/eval/evalDebugMisc.ml

@@ -0,0 +1,185 @@
+open Ast
+open Globals
+open MacroApi
+open EvalContext
+open EvalHash
+open EvalValue
+open EvalEncode
+
+type debug_connection = {
+	wait : context -> (env -> value) -> env -> value;
+	bp_stop : context -> env -> unit;
+	exc_stop : context -> value -> pos -> unit;
+}
+
+exception BreakHere
+
+(* Breakpoints *)
+
+let make_breakpoint =
+	let id = ref (-1) in
+	(fun file line state column ->
+		incr id;
+		{
+			bpid = !id;
+			bpfile = file;
+			bpline = line;
+			bpstate = state;
+			bpcolumn = column;
+		}
+	)
+
+let iter_breakpoints ctx f =
+	Hashtbl.iter (fun _ breakpoints ->
+		Hashtbl.iter (fun _ breakpoint -> f breakpoint) breakpoints
+	) ctx.debug.breakpoints
+
+let add_breakpoint ctx file line column =
+	let hash = hash_s (Path.unique_full_path (Common.find_file (ctx.curapi.get_com()) file)) in
+	let h = try
+		Hashtbl.find ctx.debug.breakpoints hash
+	with Not_found ->
+		let h = Hashtbl.create 0 in
+		Hashtbl.add ctx.debug.breakpoints hash h;
+		h
+	in
+	let breakpoint = make_breakpoint hash line BPEnabled column in
+	Hashtbl.replace h line breakpoint;
+	breakpoint
+
+let delete_breakpoint ctx file line =
+	let hash = hash_s (Path.unique_full_path (Common.find_file (ctx.curapi.get_com()) file)) in
+	let h = Hashtbl.find ctx.debug.breakpoints hash in
+	Hashtbl.remove h line
+
+let find_breakpoint ctx sid =
+	let found = ref None in
+	let id = try int_of_string sid with _ -> raise Not_found in
+	try
+		iter_breakpoints ctx (fun breakpoint ->
+			if breakpoint.bpid = id then begin
+				found := Some breakpoint;
+				raise Exit
+			end
+		);
+		raise Not_found
+	with Exit ->
+		match !found with None -> assert false | Some breakpoint -> breakpoint
+
+
+(* Helper *)
+
+exception Parse_expr_error of string
+
+let parse_expr ctx s p =
+	let error s = raise (Parse_expr_error s) in
+	Parser.parse_expr_string (ctx.curapi.get_com()) s p error false
+
+(* Vars *)
+
+let get_var_slot_by_name scopes name =
+	let rec loop scopes = match scopes with
+		| scope :: scopes ->
+			begin try
+				let id = Hashtbl.find scope.local_ids name in
+				let slot = Hashtbl.find scope.locals id in
+				slot + scope.local_offset
+			with Not_found ->
+				loop scopes
+			end
+		| [] ->
+			raise Not_found
+	in
+	loop scopes
+
+let get_capture_slot_by_name capture_infos name =
+	let ret = ref None in
+	try
+		Hashtbl.iter (fun slot name' ->
+			if name = name' then begin
+				ret := (Some slot);
+				raise Exit
+			end
+		) capture_infos;
+		raise Not_found
+	with Exit ->
+		match !ret with None -> assert false | Some name -> name
+
+let get_variable capture_infos scopes name env =
+	try
+		let slot = get_var_slot_by_name scopes name in
+		let value = env.env_locals.(slot) in
+		value
+	with Not_found ->
+		let slot = get_capture_slot_by_name capture_infos name in
+		let value = try env.env_captures.(slot) with _ -> raise Not_found in
+		!value
+
+(* Expr to value *)
+
+let resolve_ident ctx env s =
+	let key = hash_s s in
+	try
+		(* 1. Variable *)
+		get_variable env.env_info.capture_infos env.env_debug.scopes s env
+	with Not_found -> try
+		(* 2. Instance *)
+		if env.env_info.static then raise Not_found;
+		let v = env.env_locals.(0) in
+		EvalField.field_raise v key
+	with Not_found -> try
+		(* 3. Static *)
+		begin match env.env_info.kind with
+			| EKMethod(i1,_) ->
+				let proto = get_static_prototype_raise ctx i1 in
+				EvalField.proto_field_raise proto key
+			| _ ->
+				raise Not_found
+		end
+	with Not_found -> try
+		(* 4. Type *)
+		VPrototype (IntMap.find key ctx.static_prototypes)
+	with Not_found ->
+		raise Exit
+
+let expr_to_value ctx env e =
+	let rec loop e = match fst e with
+		| EConst cst ->
+			begin match cst with
+				| String s -> "",encode_string s
+				| Int s -> "",VInt32 (Int32.of_string s)
+				| Float s -> "",VFloat (float_of_string s)
+				| Ident "true" -> "",VTrue
+				| Ident "false" -> "",VFalse
+				| Ident "null" -> "",VNull
+				| Ident s ->
+					let value = resolve_ident ctx env s in
+					s,value
+				| _ -> raise Exit
+			end
+		| EArray(e1,eidx) ->
+			let n1,v1 = loop e1 in
+			let nidx,vidx = loop eidx in
+			let idx = match vidx with VInt32 i -> Int32.to_int i | _ -> raise Exit in
+			let n = Printf.sprintf "%s[%d]" n1 idx in
+			begin match v1 with
+				| VArray va ->
+					let v = EvalArray.get va idx in
+					(n,v)
+				| VVector vv ->
+					let v = Array.get vv idx in
+					(n,v)
+				| VEnumValue ev ->
+					let v = Array.get ev.eargs idx in
+					(n,v)
+				| _ ->
+					raise Exit
+			end
+		| EField(e1,s) ->
+			let n1,v1 = loop e1 in
+			let v = EvalField.field v1 (hash_s s) in
+			(Printf.sprintf "%s.%s" n1 s),v
+		| _ ->
+			raise Exit
+	in
+	loop e

+ 571 - 0
src/macro/eval/evalDebugSocket.ml

@@ -0,0 +1,571 @@
+open Gc
+open Ast
+open Type
+open Globals
+open MacroApi
+open Unix
+open Json
+open EvalContext
+open EvalValue
+open EvalHash
+open EvalPrinting
+open EvalMisc
+open EvalDebugMisc
+
+module JsonRpc = struct
+	let jsonrpc_field = "jsonrpc", JString "2.0"
+
+	let notification method_name params =
+		let fl = [
+			jsonrpc_field;
+			"method", JString method_name;
+		] in
+		let fl = Option.map_default (fun params -> ("params",params) :: fl) fl params in
+		JObject fl
+
+	let result id data =
+		JObject [
+			jsonrpc_field;
+			"id", id;
+			"result", data;
+		]
+
+	let error id code message =
+		JObject [
+			jsonrpc_field;
+			"id", id;
+			"error", JObject [
+				"code", JInt code;
+				"message", JString message;
+			];
+		]
+
+	type json_rpc_error =
+		| Parse_error of string
+		| Invalid_request of string
+		| Method_not_found of Json.t * string (* id->methodname *)
+		| Invalid_params of Json.t
+		| Custom of Json.t * int * string (* id->code->message *)
+
+	exception JsonRpc_error of json_rpc_error
+
+	let handle_jsonrpc_error f output =
+		try f () with JsonRpc_error e ->
+			match e with
+			| Parse_error s -> output (error JNull (-32700) s)
+			| Invalid_request s -> output (error JNull (-32600) s)
+			| Method_not_found (id,meth) -> output (error id (-32601) (Printf.sprintf "Method `%s` not found" meth))
+			| Invalid_params id -> output (error id (-32602) "Invalid params")
+			| Custom (id,code,msg) -> output (error id code msg)
+
+	let process_request input handle output =
+		let open Json.Reader in
+		let lexbuf = Sedlexing.Utf8.from_string input in
+		let json = try read_json lexbuf with Json_error s -> raise (JsonRpc_error (Parse_error s)) in
+		let fields = match json with JObject fl -> fl | _ -> raise (JsonRpc_error (Invalid_request "not an object")) in
+		let get_field name map =
+			let field = try List.find (fun (n,_) -> n = name) fields with Not_found -> raise (JsonRpc_error (Invalid_request ("no `" ^ name ^ "` field"))) in
+			let value = map (snd field) in
+			match value with
+			| None -> raise (JsonRpc_error (Invalid_request (Printf.sprintf "`%s` field has invalid data" name)))
+			| Some v -> v
+		in
+		let id = get_field "id" (fun v -> Some v) in
+		let meth = get_field "method" (function JString s -> Some s | _ -> None) in
+		let params =
+			try
+				let f = List.find (fun (n,_) -> n = "params") fields in
+			 	Some (snd f)
+			with Not_found ->
+				None
+		in
+		let res = handle id meth params in
+		output id res
+end
+
+module Transport = struct
+	let read_byte this i = int_of_char (Bytes.get this i)
+
+	let read_ui16 this i =
+		let ch1 = read_byte this i in
+		let ch2 = read_byte this (i + 1) in
+		ch1 lor (ch2 lsl 8)
+
+	let read_string socket =
+		match socket.socket with
+			| None ->
+				failwith "no socket" (* TODO: reconnect? *)
+			| Some socket ->
+				let buf = Bytes.create 2 in
+				let _ = recv socket buf 0 2 [] in
+				let i = read_ui16 buf 0 in
+				let buf = Bytes.create i in
+				let _ = recv socket buf 0 i [] in
+				Bytes.to_string buf
+
+	let send_string socket s =
+		match socket.socket with
+		| None ->
+			failwith "no socket" (* TODO: reconnect? *)
+		| Some socket ->
+			let l = String.length s in
+			assert (l < 0xFFFF);
+			let buf = Bytes.make 2 ' ' in
+			Bytes.set buf 0 (Char.unsafe_chr l);
+			Bytes.set buf 1 (Char.unsafe_chr (l lsr 8));
+			ignore(send socket buf 0 2 []);
+			ignore(send socket (Bytes.unsafe_of_string s) 0 (String.length s) [])
+end
+
+(* Printing *)
+
+
+let print_json socket json =
+	let b = Buffer.create 0 in
+	write_json (Buffer.add_string b) json;
+	Transport.send_string socket (Buffer.contents b)
+
+let output_event socket event data =
+	print_json socket (JsonRpc.notification event data)
+
+let var_to_json name value access =
+	let jv t v structured =
+		JObject ["name",JString name;"type",JString t;"value",JString v;"structured",JBool structured;"access",JString access]
+	in
+	let string_repr s = "\"" ^ (Ast.s_escape (Lazy.force s)) ^ "\"" in
+	let level2_value_repr = function
+		| VNull -> "null"
+		| VTrue -> "true"
+		| VFalse -> "false"
+		| VInt32 i -> Int32.to_string i
+		| VFloat f -> string_of_float f
+		| VEnumValue ve ->
+			let name = EvalPrinting.s_enum_ctor_name ve in
+			begin match ve.eargs with
+				| [||] -> name
+				| vl -> name ^ "(...)"
+			end
+		| VObject o -> "{...}"
+		| VString(_,s) -> string_repr s
+		| VArray _ | VVector _ -> "[...]"
+		| VInstance vi -> (rev_hash_s vi.iproto.ppath) ^ " {...}"
+		| VPrototype proto -> Rope.to_string (s_proto_kind proto)
+		| VFunction _ | VFieldClosure _ -> "<fun>"
+	in
+	let fields_string fields =
+		let l = List.map (fun (name, value) -> Printf.sprintf "%s: %s" (rev_hash_s name) (level2_value_repr value)) fields in
+		Printf.sprintf "{%s}" (String.concat ", " l)
+	in
+	let array_elems l =
+		let l = List.map level2_value_repr l in
+		Printf.sprintf "[%s]" (String.concat ", " l)
+	in
+	let value_string v = match v with
+		| VNull -> jv "NULL" "null" false
+		| VTrue -> jv "Bool" "true" false
+		| VFalse -> jv "Bool" "false" false
+		| VInt32 i -> jv "Int" (Int32.to_string i) false
+		| VFloat f -> jv "Float" (string_of_float f) false
+		| VEnumValue ve ->
+			let type_s = rev_hash_s ve.epath in
+			let name = EvalPrinting.s_enum_ctor_name ve in
+			let value_s,is_structured = match ve.eargs with
+				| [||] -> name, false
+				| vl ->
+					let l = Array.to_list (Array.map level2_value_repr vl) in
+					let s = Printf.sprintf "%s(%s)" name (String.concat ", " l) in
+					s, true
+			in
+			jv type_s value_s is_structured
+		| VObject o -> jv "Anonymous" (fields_string (object_fields o)) true (* TODO: false for empty structures *)
+		| VString(_,s) -> jv "String" (string_repr s) false
+		| VArray va -> jv "Array" (array_elems (EvalArray.to_list va)) true (* TODO: false for empty arrays *)
+		| VVector vv -> jv "Vector" (array_elems (Array.to_list vv)) true
+		| VInstance vi ->
+			let class_name = rev_hash_s vi.iproto.ppath in
+			jv class_name (class_name ^ " " ^ (fields_string (instance_fields vi))) true
+		| VPrototype proto -> jv "Anonymous" (Rope.to_string (s_proto_kind proto)) false (* TODO: show statics *)
+		| VFunction _ | VFieldClosure _ -> jv "Function" "<fun>" false
+	in
+	value_string value
+
+let get_call_stack_envs ctx kind p =
+	let envs = match call_stack ctx with
+		| _ :: envs -> envs
+		| [] -> []
+	in
+	let rec loop delta envs = match envs with
+		| _ :: envs when delta < 0 -> loop (delta + 1) envs
+		| _ -> envs
+	in
+	loop ctx.debug.environment_offset_delta envs
+
+let output_call_stack ctx kind p =
+	let envs = get_call_stack_envs ctx kind p in
+	let id = ref (-1) in
+	let stack_item kind p artificial =
+		incr id;
+		let line1,col1,line2,col2 = Lexer.get_pos_coords p in
+		JObject [
+			"id",JInt !id;
+			"name",JString (kind_name (get_eval ctx) kind);
+			"source",JString (Path.get_real_path p.pfile);
+			"line",JInt line1;
+			"column",JInt col1;
+			"endLine",JInt line2;
+			"endColumn",JInt col2;
+			"artificial",JBool artificial;
+		]
+	in
+	let l = [stack_item kind p false] in
+	let stack = List.fold_left (fun acc env ->
+		let p = {pmin = env.env_leave_pmin; pmax = env.env_leave_pmax; pfile = rev_file_hash env.env_info.pfile} in
+		(stack_item env.env_info.kind p (env.env_leave_pmin < 0)) :: acc
+	) l envs in
+	JArray (List.rev stack)
+
+let output_scopes capture_infos scopes =
+	let mk_scope id name pos =
+		let fl = ["id",JInt id; "name",JString name] in
+		let fl =
+			if pos <> null_pos then
+				let line1,col1,line2,col2 = Lexer.get_pos_coords pos in
+				("pos",JObject [
+					"source",JString (Path.get_real_path pos.pfile);
+					"line",JInt line1;
+					"column",JInt col1;
+					"endLine",JInt line2;
+					"endColumn",JInt col2;
+				]) :: fl
+			else
+				fl
+		in
+		JObject fl
+	in
+	let _,scopes = List.fold_left (fun (id,acc) scope ->
+		if Hashtbl.length scope.local_infos <> 0 then
+			(id + 1), (mk_scope id "Locals" scope.pos) :: acc
+		else
+			(id + 1), acc
+	) (1,[]) scopes in
+	let scopes = List.rev scopes in
+	let scopes = if Hashtbl.length capture_infos = 0 then scopes else (mk_scope 0 "Captures" null_pos) :: scopes in
+	JArray scopes
+
+let output_capture_vars env =
+	let infos = env.env_info.capture_infos in
+	let vars = Hashtbl.fold (fun slot name acc ->
+		let value = !(env.env_captures.(slot)) in
+		(var_to_json name value name) :: acc
+	) infos [] in
+	JArray vars
+
+let output_scope_vars env scope =
+	let vars = Hashtbl.fold (fun local_slot name acc ->
+		let slot = local_slot + scope.local_offset in
+		let value = env.env_locals.(slot) in
+		(var_to_json name value name) :: acc
+	) scope.local_infos [] in
+	JArray vars
+
+let output_inner_vars v access =
+	let children = match v with
+		| VNull | VTrue | VFalse | VInt32 _ | VFloat _ | VFunction _ | VFieldClosure _ -> []
+		| VEnumValue ve ->
+			begin match ve.eargs with
+				| [||] -> []
+				| vl ->
+					Array.to_list (Array.mapi (fun i v ->
+						let n = Printf.sprintf "[%d]" i in
+						let a = access ^ n in
+						n, v, a
+					) vl)
+			end
+		| VObject o ->
+			let fields = object_fields o in
+			List.map (fun (n,v) ->
+				let n = rev_hash_s n in
+				let a = access ^ "." ^ n in
+				n, v, a
+			) fields
+		| VString(_,s) -> []
+		| VArray va ->
+			let l = EvalArray.to_list va in
+			List.mapi (fun i v ->
+				let n = Printf.sprintf "[%d]" i in
+				let a = access ^ n in
+				n, v, a
+			) l
+		| VVector vv ->
+			let l = Array.to_list vv in
+			List.mapi (fun i v ->
+				let n = Printf.sprintf "[%d]" i in
+				let a = access ^ n in
+				n, v, a
+			) l
+		| VInstance vi ->
+			let fields = instance_fields vi in
+			List.map (fun (n,v) ->
+				let n = rev_hash_s n in
+				let a = access ^ "." ^ n in
+				n, v, a
+			) fields
+		| VPrototype proto -> [] (* TODO *)
+	in
+	let vars = List.map (fun (n,v,a) -> var_to_json n v a) children in
+	JArray vars
+
+type command_outcome =
+	| Loop of Json.t
+	| Run of Json.t * EvalContext.env
+	| Wait of Json.t * EvalContext.env
+
+
+let make_connection socket =
+	(* Reads input and reacts accordingly. *)
+	let rec wait ctx run env =
+		let get_real_env ctx =
+			ctx.debug.environment_offset_delta <- 0;
+			DynArray.get (get_eval ctx).environments ((get_eval ctx).environment_offset - 1);
+		in
+		let rec loop () =
+			let handle_request id name params =
+				let error msg =
+					let open JsonRpc in
+					raise (JsonRpc_error (Custom (id, 1, msg)))
+				in
+				let invalid_params () =
+					let open JsonRpc in
+					raise (JsonRpc_error (Invalid_params id))
+				in
+				let rec move_frame offset =
+					if offset < 0 || offset >= (get_eval ctx).environment_offset then begin
+						error (Printf.sprintf "Frame out of bounds: %i (valid range is %i - %i)" offset 0 ((get_eval ctx).environment_offset - 1))
+					end else begin
+						ctx.debug.environment_offset_delta <- ((get_eval ctx).environment_offset - offset - 1);
+						Wait (JNull, (DynArray.get (get_eval ctx).environments offset))
+					end
+				in
+				match name with
+				| "continue" ->
+					let env = get_real_env ctx in
+					ctx.debug.debug_state <- (if ctx.debug.debug_state = DbgStart then DbgRunning else DbgContinue);
+					Run (JNull,env)
+				| "stepIn" ->
+					let env = get_real_env ctx in
+					Run (JNull,env)
+				| "next" ->
+					let env = get_real_env ctx in
+					ctx.debug.debug_state <- DbgNext (get_eval ctx).environment_offset;
+					Run (JNull,env)
+				| "stepOut" ->
+					let env = get_real_env ctx in
+					ctx.debug.debug_state <- DbgFinish (get_eval ctx).environment_offset;
+					Run (JNull,env)
+				| "stackTrace" ->
+					Loop (output_call_stack ctx env.env_info.kind env.env_debug.expr.epos)
+				| "setBreakpoints" ->
+					let file, bps =
+						match params with
+						| Some (JObject fl) ->
+							let file = try List.find (fun (n,_) -> n = "file") fl with Not_found -> invalid_params () in
+							let file = match (snd file) with JString s -> s | _ -> invalid_params () in
+							let parse_breakpoint = function
+								| JObject fl ->
+									let line = try List.find (fun (n,_) -> n = "line") fl with Not_found -> invalid_params () in
+									let line = match (snd line) with JInt s -> s | _ -> invalid_params () in
+									let column = try Some (List.find (fun (n,_) -> n = "column") fl) with Not_found -> None in
+									let column = Option.map_default (fun (_,v) -> match v with JInt i -> BPColumn i | _ -> invalid_params ()) BPAny column in
+									line,column
+								| _ -> invalid_params ()
+							in
+							let bps = try List.find (fun (n,_) -> n = "breakpoints") fl with Not_found -> invalid_params () in
+							let bps = match (snd bps) with JArray jl -> jl | _ -> invalid_params () in
+							let bps = List.map parse_breakpoint bps in
+							file, bps
+						| _ ->
+							invalid_params ();
+					in
+					let hash = hash_s (Path.unique_full_path (Common.find_file (ctx.curapi.get_com()) file)) in
+					let h =
+						try
+							let h = Hashtbl.find ctx.debug.breakpoints hash in
+							Hashtbl.clear h;
+							h
+						with Not_found ->
+							let h = Hashtbl.create (List.length bps) in
+							Hashtbl.add ctx.debug.breakpoints hash h;
+							h
+					in
+					let bps = List.map (fun (line,column) ->
+						let bp = make_breakpoint hash line BPEnabled column in
+						Hashtbl.add h line bp;
+						JObject ["id",JInt bp.bpid]
+					) bps in
+					Loop (JArray bps)
+				| "setBreakpoint" ->
+					let file,line,column =
+						match params with
+						| Some (JObject fl) ->
+							let file = try List.find (fun (n,_) -> n = "file") fl with Not_found -> invalid_params () in
+							let file = match (snd file) with JString s -> s | _ -> invalid_params () in
+							let line = try List.find (fun (n,_) -> n = "line") fl with Not_found -> invalid_params () in
+							let line = match (snd line) with JInt s -> s | _ -> invalid_params () in
+							let column = try Some (List.find (fun (n,_) -> n = "column") fl) with Not_found -> None in
+							let column = Option.map_default (fun (_,v) -> match v with JInt i -> BPColumn i | _ -> invalid_params ()) BPAny column in
+							file,line,column
+						| _ ->
+							invalid_params ();
+					in
+					begin try
+						let breakpoint = add_breakpoint ctx file line column in
+						Loop (JObject ["id",JInt breakpoint.bpid])
+					with Not_found ->
+						invalid_params ();
+					end
+				| "removeBreakpoint" ->
+					let id =
+						match params with
+						| Some (JObject fl) ->
+							let id = try List.find (fun (n,_) -> n = "id") fl with Not_found -> invalid_params () in
+							(match (snd id) with JInt s -> s | _ -> invalid_params ())
+						| _ -> invalid_params ()
+					in
+					begin try
+						Hashtbl.iter (fun _ h ->
+							let to_delete = ref [] in
+							Hashtbl.iter (fun k breakpoint -> if breakpoint.bpid = id then to_delete := k :: !to_delete) h;
+							List.iter (fun k -> Hashtbl.remove h k) !to_delete;
+						) ctx.debug.breakpoints
+					with Not_found ->
+						error (Printf.sprintf "Unknown breakpoint: %d" id)
+					end;
+					Loop JNull
+				| "switchFrame" ->
+					let frame =
+						match params with
+						| Some (JObject fl) ->
+							let id = try List.find (fun (n,_) -> n = "id") fl with Not_found -> invalid_params () in
+							(match (snd id) with JInt s -> s | _ -> invalid_params ())
+						| _ -> invalid_params ()
+					in
+					move_frame ((get_eval ctx).environment_offset - frame - 1)
+				| "getScopes" ->
+					Loop (output_scopes env.env_info.capture_infos env.env_debug.scopes);
+				| "getScopeVariables" ->
+					let sid =
+						match params with
+						| Some (JObject fl) ->
+							let id = try List.find (fun (n,_) -> n = "id") fl with Not_found -> invalid_params () in
+							(match (snd id) with JInt s -> s | _ -> invalid_params ())
+						| _ -> invalid_params ()
+					in
+					begin
+						let vars =
+							try
+								if sid = 0 then begin
+									output_capture_vars env
+								end else begin
+									let scope = try List.nth env.env_debug.scopes (sid - 1) with _ -> raise Exit in
+									output_scope_vars env scope
+								end
+							with Exit ->
+								error "Invalid scope id"
+						in
+						Loop vars
+					end
+				| "getStructure" ->
+					let e =
+						match params with
+						| Some (JObject fl) ->
+							let id = try List.find (fun (n,_) -> n = "expr") fl with Not_found -> invalid_params () in
+							(match (snd id) with JString s -> s | _ -> invalid_params ())
+						| _ -> invalid_params ()
+					in
+					begin try
+						let e = parse_expr ctx e env.env_debug.expr.epos in
+						begin try
+							let access,v = expr_to_value ctx env e in
+							Loop (output_inner_vars v access)
+						with Exit ->
+							error ("Don't know how to handle this expression: " ^ (Ast.s_expr e))
+						end
+					with Parse_expr_error e ->
+						error e
+					end
+				| "setVariable" ->
+					let expr_s,value =
+						match params with
+						| Some (JObject fl) ->
+							let expr = try List.find (fun (n,_) -> n = "expr") fl with Not_found -> invalid_params () in
+							let expr = match (snd expr) with JString s -> s | _ -> invalid_params () in
+							let value = try List.find (fun (n,_) -> n = "value") fl with Not_found -> invalid_params () in
+							let value = match (snd value) with JString s -> s | _ -> invalid_params () in
+							expr,value
+						| _ ->
+							invalid_params ();
+					in
+					let parse s = parse_expr ctx s env.env_debug.expr.epos in
+					begin try
+						let expr,value = parse expr_s,parse value in
+						begin try
+							let _,value = expr_to_value ctx env value in
+							begin match fst expr with
+								(* TODO: support setting array elements and enum values *)
+								| EField(e1,s) ->
+									let _,v1 = expr_to_value ctx env e1 in
+									set_field v1 (hash_s s) value;
+									Loop (var_to_json s value expr_s)
+								| EConst (Ident s) ->
+									begin try
+										let slot = get_var_slot_by_name env.env_debug.scopes s in
+										env.env_locals.(slot) <- value;
+										Loop (var_to_json name value s)
+									with Not_found ->
+										error ("No variable found: " ^ s);
+									end
+								| _ ->
+									raise Exit
+							end
+						with Exit ->
+							error "Don't know how to handle this expression"
+						end
+					with Parse_expr_error e ->
+						error e
+					end
+				| meth ->
+					let open JsonRpc in
+					raise (JsonRpc_error (Method_not_found (id, meth)))
+			in
+			let process_outcome id outcome =
+				let output j = print_json socket (JsonRpc.result id j) in
+				match outcome with
+				| Loop result ->
+					output result;
+					loop ()
+				| Run (result,env) ->
+					output result;
+					run env
+				| Wait (result,env) ->
+					output result;
+					wait ctx run env;
+			in
+			let send_output_and_continue json =
+				print_json socket json;
+				loop ();
+			in
+			JsonRpc.handle_jsonrpc_error (fun () -> JsonRpc.process_request (Transport.read_string socket) handle_request process_outcome) send_output_and_continue;
+		in
+		loop ()
+	in
+	let output_breakpoint_stop _ _ =
+		output_event socket "breakpointStop" None
+	in
+	let output_exception_stop _ v _ =
+		output_event socket "exceptionStop" (Some (JObject ["text",JString (value_string v)]))
+	in
+	{
+		wait = wait;
+		bp_stop = output_breakpoint_stop;
+		exc_stop = output_exception_stop;
+	}

+ 116 - 0
src/macro/eval/evalDecode.ml

@@ -0,0 +1,116 @@
+(*
+	The Haxe Compiler
+	Copyright (C) 2005-2017  Haxe Foundation
+
+	This program is free software; you can redistribute it and/or
+	modify it under the terms of the GNU General Public License
+	as published by the Free Software Foundation; either version 2
+	of the License, or (at your option) any later version.
+
+	This program is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+
+	You should have received a copy of the GNU General Public License
+	along with this program; if not, write to the Free Software
+	Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ *)
+
+open Globals
+open EvalValue
+open EvalExceptions
+
+let decode_object v = match v with
+	| VObject o -> o
+	| _ -> unexpected_value v "object"
+
+let decode_enum v = match v with
+	| VEnumValue ev -> ev.eindex,Array.to_list ev.eargs
+	| _ -> unexpected_value v "enum"
+
+let decode_enum_with_pos v = match v with
+	| VEnumValue ev -> (ev.eindex,Array.to_list ev.eargs),(match ev.enpos with None -> null_pos | Some p -> p)
+	| _ -> unexpected_value v "enum"
+
+let decode_instance v = match v with
+	| VInstance vi -> vi
+	| _ -> unexpected_value v "instance"
+
+let decode_array v = match v with
+	| VArray va -> EvalArray.to_list va
+	| _ -> unexpected_value v "array"
+
+let decode_vector v = match v with
+	| VVector vv -> vv
+	| _ -> unexpected_value v "vector"
+
+let decode_varray v = match v with
+	| VArray va -> va
+	| _ -> unexpected_value v "array"
+
+let decode_string v = match v with
+	| VString(r,s) -> Lazy.force s
+	| _ -> unexpected_value v "string"
+
+let decode_rope v = match v with
+	| VString(s,_) -> s
+	| _ -> unexpected_value v "string"
+
+let decode_rope_string v = match v with
+	| VString(r,s) -> r,s
+	| _ -> unexpected_value v "string"
+
+let decode_bytes v = match v with
+	| VInstance {ikind=IBytes s} -> s
+	| _ -> unexpected_value v "string"
+
+let decode_i32 v = match v with
+	| VInt32 i -> i
+	| VFloat f -> (Int32.of_float f)
+	| _ -> unexpected_value v "int"
+
+let decode_int v = match v with
+	| VInt32 i -> Int32.to_int i
+	| VFloat f -> int_of_float f
+	| _ -> unexpected_value v "int"
+
+let decode_float v = match v with
+	| VFloat f -> f
+	| _ -> unexpected_value v "float"
+
+let decode_bool v = match v with
+	| VTrue -> true
+	| VFalse -> false
+	| _ -> unexpected_value v "bool"
+
+let default_int v vd = match v with
+	| VNull -> vd
+	| VInt32 i -> Int32.to_int i
+	| VFloat f -> int_of_float f
+	| _ -> unexpected_value v "int"
+
+let decode_unsafe v = match v with
+	| VInstance {ikind = IRef o} -> o
+	| _ -> unexpected_value v "unsafe"
+
+let decode_lazytype v = match v with
+	| VInstance {ikind=ILazyType(t,_)} -> t
+	| _ -> unexpected_value v "lazy type"
+
+let decode_tdecl v = match v with
+	| VInstance {ikind=ITypeDecl t} -> t
+	| _ -> unexpected_value v "type declaration"
+
+let decode_pos v = match v with
+	| VInstance {ikind=IPos p} -> p
+	| _ -> raise MacroApi.Invalid_expr (* maybe_decode_pos relies on this being raised *)
+
+let rec decode_ref v : 'a = match v with
+	| VInstance {ikind=IRef r} -> Obj.obj r
+	| _ -> unexpected_value v "unsafe"
+
+let num = function
+	| VInt32 i -> Int32.to_float i
+	| VFloat f -> f
+	| v -> unexpected_value v "number"

+ 1502 - 0
src/macro/eval/evalEmitter.ml

@@ -0,0 +1,1502 @@
+(*
+	The Haxe Compiler
+	Copyright (C) 2005-2017  Haxe Foundation
+
+	This program is free software; you can redistribute it and/or
+	modify it under the terms of the GNU General Public License
+	as published by the Free Software Foundation; either version 2
+	of the License, or (at your option) any later version.
+
+	This program is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+
+	You should have received a copy of the GNU General Public License
+	along with this program; if not, write to the Free Software
+	Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ *)
+
+open Globals
+open Ast
+open EvalHash
+open EvalValue
+open EvalEncode
+open EvalDecode
+open EvalContext
+open EvalPrinting
+open EvalExceptions
+open EvalField
+open EvalMisc
+
+type varacc =
+	| Local of int
+	| Env of int
+
+(* Helper *)
+
+let throw_string s p =
+	throw (encode_string s) p
+
+let invalid_binop op v1 v2 p =
+	throw_string (Printf.sprintf "Invalid operation: %s %s %s" (value_string v1) (s_binop op) (value_string v2)) p
+
+let unexpected_value_p v s p =
+	let str = Printf.sprintf "Unexpected value %s, expected %s" (value_string v) s in
+	throw_string str p
+
+let cannot_call v p =
+	throw (encode_string ("Cannot call " ^ (value_string v))) p
+
+let decode_int_p v p = match v with
+	| VInt32 i -> Int32.to_int i
+	| VFloat f -> int_of_float f
+	| _ -> unexpected_value_p v "int" p
+
+(* Emitter *)
+
+let apply env exec =
+	exec env
+
+(* Objects and values *)
+
+let emit_null _ = vnull
+
+let emit_local_declaration i exec env =
+	env.env_locals.(i) <- exec env;
+	vnull
+
+let emit_capture_declaration i exec env =
+	env.env_captures.(i) <- ref (exec env);
+	vnull
+
+let emit_const v _ = v
+
+let emit_new_array env =
+	encode_array_instance (EvalArray.create [||])
+
+let emit_new_vector_int i env =
+	encode_vector_instance (Array.make i vnull)
+
+let emit_new_vector exec p env =
+	encode_vector_instance (Array.make (decode_int_p (exec env) p) vnull)
+
+let emit_special_instance f execs env =
+	let vl = List.map (apply env) execs in
+	f vl
+
+let emit_object_declaration proto fa env =
+	let a = Array.make (Array.length fa) vnull in
+	Array.iter (fun (i,exec) -> a.(i) <- exec env) fa;
+	vobject {
+		ofields = a;
+		oproto = proto;
+		oextra = IntMap.empty;
+		oremoved = IntMap.empty;
+	}
+
+let emit_array_declaration execs env =
+	let vl = Array.map (apply env) execs in
+	encode_array_instance (EvalArray.create vl)
+
+let emit_type_expr proto env = proto
+
+let emit_mk_pos exec1 exec2 exec3 env =
+	let file = exec1 env in
+	let min = exec2 env in
+	let max = exec3 env in
+	encode_pos { pfile = decode_string file; pmin = decode_int min; pmax = decode_int max }
+
+let emit_enum_construction0 key i p env =
+	encode_enum_value key i [||] p
+
+let emit_enum_construction1 key i exec1 p env =
+	let v1 = exec1 env in
+	encode_enum_value key i [|v1|] p
+
+let emit_enum_construction2 key i exec1 exec2 p env =
+	let v1 = exec1 env in
+	let v2 = exec2 env in
+	encode_enum_value key i [|v1;v2|] p
+
+let emit_enum_construction3 key i exec1 exec2 exec3 p env =
+	let v1 = exec1 env in
+	let v2 = exec2 env in
+	let v3 = exec3 env in
+	encode_enum_value key i [|v1;v2;v3|] p
+
+let emit_enum_construction4 key i exec1 exec2 exec3 exec4 p env =
+	let v1 = exec1 env in
+	let v2 = exec2 env in
+	let v3 = exec3 env in
+	let v4 = exec4 env in
+	encode_enum_value key i [|v1;v2;v3;v4|] p
+
+let emit_enum_construction5 key i exec1 exec2 exec3 exec4 exec5 p env =
+	let v1 = exec1 env in
+	let v2 = exec2 env in
+	let v3 = exec3 env in
+	let v4 = exec4 env in
+	let v5 = exec5 env in
+	encode_enum_value key i [|v1;v2;v3;v4;v5|] p
+
+let emit_enum_construction key i execs p env =
+	encode_enum_value key i (Array.map (fun exec -> exec env) execs) p
+
+(* Branching *)
+
+let emit_if exec_cond exec_then exec_else env =
+	match exec_cond env with
+	| VTrue -> exec_then env
+	| _ -> exec_else env
+
+let emit_enum_switch_array exec cases exec_def p env = match exec env with
+	| VEnumValue ev ->
+		let i = ev.eindex in
+		if i >= Array.length cases || i < 0 then exec_def env
+		else (Array.unsafe_get cases i) env
+	| v ->
+		unexpected_value_p v "enum value" p
+
+let emit_int_switch_array exec cases exec_def p env = match exec env with
+	| VInt32 i32 ->
+		let i = Int32.to_int i32 in
+		if i >= Array.length cases || i < 0 then exec_def env
+		else (Array.unsafe_get cases i) env
+	| VNull ->
+		exec_def env
+	| v ->
+		unexpected_value_p v "int" p
+
+let emit_int_switch_array_shift shift exec cases exec_def p env = match exec env with
+	| VInt32 i32 ->
+		let i = Int32.to_int i32 + shift in
+		if i >= Array.length cases || i < 0 then exec_def env
+		else (Array.unsafe_get cases i) env
+	| VNull ->
+		exec_def env
+	| v ->
+		unexpected_value_p v "int" p
+
+let emit_int_switch_map exec cases exec_def p env = match exec env with
+	| VInt32 i32 ->
+		let i = Int32.to_int i32 in
+		begin try
+			(IntMap.find i cases) env
+		with Not_found ->
+			exec_def env
+		end
+	| v ->
+		unexpected_value_p v "int" p
+
+let emit_constant_switch exec execs constants exec_def env =
+	let v1 = exec env in
+	let rec loop v1 i =
+		if i >= Array.length constants then exec_def env
+		else if List.exists (fun v2 -> equals v1 v2) (Array.unsafe_get constants i) then
+			(Array.unsafe_get execs i) env
+		else
+			loop v1 (i + 1)
+	in
+	loop v1 0
+
+let emit_switch exec execs patterns exec_def env =
+	let v1 = exec env in
+	let rec loop v1 i =
+		if i >= Array.length patterns then exec_def env
+		else if List.exists (fun exec -> equals v1 (exec env)) patterns.(i) then
+			execs.(i) env
+		else
+			loop v1 (i + 1)
+	in
+	loop v1 0
+
+let emit_int_iterator slot exec1 exec2 p1 p2 env =
+	let i1 = decode_int_p (env.env_locals.(slot)) p1 in
+	let i2 = decode_int_p (exec1 env) p2 in
+	for i = i1 to i2 - 1 do
+		env.env_locals.(slot) <- vint i;
+		ignore(exec2 env);
+	done;
+	vnull
+
+let emit_int_iterator_continue slot exec1 exec2 p1 p2 env =
+	let i1 = decode_int_p (env.env_locals.(slot)) p1 in
+	let i2 = decode_int_p (exec1 env) p2 in
+	for i = i1 to i2 - 1 do
+		env.env_locals.(slot) <- vint i;
+		(try ignore(exec2 env) with Continue -> ())
+	done;
+	vnull
+
+let emit_int_iterator_break slot exec1 exec2 p1 p2 env =
+	let i1 = decode_int_p (env.env_locals.(slot)) p1 in
+	let i2 = decode_int_p (exec1 env) p2 in
+	begin try
+		for i = i1 to i2 - 1 do
+			env.env_locals.(slot) <- vint i;
+			ignore(exec2 env);
+		done;
+	with Break ->
+		()
+	end;
+	vnull
+
+let emit_int_iterator_break_continue slot exec1 exec2 p1 p2 env =
+	let i1 = decode_int_p (env.env_locals.(slot)) p1 in
+	let i2 = decode_int_p (exec1 env) p1 in
+	begin try
+		for i = i1 to i2 - 1 do
+			env.env_locals.(slot) <- vint i;
+			(try ignore(exec2 env) with Continue -> ())
+		done;
+	with Break ->
+		()
+	end;
+	vnull
+
+let emit_while_gte exec1 f exec2 env =
+	while (num (exec1 env) >= f) do exec2 env done;
+	vnull
+
+let rec run_while_continue exec_cond exec_body env =
+	try
+		while is_true (exec_cond env) do exec_body env done;
+	with Continue ->
+		run_while_continue exec_cond exec_body env
+
+let emit_while exec_cond exec_body env =
+	while is_true (exec_cond env) do exec_body env done;
+	vnull
+
+let emit_while_break exec_cond exec_body env =
+	begin try
+		while is_true (exec_cond env) do exec_body env done;
+	with Break ->
+		()
+	end;
+	vnull
+
+let emit_while_continue exec_cond exec_body env =
+	run_while_continue exec_cond exec_body env;
+	vnull
+
+let emit_while_break_continue exec_cond exec_body env =
+	(try run_while_continue exec_cond exec_body env with Break -> ());
+	vnull
+
+let emit_do_while exec_cond exec_body env =
+	ignore(exec_body env);
+	while is_true (exec_cond env) do exec_body env done;
+	vnull
+
+let emit_do_while_break exec_cond exec_body env =
+	begin try
+		ignore(exec_body env);
+		while is_true (exec_cond env) do exec_body env done;
+	with Break ->
+		()
+	end;
+	vnull
+
+let emit_do_while_continue exec_cond exec_body env =
+	(try ignore(exec_body env) with Continue -> ());
+	run_while_continue exec_cond exec_body env;
+	vnull
+
+let emit_do_while_break_continue exec_cond exec_body env =
+	begin try
+		ignore(exec_body env); run_while_continue exec_cond exec_body env
+	with
+		| Break -> ()
+		| Continue -> try run_while_continue exec_cond exec_body env with Break -> ()
+	end;
+	vnull
+
+let emit_try exec catches env =
+	let ctx = get_ctx() in
+	let eval = get_eval ctx in
+	let environment_offset = eval.environment_offset in
+	if ctx.debug.support_debugger then begin
+		List.iter (fun (_,path,_) -> Hashtbl.add ctx.debug.caught_types path true) catches
+	end;
+	let restore () =
+		List.iter (fun (_,path,_) -> Hashtbl.remove ctx.debug.caught_types path) catches
+	in
+	let v = try
+		let v = exec env in
+		restore();
+		v
+	with RunTimeException(v,_,_) as exc ->
+		restore();
+		build_exception_stack ctx environment_offset;
+		eval.environment_offset <- environment_offset;
+		let exec,_,varacc =
+			try
+				List.find (fun (_,path,i) -> is v path) catches
+			with Not_found ->
+				raise exc
+		in
+		begin match varacc with
+			| Local slot -> env.env_locals.(slot) <- v
+			| Env slot -> env.env_captures.(slot) <- ref v
+		end;
+		exec env
+	in
+	v
+
+(* Control flow *)
+
+let emit_block1 exec1 env =
+	exec1 env
+
+let emit_block2 exec1 exec2 env =
+	ignore(exec1 env);
+	exec2 env
+
+let emit_block3 exec1 exec2 exec3 env =
+	ignore(exec1 env);
+	ignore(exec2 env);
+	exec3 env
+
+let emit_block4 exec1 exec2 exec3 exec4 env =
+	ignore(exec1 env);
+	ignore(exec2 env);
+	ignore(exec3 env);
+	exec4 env
+
+let emit_block5 exec1 exec2 exec3 exec4 exec5 env =
+	ignore(exec1 env);
+	ignore(exec2 env);
+	ignore(exec3 env);
+	ignore(exec4 env);
+	exec5 env
+
+let emit_block execs env =
+	let l = Array.length execs in
+	for i = 0 to l - 2 do
+		ignore((Array.unsafe_get execs i) env)
+	done;
+	(Array.unsafe_get execs (l -1)) env
+
+let emit_value exec env =
+	exec env
+
+let emit_return_null _ = raise (Return vnull)
+
+let emit_return_value exec env = raise (Return (exec env))
+
+let emit_break env = raise Break
+
+let emit_continue env = raise Continue
+
+let emit_throw exec p env = throw (exec env) p
+
+let emit_safe_cast exec t p env =
+	let v1 = exec env in
+	if not (is v1 t) then throw_string "Class cast error" p;
+	v1
+
+(* Calls *)
+
+(* super.call() - immediate *)
+
+let emit_super_field_call slot proto i execs p env =
+	let vthis = env.env_locals.(slot) in
+	let vf = proto.pfields.(i) in
+	let vl = List.map (fun f -> f env) execs in
+	call_value_on vthis vf vl
+
+(* Type.call() - immediate *)
+
+let call0 v p env =
+	env.env_leave_pmin <- p.pmin;
+	env.env_leave_pmax <- p.pmax;
+	match v with
+	| VFunction (Fun0 f,_) -> f ()
+	| VFunction (FunN f,_) -> f []
+	| VFieldClosure(v0,f) -> call_function f [v0]
+	| VInstance {ikind = ILazyType(_,get)} -> get()
+	| _ -> cannot_call v p
+
+let call1 v v1 p env =
+	env.env_leave_pmin <- p.pmin;
+	env.env_leave_pmax <- p.pmax;
+	match v with
+	| VFunction (Fun1 f,_) -> f v1
+	| VFunction (FunN f,_) -> f [v1]
+	| VFieldClosure(v0,f) -> call_function f [v0;v1]
+	| _ -> cannot_call v p
+
+let call2 v v1 v2 p env =
+	env.env_leave_pmin <- p.pmin;
+	env.env_leave_pmax <- p.pmax;
+	match v with
+	| VFunction (Fun2 f,_) -> f v1 v2
+	| VFunction (FunN f,_) -> f [v1;v2]
+	| VFieldClosure(v0,f) -> call_function f [v0;v1;v2]
+	| _ -> cannot_call v p
+
+let call3 v v1 v2 v3 p env =
+	env.env_leave_pmin <- p.pmin;
+	env.env_leave_pmax <- p.pmax;
+	match v with
+	| VFunction (Fun3 f,_) -> f v1 v2 v3
+	| VFunction (FunN f,_) -> f [v1;v2;v3]
+	| VFieldClosure(v0,f) -> call_function f [v0;v1;v2;v3]
+	| _ -> cannot_call v p
+
+let call4 v v1 v2 v3 v4 p env =
+	env.env_leave_pmin <- p.pmin;
+	env.env_leave_pmax <- p.pmax;
+	match v with
+	| VFunction (Fun4 f,_) -> f v1 v2 v3 v4
+	| VFunction (FunN f,_) -> f [v1;v2;v3;v4]
+	| VFieldClosure(v0,f) -> call_function f [v0;v1;v2;v3;v4]
+	| _ -> cannot_call v p
+
+let call5 v v1 v2 v3 v4 v5 p env =
+	env.env_leave_pmin <- p.pmin;
+	env.env_leave_pmax <- p.pmax;
+	match v with
+	| VFunction (Fun5 f,_) -> f v1 v2 v3 v4 v5
+	| VFunction (FunN f,_) -> f [v1;v2;v3;v4;v5]
+	| VFieldClosure(v0,f) -> call_function f [v0;v1;v2;v3;v4;v5]
+	| _ -> cannot_call v p
+
+let emit_proto_field_call proto i execs p =
+	match execs with
+		| [] ->
+			let vf = lazy (match proto.pfields.(i) with VFunction (Fun0 f,_) -> f | v -> cannot_call v p) in
+			(fun env ->
+				env.env_leave_pmin <- p.pmin;
+				env.env_leave_pmax <- p.pmax;
+				(Lazy.force vf) ()
+			)
+		| [exec1] ->
+			let vf = lazy (match proto.pfields.(i) with VFunction (Fun1 f,_) -> f | v -> cannot_call v p) in
+			(fun env ->
+				let f = Lazy.force vf in
+				let v1 = exec1 env in
+				env.env_leave_pmin <- p.pmin;
+				env.env_leave_pmax <- p.pmax;
+				f v1
+			)
+		| [exec1;exec2] ->
+			let vf = lazy (match proto.pfields.(i) with VFunction (Fun2 f,_) -> f | v -> cannot_call v p) in
+			(fun env ->
+				let f = Lazy.force vf in
+				let v1 = exec1 env in
+				let v2 = exec2 env in
+				env.env_leave_pmin <- p.pmin;
+				env.env_leave_pmax <- p.pmax;
+				f v1 v2
+			)
+		| [exec1;exec2;exec3] ->
+			let vf = lazy (match proto.pfields.(i) with VFunction (Fun3 f,_) -> f | v -> cannot_call v p) in
+			(fun env ->
+				let f = Lazy.force vf in
+				let v1 = exec1 env in
+				let v2 = exec2 env in
+				let v3 = exec3 env in
+				env.env_leave_pmin <- p.pmin;
+				env.env_leave_pmax <- p.pmax;
+				f v1 v2 v3
+			)
+		| [exec1;exec2;exec3;exec4] ->
+			let vf = lazy (match proto.pfields.(i) with VFunction (Fun4 f,_) -> f | v -> cannot_call v p) in
+			(fun env ->
+				let f = Lazy.force vf in
+				let v1 = exec1 env in
+				let v2 = exec2 env in
+				let v3 = exec3 env in
+				let v4 = exec4 env in
+				env.env_leave_pmin <- p.pmin;
+				env.env_leave_pmax <- p.pmax;
+				f v1 v2 v3 v4
+			)
+		| [exec1;exec2;exec3;exec4;exec5] ->
+			let vf = lazy (match proto.pfields.(i) with VFunction (Fun5 f,_) -> f | v -> cannot_call v p) in
+			(fun env ->
+				let f = Lazy.force vf in
+				let v1 = exec1 env in
+				let v2 = exec2 env in
+				let v3 = exec3 env in
+				let v4 = exec4 env in
+				let v5 = exec5 env in
+				env.env_leave_pmin <- p.pmin;
+				env.env_leave_pmax <- p.pmax;
+				f v1 v2 v3 v4 v5
+			)
+		| _ ->
+			let vf = lazy (match proto.pfields.(i) with VFunction (FunN f,_) -> f | v -> cannot_call v p) in
+			(fun env ->
+				let f = Lazy.force vf in
+				let vl = List.map (fun exec -> exec env) execs in
+				env.env_leave_pmin <- p.pmin;
+				env.env_leave_pmax <- p.pmax;
+				f vl
+			)
+
+(* instance.call() where call is overridden - dynamic dispatch *)
+
+let emit_method_call exec name execs p =
+	let vf vthis = match vthis with
+		| VInstance {iproto = proto} | VPrototype proto -> proto_field_raise proto name
+		| VString _ -> proto_field_raise (get_ctx()).string_prototype name
+		| _ -> unexpected_value_p vthis "instance" p
+	in
+	match execs with
+		| [] ->
+			(fun env ->
+				let vthis = exec env in
+				let vf = vf vthis in
+				call1 vf vthis p env
+			)
+		| [exec1] ->
+			(fun env ->
+				let vthis = exec env in
+				let vf = vf vthis in
+				let v1 = exec1 env in
+				call2 vf vthis v1 p env
+			)
+		| [exec1;exec2] ->
+			(fun env ->
+				let vthis = exec env in
+				let vf = vf vthis in
+				let v1 = exec1 env in
+				let v2 = exec2 env in
+				call3 vf vthis v1 v2 p env
+			)
+		| [exec1;exec2;exec3] ->
+			(fun env ->
+				let vthis = exec env in
+				let vf = vf vthis in
+				let v1 = exec1 env in
+				let v2 = exec2 env in
+				let v3 = exec3 env in
+				call4 vf vthis v1 v2 v3 p env
+			)
+		| [exec1;exec2;exec3;exec4] ->
+			(fun env ->
+				let vthis = exec env in
+				let vf = vf vthis in
+				let v1 = exec1 env in
+				let v2 = exec2 env in
+				let v3 = exec3 env in
+				let v4 = exec4 env in
+				call5 vf vthis v1 v2 v3 v4 p env
+			)
+		| _ ->
+			(fun env ->
+				let vthis = exec env in
+				let vf = vf vthis in
+				let vl = List.map (apply env) execs in
+				env.env_leave_pmin <- p.pmin;
+				env.env_leave_pmax <- p.pmax;
+				call_value_on vthis vf vl
+			)
+
+(* instance.call() where call is not a method - lookup + this-binding *)
+
+let emit_field_call exec name execs p env =
+	let vthis = exec env in
+	let vf = field vthis name in
+	env.env_leave_pmin <- p.pmin;
+	env.env_leave_pmax <- p.pmax;
+	call_value_on vthis vf (List.map (apply env) execs)
+
+(* new() - immediate + this-binding *)
+
+let emit_constructor_call0 proto vf p env =
+	let vthis = create_instance_direct proto in
+	env.env_leave_pmin <- p.pmin;
+	env.env_leave_pmax <- p.pmax;
+	ignore((Lazy.force vf) vthis);
+	vthis
+
+let emit_constructor_call1 proto vf exec1 p env =
+	let f = Lazy.force vf in
+	let vthis = create_instance_direct proto in
+	let v1 = exec1 env in
+	env.env_leave_pmin <- p.pmin;
+	env.env_leave_pmax <- p.pmax;
+	ignore(f vthis v1);
+	vthis
+
+let emit_constructor_call2 proto vf exec1 exec2 p env =
+	let f = Lazy.force vf in
+	let vthis = create_instance_direct proto in
+	let v1 = exec1 env in
+	let v2 = exec2 env in
+	env.env_leave_pmin <- p.pmin;
+	env.env_leave_pmax <- p.pmax;
+	ignore(f vthis v1 v2);
+	vthis
+
+let emit_constructor_call3 proto vf exec1 exec2 exec3 p env =
+	let f = Lazy.force vf in
+	let vthis = create_instance_direct proto in
+	let v1 = exec1 env in
+	let v2 = exec2 env in
+	let v3 = exec3 env in
+	env.env_leave_pmin <- p.pmin;
+	env.env_leave_pmax <- p.pmax;
+	ignore(f vthis v1 v2 v3);
+	vthis
+
+let emit_constructor_call4 proto vf exec1 exec2 exec3 exec4 p env =
+	let f = Lazy.force vf in
+	let vthis = create_instance_direct proto in
+	let v1 = exec1 env in
+	let v2 = exec2 env in
+	let v3 = exec3 env in
+	let v4 = exec4 env in
+	env.env_leave_pmin <- p.pmin;
+	env.env_leave_pmax <- p.pmax;
+	ignore(f vthis v1 v2 v3 v4);
+	vthis
+
+let emit_constructor_callN proto vf execs p env =
+	let f = Lazy.force vf in
+	let vthis = create_instance_direct proto in
+	let vl = List.map (fun exec -> exec env) execs in
+	env.env_leave_pmin <- p.pmin;
+	env.env_leave_pmax <- p.pmax;
+	ignore(f (vthis :: vl));
+	vthis
+
+let emit_constructor_call proto fnew execs p =
+	match execs with
+		| [] ->
+			let vf = lazy (match Lazy.force fnew with VFunction (Fun1 f,_) -> f | v -> cannot_call v p) in
+			emit_constructor_call0 proto vf p
+		| [exec1] ->
+			let vf = lazy (match Lazy.force fnew with VFunction (Fun2 f,_) -> f | v -> cannot_call v p) in
+			emit_constructor_call1 proto vf exec1 p
+		| [exec1;exec2] ->
+			let vf = lazy (match Lazy.force fnew with VFunction (Fun3 f,_) -> f | v -> cannot_call v p) in
+			emit_constructor_call2 proto vf exec1 exec2 p
+		| [exec1;exec2;exec3] ->
+			let vf = lazy (match Lazy.force fnew with VFunction (Fun4 f,_) -> f | v -> cannot_call v p) in
+			emit_constructor_call3 proto vf exec1 exec2 exec3 p
+		| [exec1;exec2;exec3;exec4] ->
+			let vf = lazy (match Lazy.force fnew with VFunction (Fun5 f,_) -> f | v -> cannot_call v p) in
+			emit_constructor_call4 proto vf exec1 exec2 exec3 exec4 p
+		| _ ->
+			let vf = lazy (match Lazy.force fnew with VFunction (FunN f,_) -> f | v -> cannot_call v p) in
+			emit_constructor_callN proto vf execs p
+
+(* super() - immediate + this-binding *)
+
+let emit_super_call0 vf p env =
+	let vthis = env.env_locals.(0) in
+	env.env_leave_pmin <- p.pmin;
+	env.env_leave_pmax <- p.pmax;
+	ignore((Lazy.force vf) vthis);
+	vthis
+
+let emit_super_call1 vf exec1 p env =
+	let f = Lazy.force vf in
+	let vthis = env.env_locals.(0) in
+	let v1 = exec1 env in
+	env.env_leave_pmin <- p.pmin;
+	env.env_leave_pmax <- p.pmax;
+	ignore(f vthis v1);
+	vthis
+
+let emit_super_call2 vf exec1 exec2 p env =
+	let f = Lazy.force vf in
+	let vthis = env.env_locals.(0) in
+	let v1 = exec1 env in
+	let v2 = exec2 env in
+	env.env_leave_pmin <- p.pmin;
+	env.env_leave_pmax <- p.pmax;
+	ignore(f vthis v1 v2);
+	vthis
+
+let emit_super_call3 vf exec1 exec2 exec3 p env =
+	let f = Lazy.force vf in
+	let vthis = env.env_locals.(0) in
+	let v1 = exec1 env in
+	let v2 = exec2 env in
+	let v3 = exec3 env in
+	env.env_leave_pmin <- p.pmin;
+	env.env_leave_pmax <- p.pmax;
+	ignore(f vthis v1 v2 v3);
+	vthis
+
+let emit_super_call4 vf exec1 exec2 exec3 exec4 p env =
+	let f = Lazy.force vf in
+	let vthis = env.env_locals.(0) in
+	let v1 = exec1 env in
+	let v2 = exec2 env in
+	let v3 = exec3 env in
+	let v4 = exec4 env in
+	env.env_leave_pmin <- p.pmin;
+	env.env_leave_pmax <- p.pmax;
+	ignore(f vthis v1 v2 v3 v4);
+	vthis
+
+let emit_super_callN vf execs p env =
+	let f = Lazy.force vf in
+	let vthis = env.env_locals.(0) in
+	let vl = List.map (fun exec -> exec env) execs in
+	env.env_leave_pmin <- p.pmin;
+	env.env_leave_pmax <- p.pmax;
+	ignore(f (vthis :: vl));
+	vthis
+
+let emit_special_super_call fnew execs env =
+	let vl = List.map (apply env) execs in
+	let vi' = fnew vl in
+	let vthis = env.env_locals.(0) in
+	(* This isn't very elegant, but it's probably a rare case to extend these types. *)
+	begin match vthis,vi' with
+		| VInstance vi,VInstance vi' -> vi.ikind <- vi'.ikind
+		| _ -> assert false
+	end;
+	vnull
+
+let emit_super_call fnew execs p =
+	match execs with
+		| [] ->
+			let vf = lazy (match Lazy.force fnew with VFunction (Fun1 f,_) -> f | v -> cannot_call v p) in
+			emit_super_call0 vf p
+		| [exec1] ->
+			let vf = lazy (match Lazy.force fnew with VFunction (Fun2 f,_) -> f | v -> cannot_call v p) in
+			emit_super_call1 vf exec1 p
+		| [exec1;exec2] ->
+			let vf = lazy (match Lazy.force fnew with VFunction (Fun3 f,_) -> f | v -> cannot_call v p) in
+			emit_super_call2 vf exec1 exec2 p
+		| [exec1;exec2;exec3] ->
+			let vf = lazy (match Lazy.force fnew with VFunction (Fun4 f,_) -> f | v -> cannot_call v p) in
+			emit_super_call3 vf exec1 exec2 exec3 p
+		| [exec1;exec2;exec3;exec4] ->
+			let vf = lazy (match Lazy.force fnew with VFunction (Fun5 f,_) -> f | v -> cannot_call v p) in
+			emit_super_call4 vf exec1 exec2 exec3 exec4 p
+		| _ ->
+			let vf = lazy (match Lazy.force fnew with VFunction (FunN f,_) -> f | v -> cannot_call v p) in
+			emit_super_callN vf execs p
+
+(* unknown call - full lookup *)
+
+let emit_call0 exec p env =
+	call0 (exec env) p env
+
+let emit_call1 exec exec1 p env =
+	let v0 = exec env in
+	let v1 = exec1 env in
+	call1 v0 v1 p env
+
+let emit_call2 exec exec1 exec2 p env =
+	let v0 = exec env in
+	let v1 = exec1 env in
+	let v2 = exec2 env in
+	call2 v0 v1 v2 p env
+
+let emit_call3 exec exec1 exec2 exec3 p env =
+	let v0 = exec env in
+	let v1 = exec1 env in
+	let v2 = exec2 env in
+	let v3 = exec3 env in
+	call3 v0 v1 v2 v3 p env
+
+let emit_call4 exec exec1 exec2 exec3 exec4 p env =
+	let v0 = exec env in
+	let v1 = exec1 env in
+	let v2 = exec2 env in
+	let v3 = exec3 env in
+	let v4 = exec4 env in
+	call4 v0 v1 v2 v3 v4 p env
+
+let emit_call5 exec exec1 exec2 exec3 exec4 exec5 p env =
+	let v0 = exec env in
+	let v1 = exec1 env in
+	let v2 = exec2 env in
+	let v3 = exec3 env in
+	let v4 = exec4 env in
+	let v5 = exec5 env in
+	call5 v0 v1 v2 v3 v4 v5 p env
+
+let emit_call exec execs p env =
+	let v1 = exec env in
+	env.env_leave_pmin <- p.pmin;
+	env.env_leave_pmax <- p.pmax;
+	call_value v1 (List.map (apply env) execs)
+
+(* Read *)
+
+let emit_local_read i env = env.env_locals.(i)
+
+let emit_capture_read i env = !(env.env_captures.(i))
+
+let emit_array_length_read exec env = match exec env with
+	| VArray va -> vint (va.alength)
+	| v -> unexpected_value v "Array"
+
+let emit_vector_length_read exec env = match exec env with
+	| VVector vv -> vint (Array.length vv)
+	| v -> unexpected_value v "Vector"
+
+let emit_bytes_length_read exec env = match exec env with
+	| VInstance {ikind = IBytes s} -> vint (Bytes.length s)
+	| v -> unexpected_value v "Bytes"
+
+let emit_proto_field_read proto i env =
+	proto.pfields.(i)
+
+let emit_instance_local_field_read iv i env = match env.env_locals.(iv) with
+	| VInstance vi -> vi.ifields.(i)
+	| VString(_,s) -> vint (String.length (Lazy.force s))
+	| v -> unexpected_value v "instance"
+
+let emit_instance_field_read exec i env = match exec env with
+	| VInstance vi -> vi.ifields.(i)
+	| VString(_,s) -> vint (String.length (Lazy.force s))
+	| v -> unexpected_value v "instance"
+
+let emit_field_closure exec name env =
+	let v = exec env in
+	dynamic_field v name
+
+let emit_anon_local_field_read iv proto i name p env =
+	match env.env_locals.(iv) with
+	| VObject o ->
+		if proto == o.oproto then o.ofields.(i)
+		else object_field o name
+	| VNull -> throw_string "field access on null" p
+	| v -> field v name
+
+let emit_anon_field_read exec proto i name p env =
+	match exec env with
+	| VObject o ->
+		if proto == o.oproto then o.ofields.(i)
+		else object_field o name
+	| VNull -> throw_string "field access on null" p
+	| v -> field v name
+
+let emit_field_read exec name p env = match exec env with
+	| VNull -> throw_string "field access on null" p
+	| v -> field v name
+
+let emit_array_local_read i exec2 p env =
+	let va = env.env_locals.(i) in
+	let vi = exec2 env in
+	let i = decode_int_p vi p in
+	if i < 0 then vnull
+	else EvalArray.get (decode_varray va) i
+
+let emit_array_read exec1 exec2 p env =
+	let va = exec1 env in
+	let vi = exec2 env in
+	let i = decode_int_p vi p in
+	if i < 0 then vnull
+	else EvalArray.get (decode_varray va) i
+
+let emit_vector_local_read i exec2 p env =
+	let vv = env.env_locals.(i) in
+	let vi = exec2 env in
+	let i = decode_int_p vi p in
+	if i < 0 then vnull
+	else Array.unsafe_get (decode_vector vv) i
+
+let emit_vector_read exec1 exec2 p env =
+	let vv = exec1 env in
+	let vi = exec2 env in
+	let i = decode_int_p vi p in
+	if i < 0 then vnull
+	else Array.unsafe_get (decode_vector vv) i
+
+let emit_enum_index exec env = match exec env with
+	| VEnumValue ev -> vint ev.eindex
+	| v -> unexpected_value v "enum value"
+
+let emit_enum_parameter_read exec i env = match exec env with
+	| VEnumValue ev -> (try ev.eargs.(i) with Not_found -> vnull)
+	| v1 -> unexpected_value v1 "enum value"
+
+let emit_string_cca exec1 exec2 p env =
+	let s = decode_string (exec1 env) in
+	let index = decode_int_p (exec2 env) p in
+	if index >= String.length s then vnull
+	else vint (int_of_char s.[index])
+
+(* Write *)
+
+let emit_bytes_length_write exec1 exec2 env =
+	let v1 = exec1 env in
+	let v2 = exec2 env in
+	set_bytes_length_field v1 v2;
+	v2
+
+let emit_local_write slot exec env =
+	let v = exec env in
+	env.env_locals.(slot) <- v;
+	v
+
+let emit_capture_write slot exec env =
+	let v = exec env in
+	env.env_captures.(slot) := v;
+	v
+
+let emit_proto_field_write proto i exec2 env =
+	let v = exec2 env in
+	proto.pfields.(i) <- v;
+	v
+
+let emit_instance_field_write exec1 i exec2 env = match exec1 env with
+	| VInstance vi ->
+		let v = exec2 env in
+		vi.ifields.(i) <- v;
+		v
+	| v -> unexpected_value v "instance"
+
+let emit_anon_field_write exec1 proto i name exec2 env =
+	let v1 = exec1 env in
+	let v2 = exec2 env in
+	begin match v1 with
+		| VObject o ->
+			if proto == o.oproto then begin
+				o.ofields.(i) <- v2;
+				o.oremoved <- IntMap.remove name o.oremoved;
+			end else set_object_field o name v2
+		| _ ->
+			set_field v1 name v2;
+	end;
+	v2
+
+let emit_field_write exec1 name exec2 env =
+	let v1 = exec1 env in
+	let v2 = exec2 env in
+	set_field v1 name v2;
+	v2
+
+let emit_array_local_write i exec2 exec3 p env =
+	let va = env.env_locals.(i) in
+	let vi = exec2 env in
+	let v3 = exec3 env in
+	let i = decode_int_p vi p in
+	if i < 0 then throw_string (Printf.sprintf "Negative array index: %i" i) p;
+	EvalArray.set (decode_varray va) i v3;
+	v3
+
+let emit_array_write exec1 exec2 exec3 p env =
+	let va = exec1 env in
+	let vi = exec2 env in
+	let v3 = exec3 env in
+	let i = decode_int_p vi p in
+	if i < 0 then throw_string (Printf.sprintf "Negative array index: %i" i) p;
+	EvalArray.set (decode_varray va) i v3;
+	v3
+
+let emit_vector_local_write i exec2 exec3 p env =
+	let vv = env.env_locals.(i) in
+	let vi = exec2 env in
+	let v3 = exec3 env in
+	let i = decode_int_p vi p in
+	if i < 0 then throw_string (Printf.sprintf "Negative vector index: %i" i) p;
+	Array.unsafe_set (decode_vector vv) i v3;
+	v3
+
+let emit_vector_write exec1 exec2 exec3 p env =
+	let vv = exec1 env in
+	let vi = exec2 env in
+	let v3 = exec3 env in
+	let i = decode_int_p vi p in
+	if i < 0 then throw_string (Printf.sprintf "Negative vector index: %i" i) p;
+	Array.unsafe_set (decode_vector vv) i v3;
+	v3
+
+(* Read + write *)
+
+let emit_local_read_write slot exec fop prefix env =
+	let v1 = env.env_locals.(slot) in
+	let v2 = exec env in
+	let v = fop v1 v2 in
+	env.env_locals.(slot) <- v;
+	if prefix then v else v1
+
+let emit_local_incr_postfix slot env =
+	let vi = env.env_locals.(slot) in
+	env.env_locals.(slot) <- vint32 (Int32.succ (decode_i32 vi));
+	vi
+
+let emit_local_incr_prefix slot env =
+	let vi = env.env_locals.(slot) in
+	let v = vint32 (Int32.succ (decode_i32 vi)) in
+	env.env_locals.(slot) <- v;
+	v
+
+let emit_local_decr_postfix slot env =
+	let vi = env.env_locals.(slot) in
+	env.env_locals.(slot) <- vint32 (Int32.pred (decode_i32 vi));
+	vi
+
+let emit_local_decr_prefix slot env =
+	let vi = env.env_locals.(slot) in
+	let v = vint32 (Int32.pred (decode_i32 vi)) in
+	env.env_locals.(slot) <- v;
+	v
+
+let emit_capture_read_write slot exec fop prefix env =
+	let v1 = !(env.env_captures.(slot)) in
+	let v2 = exec env in
+	let v = fop v1 v2 in
+	env.env_captures.(slot) := v;
+	if prefix then v else v1
+
+let emit_capture_incr_postfix slot env =
+	let vi = !(env.env_captures.(slot)) in
+	env.env_captures.(slot) := vint32 (Int32.succ (decode_i32 vi));
+	vi
+
+let emit_capture_incr_prefix slot env =
+	let vi = !(env.env_captures.(slot)) in
+	let v = vint32 (Int32.succ (decode_i32 vi)) in
+	env.env_captures.(slot) := v;
+	v
+
+let emit_capture_decr_postfix slot env =
+	let vi = !(env.env_captures.(slot)) in
+	env.env_captures.(slot) := vint32 (Int32.pred (decode_i32 vi));
+	vi
+
+let emit_capture_decr_prefix slot env =
+	let vi = !(env.env_captures.(slot)) in
+	let v = vint32 (Int32.pred (decode_i32 vi)) in
+	env.env_captures.(slot) := v;
+	v
+
+let emit_proto_field_read_write proto i exec2 fop prefix env =
+	let vf = proto.pfields.(i) in
+	let v2 = exec2 env in
+	let v = fop vf v2 in
+	proto.pfields.(i) <- v;
+	if prefix then v else vf
+
+let instance_field_read_write vi i exec2 fop prefix env =
+	let vf = vi.ifields.(i) in
+	let v2 = exec2 env in
+	let v = fop vf v2 in
+	vi.ifields.(i) <- v;
+	if prefix then v else vf
+
+let emit_instance_field_read_write exec1 i exec2 fop prefix env = match exec1 env with
+	| VInstance vi -> instance_field_read_write vi i exec2 fop prefix env
+	| v -> unexpected_value v "instance"
+
+let emit_field_read_write exec1 name exec2 fop prefix env =
+	let v1 = exec1 env in
+	match v1 with
+	| VObject o ->
+		let vf = object_field o name in
+		let v2 = exec2 env in
+		let v = fop vf v2 in
+		set_object_field o name v;
+		if prefix then v else vf
+	| VInstance vi ->
+		let i = get_instance_field_index vi.iproto name null_pos in
+		instance_field_read_write vi i exec2 fop prefix env
+	| VPrototype proto ->
+		let i = get_proto_field_index proto name in
+		emit_proto_field_read_write proto i exec2 fop prefix env
+	| _ ->
+		let vf = field v1 name in
+		let v2 = exec2 env in
+		let v = fop vf v2 in
+		set_field v1 name v;
+		if prefix then v else vf
+
+let emit_array_local_read_write i exec2 exec3 fop prefix p env =
+	let va1 = env.env_locals.(i) in
+	let va2 = exec2 env in
+	let va = decode_varray va1 in
+	let i = decode_int_p va2 p in
+	if i < 0 then throw_string (Printf.sprintf "Negative array index: %i" i) p;
+	let v = EvalArray.get va i in
+	let v2 = exec3 env in
+	let v3 = fop v v2 in
+	EvalArray.set va i v3;
+	if prefix then v3 else v
+
+let emit_array_read_write exec1 exec2 exec3 fop prefix p env =
+	let va1 = exec1 env in
+	let va2 = exec2 env in
+	let va = decode_varray va1 in
+	let i = decode_int_p va2 p in
+	if i < 0 then throw_string (Printf.sprintf "Negative array index: %i" i) p;
+	let v = EvalArray.get va i in
+	let v2 = exec3 env in
+	let v3 = fop v v2 in
+	EvalArray.set va i v3;
+	if prefix then v3 else v
+
+let emit_vector_local_read_write i exec2 exec3 fop prefix p env =
+	let va1 = env.env_locals.(i) in
+	let va2 = exec2 env in
+	let va = decode_vector va1 in
+	let i = decode_int_p va2 p in
+	if i < 0 then throw_string (Printf.sprintf "Negative vector index: %i" i) p;
+	let v = Array.unsafe_get va i in
+	let v2 = exec3 env in
+	let v3 = fop v v2 in
+	Array.unsafe_set va i v3;
+	if prefix then v3 else v
+
+let emit_vector_read_write exec1 exec2 exec3 fop prefix p env =
+	let va1 = exec1 env in
+	let va2 = exec2 env in
+	let va = decode_vector va1 in
+	let i = decode_int_p va2 p in
+	if i < 0 then throw_string (Printf.sprintf "Negative vector index: %i" i) p;
+	let v = Array.unsafe_get va i in
+	let v2 = exec3 env in
+	let v3 = fop v v2 in
+	Array.unsafe_set va i v3;
+	if prefix then v3 else v
+
+(* Ops *)
+
+let emit_eq_null exec env = match exec env with
+	| VNull -> VTrue
+	| _ -> VFalse
+
+let emit_not_eq_null exec env = match exec env with
+	| VNull -> VFalse
+	| _ -> VTrue
+
+let op_add v1 v2 = match v1,v2 with
+	| VInt32 i1,VInt32 i2 -> vint32 (Int32.add i1 i2)
+	| VFloat f1,VFloat f2 -> vfloat (f1 +. f2)
+	| VInt32 i,VFloat f | VFloat f,VInt32 i -> vfloat ((Int32.to_float i) +. f)
+	| VString(s1,_),VString(s2,_) -> encode_rope (Rope.concat2 s1 s2)
+	| VString(s1,_),v2 -> encode_rope (Rope.concat2 s1 (s_value 0 v2))
+	| v1,VString(s2,_) -> encode_rope (Rope.concat2 (s_value 0 v1) s2)
+	| v1,v2 -> encode_rope (Rope.concat2 (s_value 0 v1) (s_value 0 v2))
+
+let op_mult p v1 v2 = match v1,v2 with
+	| VInt32 i1,VInt32 i2 -> vint32 (Int32.mul i1 i2)
+	| VFloat f1,VFloat f2 -> vfloat (f1 *. f2)
+	| VInt32 i,VFloat f | VFloat f,VInt32 i -> vfloat ((Int32.to_float i) *. f)
+	| _ -> invalid_binop OpMult v1 v2 p
+
+let op_div p v1 v2 = match v1,v2 with
+	| VInt32 i1,VInt32 i2 -> vfloat ((Int32.to_float i1) /. (Int32.to_float i2))
+	| VFloat f1,VFloat f2 -> vfloat (f1 /. f2)
+	| VInt32 i1,VFloat f2 -> vfloat ((Int32.to_float i1) /. f2)
+	| VFloat f1,VInt32 i2 -> vfloat (f1 /. (Int32.to_float i2))
+	| _ -> invalid_binop OpDiv v1 v2 p
+
+let op_sub p v1 v2 = match v1,v2 with
+	| VInt32 i1,VInt32 i2 -> vint32 (Int32.sub i1 i2)
+	| VFloat f1,VFloat f2 -> vfloat (f1 -. f2)
+	| VInt32 i1,VFloat f2 -> vfloat ((Int32.to_float i1) -. f2)
+	| VFloat f1,VInt32 i2 -> vfloat (f1 -. (Int32.to_float i2))
+	| _ -> invalid_binop OpSub v1 v2 p
+
+let op_eq v1 v2 = vbool (equals v1 v2)
+
+let op_not_eq v1 v2 = vbool (not (equals v1 v2))
+
+let op_gt v1 v2 = vbool (compare v1 v2 = CSup)
+
+let op_gte v1 v2 = vbool (match compare v1 v2 with CSup | CEq -> true | _ -> false)
+
+let op_lt v1 v2 = vbool (compare v1 v2 = CInf)
+
+let op_lte v1 v2 = vbool (match compare v1 v2 with CInf | CEq -> true | _ -> false)
+
+let op_and p v1 v2 = match v1,v2 with
+	| VInt32 i1,VInt32 i2 -> vint32 (Int32.logand i1 i2)
+	| _ -> invalid_binop OpAnd v1 v2 p
+
+let op_or p v1 v2 = match v1,v2 with
+	| VInt32 i1,VInt32 i2 -> vint32 (Int32.logor i1 i2)
+	| _ -> invalid_binop OpOr v1 v2 p
+
+let op_xor p v1 v2 = match v1,v2 with
+	| VInt32 i1,VInt32 i2 -> vint32 (Int32.logxor i1 i2)
+	| _ -> invalid_binop OpXor v1 v2 p
+
+let op_shl p v1 v2 = match v1,v2 with
+	| VInt32 i1,VInt32 i2 -> vint32 (Int32.shift_left i1 (Int32.to_int i2))
+	| _ -> invalid_binop OpShl v1 v2 p
+
+let op_shr p v1 v2 = match v1,v2 with
+	| VInt32 i1,VInt32 i2 -> vint32 (Int32.shift_right i1 (Int32.to_int i2))
+	| _ -> invalid_binop OpShr v1 v2 p
+
+let op_ushr p v1 v2 = match v1,v2 with
+	| VInt32 i1,VInt32 i2 -> vint32 (Int32.shift_right_logical i1 (Int32.to_int i2))
+	| _ -> invalid_binop OpUShr v1 v2 p
+
+let op_mod p v1 v2 = match v1,v2 with
+	| VInt32 i1,VInt32 i2 -> vint32 (Int32.rem i1 i2)
+	| VFloat f1,VFloat f2 -> vfloat (mod_float f1 f2)
+	| VInt32 i1,VFloat f2 -> vfloat (mod_float (Int32.to_float i1) f2)
+	| VFloat f1,VInt32 i2 -> vfloat (mod_float f1 (Int32.to_float i2))
+	| _ -> invalid_binop OpMod v1 v2 p
+
+let emit_op_add exec1 exec2 env =
+	let v1 = exec1 env in
+	let v2 = exec2 env in
+	op_add v1 v2
+
+let emit_op_mult p exec1 exec2 env =
+	let v1 = exec1 env in
+	let v2 = exec2 env in
+	op_mult p v1 v2
+
+let emit_op_div p exec1 exec2 env =
+	let v1 = exec1 env in
+	let v2 = exec2 env in
+	op_div p v1 v2
+
+let emit_op_sub p exec1 exec2 env =
+	let v1 = exec1 env in
+	let v2 = exec2 env in
+	op_sub p v1 v2
+
+let emit_op_eq exec1 exec2 env =
+	let v1 = exec1 env in
+	let v2 = exec2 env in
+	vbool (equals v1 v2)
+
+let emit_op_not_eq exec1 exec2 env =
+	let v1 = exec1 env in
+	let v2 = exec2 env in
+	vbool  (not (equals v1 v2))
+
+let emit_op_gt exec1 exec2 env =
+	let v1 = exec1 env in
+	let v2 = exec2 env in
+	vbool (compare v1 v2 = CSup)
+
+let emit_op_gte exec1 exec2 env =
+	let v1 = exec1 env in
+	let v2 = exec2 env in
+	vbool (match compare v1 v2 with CSup | CEq -> true | _ -> false)
+
+let emit_op_lt exec1 exec2 env =
+	let v1 = exec1 env in
+	let v2 = exec2 env in
+	vbool (compare v1 v2 = CInf)
+
+let emit_op_lte exec1 exec2 env =
+	let v1 = exec1 env in
+	let v2 = exec2 env in
+	vbool (match compare v1 v2 with CInf | CEq -> true | _ -> false)
+
+let emit_op_and p exec1 exec2 env =
+	let v1 = exec1 env in
+	let v2 = exec2 env in
+	op_and p v1 v2
+
+let emit_op_or p exec1 exec2 env =
+	let v1 = exec1 env in
+	let v2 = exec2 env in
+	op_or p v1 v2
+
+let emit_op_xor p exec1 exec2 env =
+	let v1 = exec1 env in
+	let v2 = exec2 env in
+	op_xor p v1 v2
+
+let emit_op_shl p exec1 exec2 env =
+	let v1 = exec1 env in
+	let v2 = exec2 env in
+	op_shl p v1 v2
+
+let emit_op_shr p exec1 exec2 env =
+	let v1 = exec1 env in
+	let v2 = exec2 env in
+	op_shr p v1 v2
+
+let emit_op_ushr p exec1 exec2 env =
+	let v1 = exec1 env in
+	let v2 = exec2 env in
+	op_ushr p v1 v2
+
+let emit_op_mod p exec1 exec2 env =
+	let v1 = exec1 env in
+	let v2 = exec2 env in
+	op_mod p v1 v2
+
+let emit_not exec env = match exec env with
+	| VNull | VFalse -> VTrue
+	| _ -> VFalse
+
+let emit_bool_and exec1 exec2 env =
+	if is_true (exec1 env) then exec2 env
+	else VFalse
+
+let emit_bool_or exec1 exec2 env =
+	if is_true (exec1 env) then VTrue
+	else exec2 env
+
+let emit_neg exec p env = match exec env with
+	| VFloat f -> vfloat (-.f)
+	| VInt32 i -> vint32 (Int32.neg i)
+	| _ -> throw_string "Invalid operation" p
+
+(* Function *)
+
+let handle_capture_arguments exec varaccs env =
+	List.iter (fun (slot,i) ->
+		env.env_captures.(i) <- ref env.env_locals.(slot)
+	) varaccs;
+	exec env
+
+let run_function ctx exec env =
+	let v = try
+		exec env
+	with
+		| Return v -> v
+	in
+	env.env_in_use <- false;
+	pop_environment ctx env;
+	v
+[@@inline]
+
+let run_function_noret ctx exec env =
+	let v = exec env in
+	env.env_in_use <- false;
+	pop_environment ctx env;
+	v
+[@@inline]
+
+let get_normal_env ctx info num_locals num_captures _ =
+	push_environment ctx info num_locals num_captures
+
+let get_closure_env ctx info num_locals num_captures refs =
+	let env = push_environment ctx info num_locals num_captures in
+	Array.iteri (fun i vr -> env.env_captures.(i) <- vr) refs;
+	env
+
+let get_normal_env_opt ctx default_env info num_locals num_captures _ =
+	if default_env.env_in_use then begin
+		push_environment ctx info num_locals num_captures
+	end else begin
+		default_env.env_in_use <- true;
+		default_env
+	end
+
+let get_closure_env_opt ctx default_env info num_locals num_captures refs =
+	let env = if default_env.env_in_use then begin
+		push_environment ctx info num_locals num_captures
+	end else begin
+		default_env.env_in_use <- true;
+		default_env
+	end in
+	Array.iteri (fun i vr -> env.env_captures.(i) <- vr) refs;
+	env
+
+let create_function ctx num_args get_env hasret refs exec =
+	match num_args with
+	| 0 ->
+		if hasret then Fun0 (fun () ->
+			let env = get_env refs in
+			run_function ctx exec env
+		)
+		else Fun0 (fun () ->
+			let env = get_env refs in
+			run_function_noret ctx exec env
+		)
+	| 1 ->
+		if hasret then Fun1 (fun v1 ->
+			let env = get_env refs in
+			env.env_locals.(0) <- v1;
+			run_function ctx exec env
+		)
+		else Fun1 (fun v1 ->
+			let env = get_env refs in
+			env.env_locals.(0) <- v1;
+			run_function_noret ctx exec env
+		)
+	| 2 ->
+		let run v1 v2 =
+			let env = get_env refs in
+			env.env_locals.(0) <- v1;
+			env.env_locals.(1) <- v2;
+			env
+		in
+		if hasret then Fun2 (fun v1 v2 ->
+			let env = run v1 v2 in
+			run_function ctx exec env
+		)
+		else Fun2 (fun v1 v2 ->
+			let env = run v1 v2 in
+			run_function_noret ctx exec env
+		)
+	| 3 ->
+		let run v1 v2 v3 =
+			let env = get_env refs in
+			env.env_locals.(0) <- v1;
+			env.env_locals.(1) <- v2;
+			env.env_locals.(2) <- v3;
+			env
+		in
+		if hasret then Fun3 (fun v1 v2 v3 ->
+			let env = run v1 v2 v3 in
+			run_function ctx exec env
+		)
+		else Fun3 (fun v1 v2 v3 ->
+			let env = run v1 v2 v3 in
+			run_function_noret ctx exec env
+		)
+	| 4 ->
+		let run v1 v2 v3 v4 =
+			let env = get_env refs in
+			env.env_locals.(0) <- v1;
+			env.env_locals.(1) <- v2;
+			env.env_locals.(2) <- v3;
+			env.env_locals.(3) <- v4;
+			env
+		in
+		if hasret then Fun4 (fun v1 v2 v3 v4 ->
+			let env = run v1 v2 v3 v4 in
+			run_function ctx exec env
+		)
+		else Fun4 (fun v1 v2 v3 v4 ->
+			let env = run v1 v2 v3 v4 in
+			run_function_noret ctx exec env
+		)
+	| 5 ->
+		let run v1 v2 v3 v4 v5 =
+			let env = get_env refs in
+			env.env_locals.(0) <- v1;
+			env.env_locals.(1) <- v2;
+			env.env_locals.(2) <- v3;
+			env.env_locals.(3) <- v4;
+			env.env_locals.(4) <- v5;
+			env
+		in
+		if hasret then Fun5 (fun v1 v2 v3 v4 v5 ->
+			let env = run v1 v2 v3 v4 v5 in
+			run_function ctx exec env
+		)
+		else Fun5 (fun v1 v2 v3 v4 v5 ->
+			let env = run v1 v2 v3 v4 v5 in
+			run_function_noret ctx exec env
+		)
+	| _ ->
+		if hasret then FunN (fun vl ->
+			let env = get_env refs in
+			List.iteri (fun i v ->
+				env.env_locals.(i) <- v
+			) vl;
+			run_function ctx exec env
+		)
+		else FunN (fun vl ->
+			let env = get_env refs in
+			List.iteri (fun i v ->
+				env.env_locals.(i) <- v
+			) vl;
+			run_function_noret ctx exec env
+		)
+
+let emit_closure ctx num_captures num_args get_env hasret exec env =
+	let refs = Array.sub env.env_captures 0 num_captures in
+	let f = create_function ctx num_args get_env hasret refs exec in
+	vstatic_function f

+ 204 - 0
src/macro/eval/evalEncode.ml

@@ -0,0 +1,204 @@
+(*
+	The Haxe Compiler
+	Copyright (C) 2005-2017  Haxe Foundation
+
+	This program is free software; you can redistribute it and/or
+	modify it under the terms of the GNU General Public License
+	as published by the Free Software Foundation; either version 2
+	of the License, or (at your option) any later version.
+
+	This program is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+
+	You should have received a copy of the GNU General Public License
+	along with this program; if not, write to the Free Software
+	Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ *)
+
+open Globals
+open EvalValue
+open EvalExceptions
+open EvalContext
+open EvalHash
+
+(* Functions *)
+
+let vifun0 f = vfunction (Fun1 (fun a -> f a))
+let vifun1 f = vfunction (Fun2 (fun a b -> f a b))
+let vifun2 f = vfunction (Fun3 (fun a b c -> f a b c))
+let vifun3 f = vfunction (Fun4 (fun a b c d -> f a b c d))
+let vifun4 f = vfunction (Fun5 (fun a b c d e -> f a b c d e))
+
+let vfun0 f = vstatic_function (Fun0 (fun vl -> f ()))
+let vfun1 f = vstatic_function (Fun1 (fun a -> f a))
+let vfun2 f = vstatic_function (Fun2 (fun a b -> f a b))
+let vfun3 f = vstatic_function (Fun3 (fun a b c -> f a b c))
+let vfun4 f = vstatic_function (Fun4 (fun a b c d -> f a b c d))
+let vfun5 f = vstatic_function (Fun5 (fun a b c d e -> f a b c d e))
+
+(* Objects *)
+
+let encode_obj _ l =
+	let ctx = get_ctx() in
+	let proto,sorted = ctx.get_object_prototype ctx l in
+	vobject {
+		ofields = Array.of_list (List.map snd sorted);
+		oproto = proto;
+		oextra = IntMap.empty;
+		oremoved = IntMap.empty;
+	}
+
+let encode_obj_s k l =
+	encode_obj k (List.map (fun (s,v) -> (hash_s s),v) l)
+
+(* Enum values *)
+
+let encode_enum_value path i vl pos =
+	venum_value {
+		eindex = i;
+		eargs = vl;
+		epath = path;
+		enpos = pos;
+	}
+
+let encode_enum i pos index pl =
+	let open MacroApi in
+	let key = match i with
+		| IExpr -> key_haxe_macro_ExprDef
+		| IBinop -> key_haxe_macro_Binop
+		| IUnop -> key_haxe_macro_Unop
+		| IConst -> key_haxe_macro_Constant
+		| ITParam -> key_haxe_macro_TypeParam
+		| ICType -> key_haxe_macro_ComplexType
+		| IField -> key_haxe_macro_FieldType
+		| IType -> key_haxe_macro_Type
+		| IFieldKind -> key_haxe_macro_FieldKind
+		| IMethodKind -> key_haxe_macro_MethodKind
+		| IVarAccess -> key_haxe_macro_VarAccess
+		| IAccess -> key_haxe_macro_Access
+		| IClassKind -> key_haxe_macro_ClassKind
+		| ITypedExpr -> key_haxe_macro_TypedExprDef
+		| ITConstant -> key_haxe_macro_TConstant
+		| IModuleType -> key_haxe_macro_ModuleType
+		| IFieldAccess -> key_haxe_macro_FieldAccess
+		| IAnonStatus -> key_haxe_macro_AnonStatus
+		| IImportMode -> key_haxe_macro_ImportMode
+	in
+	encode_enum_value key index (Array.of_list pl) pos
+
+(* Instances *)
+
+let create_instance_direct proto =
+	vinstance {
+		ifields = if Array.length proto.pinstance_fields = 0 then proto.pinstance_fields else Array.copy proto.pinstance_fields;
+		iproto = proto;
+		ikind = INormal;
+	}
+
+let create_instance ?(kind=INormal) path =
+	let proto = get_instance_prototype (get_ctx()) path null_pos in
+	{
+		ifields = if Array.length proto.pinstance_fields = 0 then proto.pinstance_fields else Array.copy proto.pinstance_fields;
+		iproto = proto;
+		ikind = kind;
+	}
+
+let encode_instance ?(kind=INormal) path =
+	vinstance (create_instance ~kind path)
+
+let encode_array_instance a =
+	VArray a
+
+let encode_vector_instance v =
+	VVector v
+
+let encode_array l =
+	encode_array_instance (EvalArray.create (Array.of_list l))
+
+let encode_string s =
+	VString(Rope.of_string s,lazy s)
+
+let encode_rope s =
+	vstring s
+
+let encode_bytes s =
+	encode_instance key_haxe_io_Bytes ~kind:(IBytes s)
+
+let encode_int_map_direct h =
+	encode_instance key_haxe_ds_IntMap ~kind:(IIntMap h)
+	
+let encode_string_map_direct h =
+	encode_instance key_haxe_ds_StringMap ~kind:(IStringMap h)
+	
+let encode_object_map_direct h =
+	encode_instance key_haxe_ds_ObjectMap ~kind:(IObjectMap (Obj.magic h))
+
+let encode_string_map convert m =
+	let h = StringHashtbl.create 0 in
+	PMap.iter (fun key value -> StringHashtbl.add h (Rope.of_string key,lazy key) (convert value)) m;
+	encode_string_map_direct h
+
+let fake_proto path =
+	let proto = {
+		ppath = path;
+		pfields = [||];
+		pnames = IntMap.empty;
+		pinstance_names = IntMap.empty;
+		pinstance_fields = [||];
+		pparent = None;
+		pkind = PInstance;
+		pvalue = vnull;
+	} in
+	proto.pvalue <- vprototype proto;
+	proto
+
+let encode_unsafe o =
+	vinstance {
+		ifields = [||];
+		iproto = fake_proto key_haxe_macro_Unsafe;
+		ikind = IRef (Obj.repr o);
+	}
+
+let encode_pos p =
+	vinstance {
+		ifields = [||];
+		iproto = fake_proto key_haxe_macro_Position;
+		ikind = IPos p;
+	}
+
+let encode_lazytype t f =
+	vinstance {
+		ifields = [||];
+		iproto = fake_proto key_haxe_macro_LazyType;
+		ikind = ILazyType(t,f);
+	}
+
+let encode_tdecl t =
+	vinstance {
+		ifields = [||];
+		iproto = fake_proto key_haxe_macro_TypeDecl;
+		ikind = ITypeDecl t;
+	}
+
+let ref_proto =
+	let proto = {
+		ppath = key_haxe_macro_Ref;
+		pfields = [||];
+		pnames = IntMap.empty;
+		pinstance_names = IntMap.add key_get 0 (IntMap.singleton key_toString 1);
+		pinstance_fields = [|vnull;vnull|];
+		pparent = None;
+		pkind = PInstance;
+		pvalue = vnull;
+	} in
+	proto.pvalue <- vprototype proto;
+	proto
+
+let encode_ref v convert tostr =
+	vinstance {
+		ifields = [|vifun0 (fun _ -> convert v);vifun0 (fun _ -> encode_string (tostr()))|];
+		iproto = ref_proto;
+		ikind = IRef (Obj.repr v);
+	}

+ 149 - 0
src/macro/eval/evalExceptions.ml

@@ -0,0 +1,149 @@
+(*
+	The Haxe Compiler
+	Copyright (C) 2005-2017  Haxe Foundation
+
+	This program is free software; you can redistribute it and/or
+	modify it under the terms of the GNU General Public License
+	as published by the Free Software Foundation; either version 2
+	of the License, or (at your option) any later version.
+
+	This program is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+
+	You should have received a copy of the GNU General Public License
+	along with this program; if not, write to the Free Software
+	Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ *)
+
+open Globals
+open EvalContext
+open EvalValue
+open EvalPrinting
+open EvalHash
+open EvalField
+
+exception Break
+exception Continue
+exception Return of value
+
+let is v path =
+	path = key_Dynamic || match v with
+	| VInt32 _ -> path = key_Int || path = key_Float
+	| VFloat f -> path = key_Float || (path = key_Int && f = (float_of_int (int_of_float f)) && f <= 2147483647. && f >= -2147483648.)
+	| VTrue | VFalse -> path = key_Bool
+	| VPrototype {pkind = PClass _} -> path = key_Class
+	| VPrototype {pkind = PEnum _} -> path = key_Enum
+	| VEnumValue ve -> path = key_EnumValue || path = ve.epath
+	| VString _ -> path = key_String
+	| VArray _ -> path = key_Array
+	| VVector _ -> path = key_eval_Vector
+	| VInstance vi ->
+		let has_interface path' =
+			try begin match (get_static_prototype_raise (get_ctx()) path').pkind with
+				| PClass interfaces -> List.mem path interfaces
+				| _ -> false
+			end with Not_found ->
+				false
+		in
+		let rec loop proto =
+			if path = proto.ppath || has_interface proto.ppath then true
+			else begin match proto.pparent with
+				| Some proto -> loop proto
+				| None -> false
+			end
+		in
+		loop vi.iproto
+	| _ -> false
+
+let s_value_kind = function
+	| VNull -> "VNull"
+	| VTrue -> "VTrue"
+	| VFalse -> "VFalse"
+	| VInt32 _ -> "VInt32"
+	| VFloat _ -> "VFloat"
+	| VEnumValue _ -> "VEnumValue"
+	| VObject _ -> "VObject"
+	| VString _ -> "VString"
+	| VArray _ -> "VArray"
+	| VVector _ -> "VVector"
+	| VInstance _ -> "VInstance"
+	| VPrototype _ -> "VPrototype"
+	| VFunction _ -> "VFunction"
+	| VFieldClosure _ -> "VFieldClosure"
+
+let unexpected_value : 'a . value -> string -> 'a = fun v s ->
+	let str = Printf.sprintf "Unexpected value %s(%s), expected %s" (s_value_kind v) (value_string v) s in
+	exc_string str
+
+let invalid_call_arg_number i i2 =
+	exc_string (Printf.sprintf "Invalid number of call arguments: Expected %i, got %i" i i2)
+
+let format_pos p =
+	let error_printer file line = Printf.sprintf "%s:%d:" file line in
+	Lexer.get_error_pos error_printer p
+
+let uncaught_exception_string v p extra =
+	(Printf.sprintf "%s : Uncaught exception %s%s" (format_pos p) (value_string v) extra)
+
+let get_exc_error_message ctx v stack p =
+	let pl = List.map (fun env -> {pfile = rev_file_hash env.env_info.pfile;pmin = env.env_leave_pmin; pmax = env.env_leave_pmax}) stack in
+	let pl = List.filter (fun p -> p <> null_pos) pl in
+	match pl with
+	| [] ->
+		let extra = if ctx.record_stack then "" else "\nNo stack information available, consider compiling with -D eval-stack" in
+		uncaught_exception_string v p extra
+	| _ ->
+		let sstack = String.concat "\n" (List.map (fun p -> Printf.sprintf "%s : Called from here" (format_pos p)) pl) in
+		Printf.sprintf "%s : Uncaught exception %s\n%s" (format_pos p) (value_string v) sstack
+
+let build_exception_stack ctx environment_offset =
+	let eval = get_eval ctx in
+	let d = if not ctx.record_stack then [] else DynArray.to_list (DynArray.sub eval.environments environment_offset (eval.environment_offset - environment_offset)) in
+	ctx.exception_stack <- List.map (fun env ->
+		env.env_in_use <- false;
+		env.env_debug.timer();
+		{pfile = rev_file_hash env.env_info.pfile;pmin = env.env_leave_pmin; pmax = env.env_leave_pmax},env.env_info.kind
+	) d
+
+let catch_exceptions ctx ?(final=(fun() -> ())) f p =
+	let prev = !get_ctx_ref in
+	select ctx;
+	let eval = get_eval ctx in
+	let environment_offset = eval.environment_offset in
+	let r = try
+		let v = f() in
+		get_ctx_ref := prev;
+		final();
+		Some v
+	with
+	| RunTimeException(v,stack,p') ->
+		build_exception_stack ctx environment_offset;
+		eval.environment_offset <- environment_offset;
+		if is v key_haxe_macro_Error then begin
+			let v1 = field v key_message in
+			let v2 = field v key_pos in
+			get_ctx_ref := prev;
+			final();
+			match v1,v2 with
+				| VString(_,s),VInstance {ikind = IPos p} ->
+					raise (Error.Error (Error.Custom (Lazy.force s),p))
+				| _ ->
+					Error.error "Something went wrong" null_pos
+		end else begin
+			(* Careful: We have to get the message before resetting the context because toString() might access it. *)
+			let msg = get_exc_error_message ctx v (match stack with [] -> [] | _ :: l -> l) (if p' = null_pos then p else p') in
+			get_ctx_ref := prev;
+			final();
+			Error.error msg null_pos
+		end
+	| MacroApi.Abort ->
+		final();
+		None
+	| exc ->
+		get_ctx_ref := prev;
+		final();
+		raise exc
+	in
+	r

+ 69 - 0
src/macro/eval/evalField.ml

@@ -0,0 +1,69 @@
+(*
+	The Haxe Compiler
+	Copyright (C) 2005-2017  Haxe Foundation
+
+	This program is free software; you can redistribute it and/or
+	modify it under the terms of the GNU General Public License
+	as published by the Free Software Foundation; either version 2
+	of the License, or (at your option) any later version.
+
+	This program is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+
+	You should have received a copy of the GNU General Public License
+	along with this program; if not, write to the Free Software
+	Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ *)
+
+open Globals
+open EvalValue
+open EvalContext
+open EvalHash
+
+let no_field =
+	vnull
+
+let proto_field_direct proto name =
+	proto.pfields.(get_proto_field_index_raise proto name)
+
+let rec proto_field_raise proto name =
+	try proto_field_direct proto name
+	with Not_found -> match proto.pparent with
+		| Some proto -> proto_field_raise proto name
+		| _ -> raise Not_found
+
+let instance_field vi name =
+	vi.ifields.(get_instance_field_index_raise vi.iproto name)
+
+let object_field_raise o name =
+	try o.ofields.(get_instance_field_index_raise o.oproto name)
+	with Not_found -> IntMap.find name o.oextra
+
+let object_field o name =
+	try object_field_raise o name with Not_found -> vnull
+
+let field_raise v f =
+	match v with
+	| VObject o -> object_field_raise o f
+	| VInstance {ikind = IBytes s} when f = key_length -> vint (Bytes.length s)
+	| VPrototype proto -> proto_field_raise proto f
+	| VArray va ->
+		if f = key_length then vint (va.alength)
+		else proto_field_direct (get_instance_prototype_raise (get_ctx()) key_Array) f
+	| VVector vv ->
+		if f = key_length then vint (Array.length vv)
+		else proto_field_direct (get_instance_prototype_raise (get_ctx()) key_eval_Vector) f
+	| VString (_,s) ->
+		if f = key_length then vint (String.length (Lazy.force s))
+		else proto_field_direct (get_ctx()).string_prototype f
+	| VInstance vi -> (try instance_field vi f with Not_found -> proto_field_raise vi.iproto f)
+	| _ -> raise Not_found
+
+let field v f =
+	try field_raise v f with Not_found -> no_field
+
+let dynamic_field v name = match field v name with
+	| VFunction(f,false) -> vfield_closure v f
+	| v -> v

+ 138 - 0
src/macro/eval/evalHash.ml

@@ -0,0 +1,138 @@
+(*
+	The Haxe Compiler
+	Copyright (C) 2005-2017  Haxe Foundation
+
+	This program is free software; you can redistribute it and/or
+	modify it under the terms of the GNU General Public License
+	as published by the Free Software Foundation; either version 2
+	of the License, or (at your option) any later version.
+
+	This program is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+
+	You should have received a copy of the GNU General Public License
+	along with this program; if not, write to the Free Software
+	Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ *)
+
+let reverse_map = Hashtbl.create 0
+let file_map = Hashtbl.create 0
+
+let rev_hash i = Hashtbl.find reverse_map i
+
+let rev_hash_s i = Rope.to_string (rev_hash i)
+
+let hash f =
+	let i = Hashtbl.hash (Rope.to_string f) in
+	Hashtbl.replace reverse_map i f;
+	i
+
+let hash_s f =
+	let i = Hashtbl.hash f in
+	Hashtbl.replace reverse_map i (Rope.of_string f);
+	i
+
+let path_hash path = hash_s (Globals.s_type_path path)
+
+let file_hash file =
+	let unique_file = Path.unique_full_path file in
+	Hashtbl.replace file_map unique_file file;
+	hash_s unique_file
+
+let rev_file_hash i =
+	let s = rev_hash_s i in
+	try Hashtbl.find file_map s with Not_found -> s
+
+let key_length = hash_s "length"
+let key_toString = hash_s "toString"
+let key_OutsideBounds = hash_s "OutsideBounds"
+let key_low = hash_s "low"
+let key_high = hash_s "high"
+let key_next = hash_s "next"
+let key_hasNext = hash_s "hasNext"
+let key___meta__ = hash_s "__meta__"
+let key_get = hash_s "get"
+let key_pos = hash_s "pos"
+let key_len = hash_s "len"
+let key_message = hash_s "message"
+let key_Array = hash_s "Array"
+let key_eval_Vector = hash_s "eval.Vector"
+let key_String = hash_s "String"
+let key_haxe_ds_StringMap = hash_s "haxe.ds.StringMap"
+let key_haxe_ds_IntMap = hash_s "haxe.ds.IntMap"
+let key_haxe_ds_ObjectMap = hash_s "haxe.ds.ObjectMap"
+let key_haxe_macro_Position = hash_s "haxe.macro.Position"
+let key_haxe_macro_LazyType = hash_s "haxe.macro.LazyType"
+let key_haxe_macro_TypeDecl = hash_s "haxe.macro.TypeDecl"
+let key_haxe_Utf8 = hash_s "haxe.Utf8"
+let key_haxe_macro_Ref = hash_s "haxe.macro.Ref"
+let key_haxe_io_Error = hash_s "haxe.io.Error"
+let key_haxe_io_Bytes = hash_s "haxe.io.Bytes"
+let key_Date = hash_s "Date"
+let key_Dynamic = hash_s "Dynamic"
+let key_ValueType = hash_s "ValueType"
+let key_EReg = hash_s "EReg"
+let key_haxe_io_BytesBuffer = hash_s "haxe.io.BytesBuffer"
+let key_StringBuf = hash_s "StringBuf"
+let key_haxe_macro_Error = hash_s "haxe.macro.Error"
+let key_Int = hash_s "Int"
+let key_Float = hash_s "Float"
+let key_Bool = hash_s "Bool"
+let key_Class = hash_s "Class"
+let key_Enum = hash_s "Enum"
+let key_EnumValue = hash_s "EnumValue"
+let key_gid = hash_s "gid"
+let key_uid = hash_s "uid"
+let key_atime = hash_s "atime"
+let key_mtime = hash_s "mtime"
+let key_ctime = hash_s "ctime"
+let key_dev = hash_s "dev"
+let key_ino = hash_s "ino"
+let key_nlink = hash_s "nlink"
+let key_rdev = hash_s "rdev"
+let key_size = hash_s "size"
+let key_mode = hash_s "mode"
+let key_haxe__Int64____Int64 = hash_s "haxe._Int64.___Int64"
+let key_haxe_macro_Unsafe = hash_s "haxe.macro.Unsafe"
+let key_sys_io__Process_NativeProcess = hash_s "sys.io._Process.NativeProcess"
+let key_sys_io_FileOutput = hash_s "sys.io.FileOutput"
+let key_sys_io_FileInput = hash_s "sys.io.FileInput"
+let key_haxe_io_Eof = hash_s "haxe.io.Eof"
+let key_haxe_macro_ExprDef = hash_s "haxe.macro.ExprDef"
+let key_haxe_macro_Binop = hash_s "haxe.macro.Binop"
+let key_haxe_macro_Unop = hash_s "haxe.macro.Unop"
+let key_haxe_macro_Constant = hash_s "haxe.macro.Constant"
+let key_haxe_macro_TypeParam = hash_s "haxe.macro.TypeParam"
+let key_haxe_macro_ComplexType = hash_s "haxe.macro.ComplexType"
+let key_haxe_macro_FieldType = hash_s "haxe.macro.FieldType"
+let key_haxe_macro_Type = hash_s "haxe.macro.Type"
+let key_haxe_macro_FieldKind = hash_s "haxe.macro.FieldKind"
+let key_haxe_macro_MethodKind = hash_s "haxe.macro.MethodKind"
+let key_haxe_macro_VarAccess = hash_s "haxe.macro.VarAccess"
+let key_haxe_macro_Access = hash_s "haxe.macro.Access"
+let key_haxe_macro_ClassKind = hash_s "haxe.macro.ClassKind"
+let key_haxe_macro_TypedExprDef = hash_s "haxe.macro.TypedExprDef"
+let key_haxe_macro_TConstant = hash_s "haxe.macro.TConstant"
+let key_haxe_macro_ModuleType = hash_s "haxe.macro.ModuleType"
+let key_haxe_macro_FieldAccess = hash_s "haxe.macro.FieldAccess"
+let key_haxe_macro_AnonStatus = hash_s "haxe.macro.AnonStatus"
+let key_haxe_macro_ImportMode = hash_s "haxe.macro.ImportMode"
+let key_haxe_CallStack = hash_s "haxe.CallStack"
+let key___init__ = hash_s "__init__"
+let key_new = hash_s "new"
+let key_questionmark = hash_s "?"
+let key_haxe_StackItem = hash_s "haxe.StackItem"
+let key_sys_net__Socket_NativeSocket = hash_s "sys.net._Socket.NativeSocket"
+let key_ip = hash_s "ip"
+let key_port = hash_s "port"
+let key_sys_net_Socket = hash_s "sys.net.Socket"
+let key_socket = hash_s "socket"
+let key_read = hash_s "read"
+let key_write = hash_s "write"
+let key_others = hash_s "others"
+let key_eval_vm_Thread = hash_s "eval.vm.Thread"
+let key_haxe_zip_Compress = hash_s "haxe.zip.Compress"
+let key_haxe_zip_Uncompress = hash_s "haxe.zip.Uncompress"
+let key_done = hash_s "done"

+ 858 - 0
src/macro/eval/evalJit.ml

@@ -0,0 +1,858 @@
+(*
+	The Haxe Compiler
+	Copyright (C) 2005-2017  Haxe Foundation
+
+	This program is free software; you can redistribute it and/or
+	modify it under the terms of the GNU General Public License
+	as published by the Free Software Foundation; either version 2
+	of the License, or (at your option) any later version.
+
+	This program is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+
+	You should have received a copy of the GNU General Public License
+	along with this program; if not, write to the Free Software
+	Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ *)
+
+open Globals
+open Ast
+open Type
+open EvalValue
+open EvalContext
+open EvalHash
+open EvalEmitter
+
+(* Helper *)
+
+let rope_path t = match follow t with
+	| TInst({cl_path=path},_) | TEnum({e_path=path},_) | TAbstract({a_path=path},_) -> Rope.of_string (s_type_path path)
+	| TDynamic _ -> Rope.of_string "Dynamic"
+	| TFun _ | TAnon _ | TMono _ | TType _ | TLazy _ -> assert false
+
+let eone = mk (TConst(TInt (Int32.one))) t_dynamic null_pos
+
+let eval_const = function
+	| TString s -> vstring (Rope.of_string s)
+	| TInt i32 -> vint32 i32
+	| TFloat f -> vfloat (float_of_string f)
+	| TBool b -> vbool b
+	| TNull -> vnull
+	| TThis | TSuper -> assert false
+
+let is_int t = match follow t with
+	| TAbstract({a_path=[],"Int"},_) -> true
+	| _ -> false
+
+let get_binop_fun op p = match op with
+	| OpAdd -> op_add
+	| OpMult -> op_mult p
+	| OpDiv -> op_div p
+	| OpSub -> op_sub p
+	| OpEq -> op_eq
+	| OpNotEq -> op_not_eq
+	| OpGt -> op_gt
+	| OpGte -> op_gte
+	| OpLt -> op_lt
+	| OpLte -> op_lte
+	| OpAnd -> op_and p
+	| OpOr -> op_or p
+	| OpXor -> op_xor p
+	| OpShl -> op_shl p
+	| OpShr -> op_shr p
+	| OpUShr -> op_ushr p
+	| OpMod -> op_mod p
+	| OpAssign | OpBoolAnd | OpBoolOr | OpAssignOp _ | OpInterval | OpArrow | OpIn -> assert false
+
+open EvalJitContext
+
+let rec op_assign ctx jit e1 e2 = match e1.eexpr with
+	| TLocal var ->
+		let exec = jit_expr jit false e2 in
+		if var.v_capture then emit_capture_write (get_capture_slot jit var.v_id) exec
+		else emit_local_write (get_slot jit var.v_id e1.epos) exec
+	| TField(e1,fa) ->
+		let name = hash_s (field_name fa) in
+		let exec1 = jit_expr jit false e1 in
+		let exec2 = jit_expr jit false e2 in
+		begin match fa with
+			| FInstance({cl_path=(["haxe";"io"],"Bytes")},_,{cf_name="length"}) ->
+				emit_bytes_length_write exec1 exec2
+			| FStatic({cl_path=path},_) | FEnum({e_path=path},_) ->
+				let proto = get_static_prototype jit.ctx (path_hash path) e1.epos in
+				emit_proto_field_write proto (get_proto_field_index proto name) exec2
+			| FInstance(c,_,_) when not c.cl_interface ->
+				let proto = get_instance_prototype jit.ctx (path_hash c.cl_path) e1.epos in
+				let i = get_instance_field_index proto name e1.epos in
+				emit_instance_field_write exec1 i exec2
+			| FAnon cf ->
+				begin match follow e1.etype with
+					| TAnon an ->
+						let l = PMap.foldi (fun k _ acc -> (hash_s k,()) :: acc) an.a_fields [] in
+						let proto,_ = ctx.get_object_prototype ctx l in
+						let i = get_instance_field_index proto name e1.epos in
+						emit_anon_field_write exec1 proto i name exec2
+					| _ ->
+						emit_field_write exec1 name exec2
+				end
+			| _ ->
+				emit_field_write exec1 name exec2
+		end
+	| TArray(ea1,ea2) ->
+		begin match (follow ea1.etype) with
+			| TInst({cl_path=(["eval"],"Vector")}, _) ->
+				begin match ea1.eexpr with
+					| TLocal var when not var.v_capture ->
+						let exec2 = jit_expr jit false ea2 in
+						let exec3 = jit_expr jit false e2 in
+						emit_vector_local_write (get_slot jit var.v_id ea1.epos) exec2 exec3 ea2.epos
+					| _ ->
+						let exec1 = jit_expr jit false ea1 in
+						let exec2 = jit_expr jit false ea2 in
+						let exec3 = jit_expr jit false e2 in
+						emit_vector_write exec1 exec2 exec3 ea2.epos
+				end
+			| _ ->
+				begin match ea1.eexpr with
+					| TLocal var when not var.v_capture ->
+						let exec2 = jit_expr jit false ea2 in
+						let exec3 = jit_expr jit false e2 in
+						emit_array_local_write (get_slot jit var.v_id ea1.epos) exec2 exec3 ea2.epos
+					| _ ->
+						let exec1 = jit_expr jit false ea1 in
+						let exec2 = jit_expr jit false ea2 in
+						let exec3 = jit_expr jit false e2 in
+						emit_array_write exec1 exec2 exec3 ea2.epos
+				end
+		end
+
+	| _ ->
+		assert false
+
+and op_assign_op jit op e1 e2 prefix = match e1.eexpr with
+	| TLocal var ->
+		let exec = jit_expr jit false e2 in
+		if var.v_capture then emit_capture_read_write (get_capture_slot jit var.v_id) exec op prefix
+		else emit_local_read_write (get_slot jit var.v_id e1.epos) exec op prefix
+	| TField(e1,fa) ->
+		let name = hash_s (field_name fa) in
+		let exec1 = jit_expr jit false e1 in
+		let exec2 = jit_expr jit false e2 in
+		begin match fa with
+			| FStatic({cl_path=path},_) ->
+				let proto = get_static_prototype jit.ctx (path_hash path) e1.epos in
+				emit_proto_field_read_write proto (get_proto_field_index proto name) exec2 op prefix
+			| FInstance(c,_,_) when not c.cl_interface ->
+				let proto = get_instance_prototype jit.ctx (path_hash c.cl_path) e1.epos in
+				let i = get_instance_field_index proto name e1.epos in
+				emit_instance_field_read_write exec1 i exec2 op prefix
+			| _ ->
+				emit_field_read_write exec1 name exec2 op prefix
+		end
+	| TArray(ea1,ea2) ->
+		begin match (follow ea1.etype) with
+			| TInst({cl_path=(["eval"],"Vector")}, _) ->
+				begin match ea1.eexpr with
+					| TLocal var when not var.v_capture ->
+						let exec2 = jit_expr jit false ea2 in
+						let exec3 = jit_expr jit false e2 in
+						emit_vector_local_read_write (get_slot jit var.v_id ea1.epos) exec2 exec3 op prefix ea2.epos
+					| _ ->
+						let exec1 = jit_expr jit false ea1 in
+						let exec2 = jit_expr jit false ea2 in
+						let exec3 = jit_expr jit false e2 in
+						emit_vector_read_write exec1 exec2 exec3 op prefix ea2.epos
+				end
+			| _ ->
+				begin match ea1.eexpr with
+					| TLocal var when not var.v_capture ->
+						let exec2 = jit_expr jit false ea2 in
+						let exec3 = jit_expr jit false e2 in
+						emit_array_local_read_write (get_slot jit var.v_id ea1.epos) exec2 exec3 op prefix ea2.epos
+					| _ ->
+						let exec1 = jit_expr jit false ea1 in
+						let exec2 = jit_expr jit false ea2 in
+						let exec3 = jit_expr jit false e2 in
+						emit_array_read_write exec1 exec2 exec3 op prefix ea2.epos
+				end
+		end
+	| _ ->
+		assert false
+
+and op_incr jit e1 prefix p = match e1.eexpr with
+	| TLocal var ->
+		begin match var.v_capture,prefix with
+			| true,true -> emit_capture_incr_prefix (get_capture_slot jit var.v_id)
+			| true,false -> emit_capture_incr_postfix (get_capture_slot jit var.v_id)
+			| false,true -> emit_local_incr_prefix (get_slot jit var.v_id e1.epos)
+			| false,false -> emit_local_incr_postfix (get_slot jit var.v_id e1.epos)
+		end
+	| _ ->
+		op_assign_op jit (get_binop_fun OpAdd p) e1 eone prefix
+
+and op_decr jit e1 prefix p = match e1.eexpr with
+	| TLocal var ->
+		begin match var.v_capture,prefix with
+			| true,true -> emit_capture_decr_prefix (get_capture_slot jit var.v_id)
+			| true,false -> emit_capture_decr_postfix (get_capture_slot jit var.v_id)
+			| false,true -> emit_local_decr_prefix (get_slot jit var.v_id e1.epos)
+			| false,false -> emit_local_decr_postfix (get_slot jit var.v_id e1.epos)
+		end
+	| _ ->
+		op_assign_op jit (get_binop_fun OpSub p) e1 eone prefix
+
+and unop jit op flag e1 p =
+	match op with
+	| Not ->
+		let exec = jit_expr jit false e1 in
+		emit_not exec
+	| Neg ->
+		let exec = jit_expr jit false e1 in
+		emit_neg exec p
+	| NegBits ->
+		let exec = jit_expr jit false e1 in
+		emit_op_sub p (fun _ -> vint32 (Int32.minus_one)) exec
+	| Increment ->
+		op_incr jit e1 (flag = Prefix) p
+	| Decrement ->
+		op_decr jit e1 (flag = Prefix) p
+
+(*
+	This is the main jit function. It turns expression [e] into a function, which can be
+	executed int an environment of type [EvalContext.env].
+*)
+and jit_expr jit return e =
+	let ctx = jit.ctx in
+	let rec loop e = match e.eexpr with
+	(* objects and values *)
+	| TVar(var,eo) ->
+		let varacc = declare_local jit var in
+		let exec = match eo with
+			| None -> emit_null
+			| Some e -> jit_expr jit false e
+		in
+		begin match varacc with
+			| Local slot -> emit_local_declaration slot exec
+			| Env slot -> emit_capture_declaration slot exec
+		end
+	| TConst TThis ->
+		emit_local_read (get_slot jit 0 e.epos)
+	| TConst ct ->
+		emit_const (eval_const ct)
+	| TObjectDecl fl ->
+		let fl = List.map (fun (s,e) -> hash_s s,jit_expr jit false e) fl in
+		let proto,_ = ctx.get_object_prototype ctx fl in
+		let fl = List.map (fun (s,exec) -> get_instance_field_index proto s e.epos,exec) fl in
+		let fa = Array.of_list fl in
+		emit_object_declaration proto fa
+	| TArrayDecl el ->
+		let execs = List.map (jit_expr jit false) el in
+		let execs = Array.of_list execs in
+		emit_array_declaration execs
+	| TTypeExpr mt ->
+		let key = path_hash (t_infos mt).mt_path in
+		let proto = get_static_prototype_as_value jit.ctx key e.epos in
+		emit_type_expr proto
+	| TFunction tf ->
+		let jit_closure = EvalJitContext.create ctx in
+		jit_closure.captures <- jit.captures;
+		jit_closure.capture_infos <- jit.capture_infos;
+		jit.num_closures <- jit.num_closures + 1;
+		let exec = jit_tfunction jit_closure true e.epos tf in
+		let num_captures = Hashtbl.length jit.captures in
+		let hasret = jit_closure.has_nonfinal_return in
+		let get_env = get_env jit_closure false (file_hash tf.tf_expr.epos.pfile) (EKLocalFunction jit.num_closures) in
+		let num_args = List.length tf.tf_args in
+		emit_closure ctx num_captures num_args get_env hasret exec
+	(* branching *)
+	| TIf(e1,e2,eo) ->
+		let exec_cond = jit_expr jit false e1 in
+		let exec_then = jit_expr jit return e2 in
+		let exec_else = match eo with
+			| None -> emit_null
+			| Some e -> jit_expr jit return e
+		in
+		emit_if exec_cond exec_then exec_else
+	| TSwitch(e1,cases,def) when is_int e1.etype ->
+		let exec = jit_expr jit false e1 in
+		let h = ref IntMap.empty in
+		let max = ref 0 in
+		let shift = ref 0 in
+		List.iter (fun (el,e) ->
+			push_scope jit e.epos;
+			let exec = jit_expr jit return e in
+			List.iter (fun e -> match e.eexpr with
+				| TConst (TInt i32) ->
+					let i = Int32.to_int i32 in
+					h := IntMap.add i exec !h;
+					if i > !max then max := i
+					else if i < !shift then shift := i
+				| _ -> assert false
+			) el;
+			pop_scope jit;
+		) cases;
+		let exec_def = match def with
+			| None -> emit_null
+			| Some e ->
+				push_scope jit e.epos;
+				let exec = jit_expr jit return e in
+				pop_scope jit;
+				exec
+		in
+		let l = !max - !shift + 1 in
+		if l < 256 then begin
+			let cases = Array.init l (fun i -> try IntMap.find (i + !shift) !h with Not_found -> exec_def) in
+			if !shift = 0 then begin match (Texpr.skip e1).eexpr with
+				| TEnumIndex e1 ->
+					let exec = jit_expr jit false e1 in
+					emit_enum_switch_array exec cases exec_def e1.epos
+				| _ ->
+					emit_int_switch_array exec cases exec_def e1.epos
+			end else
+				emit_int_switch_array_shift (- !shift) exec cases exec_def e1.epos
+		end else
+			emit_int_switch_map exec !h exec_def e1.epos
+	| TSwitch(e1,cases,def) ->
+		let exec = jit_expr jit false e1 in
+		let execs = DynArray.create () in
+		let constants = DynArray.create () in
+		let patterns = DynArray.create () in
+		let is_complex = ref false in
+		(* This is slightly insane... *)
+		List.iter (fun (el,e) ->
+			push_scope jit e.epos;
+			begin try
+				if !is_complex then raise Exit;
+				let el = List.map (fun e -> match e.eexpr with
+					| TConst ct -> eval_const ct
+					| _ -> raise Exit
+				) el in
+				DynArray.add constants el
+			with Exit ->
+				is_complex := true;
+				let el = List.map (jit_expr jit false) el in
+				DynArray.add patterns el
+			end;
+			DynArray.add execs (jit_expr jit return e);
+			pop_scope jit;
+		) cases;
+		let exec_def = match def with
+			| None ->
+				emit_null
+			| Some e ->
+				push_scope jit e.epos;
+				let exec = jit_expr jit return e in
+				pop_scope jit;
+				exec
+		in
+		if !is_complex then begin
+			let l = DynArray.length constants in
+			let all_patterns = Array.init (l + DynArray.length patterns) (fun i ->
+				if i >= l then DynArray.get patterns (i - l) else (List.map (fun ct -> fun _ -> ct) (DynArray.get constants i))
+			) in
+			emit_switch exec (DynArray.to_array execs) all_patterns exec_def
+		end else begin
+			emit_constant_switch exec (DynArray.to_array execs) (DynArray.to_array constants) exec_def
+		end
+	| TWhile({eexpr = TParenthesis e1},e2,flag) ->
+		loop {e with eexpr = TWhile(e1,e2,flag)}
+	| TWhile({eexpr = TBinop(OpLt,{eexpr = TLocal v;epos=pv},eto)},e2,NormalWhile) when (Meta.has Meta.ForLoopVariable v.v_meta) ->
+		let has_break = ref false in
+		let has_continue = ref false in
+		let rec loop e = match e.eexpr with
+			| TUnop(Increment,_,({eexpr = TLocal v'} as e1)) when v == v' -> e1
+			| TWhile _ | TFor _ -> e
+			| TBreak -> has_break := true; e
+			| TContinue -> has_continue := true; e
+			| _ -> Type.map_expr loop e
+		in
+		let e2 = loop e2 in
+		let slot = get_slot jit v.v_id pv in
+		let exec1 = jit_expr jit false eto in
+		let exec2 = jit_expr jit false e2 in
+		begin match !has_break,!has_continue with
+			| false,false -> emit_int_iterator slot exec1 exec2 pv eto.epos
+			| true,false -> emit_int_iterator_break slot exec1 exec2 pv eto.epos
+			| false,true -> emit_int_iterator_continue slot exec1 exec2 pv eto.epos
+			| true,true -> emit_int_iterator_break_continue slot exec1 exec2 pv eto.epos
+		end
+	| TWhile(e1,e2,flag) ->
+		let has_break = ref false in
+		let has_continue = ref false in
+		let rec loop e = match e.eexpr with
+			| TContinue -> has_continue := true; if !has_break then raise Exit
+			| TBreak -> has_break := true; if !has_continue then raise Exit
+			| TFunction _ | TWhile _ | TFor _ -> ()
+			| _ -> Type.iter loop e
+		in
+		(try loop e2 with Exit -> ());
+		begin match e1.eexpr with
+			| TBinop(OpGte,e1,{eexpr = TConst (TFloat s)}) when not !has_break && not !has_continue && flag = NormalWhile ->
+				let f = float_of_string s in
+				let exec1 = jit_expr jit false e1 in
+				let exec2 = jit_expr jit false e2 in
+				emit_while_gte exec1 f exec2
+			| _ ->
+				let exec_cond = jit_expr jit false e1 in
+				let exec_body = jit_expr jit false e2 in
+				(* This is a bit moronic, but it does avoid run-time branching and setting up some exception
+					handlers for break/continue, so it might be worth it... *)
+				begin match flag,!has_break,!has_continue with
+					| NormalWhile,false,false -> emit_while exec_cond exec_body
+					| NormalWhile,true,false -> emit_while_break exec_cond exec_body
+					| NormalWhile,false,true -> emit_while_continue exec_cond exec_body
+					| NormalWhile,true,true -> emit_while_break_continue exec_cond exec_body
+					| DoWhile,false,false -> emit_do_while exec_cond exec_body
+					| DoWhile,true,false -> emit_do_while_break exec_cond exec_body
+					| DoWhile,false,true -> emit_do_while_continue exec_cond exec_body
+					| DoWhile,true,true -> emit_do_while_break_continue exec_cond exec_body
+				end
+		end
+	| TTry(e1,catches) ->
+		let exec = jit_expr jit return e1 in
+		let catches = List.map (fun (var,e) ->
+			push_scope jit e.epos;
+			let varacc = declare_local jit var in
+			let exec = jit_expr jit return e in
+			pop_scope jit;
+			let key = hash (rope_path var.v_type) in
+			exec,key,varacc
+		) catches in
+		emit_try exec catches
+	(* control flow *)
+	| TBlock [] ->
+		emit_null
+	| TBlock el when ctx.debug.support_debugger ->
+		let e1,el = match List.rev el with
+			| e1 :: el -> e1,List.rev el
+			| [] -> assert false
+		in
+		push_scope jit e.epos;
+		let execs = List.map (jit_expr jit false) el in
+		let exec1 = jit_expr jit return e1 in
+		pop_scope jit;
+		emit_block (Array.of_list (execs @ [exec1]))
+	| TBlock [e1] ->
+		push_scope jit e.epos;
+		let f = loop e1 in
+		pop_scope jit;
+		f
+	| TBlock [e1;e2] ->
+		push_scope jit e.epos;
+		let exec1 = jit_expr jit false e1 in
+		let exec2 = jit_expr jit return e2 in
+		pop_scope jit;
+		emit_block2 exec1 exec2
+	| TBlock [e1;e2;e3] ->
+		push_scope jit e.epos;
+		let exec1 = jit_expr jit false e1 in
+		let exec2 = jit_expr jit false e2 in
+		let exec3 = jit_expr jit return e3 in
+		pop_scope jit;
+		emit_block3 exec1 exec2 exec3
+	| TBlock [e1;e2;e3;e4] ->
+		push_scope jit e.epos;
+		let exec1 = jit_expr jit false e1 in
+		let exec2 = jit_expr jit false e2 in
+		let exec3 = jit_expr jit false e3 in
+		let exec4 = jit_expr jit return e4 in
+		pop_scope jit;
+		emit_block4 exec1 exec2 exec3 exec4
+	| TBlock [e1;e2;e3;e4;e5] ->
+		push_scope jit e.epos;
+		let exec1 = jit_expr jit false e1 in
+		let exec2 = jit_expr jit false e2 in
+		let exec3 = jit_expr jit false e3 in
+		let exec4 = jit_expr jit false e4 in
+		let exec5 = jit_expr jit return e5 in
+		pop_scope jit;
+		emit_block5 exec1 exec2 exec3 exec4 exec5
+	| TBlock el ->
+		let d = DynArray.create () in
+		let add = DynArray.add d in
+		let rec loop el = match el with
+			| e1 :: e2 :: e3 :: e4 :: e5 :: el ->
+				let exec1 = jit_expr jit false e1 in
+				let exec2 = jit_expr jit false e2 in
+				let exec3 = jit_expr jit false e3 in
+				let exec4 = jit_expr jit false e4 in
+				let exec5 = jit_expr jit (return && el = []) e5 in
+				add (emit_block5 exec1 exec2 exec3 exec4 exec5);
+				loop el
+			| e1 :: e2 :: e3 :: e4 :: el ->
+				let exec1 = jit_expr jit false e1 in
+				let exec2 = jit_expr jit false e2 in
+				let exec3 = jit_expr jit false e3 in
+				let exec4 = jit_expr jit (return && el = []) e4 in
+				add (emit_block4 exec1 exec2 exec3 exec4);
+				loop el
+			| e1 :: e2 :: e3 :: el ->
+				let exec1 = jit_expr jit false e1 in
+				let exec2 = jit_expr jit false e2 in
+				let exec3 = jit_expr jit (return && el = []) e3 in
+				add (emit_block3 exec1 exec2 exec3);
+				loop el
+			| e1 :: e2 :: el ->
+				let exec1 = jit_expr jit false e1 in
+				let exec2 = jit_expr jit (return && el = []) e2 in
+				add (emit_block2 exec1 exec2);
+				loop el
+			| [e1] ->
+				let exec1 = jit_expr jit return e1 in
+				add (emit_block1 exec1);
+			| [] ->
+				()
+		in
+		push_scope jit e.epos;
+		loop el;
+		pop_scope jit;
+		emit_block (DynArray.to_array d)
+	| TReturn None ->
+		if return then emit_null
+		else begin
+			jit.has_nonfinal_return <- true;
+			emit_return_null
+		end
+	| TReturn (Some e1) ->
+		let exec = jit_expr jit false e1 in
+		if return then emit_value exec
+		else begin
+			jit.has_nonfinal_return <- true;
+			emit_return_value exec
+		end
+	| TBreak ->
+		emit_break
+	| TContinue ->
+		emit_continue
+	| TThrow e1 ->
+		let exec = jit_expr jit false e1 in
+		emit_throw exec e.epos
+	| TCast(e1,Some mt) ->
+		let exec = jit_expr jit false e1 in
+		let t = type_of_module_type mt in
+		emit_safe_cast exec (hash (rope_path t)) e.epos
+	(* calls *)
+	| TCall(e1,el) ->
+		begin match e1.eexpr with
+		| TField({eexpr = TConst TSuper;epos=pv},FInstance(c,_,cf)) ->
+			let proto = get_instance_prototype ctx (path_hash c.cl_path) e1.epos in
+			let name = hash_s cf.cf_name in
+			let i = get_proto_field_index proto name in
+			let slot = get_slot jit 0 pv in
+			let execs = List.map (jit_expr jit false) el in
+			emit_super_field_call slot proto i execs e.epos
+		| TField(ef,fa) ->
+			let name = hash_s (field_name fa) in
+			let execs = List.map (jit_expr jit false) el in
+			let is_overridden c s_name =
+				try
+					Hashtbl.find ctx.overrides (c.cl_path,s_name)
+				with Not_found ->
+					false
+			in
+			let is_proper_method cf = match cf.cf_kind with
+				| Method MethDynamic -> false
+				| Method _ -> true
+				| Var _ -> false
+			in
+			let instance_call c =
+				let exec = jit_expr jit false ef in
+				let proto = get_instance_prototype ctx (path_hash c.cl_path) ef.epos in
+				let i = get_proto_field_index proto name in
+				emit_proto_field_call proto i (exec :: execs) e.epos
+			in
+			let default () =
+				let exec = jit_expr jit false ef in
+				emit_method_call exec name execs e.epos
+			in
+			begin match fa with
+				| FStatic({cl_path=[],"StringTools"},{cf_name="fastCodeAt"}) ->
+					begin match execs with
+						| [exec1;exec2] -> emit_string_cca exec1 exec2 e.epos
+						| _ -> assert false
+					end
+				| FEnum({e_path=path},ef) ->
+					let key = path_hash path in
+					let pos = Some ef.ef_pos in
+					begin match execs with
+						| [] -> emit_enum_construction0 key ef.ef_index pos
+						| [exec1] -> emit_enum_construction1 key ef.ef_index exec1 pos
+						| [exec1;exec2] -> emit_enum_construction2 key ef.ef_index exec1 exec2 pos
+						| [exec1;exec2;exec3] -> emit_enum_construction3 key ef.ef_index exec1 exec2 exec3 pos
+						| [exec1;exec2;exec3;exec4] -> emit_enum_construction4 key ef.ef_index exec1 exec2 exec3 exec4 pos
+						| [exec1;exec2;exec3;exec4;exec5] -> emit_enum_construction5 key ef.ef_index exec1 exec2 exec3 exec4 exec5 pos
+						| _ -> emit_enum_construction key ef.ef_index (Array.of_list execs) pos
+					end
+				| FStatic({cl_path=path},cf) when is_proper_method cf ->
+					let proto = get_static_prototype ctx (path_hash path) ef.epos in
+					let i = get_proto_field_index proto name in
+					emit_proto_field_call proto i execs e.epos
+				| FInstance(c,_,cf) when is_proper_method cf ->
+					if is_overridden c cf.cf_name then
+						default()
+					else if not c.cl_interface then
+						instance_call c
+					else if c.cl_implements = [] && c.cl_super = None then begin match c.cl_descendants with
+						| [c'] when not c'.cl_interface && not (is_overridden c' cf.cf_name) ->
+							instance_call c'
+						| _ ->
+							default()
+					end else
+						default()
+				| _ ->
+					let exec = jit_expr jit false ef in
+					emit_field_call exec name execs e.epos
+			end
+		| TConst TSuper ->
+			begin match follow e1.etype with
+			| TInst(c,_) ->
+				let key = (path_hash c.cl_path) in
+				let execs = List.map (jit_expr jit false) el in
+				begin try
+					let f = get_special_instance_constructor_raise ctx key in
+					emit_special_super_call f execs
+				with Not_found ->
+					let fnew = get_instance_constructor jit.ctx key e1.epos in
+					emit_super_call fnew execs e.epos
+				end
+			| _ -> assert false
+			end
+		| _ ->
+			match e1.eexpr,el with
+			| TIdent "$__mk_pos__",[file;min;max] ->
+				let exec1 = jit_expr jit false file in
+				let exec2 = jit_expr jit false min in
+				let exec3 = jit_expr jit false max in
+				emit_mk_pos exec1 exec2 exec3
+			| TIdent "$__delayed_call__",[{eexpr = TConst(TInt i)}] ->
+				let f = ctx.curapi.MacroApi.delayed_macro (Int32.to_int i) in
+				(fun env ->
+					let f = f() in
+					f()
+				)
+			| _ ->
+				let exec = jit_expr jit false e1 in
+				let execs = List.map (jit_expr jit false) el in
+				begin match execs with
+					| [] -> emit_call0 exec e.epos
+					| [exec1] -> emit_call1 exec exec1 e.epos
+					| [exec1;exec2] -> emit_call2 exec exec1 exec2 e.epos
+					| [exec1;exec2;exec3] -> emit_call3 exec exec1 exec2 exec3 e.epos
+					| [exec1;exec2;exec3;exec4] -> emit_call4 exec exec1 exec2 exec3 exec4 e.epos
+					| _ -> emit_call exec execs e.epos
+				end
+		end
+	| TNew({cl_path=[],"Array"},_,_) ->
+		emit_new_array
+	| TNew({cl_path=["eval"],"Vector"},_,[e1]) ->
+		begin match e1.eexpr with
+			| TConst (TInt i32) ->
+				emit_new_vector_int (Int32.to_int i32)
+			| _ ->
+				let exec1 = jit_expr jit false e1 in
+				emit_new_vector exec1 e1.epos
+		end
+	| TNew(c,_,el) ->
+		let execs = List.map (jit_expr jit false) el in
+		let key = path_hash c.cl_path in
+		begin try
+			let f = get_special_instance_constructor_raise ctx key in
+			emit_special_instance f execs
+		with Not_found ->
+			let fnew = get_instance_constructor jit.ctx key e.epos in
+			let proto = get_instance_prototype jit.ctx key e.epos in
+			emit_constructor_call proto fnew execs e.epos
+		end
+	(* read *)
+	| TLocal var ->
+		if var.v_capture then emit_capture_read (get_capture_slot jit var.v_id)
+		else emit_local_read (get_slot jit var.v_id e.epos)
+	| TField(e1,fa) ->
+		let name = hash_s (field_name fa) in
+		begin match fa with
+			| FInstance({cl_path=([],"Array")},_,{cf_name="length"}) -> emit_array_length_read (jit_expr jit false e1)
+			| FInstance({cl_path=(["eval"],"Vector")},_,{cf_name="length"}) -> emit_vector_length_read (jit_expr jit false e1)
+			| FInstance({cl_path=(["haxe";"io"],"Bytes")},_,{cf_name="length"}) -> emit_bytes_length_read (jit_expr jit false e1)
+			| FStatic({cl_path=path},_) | FEnum({e_path=path},_) ->
+				let proto = get_static_prototype ctx (path_hash path) e1.epos in
+				emit_proto_field_read proto (get_proto_field_index proto name)
+			| FInstance(c,_,_) when not c.cl_interface ->
+				let proto = get_instance_prototype ctx (path_hash c.cl_path) e1.epos in
+				let i = get_instance_field_index proto name e1.epos in
+				begin match e1.eexpr with
+					| TLocal var when not var.v_capture -> emit_instance_local_field_read (get_slot jit var.v_id e1.epos) i
+					| _ -> emit_instance_field_read (jit_expr jit false e1) i
+				end
+			| FAnon _ ->
+				begin match follow e1.etype with
+					| TAnon an ->
+						let l = PMap.foldi (fun k _ acc -> (hash_s k,()) :: acc) an.a_fields [] in
+						let proto,_ = ctx.get_object_prototype ctx l in
+						let i = get_instance_field_index proto name e1.epos in
+						begin match e1.eexpr with
+							| TLocal var when not var.v_capture -> emit_anon_local_field_read (get_slot jit var.v_id e1.epos) proto i name e1.epos
+							| _ -> emit_anon_field_read (jit_expr jit false e1) proto i name e1.epos
+						end
+					| _ ->
+						emit_field_read (jit_expr jit false e1) name e1.epos
+				end
+			| FClosure _ | FDynamic _ ->
+				let exec = jit_expr jit false e1 in
+				emit_field_closure exec name
+			| _ ->
+				let exec = jit_expr jit false e1 in
+				emit_field_read exec name e1.epos
+		end
+	| TArray(e1,e2) ->
+		begin match (follow e1.etype) with
+			| TInst({cl_path=(["eval"],"Vector")}, _) ->
+				begin match e1.eexpr with
+					| TLocal var when not var.v_capture ->
+						emit_vector_local_read (get_slot jit var.v_id e1.epos) (jit_expr jit false e2) e2.epos
+					| _ ->
+						let exec1 = jit_expr jit false e1 in
+						let exec2 = jit_expr jit false e2 in
+						emit_vector_read exec1 exec2 e2.epos
+				end
+			| _ ->
+				begin match e1.eexpr with
+					| TLocal var when not var.v_capture ->
+						emit_array_local_read (get_slot jit var.v_id e1.epos) (jit_expr jit false e2) e2.epos
+					| _ ->
+						let exec1 = jit_expr jit false e1 in
+						let exec2 = jit_expr jit false e2 in
+						emit_array_read exec1 exec2 e2.epos
+				end
+		end
+	| TEnumParameter(e1,_,i) ->
+		let exec = jit_expr jit false e1 in
+		emit_enum_parameter_read exec i
+	| TEnumIndex e1 ->
+		let exec = jit_expr jit false e1 in
+		emit_enum_index exec
+	(* ops *)
+	| TBinop(OpEq,e1,{eexpr = TConst TNull}) | TBinop(OpEq,{eexpr = TConst TNull},e1) ->
+		let exec = jit_expr jit false e1 in
+		emit_eq_null exec
+	| TBinop(OpNotEq,e1,{eexpr = TConst TNull}) | TBinop(OpNotEq,{eexpr = TConst TNull},e1) ->
+		let exec = jit_expr jit false e1 in
+		emit_not_eq_null exec
+	| TBinop(op,e1,e2) ->
+		begin match op with
+		| OpAssign ->
+			op_assign ctx jit e1 e2
+		| OpAssignOp op ->
+			let f = get_binop_fun op e.epos in
+			op_assign_op jit f e1 e2 true
+		| OpBoolAnd ->
+			let exec1 = jit_expr jit false e1 in
+			let exec2 = jit_expr jit false e2 in
+			emit_bool_and exec1 exec2
+		| OpBoolOr ->
+			let exec1 = jit_expr jit false e1 in
+			let exec2 = jit_expr jit false e2 in
+			emit_bool_or exec1 exec2
+		| _ ->
+			let exec1 = jit_expr jit false e1 in
+			let exec2 = jit_expr jit false e2 in
+			begin match op with
+				| OpAdd -> emit_op_add exec1 exec2
+				| OpMult -> emit_op_mult e.epos exec1 exec2
+				| OpDiv -> emit_op_div e.epos exec1 exec2
+				| OpSub -> emit_op_sub e.epos exec1 exec2
+				| OpEq -> emit_op_eq exec1 exec2
+				| OpNotEq -> emit_op_not_eq exec1 exec2
+				| OpGt -> emit_op_gt exec1 exec2
+				| OpGte -> emit_op_gte exec1 exec2
+				| OpLt -> emit_op_lt exec1 exec2
+				| OpLte -> emit_op_lte exec1 exec2
+				| OpAnd -> emit_op_and e.epos exec1 exec2
+				| OpOr -> emit_op_or e.epos exec1 exec2
+				| OpXor -> emit_op_xor e.epos exec1 exec2
+				| OpShl -> emit_op_shl e.epos exec1 exec2
+				| OpShr -> emit_op_shr e.epos exec1 exec2
+				| OpUShr -> emit_op_ushr e.epos exec1 exec2
+				| OpMod -> emit_op_mod e.epos exec1 exec2
+				| _ -> assert false
+			end
+		end
+	| TUnop(op,flag,v1) ->
+		unop jit op flag v1 e.epos
+	(* rewrites/skips *)
+	| TFor(v,e1,e2) ->
+		loop (Codegen.for_remap (ctx.curapi.MacroApi.get_com()) v e1 e2 e.epos)
+	| TParenthesis e1 | TMeta(_,e1) | TCast(e1,None) ->
+		loop e1
+	| TIdent s ->
+		Error.error ("Unknown identifier: " ^ s) e.epos
+	in
+	let f = loop e in
+	if ctx.debug.support_debugger then begin match e.eexpr with
+		| TConst _ | TLocal _ | TTypeExpr _ | TBlock _ | TField _ -> f
+		| _ -> EvalDebug.debug_loop jit e f
+	end else
+		f
+
+and jit_tfunction jit static pos tf =
+	let ctx = jit.ctx in
+	push_scope jit pos;
+	(* Declare `this` (if not static) and function arguments as local variables. *)
+	if not static then ignore(declare_local_this jit);
+	let varaccs = ExtList.List.filter_map (fun (var,_) ->
+		let slot = add_local jit var in
+		if var.v_capture then Some (slot,add_capture jit var) else None
+	) tf.tf_args in
+	(* Add conditionals for default values. *)
+	let e = List.fold_left (fun e (v,cto) -> match cto with
+		| None -> e
+		| Some ct -> concat (Codegen.set_default (ctx.curapi.MacroApi.get_com()) v ct e.epos) e
+	) tf.tf_expr tf.tf_args in
+	(* Jit the function expression. *)
+	let exec = jit_expr jit true e in
+	(* Deal with captured arguments, if necessary. *)
+	let exec = match varaccs with
+		| [] -> exec
+		| _ -> handle_capture_arguments exec varaccs
+	in
+	pop_scope jit;
+	exec
+
+and get_env jit static file info =
+	let ctx = jit.ctx in
+	let num_locals = jit.max_num_locals in
+	let num_captures = Hashtbl.length jit.captures in
+	let info = create_env_info static file info jit.capture_infos in
+	if ctx.record_stack || num_captures > 0 then begin
+		match info.kind with
+		| EKLocalFunction _ -> get_closure_env ctx info num_locals num_captures
+		| _ -> get_normal_env ctx info num_locals num_captures
+	end else begin
+		let default_env = create_default_environment ctx info num_locals in
+		match info.kind with
+		| EKLocalFunction _ -> get_closure_env_opt ctx default_env info num_locals num_captures
+		| _ -> get_normal_env_opt ctx default_env info num_locals num_captures
+	end
+
+(* Creates a [EvalValue.vfunc] of function [tf], which can be [static] or not. *)
+let jit_tfunction ctx key_type key_field tf static pos =
+	let t = Common.timer [(if ctx.is_macro then "macro" else "interp");"jit"] in
+	(* Create a new JitContext with an initial scope *)
+	let jit = EvalJitContext.create ctx in
+	let exec = jit_tfunction jit static pos tf in
+	(* Create the [vfunc] instance depending on the number of arguments. *)
+	let hasret = jit.has_nonfinal_return in
+	let get_env = get_env jit static (file_hash tf.tf_expr.epos.pfile) (EKMethod(key_type,key_field)) in
+	let num_args = List.length tf.tf_args + (if not static then 1 else 0) in
+	let f = create_function ctx num_args get_env hasret empty_array exec in
+	t();
+	f
+
+(* JITs expression [e] to a function. This is used for expressions that are not in a method. *)
+let jit_expr ctx e =
+	let t = Common.timer [(if ctx.is_macro then "macro" else "interp");"jit"] in
+	let jit = EvalJitContext.create ctx in
+	let f = jit_expr jit false (mk_block e) in
+	t();
+	jit,f

+ 144 - 0
src/macro/eval/evalJitContext.ml

@@ -0,0 +1,144 @@
+open Type
+open EvalContext
+open EvalEmitter
+
+(*
+	JitContext keeps track of allocated local variables and closures. Scopes can be pushed
+	and popped and influence the "slots" of variables. It also manages the maximum number
+	of locals allocated at the same time, which the run-time can use to allocate a fixed
+	array.
+*)
+
+type t = {
+	ctx : context;
+	(* The scope stack. *)
+	mutable scopes : scope list;
+	(* The captured variables declared in this context. Maps variable IDs to capture slots. *)
+	mutable captures : (int,int) Hashtbl.t;
+	(* The current number of locals. *)
+	mutable num_locals : int;
+	(* The maximum number of locals. *)
+	mutable max_num_locals : int;
+	(* The number of closures in this context.*)
+	mutable num_closures : int;
+	(* Whether or not this function has a return that's not at the end of control flow. *)
+	mutable has_nonfinal_return : bool;
+	(* The name of capture variables. Maps local slots to variable names. Only filled in debug mode. *)
+	mutable capture_infos : (int,var_info) Hashtbl.t;
+}
+
+(* Creates a new context *)
+let create ctx = {
+	ctx = ctx;
+	scopes = [];
+	captures = Hashtbl.create 0;
+	num_locals = 0;
+	max_num_locals = 0;
+	num_closures = 0;
+	has_nonfinal_return = false;
+	capture_infos = Hashtbl.create 0;
+}
+
+(* Returns the number of locals in [scope]. *)
+let num_locals scope =
+	Hashtbl.length scope.locals
+
+(* Pushes a new scope onto context [jit]. *)
+let push_scope jit pos =
+	let scope = {
+		local_offset = jit.num_locals;
+		locals = Hashtbl.create 0;
+		local_infos = Hashtbl.create 0;
+		local_ids = Hashtbl.create 0;
+		pos = pos;
+	} in
+	jit.scopes <- scope :: jit.scopes
+
+(* Pops the top scope from context [jit] .*)
+let pop_scope jit = match jit.scopes with
+	| scope :: tl ->
+		jit.scopes <- tl;
+		jit.num_locals <- jit.num_locals - (num_locals scope);
+	| [] ->
+		assert false
+
+(* Increases number of locals and updates maximum number of locals if necessary *)
+let increase_num_locals jit =
+	jit.num_locals <- jit.num_locals + 1;
+	if jit.num_locals > jit.max_num_locals then jit.max_num_locals <- jit.num_locals
+
+(* Adds capture variable [var] to context [jit]. *)
+let add_capture jit var =
+	let i = Hashtbl.length jit.captures in
+	Hashtbl.add jit.captures var.v_id i;
+	if jit.ctx.debug.support_debugger then begin
+		Hashtbl.replace jit.capture_infos i var.v_name
+	end;
+	i
+
+(* Adds variable [var] to the current top scope of context [jit]. *)
+let add_local jit var = match jit.scopes with
+	| [] -> assert false
+	| scope :: _ ->
+		let i = Hashtbl.length scope.locals in
+		Hashtbl.add scope.locals var.v_id i;
+		increase_num_locals jit;
+		let slot = scope.local_offset + i in
+		if jit.ctx.debug.support_debugger then begin
+			Hashtbl.replace scope.local_ids var.v_name var.v_id;
+			Hashtbl.replace scope.local_infos i var.v_name
+		end;
+		slot
+
+(*
+	Declares variable [var] in context [jit]. If the variable is captured, it is added to
+	the capture hash table. Otherwise it is added to the local hash table of the top scope.
+
+	Returns either [Env slot] if the variable is captured or [Local slot] otherwise.
+*)
+let declare_local jit var =
+	if var.v_capture then Env (add_capture jit var)
+	else Local (add_local jit var)
+
+(*
+	Declares function argument [var] in context [jit].
+
+	All function arguments are added as local variables. If the variable is captured, it
+	is also added as a capture variable. This is handled by [EvalEmitter.handle_capture_arguments].
+*)
+let declare_arg jit var =
+	let varacc = add_local jit var in
+	if var.v_capture then add_capture jit var,Some varacc else varacc,None
+
+(* Declares a variable for `this` in context [jit]. *)
+let declare_local_this jit = match jit.scopes with
+	| [] -> assert false
+	| scope :: _ ->
+		let i = Hashtbl.length scope.locals in
+		Hashtbl.add scope.locals 0 i;
+		increase_num_locals jit;
+		if jit.ctx.debug.support_debugger then begin
+			Hashtbl.replace scope.local_ids "this" 0;
+			Hashtbl.replace scope.local_infos 0 "this"
+		end;
+		Local i
+
+(* Gets the slot of variable id [vid] in context [jit]. *)
+let get_slot_raise jit vid =
+	let rec loop scopes = match scopes with
+		| [] -> raise Not_found
+		| scope :: scopes ->
+			try
+				scope.local_offset + Hashtbl.find scope.locals vid
+			with Not_found ->
+				loop scopes
+	in
+	loop jit.scopes
+
+let get_slot jit vid p =
+	try get_slot_raise jit vid
+	with Not_found -> throw_string "Unbound variable" p
+
+(* Gets the slot of captured variable id [vid] in context [jit]. *)
+let get_capture_slot jit vid =
+	Hashtbl.find jit.captures vid

+ 427 - 0
src/macro/eval/evalMain.ml

@@ -0,0 +1,427 @@
+(*
+	The Haxe Compiler
+	Copyright (C) 2005-2017  Haxe Foundation
+
+	This program is free software; you can redistribute it and/or
+	modify it under the terms of the GNU General Public License
+	as published by the Free Software Foundation; either version 2
+	of the License, or (at your option) any later version.
+
+	This program is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+
+	You should have received a copy of the GNU General Public License
+	along with this program; if not, write to the Free Software
+	Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ *)
+
+open Globals
+open Ast
+open Type
+open Common
+open EvalValue
+open EvalContext
+open EvalPrototype
+open EvalExceptions
+open EvalJit
+open EvalJitContext
+open EvalPrinting
+open EvalMisc
+open EvalHash
+open EvalEncode
+open EvalField
+open MacroApi
+
+(* Create *)
+
+let sid = ref (-1)
+
+let stdlib = ref None
+let debug = ref None
+
+let create com api is_macro =
+	let t = Common.timer [(if is_macro then "macro" else "interp");"create"] in
+	incr sid;
+	let builtins = match !stdlib with
+		| None ->
+			let builtins = {
+				static_builtins = IntMap.empty;
+				instance_builtins = IntMap.empty;
+				constructor_builtins = Hashtbl.create 0;
+				empty_constructor_builtins = Hashtbl.create 0;
+			} in
+			EvalStdLib.init_standard_library builtins;
+			stdlib := Some builtins;
+			builtins
+		| Some (builtins) ->
+			builtins
+	in
+	let debug = match !debug with
+		| None ->
+			let support_debugger = Common.defined com Define.EvalDebugger in
+			let socket =
+				try
+					if not support_debugger then raise Exit;
+					let fail msg =
+						print_endline msg;
+						raise Exit
+					in
+					let s = Common.defined_value com Define.EvalDebugger in
+					if s = "1" then raise Exit;
+					let host,port = try ExtString.String.split s ":" with _ -> fail "Invalid host format, expected host:port" in
+					let host = try Unix.inet_addr_of_string host with exc -> fail (Printexc.to_string exc) in
+					let port = try int_of_string port with _ -> fail "Invalid port, expected int" in
+					let socket = try (Unix.socket Unix.PF_INET Unix.SOCK_STREAM) 0 with exc -> fail (Printexc.to_string exc) in
+					Unix.connect socket (Unix.ADDR_INET (host,port));
+					Some {addr = host; port = port; socket = Some socket}
+				with _ ->
+					None
+			in
+			let debug' = {
+				debug = com.Common.debug || support_debugger;
+				breakpoints = Hashtbl.create 0;
+				support_debugger = support_debugger;
+				debug_state = DbgStart;
+				breakpoint = EvalDebugMisc.make_breakpoint 0 0 BPDisabled BPAny;
+				caught_types = Hashtbl.create 0;
+				environment_offset_delta = 0;
+				debug_socket = socket;
+			} in
+			debug := Some debug';
+			debug'
+		| Some debug ->
+			debug
+	in
+	let detail_times = Common.defined com Define.EvalTimes in
+	let record_stack = debug.support_debugger || detail_times || Common.defined com Define.EvalStack in
+	let evals = DynArray.create () in
+	let eval = {
+		environments = DynArray.make 32;
+		environment_offset = 0;
+	} in
+	DynArray.add evals eval;
+	let rec ctx = {
+		ctx_id = !sid;
+		is_macro = is_macro;
+		debug = debug;
+		record_stack = record_stack;
+		detail_times = detail_times;
+		curapi = api;
+		builtins = builtins;
+		type_cache = IntMap.empty;
+		overrides = Hashtbl.create 0;
+		had_error = false;
+		(* prototypes *)
+		string_prototype = fake_proto key_String;
+		static_prototypes = IntMap.empty;
+		instance_prototypes = IntMap.empty;
+		constructors = IntMap.empty;
+		get_object_prototype = get_object_prototype;
+		(* eval *)
+		eval = eval;
+		exception_stack = [];
+	} in
+	t();
+	ctx
+
+(* API for macroContext.ml *)
+
+let eval_delayed ctx e =
+	let jit,f = jit_expr ctx e in
+	let info = create_env_info true (file_hash e.epos.pfile) EKDelayed jit.capture_infos in
+	fun () ->
+		let env = push_environment ctx info jit.max_num_locals (Hashtbl.length jit.captures) in
+		match catch_exceptions ctx (fun () -> Std.finally (fun _ -> pop_environment ctx env) f env) e.epos with
+			| Some v -> v
+			| None -> vnull
+
+let call_path ctx path f vl api =
+	if ctx.had_error then
+		None
+	else begin
+		let old = ctx.curapi in
+		ctx.curapi <- api;
+		let path = match List.rev path with
+			| [] -> assert false
+			| name :: path -> List.rev path,name
+		in
+		catch_exceptions ctx ~final:(fun () -> ctx.curapi <- old) (fun () ->
+			let vtype = get_static_prototype_as_value ctx (path_hash path) api.pos in
+			let vfield = field vtype (hash_s f) in
+			call_value_on vtype vfield vl
+		) api.pos
+	end
+
+let value_signature v =
+	let buf = Buffer.create 0 in
+	let add s = Buffer.add_string buf s in
+	let addc c = Buffer.add_char buf c in
+	let scache = Hashtbl.create 0 in
+	let adds s =
+		try
+			let i = Hashtbl.find scache s in
+			addc 'R';
+			add (string_of_int i)
+		with Not_found ->
+			Hashtbl.add scache s (Hashtbl.length scache);
+			addc 'y';
+			let s = EvalStdLib.StdStringTools.url_encode s in
+			add (string_of_int (Rope.length s));
+			addc ':';
+			add (Rope.to_string s)
+	in
+	let cache = ValueHashtbl.create 0 in
+	let cache_length = ref 0 in
+	let cache v f =
+		try
+			let i = ValueHashtbl.find cache v in
+			addc 'r';
+			add (string_of_int i)
+		with Not_found ->
+			let i = !cache_length in
+			ValueHashtbl.add cache v i;
+			incr cache_length;
+			f()
+	in
+	let function_count = ref 0 in
+	let rec loop v = match v with
+		| VNull -> addc 'n'
+		| VTrue -> addc 't'
+		| VFalse -> addc 'f'
+		| VInt32 i when i = Int32.zero -> addc 'z'
+		| VInt32 i ->
+			addc 'i';
+			add (Int32.to_string i)
+		| VFloat f ->
+			if f = neg_infinity then addc 'm'
+			else if f = infinity then addc 'p'
+			else if f <> f then addc 'k'
+			else begin
+				addc 'd';
+				add (string_of_float f)
+			end
+		| VEnumValue ve ->
+			cache v (fun () ->
+				addc 'j';
+				adds (rev_hash_s ve.epath);
+				addc ':';
+				add (string_of_int ve.eindex);
+				addc ':';
+				add (string_of_int (Array.length ve.eargs));
+				Array.iter loop ve.eargs;
+			)
+		| VObject o ->
+			cache v (fun () ->
+				addc 'o';
+				let fields = object_fields o in
+				loop_fields fields;
+				addc 'g'
+			)
+		| VInstance {ikind = IDate f} ->
+			cache v (fun () ->
+				addc 'v';
+				add (Rope.to_string (s_date f))
+			)
+		| VInstance {ikind = IStringMap map} ->
+			cache v (fun() ->
+				addc 'b';
+				StringHashtbl.iter (fun (_,s) value ->
+					adds (Lazy.force s);
+					loop value
+				) map;
+				addc 'h'
+			)
+		| VInstance {ikind = IIntMap map} ->
+			cache v (fun () ->
+				addc 'q';
+				IntHashtbl.iter (fun i value ->
+					addc ':';
+					add (string_of_int i);
+					loop value
+				) map;
+				addc 'h'
+			)
+		| VInstance {ikind = IObjectMap map} ->
+			cache v (fun() ->
+				addc 'M';
+				ValueHashtbl.iter (fun key value ->
+					loop key;
+					loop value
+				) (Obj.magic map);
+				addc 'h'
+			)
+		| VInstance {ikind = IBytes b} ->
+			cache v (fun () ->
+				addc 's';
+				let base64_chars = [|
+					'A';'B';'C';'D';'E';'F';'G';'H';'I';'J';'K';'L';'M';'N';'O';'P';
+					'Q';'R';'S';'T';'U';'V';'W';'X';'Y';'Z';'a';'b';'c';'d';'e';'f';
+					'g';'h';'i';'j';'k';'l';'m';'n';'o';'p';'q';'r';'s';'t';'u';'v';
+					'w';'x';'y';'z';'0';'1';'2';'3';'4';'5';'6';'7';'8';'9';'%';':'
+				|] in
+				let s = Bytes.unsafe_to_string (Base64.str_encode ~tbl:(base64_chars) (Bytes.unsafe_to_string b)) in
+				add (string_of_int (String.length s));
+				addc ':';
+				add s
+			)
+		| VInstance i ->
+			cache v (fun () ->
+				addc 'c';
+				adds (rev_hash_s i.iproto.ppath);
+				let fields = instance_fields i in
+				loop_fields fields;
+				addc 'g';
+			)
+		| VString(_,s) ->
+			adds (Lazy.force s)
+		| VArray {avalues = a} | VVector a ->
+			cache v (fun () ->
+				addc 'a';
+				let nulls null_count =
+					if null_count > 0 then begin
+						addc 'u';
+						add (string_of_int null_count);
+					end
+				in
+				let rec loop2 null_count vl = match vl with
+					| VNull :: vl -> loop2 (null_count + 1) vl
+					| v :: vl ->
+						nulls null_count;
+						loop v;
+						loop2 0 vl
+					| [] ->
+						nulls null_count
+				in
+				loop2 0 (Array.to_list a);
+				addc 'h'
+			)
+		| VPrototype {pkind = PClass _; ppath = path} ->
+			addc 'A';
+			adds (rev_hash_s path)
+		| VPrototype {pkind = PEnum _; ppath = path} ->
+			addc 'B';
+			adds (rev_hash_s path)
+		| VPrototype _ ->
+			assert false
+		| VFunction _ | VFieldClosure _ ->
+			(* Custom format: enumerate functions as F0, F1 etc. *)
+			cache v (fun () ->
+				addc 'F';
+				add (string_of_int !function_count);
+				incr function_count
+			)
+	and loop_fields fields =
+		List.iter (fun (name,v) ->
+			adds (rev_hash_s name);
+			loop v;
+		) fields
+	in
+	loop v;
+	Digest.string (Buffer.contents buf)
+
+let prepare_callback v n =
+	match v with
+	| VFunction _ | VFieldClosure _ ->
+		let ctx = get_ctx() in
+		(fun args -> match catch_exceptions ctx (fun() -> call_value v args) null_pos with
+			| Some v -> v
+			| None -> vnull)
+	| _ ->
+		raise Invalid_expr
+
+let init ctx = ()
+
+let setup get_api =
+	let api = get_api (fun() -> (get_ctx()).curapi.get_com()) (fun() -> (get_ctx()).curapi) in
+	List.iter (fun (n,v) -> match v with
+		| VFunction(f,b) ->
+			let v = match f with
+				| Fun0 f -> VFunction (Fun0 (fun () -> try f () with Sys_error msg | Failure msg -> exc_string msg),b)
+				| Fun1 f -> VFunction (Fun1 (fun a -> try f a with Sys_error msg | Failure msg -> exc_string msg),b)
+				| Fun2 f -> VFunction (Fun2 (fun a b -> try f a b with Sys_error msg | Failure msg -> exc_string msg),b)
+				| Fun3 f -> VFunction (Fun3 (fun a b c -> try f a b c with Sys_error msg | Failure msg -> exc_string msg),b)
+				| Fun4 f -> VFunction (Fun4 (fun a b c d -> try f a b c d with Sys_error msg | Failure msg -> exc_string msg),b)
+				| Fun5 f -> VFunction (Fun5 (fun a b c d e -> try f a b c d e with Sys_error msg | Failure msg -> exc_string msg),b)
+				| FunN f -> VFunction (FunN (fun vl -> try f vl with Sys_error msg | Failure msg -> exc_string msg),b)
+			in
+			Hashtbl.replace EvalStdLib.macro_lib n v
+		| _ -> assert false
+	) api;
+	Globals.macro_platform := Globals.Eval
+
+let can_reuse ctx types = true
+
+let do_reuse ctx api =
+	ctx.curapi <- api
+
+let set_error ctx b =
+	(* TODO: Have to reset this somewhere if running compilation server. But where... *)
+	ctx.had_error <- b
+
+let add_types ctx types ready =
+	ignore(catch_exceptions ctx (fun () -> ignore(add_types ctx types ready)) null_pos)
+
+let compiler_error msg pos =
+	let vi = encode_instance key_haxe_macro_Error in
+	match vi with
+	| VInstance i ->
+		set_instance_field i key_message (encode_string msg);
+		set_instance_field i key_pos (encode_pos pos);
+		exc vi
+	| _ ->
+		assert false
+
+let rec value_to_expr v p =
+	let path i =
+		let mt = IntMap.find i (get_ctx()).type_cache in
+		let make_path t =
+			let rec loop = function
+				| [] -> assert false
+				| [name] -> (EConst (Ident name),p)
+				| name :: l -> (EField (loop l,name),p)
+			in
+			let t = t_infos t in
+			loop (List.rev (if t.mt_module.m_path = t.mt_path then fst t.mt_path @ [snd t.mt_path] else fst t.mt_module.m_path @ [snd t.mt_module.m_path;snd t.mt_path]))
+		in
+		make_path mt
+	in
+	match v with
+	| VNull -> (EConst (Ident "null"),p)
+	| VTrue -> (EConst (Ident "true"),p)
+	| VFalse -> (EConst (Ident "false"),p)
+	| VInt32 i -> (EConst (Int (Int32.to_string i)),p)
+	| VFloat f -> haxe_float f p
+	| VString(r,s) -> (EConst (String (Lazy.force s)),p)
+	| VArray va -> (EArrayDecl (List.map (fun v -> value_to_expr v p) (EvalArray.to_list va)),p)
+	| VObject o -> (EObjectDecl (List.map (fun (k,v) -> ((rev_hash_s k,p),(value_to_expr v p))) (object_fields o)),p)
+	| VEnumValue e ->
+		let epath =
+			let proto = get_static_prototype_raise (get_ctx()) e.epath in
+			let expr = path e.epath in
+			let name = match proto.pkind with
+				| PEnum names -> List.nth names e.eindex
+				| _ -> assert false
+			in
+			(EField (expr, name), p)
+		in
+		begin
+			match e.eargs with
+			| [||] -> epath
+			| _ ->
+				let args = List.map (fun v -> value_to_expr v p) (Array.to_list e.eargs) in
+				(ECall (epath, args), p)
+		end
+
+	| _ -> exc_string ("Cannot convert " ^ (value_string v) ^ " to expr")
+
+let encode_obj = encode_obj_s
+
+let field v f = field v (EvalHash.hash_s f)
+
+let value_string = value_string
+
+let exc_string = exc_string
+
+let eval_expr ctx e = eval_expr ctx key_questionmark key_questionmark e

Kaikkia tiedostoja ei voida näyttää, sillä liian monta tiedostoa muuttui tässä diffissä