Переглянути джерело

[cpp] breakup generator (#11785)

* move some ast stuff into separate files

* remove unused ctx argument from type building functions

* Move source writer into its own module

* context moved into its own module

* retyper module

* rename some ctx_ prefixed functions to be clearer

* split out cppia

* rename another ctx_ function

* hash module

* breakup cpp generator

* remove dead functions

* format a bit

* Some more minor shuffling

* start file names with lower case
Aidan Lee 1 рік тому
батько
коміт
f82db0a79c

+ 158 - 0
src/generators/cpp/cppAst.ml

@@ -0,0 +1,158 @@
+open Extlib_leftovers
+open Ast
+open Type
+open Error
+open Common
+open Globals
+
+type tcpp =
+  | TCppDynamic
+  | TCppUnchanged
+  | TCppObject
+  | TCppObjectPtr
+  | TCppVoid
+  | TCppNull
+  | TCppEnum of tenum
+  | TCppScalar of string
+  | TCppString
+  | TCppFastIterator of tcpp
+  | TCppPointer of string * tcpp
+  | TCppRawPointer of string * tcpp
+  | TCppFunction of tcpp list * tcpp * string
+  | TCppObjCBlock of tcpp list * tcpp
+  | TCppRest of tcpp
+  | TCppReference of tcpp
+  | TCppStruct of tcpp
+  | TCppStar of tcpp * bool
+  | TCppVoidStar
+  | TCppVarArg
+  | TCppAutoCast
+  | TCppDynamicArray
+  | TCppObjectArray of tcpp
+  | TCppWrapped of tcpp
+  | TCppScalarArray of tcpp
+  | TCppObjC of tclass
+  | TCppNativePointer of tclass
+  | TCppVariant
+  | TCppCode of tcpp
+  | TCppInst of tclass * tcpp list
+  | TCppInterface of tclass
+  | TCppProtocol of tclass
+  | TCppClass
+  | TCppGlobal
+
+and tcppexpr = { cppexpr : tcpp_expr_expr; cpptype : tcpp; cpppos : pos }
+
+and tcpp_closure = {
+  close_type : tcpp;
+  close_args : (tvar * texpr option) list;
+  close_expr : tcppexpr;
+  close_id : int;
+  close_undeclared : (string, tvar) Hashtbl.t;
+  close_this : tcppthis option;
+}
+
+and tcppcrementop = CppIncrement | CppDecrement
+and tcppunop = CppNeg | CppNegBits | CppNot
+and tcppthis = ThisReal | ThisFake | ThisDynamic
+
+and tcppvarloc =
+  | VarLocal of tvar
+  | VarClosure of tvar
+  | VarThis of tclass_field * tcpp
+  | VarInstance of tcppexpr * tclass_field * string * string
+  | VarInterface of tcppexpr * tclass_field
+  | VarStatic of tclass * bool * tclass_field
+  | VarInternal of tcppexpr * string * string
+
+and tcppinst = InstPtr | InstObjC | InstStruct
+
+and tcppfuncloc =
+  | FuncThis of tclass_field * tcpp
+  | FuncInstance of tcppexpr * tcppinst * tclass_field
+  | FuncStatic of tclass * bool * tclass_field
+  | FuncTemplate of tclass * tclass_field * path * bool
+  | FuncInterface of tcppexpr * tclass * tclass_field
+  | FuncEnumConstruct of tenum * tenum_field
+  | FuncSuperConstruct of tcpp
+  | FuncSuper of tcppthis * tcpp * tclass_field
+  | FuncNew of tcpp
+  | FuncExpression of tcppexpr
+  | FuncInternal of tcppexpr * string * string
+  | FuncExtern of string * bool
+  | FuncFromStaticFunction
+
+and tcpparrayloc =
+  | ArrayTyped of tcppexpr * tcppexpr * tcpp
+  | ArrayPointer of tcppexpr * tcppexpr
+  | ArrayRawPointer of tcppexpr * tcppexpr
+  | ArrayObject of tcppexpr * tcppexpr * tcpp
+  | ArrayVirtual of tcppexpr * tcppexpr
+  | ArrayImplements of tclass * tcppexpr * tcppexpr
+  | ArrayDynamic of tcppexpr * tcppexpr
+
+and tcpplvalue =
+  | CppVarRef of tcppvarloc
+  | CppArrayRef of tcpparrayloc
+  | CppDynamicRef of tcppexpr * string
+  | CppExternRef of string * bool
+
+and tcpp_expr_expr =
+  | CppInt of int32
+  | CppFloat of string
+  | CppString of string
+  | CppBool of bool
+  | CppNull
+  | CppNullAccess
+  | CppNil
+  | CppThis of tcppthis
+  | CppSuper of tcppthis
+  | CppCode of string * tcppexpr list
+  | CppClosure of tcpp_closure
+  | CppVar of tcppvarloc
+  | CppExtern of string * bool
+  | CppDynamicField of tcppexpr * string
+  | CppFunction of tcppfuncloc * tcpp
+  | CppEnumIndex of tcppexpr
+  | CppEnumField of tenum * tenum_field
+  | CppCall of tcppfuncloc * tcppexpr list
+  | CppFunctionAddress of tclass * tclass_field
+  | CppNewNative of tcppexpr
+  | CppAddressOf of tcppexpr
+  | CppDereference of tcppexpr
+  | CppArray of tcpparrayloc
+  | CppCrement of tcppcrementop * Ast.unop_flag * tcpplvalue
+  | CppSet of tcpplvalue * tcppexpr
+  | CppModify of Ast.binop * tcpplvalue * tcppexpr
+  | CppBinop of Ast.binop * tcppexpr * tcppexpr
+  | CppCompare of string * tcppexpr * tcppexpr * Ast.binop
+  | CppNullCompare of string * tcppexpr
+  | CppObjectDecl of (string * tcppexpr) list * bool
+  | CppPosition of string * int32 * string * string
+  | CppArrayDecl of tcppexpr list
+  | CppUnop of tcppunop * tcppexpr
+  | CppVarDecl of tvar * tcppexpr option
+  | CppBlock of tcppexpr list * tcpp_closure list * bool
+  | CppFor of tvar * tcppexpr * tcppexpr
+  | CppIf of tcppexpr * tcppexpr * tcppexpr option
+  | CppWhile of tcppexpr * tcppexpr * Ast.while_flag * int
+  | CppIntSwitch of tcppexpr * (Int32.t list * tcppexpr) list * tcppexpr option
+  | CppSwitch of
+      tcppexpr * tcpp * (tcppexpr list * tcppexpr) list * tcppexpr option * int
+  | CppTry of tcppexpr * (tvar * tcppexpr) list
+  | CppBreak
+  | CppContinue
+  | CppClassOf of path * bool
+  | CppGoto of int
+  | CppReturn of tcppexpr option
+  | CppThrow of tcppexpr
+  | CppEnumParameter of tcppexpr * tenum_field * int
+  | CppTCast of tcppexpr * tcpp
+  | CppCast of tcppexpr * tcpp
+  | CppCastStatic of tcppexpr * tcpp
+  | CppCastScalar of tcppexpr * string
+  | CppCastVariant of tcppexpr
+  | CppCastObjC of tcppexpr * tclass
+  | CppCastObjCBlock of tcppexpr * tcpp list * tcpp
+  | CppCastProtocol of tcppexpr * tclass
+  | CppCastNative of tcppexpr

+ 730 - 0
src/generators/cpp/cppAstTools.ml

@@ -0,0 +1,730 @@
+open Extlib_leftovers
+open Ast
+open Type
+open Error
+open Common
+open Globals
+open CppAst
+open CppTypeUtils
+
+let follow = Abstract.follow_with_abstracts
+
+(*
+   A class_path is made from a package (array of strings) and a class name.
+   Join these together, inclding a separator.  eg, "/" for includes : pack1/pack2/Name or "::"
+   for namespace "pack1::pack2::Name"
+*)
+let join_class_path path separator =
+  let result =
+    match (fst path, snd path) with
+    | [], s -> s
+    | el, s -> String.concat separator el ^ separator ^ s
+  in
+  if String.contains result '+' then
+    let idx = String.index result '+' in
+    String.sub result 0 idx
+    ^ String.sub result (idx + 1) (String.length result - idx - 1)
+  else result
+
+let class_text path = "::" ^ join_class_path path "::"
+
+let is_internal_member member =
+  member = "toString" || String.length member > 1 && String.sub member 0 2 = "__" &&
+  match member with
+  | "__ArgCount"
+  | "__ArrayImplRef"
+  | "__CStr"
+  | "__Compare"
+  | "__Create"
+  | "__CreateEmpty"
+  | "__FieldRef"
+  | "__FindArgCount"
+  | "__GetFieldMap"
+  | "__GetHandle"
+  | "__GetItem"
+  | "__GetScriptCallable"
+  | "__GetScriptVTable"
+  | "__Param"
+  | "__Remove"
+  | "__SGetClass"
+  | "__Set"
+  | "__SetItem"
+  | "__TArrayImplRef"
+  | "__ToDouble"
+  | "__ToInt"
+  | "__ToInterface"
+  | "__ToObject"
+  | "__Visit"
+  | "__WCStr"
+  | "__a"
+  | "__blit"
+  | "__boot"
+  | "__boot_all"
+  | "__compare"
+  | "__concat"
+  | "__construct"
+  | "__copy"
+  | "__filter"
+  | "__get_args"
+  | "__hx_dump_stack"
+  | "__hx_field_iter"
+  | "__hxt_gc_new"
+  | "__indexOf"
+  | "__insert"
+  | "__instanceof"
+  | "__int"
+  | "__iterator"
+  | "__join"
+  | "__lastIndexOf"
+  | "__loadprim"
+  | "__mClass"
+  | "__mDynamicFields"
+  | "__map"
+  | "__memcmp"
+  | "__new"
+  | "__pop"
+  | "__prime"
+  | "__push"
+  | "__qsort"
+  | "__unshift"
+  | "__unsafeStringReference"
+  | "__time_stamp"
+  | "__superString"
+  | "__splice"
+  | "__shift"
+  | "__slice"
+  | "__sort"
+  | "__s_id"
+  | "__run"
+  | "__root"
+  | "__register"
+  | "__remove"
+  | "__removeAt"
+  | "__reverse"
+  | "__zero"
+  | "__Field"
+  | "__IField"
+  | "__Run"
+  | "__Is"
+  | "__GetClass"
+  | "__GetType"
+  | "__ToString"
+  | "__s"
+  | "__GetPtr"
+  | "__SetField"
+  | "__length"
+  | "__IsArray"
+  | "__SetThis"
+  | "__Internal"
+  | "__EnumParams"
+  | "__Index"
+  | "__Tag"
+  | "__GetFields"
+  | "__HasField"
+  | "__get"
+  | "__set"
+  | "__unsafe_get"
+  | "__unsafe_set"
+  | "__global__"
+  | "__SetSize"
+  | "__trace"
+  | "__GetRealObject"
+  | "__SetSizeExact"
+  | "__cpp__"
+  | "__URLEncode"
+  | "__URLDecode"
+  | "__IsEnum" ->
+    true
+  | _ ->
+    String.length member > 4 && String.sub member 0 4 = "__hx"
+
+let is_known_member member =
+  match member with "__meta__" | "__rtti" | "_Compare" -> true | _ -> false
+
+(* Convert function names that can't be written in c++ ... *)
+let keyword_remap name =
+  if is_internal_member name || is_known_member name then name
+  else if String.length name > 1 && String.sub name 0 2 = "__" then
+    "_hx_" ^ name
+  else
+    match name with
+    | "int"
+    | "Int"
+    | "Bool"
+    | "super"
+    | "auto"
+    | "char"
+    | "const"
+    | "delete"
+    | "double"
+    | "Float"
+    | "enum"
+    | "extern"
+    | "float"
+    | "friend"
+    | "goto"
+    | "long"
+    | "operator"
+    | "protected"
+    | "register"
+    | "short"
+    | "signed"
+    | "sizeof"
+    | "template"
+    | "typedef"
+    | "union"
+    | "unsigned"
+    | "void"
+    | "volatile"
+    | "or"
+    | "and"
+    | "xor"
+    | "or_eq"
+    | "not"
+    | "and_eq"
+    | "xor_eq"
+    | "typeof"
+    | "stdin"
+    | "stdout"
+    | "stderr"
+    | "system"
+    | "BIG_ENDIAN"
+    | "LITTLE_ENDIAN"
+    | "assert"
+    | "NULL"
+    | "wchar_t"
+    | "EOF"
+    | "bool"
+    | "const_cast"
+    | "dynamic_cast"
+    | "explicit"
+    | "export"
+    | "mutable"
+    | "namespace"
+    | "reinterpret_cast"
+    | "static_cast"
+    | "typeid"
+    | "typename"
+    | "virtual"
+    | "_Complex"
+    | "INFINITY"
+    | "NAN"
+    | "INT_MIN"
+    | "INT_MAX"
+    | "INT8_MIN"
+    | "INT8_MAX"
+    | "UINT8_MAX"
+    | "INT16_MIN"
+    | "INT16_MAX"
+    | "UINT16_MAX"
+    | "INT32_MIN"
+    | "INT32_MAX"
+    | "UINT32_MAX"
+    | "asm"
+    | "near"
+    | "far"
+    | "_w64"
+    | "HX_"
+    | "HXLINE"
+    | "HXDLIN"
+    | "NO"
+    | "YES"
+    | "abstract"
+    | "decltype"
+    | "finally"
+    | "nullptr"
+    | "static_assert"
+    | "struct"
+    | "_Atomic"
+    | "constexpr"
+    | "consteval"
+    | "constinit"
+    | "co_await"
+    | "co_return"
+    | "co_yield"
+    | "alignas"
+    | "alignof"
+    | "_Alignas"
+    | "_Alignof"
+    | "requires" ->
+      "_hx_" ^ name
+    | x -> x
+
+let remap_class_path class_path =
+  let path_remap with_keywords name =
+    let len = String.length name in
+    if len > 3 && String.sub name 0 3 = " ::" then String.sub name 3 (len - 3)
+    else if len > 2 && String.sub name 0 2 = "::" then
+      String.sub name 2 (len - 2)
+    else if with_keywords then keyword_remap name
+    else name
+  in
+  ( List.map (path_remap true) (fst class_path),
+    path_remap false (snd class_path) )
+
+let join_class_path_remap path separator =
+  match join_class_path (remap_class_path path) separator with
+  | "Class" -> "hx::Class"
+  | x -> x
+
+let rec s_tcpp = function
+  | CppInt _ -> "CppInt"
+  | CppFloat _ -> "CppFloat"
+  | CppString _ -> "CppString"
+  | CppBool _ -> "CppBool"
+  | CppNull -> "CppNull"
+  | CppNil -> "CppNil"
+  | CppThis _ -> "CppThis"
+  | CppSuper _ -> "CppSuper"
+  | CppCode _ -> "CppCode"
+  | CppClosure _ -> "CppClosure"
+  | CppVar (VarLocal _) -> "CppVarLocal"
+  | CppVar (VarClosure _) -> "CppVarClosure"
+  | CppVar (VarThis _) -> "CppVarThis"
+  | CppVar (VarInstance (expr, field, clazz, op)) ->
+      "CppVarInstance(" ^ clazz ^ "::" ^ op ^ field.cf_name ^ ")"
+  | CppVar (VarInterface _) -> "CppVarInterface"
+  | CppVar (VarStatic (_, true, _)) -> "CppObjcVarStatic"
+  | CppVar (VarStatic _) -> "CppVarStatic"
+  | CppVar (VarInternal _) -> "CppVarInternal"
+  | CppDynamicField _ -> "CppDynamicField"
+  | CppExtern _ -> "CppExtern"
+  | CppFunction _ -> "CppFunction"
+  | CppEnumIndex _ -> "CppEnumIndex"
+  | CppEnumField _ -> "CppEnumField"
+  | CppNullAccess -> "CppNullAccess"
+  | CppCall (FuncThis _, _) -> "CppCallThis"
+  | CppCall (FuncInstance (obj, inst, field), _) ->
+      (match inst with
+      | InstObjC -> "CppCallObjCInstance("
+      | InstPtr -> "CppCallInstance("
+      | _ -> "CppCallStruct(")
+      ^ tcpp_to_string obj.cpptype ^ "," ^ field.cf_name ^ ")"
+  | CppCall (FuncInterface _, _) -> "CppCallInterface"
+  | CppCall (FuncStatic (_, objC, _), _) ->
+      if objC then "CppCallStaticObjC" else "CppCallStatic"
+  | CppCall (FuncTemplate _, _) -> "CppCallTemplate"
+  | CppCall (FuncEnumConstruct _, _) -> "CppCallEnumConstruct"
+  | CppCall (FuncSuperConstruct _, _) -> "CppCallSuperConstruct"
+  | CppCall (FuncSuper _, _) -> "CppCallSuper"
+  | CppCall (FuncNew _, _) -> "CppCallNew"
+  | CppCall (FuncExpression _, _) -> "CppCallExpression"
+  | CppCall (FuncInternal _, _) -> "CppCallInternal"
+  | CppCall (FuncExtern _, _) -> "CppCallExtern"
+  | CppCall (FuncFromStaticFunction, _) -> "CppCallFromStaticFunction"
+  | CppNewNative _ -> "CppNewNative"
+  | CppAddressOf _ -> "CppAddressOf"
+  | CppDereference _ -> "CppDereference"
+  | CppFunctionAddress _ -> "CppFunctionAddress"
+  | CppArray _ -> "CppArray"
+  | CppCrement _ -> "CppCrement"
+  | CppSet _ -> "CppSet"
+  | CppModify _ -> "CppModify"
+  | CppBinop _ -> "CppBinop"
+  | CppCompare _ -> "CppCompare"
+  | CppNullCompare _ -> "CppNullCompare"
+  | CppObjectDecl _ -> "CppObjectDecl"
+  | CppPosition _ -> "CppPosition"
+  | CppArrayDecl _ -> "CppArrayDecl"
+  | CppUnop _ -> "CppUnop"
+  | CppVarDecl _ -> "CppVarDecl"
+  | CppBlock _ -> "CppBlock"
+  | CppFor _ -> "CppFor"
+  | CppIf _ -> "CppIf"
+  | CppWhile _ -> "CppWhile"
+  | CppIntSwitch _ -> "CppIntSwitch"
+  | CppSwitch _ -> "CppSwitch"
+  | CppTry _ -> "CppTry"
+  | CppBreak -> "CppBreak"
+  | CppContinue -> "CppContinue"
+  | CppClassOf _ -> "CppClassOf"
+  | CppGoto _ -> "CppGoto"
+  | CppReturn _ -> "CppReturn"
+  | CppThrow _ -> "CppThrow"
+  | CppEnumParameter _ -> "CppEnumParameter"
+  | CppTCast _ -> "CppTCast"
+  | CppCast _ -> "CppCast"
+  | CppCastStatic _ -> "CppCastStatic"
+  | CppCastScalar _ -> "CppCastScalar"
+  | CppCastVariant _ -> "CppCastVariant"
+  | CppCastObjC _ -> "CppCastObjC"
+  | CppCastObjCBlock _ -> "CppCastObjCBlock"
+  | CppCastProtocol _ -> "CppCastProtocol"
+  | CppCastNative _ -> "CppCastNative"
+
+and tcpp_to_string_suffix suffix tcpp =
+  match tcpp with
+  | TCppDynamic -> " ::Dynamic"
+  | TCppUnchanged -> " ::Dynamic/*Unchanged*/"
+  | TCppObject -> " ::Dynamic"
+  | TCppObjectPtr -> " ::hx::Object *"
+  | TCppReference t -> tcpp_to_string t ^ " &"
+  | TCppStruct t -> "cpp::Struct< " ^ tcpp_to_string t ^ " >"
+  | TCppStar (t, const) ->
+      (if const then "const " else "") ^ tcpp_to_string t ^ " *"
+  | TCppVoid -> "void"
+  | TCppVoidStar -> "void *"
+  | TCppRest _ -> "vaarg_list"
+  | TCppVarArg -> "vararg"
+  | TCppAutoCast -> "::cpp::AutoCast"
+  | TCppVariant -> "::cpp::Variant"
+  | TCppEnum enum -> " ::" ^ join_class_path_remap enum.e_path "::" ^ suffix
+  | TCppScalar scalar -> scalar
+  | TCppString -> "::String"
+  | TCppFastIterator it ->
+      "::cpp::FastIterator" ^ suffix ^ "< " ^ tcpp_to_string it ^ " >"
+  | TCppPointer (ptrType, valueType) ->
+      "::cpp::" ^ ptrType ^ "< " ^ tcpp_to_string valueType ^ " >"
+  | TCppRawPointer (constName, valueType) ->
+      constName ^ tcpp_to_string valueType ^ "*"
+  | TCppFunction (argTypes, retType, abi) ->
+      let args = String.concat "," (List.map tcpp_to_string argTypes) in
+      "::cpp::Function< " ^ tcpp_to_string retType ^ " " ^ abi ^ " (" ^ args
+      ^ ") >"
+  | TCppObjCBlock (argTypes, retType) ->
+      tcpp_objc_block_struct argTypes retType ^ "::t"
+  | TCppDynamicArray -> "::cpp::VirtualArray" ^ suffix
+  | TCppObjectArray _ -> "::Array" ^ suffix ^ "< ::Dynamic>"
+  | TCppWrapped _ -> " ::Dynamic"
+  | TCppScalarArray value ->
+      "::Array" ^ suffix ^ "< " ^ tcpp_to_string value ^ " >"
+  | TCppObjC klass ->
+      let path = join_class_path_remap klass.cl_path "::" in
+      if has_class_flag klass CInterface then "id < " ^ path ^ ">"
+      else path ^ " *"
+  | TCppProtocol interface ->
+      let path =
+        match get_meta_string interface.cl_meta Meta.ObjcProtocol with
+        | Some p -> p
+        | None -> join_class_path_remap interface.cl_path "::"
+      in
+      "id < " ^ path ^ ">"
+  | TCppNativePointer klass ->
+      let name = join_class_path_remap klass.cl_path "::" in
+      if suffix = "_obj" then name else "::hx::Native< " ^ name ^ "* >"
+  | TCppInst (klass, p) ->
+      cpp_class_path_of klass p ^ if is_native_class klass then "" else suffix
+  | TCppInterface klass when suffix = "_obj" ->
+      cpp_class_path_of klass [] ^ suffix
+  | TCppInterface _ -> "::Dynamic"
+  | TCppClass -> "::hx::Class" ^ suffix
+  | TCppGlobal -> "::Dynamic"
+  | TCppNull -> " ::Dynamic"
+  | TCppCode _ -> "Code"
+
+and tcpp_objc_block_struct argTypes retType =
+  let args = String.concat "," (List.map tcpp_to_string argTypes) in
+  let ret = tcpp_to_string retType in
+  let suffix = string_of_int (List.length argTypes) in
+  if ret = "void" then
+    if List.length argTypes = 0 then "::hx::TObjcBlockVoidVoid"
+    else "::hx::TObjcBlockVoidArgs" ^ suffix ^ "< " ^ args ^ " >"
+  else if List.length argTypes = 0 then "::hx::TObjcBlockRetVoid< " ^ ret ^ " >"
+  else "::hx::TObjcBlockRetArgs" ^ suffix ^ "< " ^ ret ^ "," ^ args ^ " >"
+
+and tcpp_to_string tcpp = tcpp_to_string_suffix "" tcpp
+
+and cpp_class_path_of klass params =
+  match get_meta_string klass.cl_meta Meta.Native with
+  | Some s ->
+      let typeParams =
+        match params with
+        | [] -> ""
+        | _ -> "< " ^ String.concat "," (List.map tcpp_to_string params) ^ " >"
+      in
+      " " ^ join_class_path_remap klass.cl_path "::" ^ typeParams
+  | None -> " ::" ^ join_class_path_remap klass.cl_path "::"
+
+(*  Get a string to represent a type.
+   The "suffix" will be nothing or "_obj", depending if we want the name of the
+   pointer class or the pointee (_obj class *)
+let rec class_string klass suffix params remap =
+   let type_string = type_string_remap remap in
+   let join_class_path_remap = if remap then join_class_path_remap else join_class_path in
+   (match klass.cl_path with
+   (* Array class *)
+   |  ([],"Array") when is_dynamic_array_param (List.hd params) ->
+           "cpp::ArrayBase" ^ suffix
+           (*"cpp::VirtualArray" ^ suffix*)
+   |  ([],"Array") -> (snd klass.cl_path) ^ suffix ^ "< " ^ (String.concat ","
+               (List.map array_element_type params) ) ^ " >"
+   (* FastIterator class *)
+   |  (["cpp"],"FastIterator") -> "::cpp::FastIterator" ^ suffix ^ "< " ^ (String.concat ","
+               (List.map type_string  params) ) ^ " >"
+   |  (["cpp"],"Pointer")
+   |  (["cpp"],"ConstPointer") ->
+        "::cpp::Pointer< " ^ (String.concat "," (List.map type_string params) ) ^ " >"
+   |  (["cpp"],"RawPointer") ->
+        " " ^ (String.concat "," (List.map type_string params) ) ^ " * "
+   |  (["cpp"],"RawConstPointer") ->
+        " const " ^ (String.concat "," (List.map type_string params) ) ^ " * "
+   |  (["cpp"],"Function") ->
+        "::cpp::Function< " ^ (cpp_function_signature_params params) ^ " >"
+   | _ when is_dynamic_type_param klass.cl_kind -> "Dynamic"
+   |  ([],"#Int") -> "/* # */int"
+   |  (["cpp"],"UInt8") -> "unsigned char"
+   |  ([],"Class") -> "::hx::Class"
+   |  ([],"EnumValue") -> "Dynamic"
+   |  ([],"Null") -> (match params with
+         | [t] ->
+            (match follow t with
+            | TAbstract ({ a_path = [],"Int" },_)
+            | TAbstract ({ a_path = [],"Float" },_)
+            | TAbstract ({ a_path = [],"Bool" },_) -> "Dynamic"
+            | TAbstract ({ a_path = ["cpp"],"UInt8" },_) -> "Dynamic"
+            | t when type_has_meta_key Meta.NotNull t -> "Dynamic"
+            | _ -> "/*NULL*/" ^ (type_string t) )
+         | _ -> die "" __LOC__);
+   (* Objective-C class *)
+   | path when is_objc_type (TInst(klass,[])) ->
+      let str = join_class_path_remap klass.cl_path "::" in
+      if suffix = "_obj" then
+         str
+      else if (has_class_flag klass CInterface) then
+         "id < " ^ str ^ ">"
+      else
+         str ^ " *"
+   (* Native interface - use pointer *)
+   | _ when (has_class_flag klass CInterface) && is_native_gen_class klass ->
+            (join_class_path_remap klass.cl_path "::") ^ " *"
+   (* Normal class *)
+   | _ when is_native_class klass ->
+      let class_params = match params with
+      | [] -> ""
+      | _ -> "< " ^ (String.concat "," (List.map type_string params)) ^ " >" in
+      (join_class_path_remap klass.cl_path "::") ^ class_params
+   | _ ->
+      let globalNamespace = match get_meta_string klass.cl_meta Meta.Native with
+      | Some s -> s
+      | None -> "::" in
+      globalNamespace ^ (join_class_path_remap klass.cl_path "::") ^ suffix)
+
+and type_has_meta_key key haxe_type =
+   match follow haxe_type with
+   | TInst (klass,_) -> Meta.has key klass.cl_meta
+   | TType (type_def,_) -> Meta.has key type_def.t_meta
+   | TEnum (enum_def,_) -> Meta.has key enum_def.e_meta
+   | _ -> false
+
+and type_string_suff suffix haxe_type remap =
+   let type_string = type_string_remap remap in
+   let join_class_path_remap = if remap then join_class_path_remap else join_class_path in
+   (match haxe_type with
+   | TMono r -> (match r.tm_type with None -> "Dynamic" ^ suffix | Some t -> type_string_suff suffix t remap)
+   | TAbstract ({ a_path = ([],"Void") },[]) -> "Void"
+   | TAbstract ({ a_path = ([],"Bool") },[]) -> "bool"
+   | TAbstract ({ a_path = ([],"Float") },[]) -> "Float"
+   | TAbstract ({ a_path = ([],"Int") },[]) -> "int"
+   | TAbstract ({ a_path = (["cpp"],"UInt8") },[]) -> "unsigned char"
+   | TAbstract( { a_path = ([], "EnumValue") }, _  ) -> "Dynamic"
+   | TAbstract ({ a_path = ([],"Null") }, [t]) ->
+    (match follow t with
+    | TAbstract ({ a_path = [],"Int" },_)
+    | TAbstract ({ a_path = [],"Float" },_)
+    | TAbstract ({ a_path = [],"Bool" },_) -> "Dynamic" ^ suffix
+    | t when type_has_meta_key Meta.NotNull t -> "Dynamic" ^ suffix
+    | _ -> type_string_suff suffix t remap)
+   | TEnum (enum,_) ->  (cpp_enum_path_of enum) ^ suffix
+   | TInst (klass,params) ->  (class_string klass suffix params remap)
+   | TType (type_def,params) ->
+      (match type_def.t_path with
+      | [] , "Array" ->
+         (match params with
+         | [t] when (type_string (follow t) ) = "Dynamic" -> "Dynamic"
+         | [t] -> "Array< " ^ (type_string (follow t) ) ^ " >"
+         | _ -> die "" __LOC__)
+      | ["cpp"] , "FastIterator" ->
+         (match params with
+         | [t] -> "::cpp::FastIterator< " ^ (type_string (follow t) ) ^ " >"
+         | _ -> die "" __LOC__)
+      | ["cpp"] , "Pointer"
+      | ["cpp"] , "ConstPointer" ->
+         (match params with
+         | [t] -> "::cpp::Pointer< " ^ (type_string (follow t) ) ^ " >"
+         | _ -> die "" __LOC__)
+      | ["cpp"] , "RawPointer" ->
+         (match params with
+         | [t] -> " " ^ (type_string (follow t) ) ^ " *"
+         | _ -> die "" __LOC__)
+      | ["cpp"] , "RawConstPointer" ->
+         (match params with
+         | [t] -> "const " ^ (type_string (follow t) ) ^ " *"
+         | _ -> die "" __LOC__)
+      | ["cpp"] , "Function" ->
+         "::cpp::Function< " ^ (cpp_function_signature_params params ) ^ " >"
+      | _ ->  type_string_suff suffix (apply_typedef type_def params) remap
+      )
+   | TFun (args,haxe_type) -> "Dynamic" ^ suffix
+   | TAnon a -> "Dynamic"
+      (*
+      (match !(a.a_status) with
+      | ClassStatics c -> type_string_suff suffix (TInst (c,List.map snd c.cl_params))
+      | EnumStatics e -> type_string_suff suffix (TEnum (e,List.map snd e.e_params))
+      | _ -> "Dynamic"  ^ suffix )
+      *)
+   | TDynamic haxe_type -> "Dynamic" ^ suffix
+   | TLazy func -> type_string_suff suffix (lazy_type func) remap
+   | TAbstract (abs,pl) when abs.a_impl <> None ->
+      type_string_suff suffix (Abstract.get_underlying_type abs pl) remap
+   | TAbstract (abs,pl) ->
+      "::" ^ (join_class_path_remap abs.a_path "::") ^ suffix
+   )
+
+and type_string_remap remap haxe_type =
+   type_string_suff "" haxe_type remap
+
+and type_string haxe_type =
+   type_string_suff "" haxe_type true
+   
+and cpp_enum_path_of enum =
+   let globalNamespace =
+      match get_meta_string enum.e_meta Meta.Native with
+      | Some s -> s
+      | None -> "::" in
+   globalNamespace ^ (join_class_path_remap enum.e_path "::")
+
+and array_element_type haxe_type =
+   match type_string haxe_type with
+   | x when cant_be_null haxe_type -> x
+   | x when is_interface_type (follow haxe_type) -> x
+   | "::String" -> "::String"
+   | _ -> "::Dynamic"
+
+and cpp_function_signature tfun abi =
+   match follow tfun with
+   | TFun(args,ret) -> (type_string ret) ^ " " ^ abi ^ "(" ^ (gen_tfun_interface_arg_list args) ^ ")"
+   | _ -> "void *"
+
+and cpp_function_signature_params params = match params with
+   | [t; abi] -> (match follow abi with
+       | TInst (klass,_) -> cpp_function_signature t (get_meta_string klass.cl_meta Meta.Abi |> Option.default "")
+       | _ -> print_endline (type_string abi);
+           die "" __LOC__ )
+   | _ ->
+      print_endline ("Params:" ^ (String.concat "," (List.map type_string params) ));
+      die "" __LOC__;
+
+and gen_interface_arg_type_name name opt typ =
+   let type_str = (type_string typ) in
+   (* type_str may have already converted Null<X> to Dynamic because of NotNull tag ... *)
+   (if (opt && (cant_be_null typ) && type_str<>"Dynamic" ) then
+      "::hx::Null< " ^ type_str ^ " > "
+   else
+      type_str ) ^ " " ^ (keyword_remap name)
+
+and gen_tfun_interface_arg_list args =
+   String.concat "," (List.map (fun (name,opt,typ) -> gen_interface_arg_type_name name opt typ) args)
+   
+and cant_be_null haxe_type =
+   is_numeric haxe_type || (type_has_meta_key Meta.NotNull haxe_type )
+
+let is_cpp_scalar cpp_type =
+   match cpp_type with
+   | TCppScalar(_) -> true
+   | _ -> false
+
+let is_cpp_array_implementer cppType =
+   match cppType with
+   | TCppInst ({ cl_array_access = Some _ }, _)
+   | TCppInterface ({ cl_array_access = Some _ }) ->
+      true
+   | _ -> false
+
+let rec cpp_is_struct_access t =
+   match t with
+   | TCppFunction _ -> true
+   | TCppStruct _-> false
+   | TCppInst (class_def, _) -> Meta.has Meta.StructAccess class_def.cl_meta
+   | TCppReference (r) -> cpp_is_struct_access r
+   | _ -> false
+
+let rec cpp_is_native_array_access t =
+   match t with
+   | TCppStruct s -> cpp_is_native_array_access s
+   | TCppReference s -> cpp_is_native_array_access s
+   | TCppInst ({ cl_array_access = Some _ } as klass, _) when is_extern_class klass && Meta.has Meta.NativeArrayAccess klass.cl_meta -> true
+   | _ -> false
+
+let cpp_is_dynamic_type = function
+   | TCppDynamic | TCppObject | TCppVariant | TCppWrapped _ | TCppGlobal | TCppNull
+   | TCppInterface _
+      -> true
+   | _ -> false
+
+let is_object_element member_type =
+  match member_type with
+   | TCppInst (x, _)
+   | TCppInterface x
+       -> not (is_extern_class x)
+   | TCppDynamic
+   | TCppObject
+   | TCppObjectPtr
+   | TCppEnum _
+   | TCppString
+   | TCppFunction _
+   | TCppDynamicArray
+   | TCppObjectArray _
+   | TCppWrapped _
+   | TCppScalarArray _
+   | TCppClass
+       -> true
+   | _ -> false
+
+let cpp_variant_type_of t = match t with
+  | TCppDynamic
+  | TCppUnchanged
+  | TCppObject
+  | TCppObjectPtr
+  | TCppReference _
+  | TCppStruct _
+  | TCppStar _
+  | TCppVoid
+  | TCppFastIterator _
+  | TCppDynamicArray
+  | TCppObjectArray _
+  | TCppScalarArray _
+  | TCppWrapped _
+  | TCppObjC _
+  | TCppObjCBlock _
+  | TCppRest _
+  | TCppInst _
+  | TCppInterface _
+  | TCppProtocol _
+  | TCppCode _
+  | TCppClass
+  | TCppGlobal
+  | TCppNull
+  | TCppEnum _ -> TCppDynamic
+  | TCppString -> TCppString
+  | TCppFunction _
+  | TCppNativePointer _
+  | TCppPointer _
+  | TCppRawPointer _
+  | TCppAutoCast
+  | TCppVarArg
+  | TCppVoidStar -> TCppVoidStar
+  | TCppScalar "Int"
+  | TCppScalar "bool"
+  | TCppScalar "Float"  -> t
+  | TCppScalar "::cpp::Int64" -> TCppScalar("Int64")
+  | TCppScalar "double"
+  | TCppScalar "float" -> TCppScalar("Float")
+  | TCppScalar _  -> TCppScalar("int")
+  | TCppVariant -> TCppVariant
+
+let cpp_cast_variant_type_of t = match t with
+  | TCppObjectArray _
+  | TCppScalarArray _
+  | TCppDynamicArray
+  | TCppClass
+  | TCppEnum _
+  | TCppInst _ -> t
+  | _ -> cpp_variant_type_of t
+
+let enum_getter_type t =
+  match cpp_variant_type_of t with
+  | TCppString -> "String"
+  | TCppScalar "int"  -> "Int"
+  | TCppScalar "bool"  -> "Bool"
+  | TCppScalar x  -> x
+  | _  -> "Object"

+ 101 - 0
src/generators/cpp/cppContext.ml

@@ -0,0 +1,101 @@
+open Extlib_leftovers
+open Ast
+open Type
+open Error
+open Common
+open Globals
+open CppAstTools
+
+(* CPP code generation context *)
+(*
+  ctx_debug_level
+    0 = no debug
+    1 = function + line debug via macros, which can be activated at cpp compile-time
+    2 = include macros for HXCPP_DEBUGGER
+    3 = annotate source with additional info about AST and types
+    4 = console output at haxe compile-time
+
+   normal = 1
+*)
+type context = {
+  ctx_common : Common.context;
+  mutable ctx_debug_level : int;
+  (* cached as required *)
+  mutable ctx_file_info : (string, string) PMap.t ref;
+  ctx_type_ids : (string, Int32.t) Hashtbl.t;
+  (* Per file *)
+  ctx_output : string -> unit;
+  ctx_writer : CppSourceWriter.source_writer;
+  ctx_file_id : int ref;
+  ctx_is_header : bool;
+  ctx_interface_slot : (string, int) Hashtbl.t ref;
+  ctx_interface_slot_count : int ref;
+  (* This is for returning from the child nodes of TSwitch && TTry *)
+  mutable ctx_real_this_ptr : bool;
+  mutable ctx_class_member_types : (string, string) Hashtbl.t;
+}
+
+let new_context common_ctx debug file_info member_types =
+  let null_file =
+    new CppSourceWriter.source_writer common_ctx ignore ignore (fun () -> ())
+  in
+  let has_def def = Common.defined_value_safe common_ctx def <> "" in
+  let result =
+    {
+      ctx_common = common_ctx;
+      ctx_writer = null_file;
+      ctx_file_id = ref (-1);
+      ctx_type_ids = Hashtbl.create 0;
+      ctx_is_header = false;
+      ctx_output = null_file#write;
+      ctx_interface_slot = ref (Hashtbl.create 0);
+      ctx_interface_slot_count = ref 2;
+      ctx_debug_level =
+        (if has_def Define.AnnotateSource then 3
+         else if has_def Define.HxcppDebugger then 2
+         else debug);
+      ctx_real_this_ptr = true;
+      ctx_class_member_types = member_types;
+      ctx_file_info = file_info;
+    }
+  in
+  result
+
+let file_context ctx writer debug header =
+  {
+    ctx with
+    ctx_writer = writer;
+    ctx_output = writer#write;
+    ctx_is_header = header;
+    ctx_file_id = ref (-1);
+  }
+
+(* todo - is this how it's done? *)
+
+let pmap_keys pmap =
+  let key_list = ref [] in
+  PMap.iter (fun key _ -> key_list :=  key :: !key_list ) pmap;
+  !key_list
+
+let pmap_values pmap =
+  let value_list = ref [] in
+  PMap.iter (fun _ value -> value_list :=  value :: !value_list ) pmap;
+  !value_list
+
+(* The Hashtbl structure seems a little odd - but here is a helper function *)
+let hash_iterate hash visitor =
+  let result = ref [] in
+  Hashtbl.iter (fun key value -> result :=  (visitor key value) :: !result ) hash;
+  !result
+
+let hash_keys hash =
+  let key_list = ref [] in
+  Hashtbl.iter (fun key value -> key_list :=  key :: !key_list ) hash;
+  !key_list
+
+let is_gc_element ctx member_type =
+  Common.defined ctx.ctx_common Define.HxcppGcGenerational && (is_object_element member_type)
+
+let strip_file ctx file = match Common.defined ctx Common.Define.AbsolutePath with
+  | true -> Path.get_full_path file
+  | false -> ctx.class_paths#relative_path file

+ 24 - 0
src/generators/cpp/cppExprUtils.ml

@@ -0,0 +1,24 @@
+open Extlib_leftovers
+open Ast
+open Type
+open Error
+open Common
+open Globals
+
+let rec remove_parens expression =
+  match expression.eexpr with
+  | TParenthesis e -> remove_parens e
+  | TMeta(_,e) -> remove_parens e
+  | _ -> expression
+
+let rec remove_parens_cast expression =
+  match expression.eexpr with
+  | TParenthesis e -> remove_parens_cast e
+  | TMeta(_,e) -> remove_parens_cast e
+  | TCast ( e,None) -> remove_parens_cast e
+  | _ -> expression
+
+let is_static_access obj =
+  match (remove_parens obj).eexpr with
+  | TTypeExpr _ -> true
+  | _ -> false

+ 1434 - 0
src/generators/cpp/cppRetyper.ml

@@ -0,0 +1,1434 @@
+open Extlib_leftovers
+open Ast
+open Type
+open Error
+open Common
+open Globals
+open CppExprUtils
+open CppTypeUtils
+open CppAst
+open CppAstTools
+open CppContext
+
+let rec cpp_type_of stack haxe_type =
+  if List.exists (fast_eq haxe_type) stack then TCppDynamic
+  else
+    let stack = haxe_type :: stack in
+    match haxe_type with
+    | TMono r -> (
+        match r.tm_type with
+        | None -> TCppDynamic
+        | Some t -> cpp_type_of stack t)
+    | TEnum (enum, params) -> TCppEnum enum
+    | TInst ({ cl_path = [], "Array"; cl_kind = KTypeParameter _ }, _) ->
+        TCppObject
+    | TInst ({ cl_kind = KTypeParameter _ }, _) -> TCppDynamic
+    | TInst (klass, params) -> cpp_instance_type stack klass params
+    | TAbstract (abs, pl) when not (Meta.has Meta.CoreType abs.a_meta) ->
+        cpp_type_from_path stack abs.a_path pl (fun () ->
+            cpp_type_of stack
+              (Abstract.get_underlying_type ~return_first:true abs pl))
+    | TAbstract (a, params) ->
+        cpp_type_from_path stack a.a_path params (fun () ->
+            if is_scalar_abstract a then
+              match get_meta_string a.a_meta Meta.Native with
+              | Some s -> TCppScalar s
+              | None -> TCppScalar (join_class_path a.a_path "::")
+            else TCppDynamic)
+    | TType (type_def, params) ->
+        cpp_type_from_path stack type_def.t_path params (fun () ->
+            cpp_type_of stack (apply_typedef type_def params))
+    | TFun _ -> TCppObject
+    | TAnon _ -> TCppObject
+    | TDynamic _ -> TCppDynamic
+    | TLazy func -> cpp_type_of stack (lazy_type func)
+
+and cpp_type_from_path stack path params default =
+  match (path, params) with
+  | ([], "Void"), _ -> TCppVoid
+  | ([], "void"), _ -> TCppVoid (* for old code with @:void *)
+  | ([], "Bool"), _ -> TCppScalar "bool"
+  | ([], "Float"), _ -> TCppScalar "Float"
+  | ([], "Int"), _ -> TCppScalar "int"
+  | ([], "EnumValue"), _ -> TCppObject
+  | ([], "Class"), _ -> TCppClass
+  | ([], "Enum"), _ -> TCppClass
+  | ([], "Single"), _ -> TCppScalar "float"
+  | ([ "cpp" ], "Char"), _ -> TCppScalar "char"
+  | ([ "cpp" ], "Object"), _ -> TCppObjectPtr
+  | ([ "cpp" ], "Float32"), _ -> TCppScalar "float"
+  | ([ "cpp" ], "Float64"), _ -> TCppScalar "double"
+  | ([ "cpp" ], "Int8"), _ -> TCppScalar "signed char"
+  | ([ "cpp" ], "Int16"), _ -> TCppScalar "short"
+  | ([ "cpp" ], "Int32"), _ -> TCppScalar "int"
+  | ([ "cpp" ], "Int64"), _ -> TCppScalar "::cpp::Int64"
+  | ([ "cpp" ], "UInt8"), _ -> TCppScalar "unsigned char"
+  | ([ "cpp" ], "UInt16"), _ -> TCppScalar "unsigned short"
+  | ([ "cpp" ], "UInt32"), _ -> TCppScalar "unsigned int"
+  | ([ "cpp" ], "UInt64"), _ -> TCppScalar "::cpp::UInt64"
+  | ([ "cpp" ], "VarArg"), _ -> TCppVarArg
+  | ([ "cpp" ], "AutoCast"), _ -> TCppAutoCast
+  | ([], "String"), [] -> TCppString
+  (* Things with type parameters hxcpp knows about ... *)
+  | ([ "cpp" ], "FastIterator"), [ p ] -> TCppFastIterator (cpp_type_of stack p)
+  | ([ "cpp" ], "Pointer"), [ p ] -> TCppPointer ("Pointer", cpp_type_of stack p)
+  | ([ "cpp" ], "ConstPointer"), [ p ] ->
+      TCppPointer ("ConstPointer", cpp_type_of stack p)
+  | ([ "cpp" ], "RawPointer"), [ p ] -> TCppRawPointer ("", cpp_type_of stack p)
+  | ([ "cpp" ], "RawConstPointer"), [ p ] ->
+      TCppRawPointer ("const ", cpp_type_of stack p)
+  | ([ "cpp" ], "Function"), [ function_type; abi ] ->
+      cpp_function_type_of stack function_type abi
+  | ([ "cpp" ], "Callable"), [ function_type ]
+  | ([ "cpp" ], "CallableData"), [ function_type ] ->
+      cpp_function_type_of_string stack function_type ""
+  | ("cpp" :: [ "objc" ], "ObjcBlock"), [ function_type ] ->
+      let args, ret = cpp_function_type_of_args_ret stack function_type in
+      TCppObjCBlock (args, ret)
+  | ([ "cpp" ], "Rest"), [ rest ] -> TCppRest (cpp_type_of stack rest)
+  | ("cpp" :: [ "objc" ], "Protocol"), [ interface_type ] -> (
+      match follow interface_type with
+      | TInst (klass, []) when has_class_flag klass CInterface ->
+          TCppProtocol klass
+      (* TODO - get the line number here *)
+      | _ ->
+          print_endline "cpp.objc.Protocol must refer to an interface";
+          die "" __LOC__)
+  | ([ "cpp" ], "Reference"), [ param ] ->
+      TCppReference (cpp_type_of stack param)
+  | ([ "cpp" ], "Struct"), [ param ] -> TCppStruct (cpp_type_of stack param)
+  | ([ "cpp" ], "Star"), [ param ] ->
+      TCppStar (cpp_type_of_pointer stack param, false)
+  | ([ "cpp" ], "ConstStar"), [ param ] ->
+      TCppStar (cpp_type_of_pointer stack param, true)
+  | ([], "Array"), [ p ] -> (
+      let arrayOf = cpp_type_of stack p in
+      match arrayOf with
+      | TCppVoid (* ? *) | TCppDynamic -> TCppDynamicArray
+      | TCppObject | TCppObjectPtr | TCppReference _ | TCppStruct _ | TCppStar _
+      | TCppEnum _ | TCppInst _ | TCppInterface _ | TCppProtocol _ | TCppClass
+      | TCppDynamicArray | TCppObjectArray _ | TCppScalarArray _ ->
+          TCppObjectArray arrayOf
+      | _ -> TCppScalarArray arrayOf)
+  | ([], "Null"), [ p ] -> cpp_type_of_null stack p
+  | _ -> default ()
+
+and cpp_type_of_null stack p =
+  let baseType = cpp_type_of stack p in
+  if type_has_meta_key Meta.NotNull p || is_cpp_scalar baseType then TCppObject
+  else baseType
+
+and cpp_type_of_pointer stack p =
+  match p with
+  | TAbstract ({ a_path = [], "Null" }, [ t ]) -> cpp_type_of stack t
+  | x -> cpp_type_of stack x
+
+(* Optional types are Dynamic if they norally could not be null *)
+and cpp_fun_arg_type_of stack tvar opt =
+  match opt with
+  | Some _ -> cpp_type_of_null stack tvar.t_type
+  | _ -> cpp_type_of stack tvar.t_type
+
+and cpp_tfun_arg_type_of stack opt t =
+  if opt then cpp_type_of_null stack t else cpp_type_of stack t
+
+and cpp_function_type_of stack function_type abi =
+  let abi =
+    match follow abi with
+    | TInst (klass1, _) ->
+        get_meta_string klass1.cl_meta Meta.Abi |> Option.default ""
+    | _ -> die "" __LOC__
+  in
+  cpp_function_type_of_string stack function_type abi
+
+and cpp_function_type_of_string stack function_type abi_string =
+  let args, ret = cpp_function_type_of_args_ret stack function_type in
+  TCppFunction (args, ret, abi_string)
+
+and cpp_function_type_of_args_ret stack function_type =
+  match follow function_type with
+  | TFun (args, ret) ->
+      (* Optional types are Dynamic if they norally could not be null *)
+      let cpp_arg_type_of (_, optional, haxe_type) =
+        if optional then cpp_type_of_null stack haxe_type
+        else cpp_type_of stack haxe_type
+      in
+      (List.map cpp_arg_type_of args, cpp_type_of stack ret)
+  | _ ->
+      (* ? *)
+      ([ TCppVoid ], TCppVoid)
+
+and cpp_instance_type stack klass params =
+  cpp_type_from_path stack klass.cl_path params (fun () ->
+      if is_objc_class klass then TCppObjC klass
+      else if has_class_flag klass CInterface && is_native_gen_class klass then
+        TCppNativePointer klass
+      else if has_class_flag klass CInterface then TCppInterface klass
+      else if
+        has_class_flag klass CExtern && not (is_internal_class klass.cl_path)
+      then
+        let tcpp_params = List.map (cpp_type_of stack) params in
+        TCppInst (klass, tcpp_params)
+      else
+        let tcpp_params = List.map (cpp_type_of stack) params in
+        TCppInst (klass, tcpp_params))
+
+let cpp_type_of = cpp_type_of []
+let cpp_type_from_path = cpp_type_from_path []
+let cpp_type_of_null = cpp_type_of_null []
+let cpp_type_of_pointer = cpp_type_of_pointer []
+let cpp_tfun_arg_type_of = cpp_tfun_arg_type_of []
+let cpp_function_type_of = cpp_function_type_of []
+let cpp_function_type_of_string = cpp_function_type_of_string []
+let cpp_function_type_of_args_ret = cpp_function_type_of_args_ret []
+let cpp_instance_type = cpp_instance_type []
+
+let expression ctx request_type function_args function_type expression_tree forInjection =
+  let rev_closures = ref [] in
+  let closureId = ref 0 in
+  let declarations = ref (Hashtbl.create 0) in
+  let undeclared = ref (Hashtbl.create 0) in
+  let uses_this = ref None in
+  let gc_stack = ref false in
+  let injection = ref forInjection in
+  let this_real = ref (if ctx.ctx_real_this_ptr then ThisReal else ThisDynamic) in
+  let file_id = ctx.ctx_file_id in
+  let function_return_type = ref (cpp_type_of function_type) in
+  let loop_stack = ref [] in
+  let forCppia = Common.defined ctx.ctx_common Define.Cppia in
+  let alloc_file_id () =
+    incr file_id;
+    !file_id
+  in
+  let begin_loop () =
+    loop_stack := (alloc_file_id (), ref false) :: !loop_stack;
+    fun () ->
+      match !loop_stack with
+      | (label_id, used) :: tl ->
+          loop_stack := tl;
+          if !used then label_id else -1
+      | [] -> abort "Invalid inernal loop handling" expression_tree.epos
+  in
+
+  (* '__trace' is at the top-level *)
+  Hashtbl.add !declarations "__trace" ();
+  List.iter (fun arg -> Hashtbl.add !declarations arg.v_name ()) function_args;
+
+  (* Helper functions *)
+
+  let cpp_const_type cval =
+    match cval with
+    | TInt i -> (CppInt i, TCppScalar "int")
+    | TBool b -> (CppBool b, TCppScalar "bool")
+    | TFloat f -> (CppFloat (Texpr.replace_separators f ""), TCppScalar "Float")
+    | TString s -> (CppString s, TCppString)
+    | _ ->
+        (* TNull, TThis & TSuper should already be handled *)
+        (CppNull, TCppNull)
+  in
+
+  let cpp_return_type haxe_type =
+    match haxe_type with TFun (_, ret) -> cpp_type_of ret | _ -> TCppDynamic
+  in
+
+  let cpp_member_return_type member = cpp_return_type member.cf_type in
+
+  let is_cpp_objc_type cpptype =
+    match cpptype with TCppObjC _ -> true | _ -> false
+  in
+
+  let cpp_is_real_array obj =
+    match obj.cpptype with
+    | TCppScalarArray _ | TCppObjectArray _ -> true
+    | _ -> false
+  in
+
+  let rec to_lvalue value =
+    match value.cppexpr with
+    | CppVar (VarClosure var as varloc)
+      when is_gc_element ctx (cpp_type_of var.v_type) ->
+        (CppVarRef varloc, true)
+    | CppVar (VarThis (member, _) as varloc)
+      when is_gc_element ctx (cpp_type_of member.cf_type) ->
+        (CppVarRef varloc, true)
+    | CppVar (VarInstance (obj, member, _, "->") as varloc)
+      when is_gc_element ctx (cpp_type_of member.cf_type) ->
+        (CppVarRef varloc, true)
+    | CppVar varloc -> (CppVarRef varloc, false)
+    | CppArray arrayloc ->
+        ( CppArrayRef arrayloc,
+          match arrayloc with
+          | ArrayObject (arrayObj, index, _) when is_gc_element ctx TCppDynamic
+            ->
+              true
+          | ArrayTyped (arrayObj, index, t) when is_gc_element ctx t -> true
+          | _ -> false )
+    | CppDynamicField (expr, name) -> (CppDynamicRef (expr, name), false)
+    | CppTCast (cppExpr, _)
+    | CppCast (cppExpr, _)
+    | CppCastStatic (cppExpr, _)
+    | CppCastObjC (cppExpr, _)
+    | CppCastObjCBlock (cppExpr, _, _)
+    | CppCastScalar (cppExpr, _) ->
+        to_lvalue cppExpr
+    | CppCastVariant cppExpr -> to_lvalue cppExpr
+    | CppExtern (name, isGlobal) -> (CppExternRef (name, isGlobal), false)
+    | _ ->
+        abort
+          ("Could not convert expression to l-value (" ^ s_tcpp value.cppexpr
+         ^ ")")
+          value.cpppos
+  in
+
+  let is_array_splice_call obj member =
+    match (obj.cpptype, member.cf_name) with
+    | TCppScalarArray _, "splice" | TCppObjectArray _, "splice" -> true
+    | _, _ -> false
+  in
+
+  let is_map_get_call obj member =
+    member.cf_name = "get"
+    &&
+    match obj.cpptype with
+    | TCppInst ({ cl_path = [ "cpp" ], "Int64Map" }, _) -> true
+    | TCppInst ({ cl_path = [ "haxe"; "ds" ], "IntMap" }, _) -> true
+    | TCppInst ({ cl_path = [ "haxe"; "ds" ], "StringMap" }, _) -> true
+    | TCppInst ({ cl_path = [ "haxe"; "ds" ], "ObjectMap" }, _) -> true
+    | _ -> false
+  in
+
+  let is_map_set_call obj member =
+    member.cf_name = "set"
+    &&
+    match obj.cpptype with
+    | TCppInst ({ cl_path = [ "cpp" ], "Int64Map" }, _) -> true
+    | TCppInst ({ cl_path = [ "haxe"; "ds" ], "IntMap" }, _) -> true
+    | TCppInst ({ cl_path = [ "haxe"; "ds" ], "StringMap" }, _) -> true
+    | TCppInst ({ cl_path = [ "haxe"; "ds" ], "ObjectMap" }, _) -> true
+    | _ -> false
+  in
+
+  let is_array_concat_call obj member =
+    match (obj.cpptype, member.cf_name) with
+    | TCppScalarArray _, "concat" | TCppObjectArray _, "concat" -> true
+    | _, _ -> false
+  in
+
+  let cpp_can_static_cast funcType inferredType =
+    match funcType with
+    | TCppReference _ | TCppStar _ | TCppStruct _ -> false
+    | _ -> (
+        match inferredType with
+        | TCppInst (cls, _) when is_extern_class cls -> false
+        | TCppEnum e when is_extern_enum e -> false
+        | TCppInst _ | TCppClass | TCppEnum _ ->
+            tcpp_to_string funcType <> tcpp_to_string inferredType
+        | _ -> false)
+  in
+
+  let cpp_is_templated_call ctx member =
+    Meta.has Meta.TemplatedCall member.cf_meta
+  in
+
+  let is_complex_compare = function
+    | TCppScalar _ -> false
+    | TCppString -> false
+    | _ -> true
+  in
+
+  let is_pointer_compare = function
+    | TCppObjectArray _ | TCppScalarArray _ | TCppDynamicArray | TCppClass
+    | TCppEnum _ ->
+        true
+    | _ -> false
+  in
+
+  let is_instance_compare = function
+    | TCppInterface _ | TCppInst _ -> true
+    | _ -> false
+  in
+
+  let cpp_append_block block expr =
+    match block.cppexpr with
+    | CppBlock (expr_list, closures, gc_stack) ->
+        {
+          block with
+          cppexpr = CppBlock (expr_list @ [ expr ], closures, gc_stack);
+        }
+    | _ -> abort "Internal error appending expression" block.cpppos
+  in
+
+  let rec const_int_of expr =
+    match expr.eexpr with
+    | TConst TInt x -> x
+    | TConst TBool x -> Int32.of_int (if x then 1 else 0)
+    | TParenthesis e -> const_int_of e
+    | _ -> raise Not_found
+  in
+
+  (* Core Retyping *)
+  let rec retype return_type expr =
+    let cpp_type_of t = cpp_type_of t in
+    let mk_cppexpr newExpr newType =
+      { cppexpr = newExpr; cpptype = newType; cpppos = expr.epos }
+    in
+    let retype_function_args args arg_types =
+      let rec map_pair args types result =
+        match (args, types) with
+        | args, [ TCppRest rest ] ->
+            List.rev (List.map (retype rest) args) @ result
+        | [], [] -> result
+        | a :: arest, t :: trest -> map_pair arest trest (retype t a :: result)
+        | _, [] -> abort "Too many args" expr.epos
+        | [], _ -> abort "Too many types" expr.epos
+      in
+      List.rev (map_pair args arg_types [])
+    in
+
+    let retypedExpr, retypedType =
+      match expr.eexpr with
+      | TEnumParameter (enumObj, enumField, enumIndex) ->
+          let retypedObj = retype TCppDynamic enumObj in
+          ( CppEnumParameter (retypedObj, enumField, enumIndex),
+            cpp_cast_variant_type_of
+              (cpp_type_of (get_nth_type enumField enumIndex)) )
+      | TEnumIndex enumObj ->
+          let retypedObj = retype TCppDynamic enumObj in
+          (CppEnumIndex retypedObj, TCppScalar "int")
+      | TConst TThis ->
+          uses_this := Some !this_real;
+          ( CppThis !this_real,
+            if !this_real = ThisDynamic then TCppDynamic
+            else cpp_type_of expr.etype )
+      | TConst TSuper ->
+          uses_this := Some !this_real;
+          ( CppSuper !this_real,
+            if !this_real = ThisDynamic then TCppDynamic
+            else cpp_type_of expr.etype )
+      | TConst TNull when is_objc_type expr.etype -> (CppNil, TCppNull)
+      | TConst x -> cpp_const_type x
+      | TIdent "__global__" ->
+          (* functions/vars will appear to be members of the virtual global object *)
+          (CppClassOf (([], ""), false), TCppGlobal)
+      | TLocal tvar ->
+          let name = tvar.v_name in
+          if Hashtbl.mem !declarations name then
+            (*print_endline ("Using existing tvar " ^ tvar.v_name);*)
+            (CppVar (VarLocal tvar), cpp_type_of tvar.v_type)
+          else (
+            (*print_endline ("Missing tvar " ^ tvar.v_name);*)
+            Hashtbl.replace !undeclared name tvar;
+            if has_var_flag tvar VCaptured then
+              (CppVar (VarClosure tvar), cpp_type_of tvar.v_type)
+            else (CppExtern (name, false), cpp_type_of tvar.v_type))
+      | TIdent name -> (CppExtern (name, false), return_type)
+      | TBreak -> (
+          if forCppia then (CppBreak, TCppVoid)
+          else
+            match !loop_stack with
+            | [] -> (CppBreak, TCppVoid)
+            | (label_id, used) :: _ ->
+                used := true;
+                (CppGoto label_id, TCppVoid))
+      | TContinue -> (CppContinue, TCppVoid)
+      | TThrow e1 -> (CppThrow (retype TCppDynamic e1), TCppVoid)
+      | TMeta ((Meta.Fixed, _, _), e) -> (
+          let cppType = retype return_type e in
+          match cppType.cppexpr with
+          | CppObjectDecl (def, false) ->
+              (CppObjectDecl (def, true), cppType.cpptype)
+          | _ -> (cppType.cppexpr, cppType.cpptype))
+      | TMeta (_, e) | TParenthesis e ->
+          let cppType = retype return_type e in
+          (cppType.cppexpr, cppType.cpptype)
+      | TField (obj, field) -> (
+          match field with
+          | FInstance (clazz, params, member)
+          | FClosure (Some (clazz, params), member) -> (
+              let funcReturn = cpp_member_return_type member in
+              let clazzType = cpp_instance_type clazz params in
+              let retypedObj = retype clazzType obj in
+              let exprType = cpp_type_of member.cf_type in
+              let is_objc = is_cpp_objc_type retypedObj.cpptype in
+
+              if retypedObj.cpptype = TCppNull then (CppNullAccess, TCppDynamic)
+              else if
+                retypedObj.cpptype = TCppDynamic
+                && not (has_class_flag clazz CInterface)
+              then
+                if is_internal_member member.cf_name then
+                  ( CppFunction
+                      (FuncInstance (retypedObj, InstPtr, member), funcReturn),
+                    exprType )
+                else (CppDynamicField (retypedObj, member.cf_name), TCppVariant)
+              else if cpp_is_struct_access retypedObj.cpptype then
+                match retypedObj.cppexpr with
+                | CppThis ThisReal ->
+                    (CppVar (VarThis (member, retypedObj.cpptype)), exprType)
+                | CppSuper this ->
+                    ( CppFunction
+                        ( FuncSuper (this, retypedObj.cpptype, member),
+                          funcReturn ),
+                      exprType )
+                | _ ->
+                    if is_var_field member then
+                      ( CppVar
+                          (VarInstance
+                             (retypedObj, member, tcpp_to_string clazzType, ".")),
+                        exprType )
+                    else
+                      ( CppFunction
+                          ( FuncInstance (retypedObj, InstStruct, member),
+                            funcReturn ),
+                        exprType )
+              else if is_var_field member then
+                let exprType =
+                  match (retypedObj.cpptype, exprType) with
+                  | TCppPointer (_, t), TCppDynamic
+                  | ( TCppRawPointer (_, t),
+                      TCppDynamic
+                      (* the 'type parameter' will show up as Dynamic *) ) ->
+                      t
+                  | _ -> exprType
+                in
+
+                match retypedObj.cppexpr with
+                | CppThis ThisReal ->
+                    (CppVar (VarThis (member, retypedObj.cpptype)), exprType)
+                | _ -> (
+                    match (retypedObj.cpptype, member.cf_name) with
+                    (* Special variable remapping ... *)
+                    | TCppDynamicArray, "length" when not forCppia ->
+                        ( CppCall
+                            (FuncInternal (retypedObj, "get_length", "->"), []),
+                          exprType )
+                    | TCppInterface _, _ | TCppDynamic, _ ->
+                        ( CppDynamicField (retypedObj, member.cf_name),
+                          TCppVariant )
+                    | TCppObjC _, _ ->
+                        ( CppVar
+                            (VarInstance
+                               ( retypedObj,
+                                 member,
+                                 tcpp_to_string clazzType,
+                                 "." )),
+                          exprType )
+                    | _ ->
+                        let operator =
+                          if
+                            cpp_is_struct_access retypedObj.cpptype
+                            || retypedObj.cpptype = TCppString
+                          then "."
+                          else "->"
+                        in
+                        ( CppVar
+                            (VarInstance
+                               ( retypedObj,
+                                 member,
+                                 tcpp_to_string clazzType,
+                                 operator )),
+                          exprType ))
+              else if
+                has_class_flag clazz CInterface
+                && not is_objc (* Use instance call for objc interfaces *)
+              then
+                ( CppFunction
+                    (FuncInterface (retypedObj, clazz, member), funcReturn),
+                  exprType )
+              else
+                let isArrayObj =
+                  match retypedObj.cpptype with
+                  | TCppDynamicArray | TCppObjectArray _ | TCppScalarArray _ ->
+                      true
+                  | _ -> false
+                in
+                (* Special array return values *)
+                let funcReturn =
+                  if isArrayObj then
+                    match member.cf_name with
+                    | "map" -> TCppDynamicArray
+                    | "splice" | "slice" | "concat" | "copy" | "filter" ->
+                        retypedObj.cpptype
+                    | _ -> funcReturn
+                  else
+                    match (retypedObj.cpptype, funcReturn) with
+                    | TCppPointer (_, t), TCppDynamic
+                    | ( TCppRawPointer (_, t),
+                        TCppDynamic
+                        (* the 'type parameter' will show up as Dynamic *) ) ->
+                        t
+                    | _ -> funcReturn
+                in
+                match retypedObj.cppexpr with
+                | CppThis ThisReal ->
+                    ( CppFunction
+                        (FuncThis (member, retypedObj.cpptype), funcReturn),
+                      exprType )
+                | CppSuper this ->
+                    ( CppFunction
+                        ( FuncSuper (this, retypedObj.cpptype, member),
+                          funcReturn ),
+                      exprType )
+                | _ ->
+                    ( CppFunction
+                        ( FuncInstance
+                            ( retypedObj,
+                              (if is_objc then InstObjC else InstPtr),
+                              member ),
+                          funcReturn ),
+                      exprType ))
+          | FStatic (_, ({ cf_name = "nativeFromStaticFunction" } as member)) ->
+              let funcReturn = cpp_member_return_type member in
+              let exprType = cpp_type_of member.cf_type in
+              (CppFunction (FuncFromStaticFunction, funcReturn), exprType)
+          | FStatic (clazz, member) ->
+              let funcReturn = cpp_member_return_type member in
+              let exprType = cpp_type_of member.cf_type in
+              let objC = is_objc_class clazz in
+              if is_var_field member then
+                (CppVar (VarStatic (clazz, objC, member)), exprType)
+              else
+                ( CppFunction (FuncStatic (clazz, objC, member), funcReturn),
+                  exprType )
+          | FClosure (None, field) | FAnon field ->
+              let obj = retype TCppDynamic obj in
+              let fieldName = field.cf_name in
+              if obj.cpptype = TCppGlobal then
+                (CppExtern (fieldName, true), cpp_type_of expr.etype)
+              else if obj.cpptype = TCppNull then (CppNullAccess, TCppDynamic)
+              else if is_internal_member fieldName then
+                let cppType = cpp_return_type expr.etype in
+                if obj.cpptype = TCppString then
+                  ( CppFunction (FuncInternal (obj, fieldName, "."), cppType),
+                    cppType )
+                else
+                  ( CppFunction (FuncInternal (obj, fieldName, "->"), cppType),
+                    cppType )
+              else (CppDynamicField (obj, field.cf_name), TCppVariant)
+          | FDynamic fieldName ->
+              let obj = retype TCppDynamic obj in
+              if obj.cpptype = TCppNull then (CppNullAccess, TCppDynamic)
+              else if fieldName = "cca" && obj.cpptype = TCppString then
+                ( CppFunction (FuncInternal (obj, "cca", "."), TCppScalar "int"),
+                  TCppDynamic )
+              else if fieldName = "__s" && obj.cpptype = TCppString then
+                ( CppVar (VarInternal (obj, ".", "utf8_str()")),
+                  TCppRawPointer ("const ", TCppScalar "char") )
+              else if fieldName = "__Index" then
+                (CppEnumIndex obj, TCppScalar "int")
+              else if is_internal_member fieldName || cpp_is_real_array obj then
+                let cppType = cpp_return_type expr.etype in
+                if obj.cpptype = TCppString then
+                  ( CppFunction (FuncInternal (obj, fieldName, "."), cppType),
+                    cppType )
+                else
+                  ( CppFunction (FuncInternal (obj, fieldName, "->"), cppType),
+                    cppType )
+              else if obj.cpptype = TCppGlobal then
+                (CppExtern (fieldName, true), cpp_type_of expr.etype)
+              else if obj.cpptype = TCppClass then
+                match obj.cppexpr with
+                | CppClassOf (path, _) ->
+                    ( CppExtern
+                        ( join_class_path_remap path "::" ^ "_obj::" ^ fieldName,
+                          true ),
+                      cpp_type_of expr.etype )
+                | _ ->
+                    ( CppVar (VarInternal (obj, "->", fieldName)),
+                      cpp_type_of expr.etype )
+              else (CppDynamicField (obj, fieldName), TCppVariant)
+          | FEnum (enum, enum_field) ->
+              (CppEnumField (enum, enum_field), TCppEnum enum))
+      | TCall ({ eexpr = TIdent "__cpp__" }, arg_list) ->
+          let cppExpr =
+            match arg_list with
+            | [ { eexpr = TConst (TString code) } ] -> CppCode (code, [])
+            | { eexpr = TConst (TString code) } :: remaining ->
+                let retypedArgs =
+                  List.map
+                    (fun arg -> retype (TCppCode (cpp_type_of arg.etype)) arg)
+                    remaining
+                in
+                CppCode (code, retypedArgs)
+            | _ -> abort "__cpp__'s first argument must be a string" expr.epos
+          in
+          (cppExpr, TCppCode (cpp_type_of expr.etype))
+      | TCall (func, args) -> (
+          let retypedFunc = retype TCppUnchanged func in
+          match retypedFunc.cpptype with
+          | TCppNull -> (CppNullAccess, TCppDynamic)
+          | TCppFunction (argTypes, retType, _) ->
+              let retypedArgs = retype_function_args args argTypes in
+              (CppCall (FuncExpression retypedFunc, retypedArgs), retType)
+          | TCppObjCBlock (argTypes, retType) ->
+              let retypedArgs = retype_function_args args argTypes in
+              (CppCall (FuncExpression retypedFunc, retypedArgs), retType)
+          | _ -> (
+              let cppType = cpp_type_of expr.etype in
+              match retypedFunc.cppexpr with
+              | CppFunction (FuncFromStaticFunction, returnType) -> (
+                  let retypedArgs = List.map (retype TCppDynamic) args in
+                  match retypedArgs with
+                  | [
+                   {
+                     cppexpr =
+                       CppFunction
+                         (FuncStatic (clazz, false, member), funcReturn);
+                   };
+                  ] ->
+                      (CppFunctionAddress (clazz, member), funcReturn)
+                  | _ ->
+                      abort
+                        "cpp.Function.fromStaticFunction must be called on \
+                         static function"
+                        expr.epos)
+              | CppEnumIndex _ ->
+                  (* Not actually a TCall...*)
+                  (retypedFunc.cppexpr, retypedFunc.cpptype)
+              | CppFunction (FuncInstance (obj, InstPtr, member), _)
+                when (not forCppia) && return_type = TCppVoid
+                     && is_array_splice_call obj member ->
+                  let retypedArgs = List.map (retype TCppDynamic) args in
+                  ( CppCall
+                      ( FuncInstance
+                          (obj, InstPtr, { member with cf_name = "removeRange" }),
+                        retypedArgs ),
+                    TCppVoid )
+              | CppFunction (FuncInstance (obj, InstPtr, member), _)
+                when is_array_concat_call obj member ->
+                  let retypedArgs = List.map (retype obj.cpptype) args in
+                  ( CppCall (FuncInstance (obj, InstPtr, member), retypedArgs),
+                    return_type )
+              | CppFunction (FuncStatic (obj, false, member), _)
+                when member.cf_name = "::hx::AddressOf" ->
+                  let arg = retype TCppUnchanged (List.hd args) in
+                  let rawType =
+                    match arg.cpptype with TCppReference x -> x | x -> x
+                  in
+                  (CppAddressOf arg, TCppRawPointer ("", rawType))
+              | CppFunction (FuncStatic (obj, false, member), _)
+                when member.cf_name = "::hx::StarOf" ->
+                  let arg = retype TCppUnchanged (List.hd args) in
+                  let rawType =
+                    match arg.cpptype with TCppReference x -> x | x -> x
+                  in
+                  (CppAddressOf arg, TCppStar (rawType, false))
+              | CppFunction (FuncStatic (obj, false, member), _)
+                when member.cf_name = "::hx::Dereference" ->
+                  let arg = retype TCppUnchanged (List.hd args) in
+                  let rawType =
+                    match arg.cpptype with TCppStar (x, _) -> x | x -> x
+                  in
+                  (CppDereference arg, TCppReference rawType)
+              | CppFunction (FuncStatic (obj, false, member), _)
+                when member.cf_name = "_hx_create_array_length" -> (
+                  let retypedArgs = List.map (retype TCppDynamic) args in
+                  (* gc_stack - not needed yet *)
+                  match return_type with
+                  | TCppObjectArray _ | TCppScalarArray _ ->
+                      (CppCall (FuncNew return_type, retypedArgs), return_type)
+                  | _ ->
+                      ( CppCall (FuncNew TCppDynamicArray, retypedArgs),
+                        return_type ))
+              | CppFunction (FuncStatic (obj, false, member), returnType)
+                when cpp_is_templated_call ctx member -> (
+                  let retypedArgs = List.map (retype TCppDynamic) args in
+                  match retypedArgs with
+                  | { cppexpr = CppClassOf (path, native) } :: rest ->
+                      ( CppCall (FuncTemplate (obj, member, path, native), rest),
+                        returnType )
+                  | _ ->
+                      abort
+                        "First parameter of template function must be a Class"
+                        retypedFunc.cpppos)
+              | CppFunction (FuncInstance (obj, InstPtr, member), _)
+                when is_map_get_call obj member ->
+                  let retypedArgs = List.map (retype TCppDynamic) args in
+                  let fname, cppType =
+                    match return_type with
+                    | TCppVoid | TCppScalar "bool" ->
+                        ( (if forCppia then "getBool" else "get_bool"),
+                          return_type )
+                    | TCppScalar "int" ->
+                        ((if forCppia then "getInt" else "get_int"), return_type)
+                    | TCppScalar "::cpp::Int64" ->
+                        ( (if forCppia then "getInt64" else "get_int64"),
+                          return_type )
+                    | TCppScalar "Float" ->
+                        ( (if forCppia then "getFloat" else "get_float"),
+                          return_type )
+                    | TCppString ->
+                        ( (if forCppia then "getString" else "get_string"),
+                          return_type )
+                    | _ -> ("get", TCppDynamic)
+                  in
+                  let func =
+                    FuncInstance (obj, InstPtr, { member with cf_name = fname })
+                  in
+                  (*
+                   if  cpp_can_static_cast cppType return_type then begin
+                      let call = mk_cppexpr (CppCall(func,retypedArgs)) cppType in
+                      CppCastStatic(call, cppType), cppType
+                   end else
+                   *)
+                  (CppCall (func, retypedArgs), cppType)
+              | CppFunction (FuncInstance (obj, InstPtr, member), _)
+                when forCppia && is_map_set_call obj member ->
+                  let retypedArgs = List.map (retype TCppDynamic) args in
+                  let fname =
+                    match retypedArgs with
+                    | [ _; { cpptype = TCppScalar "bool" } ] -> "setBool"
+                    | [ _; { cpptype = TCppScalar "int" } ] -> "setInt"
+                    | [ _; { cpptype = TCppScalar "::cpp::Int64" } ] ->
+                        "setInt64"
+                    | [ _; { cpptype = TCppScalar "Float" } ] -> "setFloat"
+                    | [ _; { cpptype = TCppString } ] -> "setString"
+                    | _ -> "set"
+                  in
+                  let func =
+                    FuncInstance (obj, InstPtr, { member with cf_name = fname })
+                  in
+                  (CppCall (func, retypedArgs), cppType)
+              | CppFunction
+                  ((FuncInstance (obj, InstPtr, member) as func), returnType)
+                when cpp_can_static_cast returnType cppType ->
+                  let retypedArgs = List.map (retype TCppDynamic) args in
+                  let call =
+                    mk_cppexpr (CppCall (func, retypedArgs)) returnType
+                  in
+                  (CppCastStatic (call, cppType), cppType)
+              (*
+                let error_printer file line = Printf.sprintf "%s:%d:" file line in
+                let epos = Lexer.get_error_pos error_printer expr.epos in
+                print_endline ( "fixed override " ^ member.cf_name ^ " @ " ^  epos ^ " " ^ (tcpp_to_string returnType) ^ "->" ^ (ctx_type_string ctx expr.etype) );
+                CppCall(func,retypedArgs), returnType
+              *)
+              (* Other functions ... *)
+              | CppFunction
+                  ( (FuncInstance
+                       (_, InstStruct, { cf_type = TFun (arg_types, _) }) as
+                     func),
+                    return_type ) ->
+                  (* For struct access classes use the types of the arguments instead of the function argument types *)
+                  (* In the case of generic extern classes a TFun arg type could be `MyClass.T` instead of the real type *)
+                  let map_args func_arg passed_arg =
+                    let name, opt, _ = func_arg in
+                    (name, opt, passed_arg.etype)
+                  in
+                  let real_types = List.map2 map_args arg_types args in
+                  let arg_types =
+                    List.map
+                      (fun (_, opt, t) -> cpp_tfun_arg_type_of opt t)
+                      real_types
+                  in
+                  let retypedArgs = retype_function_args args arg_types in
+                  (CppCall (func, retypedArgs), return_type)
+              | CppFunction
+                  ( (FuncInstance (_, _, { cf_type = TFun (arg_types, _) }) as
+                     func),
+                    returnType )
+              | CppFunction
+                  ( (FuncStatic (_, _, { cf_type = TFun (arg_types, _) }) as func),
+                    returnType )
+              | CppFunction
+                  ( (FuncThis ({ cf_type = TFun (arg_types, _) }, _) as func),
+                    returnType ) ->
+                  let arg_types =
+                    List.map
+                      (fun (_, opt, t) -> cpp_tfun_arg_type_of opt t)
+                      arg_types
+                  in
+                  (* retype args specifically (not just CppDynamic) *)
+                  let retypedArgs = retype_function_args args arg_types in
+                  (CppCall (func, retypedArgs), returnType)
+              | CppFunction (func, returnType) ->
+                  let retypedArgs = List.map (retype TCppDynamic) args in
+                  (CppCall (func, retypedArgs), returnType)
+              | CppEnumField (enum, field) ->
+                  (* TODO - proper re-typing *)
+                  let retypedArgs = List.map (retype TCppDynamic) args in
+                  ( CppCall (FuncEnumConstruct (enum, field), retypedArgs),
+                    cppType )
+              | CppSuper _ ->
+                  (* TODO - proper re-typing *)
+                  let retypedArgs = List.map (retype TCppDynamic) args in
+                  ( CppCall (FuncSuperConstruct retypedFunc.cpptype, retypedArgs),
+                    TCppVoid )
+              | CppDynamicField (expr, name) -> (
+                  let retypedArgs = List.map (retype TCppDynamic) args in
+                  (* Special function calls *)
+                  match (expr.cpptype, name) with
+                  | TCppGlobal, _ ->
+                      let retypedArgs = List.map (retype TCppUnchanged) args in
+                      (CppCall (FuncExtern (name, true), retypedArgs), cppType)
+                  | TCppString, _ ->
+                      ( CppCall (FuncInternal (expr, name, "."), retypedArgs),
+                        cppType )
+                  | _, "__Tag" ->
+                      ( CppCall
+                          (FuncInternal (expr, "_hx_getTag", "->"), retypedArgs),
+                        cppType )
+                  | _, name when is_internal_member name ->
+                      ( CppCall (FuncInternal (expr, name, "->"), retypedArgs),
+                        cppType )
+                  | _ ->
+                      (* not special *)
+                      ( CppCall (FuncExpression retypedFunc, retypedArgs),
+                        TCppDynamic ))
+              | CppExtern (name, isGlobal) ->
+                  let retypedArgs = List.map (retype TCppUnchanged) args in
+                  (CppCall (FuncExtern (name, isGlobal), retypedArgs), cppType)
+              | _ ->
+                  let retypedArgs = List.map (retype TCppDynamic) args in
+                  ( CppCall (FuncExpression retypedFunc, retypedArgs),
+                    TCppDynamic )))
+      | TNew (class_def, params, args) ->
+          let constructor_type =
+            match
+              OverloadResolution.maybe_resolve_constructor_overload class_def
+                params args
+            with
+            | None -> abort "Could not find overload" expr.epos
+            | Some (_, constructor, _) -> constructor.cf_type
+          in
+          let arg_types, _ = cpp_function_type_of_args_ret constructor_type in
+          let retypedArgs = retype_function_args args arg_types in
+          let created_type = cpp_type_of expr.etype in
+          (gc_stack :=
+             !gc_stack
+             ||
+             match created_type with
+             | TCppInst (t, _) -> not (is_native_class t)
+             | _ -> false);
+          (CppCall (FuncNew created_type, retypedArgs), created_type)
+      | TFunction func ->
+          let old_this_real = !this_real in
+          this_real := ThisFake;
+          (* TODO - this_dynamic ? *)
+          let old_undeclared = Hashtbl.copy !undeclared in
+          let old_declarations = Hashtbl.copy !declarations in
+          let old_uses_this = !uses_this in
+          let old_gc_stack = !gc_stack in
+          let old_return_type = !function_return_type in
+          let ret = cpp_type_of func.tf_type in
+          function_return_type := ret;
+          uses_this := None;
+          undeclared := Hashtbl.create 0;
+          declarations := Hashtbl.create 0;
+          List.iter
+            (fun (tvar, _) -> Hashtbl.add !declarations tvar.v_name ())
+            func.tf_args;
+          let cppExpr = retype TCppVoid (mk_block func.tf_expr) in
+          let result =
+            {
+              close_expr = cppExpr;
+              close_id = !closureId;
+              close_undeclared = !undeclared;
+              close_type = ret;
+              close_args = func.tf_args;
+              close_this = !uses_this;
+            }
+          in
+          incr closureId;
+          declarations := old_declarations;
+          undeclared := old_undeclared;
+          Hashtbl.iter
+            (fun name tvar ->
+              if not (Hashtbl.mem !declarations name) then
+                Hashtbl.replace !undeclared name tvar)
+            result.close_undeclared;
+          function_return_type := old_return_type;
+          this_real := old_this_real;
+          uses_this :=
+            if !uses_this != None then Some old_this_real else old_uses_this;
+          gc_stack := old_gc_stack;
+          rev_closures := result :: !rev_closures;
+          (CppClosure result, TCppDynamic)
+      | TArray (e1, e2) ->
+          let arrayExpr, elemType =
+            match cpp_is_native_array_access (cpp_type_of e1.etype) with
+            | true ->
+                let retypedObj = retype TCppUnchanged e1 in
+                let retypedIdx = retype (TCppScalar "int") e2 in
+                ( CppArray (ArrayRawPointer (retypedObj, retypedIdx)),
+                  cpp_type_of expr.etype )
+            | false -> (
+                let retypedObj = retype TCppDynamic e1 in
+                let retypedIdx = retype (TCppScalar "int") e2 in
+                match retypedObj.cpptype with
+                | TCppScalarArray scalar ->
+                    ( CppArray (ArrayTyped (retypedObj, retypedIdx, scalar)),
+                      scalar )
+                | TCppPointer (_, elem) ->
+                    (CppArray (ArrayPointer (retypedObj, retypedIdx)), elem)
+                | TCppRawPointer (_, elem) ->
+                    (CppArray (ArrayRawPointer (retypedObj, retypedIdx)), elem)
+                | TCppObjectArray TCppDynamic ->
+                    ( CppArray
+                        (ArrayObject (retypedObj, retypedIdx, TCppDynamic)),
+                      TCppDynamic )
+                | TCppObjectArray elem ->
+                    (CppArray (ArrayObject (retypedObj, retypedIdx, elem)), elem)
+                | TCppInst (({ cl_array_access = Some _ } as klass), _) ->
+                    ( CppArray (ArrayImplements (klass, retypedObj, retypedIdx)),
+                      cpp_type_of expr.etype )
+                | TCppDynamicArray ->
+                    ( CppArray (ArrayVirtual (retypedObj, retypedIdx)),
+                      TCppDynamic )
+                | _ ->
+                    ( CppArray (ArrayDynamic (retypedObj, retypedIdx)),
+                      TCppDynamic ))
+          in
+          let returnType = cpp_type_of expr.etype in
+          if cpp_can_static_cast elemType returnType then
+            ( CppCastStatic (mk_cppexpr arrayExpr returnType, returnType),
+              returnType )
+          else (arrayExpr, elemType)
+      | TTypeExpr module_type ->
+          (* If we try and use the coreType / runtimeValue cpp.Int64 abstract with Class<T> then we get a class decl of the abstract *)
+          (* as that abstract has functions in its declaration *)
+          (* Intercept it and replace it with the path of the actual int64 type so the generated cpp is correct *)
+          let path =
+            match module_type with
+            | TClassDecl { cl_path = [ "cpp"; "_Int64" ], "Int64_Impl_" } ->
+                ([ "cpp" ], "Int64")
+            | _ -> t_path module_type
+          in
+          (CppClassOf (path, is_native_gen_module module_type), TCppClass)
+      | TBinop (op, left, right) -> (
+          let binOpType =
+            match op with
+            | OpDiv -> TCppScalar "Float"
+            | OpBoolAnd | OpBoolOr -> TCppScalar "bool"
+            | OpAnd | OpOr | OpXor | OpShl | OpShr | OpUShr -> TCppScalar "int"
+            | OpAssign -> (retype TCppUnchanged left).cpptype
+            | OpMult | OpSub -> cpp_type_of expr.etype
+            | _ -> TCppUnchanged
+          in
+          let e1 = retype binOpType left in
+          let e2 = retype binOpType right in
+
+          let complex =
+            is_complex_compare e1.cpptype || is_complex_compare e2.cpptype
+          in
+          let pointer =
+            is_pointer_compare e1.cpptype || is_pointer_compare e2.cpptype
+          in
+          let instance =
+            is_instance_compare e1.cpptype || is_instance_compare e2.cpptype
+          in
+          let e1_null = e1.cpptype = TCppNull in
+          let e2_null = e2.cpptype = TCppNull in
+          let reference =
+            match op with
+            | OpAssign ->
+                let lvalue, gc = to_lvalue e1 in
+                if gc then gc_stack := true;
+                CppSet (lvalue, e2)
+            | OpAssignOp op ->
+                let lvalue, gc = to_lvalue e1 in
+                if gc then gc_stack := true;
+                CppModify (op, lvalue, e2)
+            | OpEq when e1_null && e2_null -> CppBool true
+            | OpGte when e1_null && e2_null -> CppBool true
+            | OpLte when e1_null && e2_null -> CppBool true
+            | OpNotEq when e1_null && e2_null -> CppBool false
+            | _ when e1_null && e2_null -> CppBool false
+            | OpEq when e1_null -> CppNullCompare ("IsNull", e2)
+            | OpGte when e1_null -> CppNullCompare ("IsNull", e2)
+            | OpLte when e1_null -> CppNullCompare ("IsNull", e2)
+            | OpNotEq when e1_null -> CppNullCompare ("IsNotNull", e2)
+            | OpEq when e2_null -> CppNullCompare ("IsNull", e1)
+            | OpGte when e2_null -> CppNullCompare ("IsNull", e1)
+            | OpLte when e2_null -> CppNullCompare ("IsNull", e1)
+            | OpNotEq when e2_null -> CppNullCompare ("IsNotNull", e1)
+            | OpEq when instance -> CppCompare ("IsInstanceEq", e1, e2, op)
+            | OpNotEq when instance -> CppCompare ("IsInstanceNotEq", e1, e2, op)
+            | OpEq when pointer -> CppCompare ("IsPointerEq", e1, e2, op)
+            | OpNotEq when pointer -> CppCompare ("IsPointerNotEq", e1, e2, op)
+            | OpEq when complex -> CppCompare ("IsEq", e1, e2, op)
+            | OpNotEq when complex -> CppCompare ("IsNotEq", e1, e2, op)
+            | OpGte when complex -> CppCompare ("IsGreaterEq", e1, e2, op)
+            | OpLte when complex -> CppCompare ("IsLessEq", e1, e2, op)
+            | OpGt when complex -> CppCompare ("IsGreater", e1, e2, op)
+            | OpLt when complex -> CppCompare ("IsLess", e1, e2, op)
+            | _ -> CppBinop (op, e1, e2)
+          in
+          match (op, e1.cpptype, e2.cpptype) with
+          (* Variant + Variant = Variant *)
+          | OpAdd, _, TCppVariant | OpAdd, TCppVariant, _ ->
+              (reference, TCppVariant)
+          | _, _, _ -> (reference, cpp_type_of expr.etype))
+      | TUnop (op, pre, e1) ->
+          let targetType =
+            match op with
+            | Not -> TCppScalar "bool"
+            | NegBits -> TCppScalar "int"
+            | _ -> cpp_type_of e1.etype
+          in
+
+          let e1 = retype targetType e1 in
+          let reference =
+            match op with
+            | Increment ->
+                let lvalue, gc = to_lvalue e1 in
+                if gc then gc_stack := true;
+                CppCrement (CppIncrement, pre, lvalue)
+            | Decrement ->
+                let lvalue, gc = to_lvalue e1 in
+                if gc then gc_stack := true;
+                CppCrement (CppDecrement, pre, lvalue)
+            | Neg -> CppUnop (CppNeg, e1)
+            | Not -> CppUnop (CppNot, e1)
+            | NegBits -> CppUnop (CppNegBits, e1)
+            | Spread -> die ~p:expr.epos "Unexpected spread operator" __LOC__
+          in
+          (reference, cpp_type_of expr.etype)
+      | TFor (v, init, block) ->
+          let old_declarations = Hashtbl.copy !declarations in
+          Hashtbl.add !declarations v.v_name ();
+          let init = retype (cpp_type_of v.v_type) init in
+          let block = retype TCppVoid (mk_block block) in
+          declarations := old_declarations;
+          (CppFor (v, init, block), TCppVoid)
+      | TWhile (e1, e2, flag) ->
+          let condition = retype (TCppScalar "bool") e1 in
+          let close = begin_loop () in
+          let block = retype TCppVoid (mk_block e2) in
+          (CppWhile (condition, block, flag, close ()), TCppVoid)
+      | TArrayDecl el ->
+          let retypedEls = List.map (retype TCppDynamic) el in
+          (CppArrayDecl retypedEls, cpp_type_of expr.etype)
+      | TBlock expr_list ->
+          let inject = !injection in
+          injection := false;
+          if return_type <> TCppVoid && not forCppia then
+            print_endline
+              ("Value from a block not handled " ^ expr.epos.pfile ^ " "
+              ^ string_of_int (Lexer.get_error_line expr.epos));
+
+          let old_declarations = Hashtbl.copy !declarations in
+          let old_closures = !rev_closures in
+          rev_closures := [];
+          let local_closures = ref [] in
+          let remaining = ref (List.length expr_list) in
+          let cppExprs =
+            List.map
+              (fun expr ->
+                let targetType =
+                  if inject && !remaining = 1 then cpp_type_of expr.etype
+                  else TCppVoid
+                in
+                decr remaining;
+                let result = retype targetType expr in
+                local_closures := !rev_closures @ !local_closures;
+                rev_closures := [];
+                result)
+              expr_list
+          in
+          declarations := old_declarations;
+          rev_closures := old_closures;
+
+          (CppBlock (cppExprs, List.rev !local_closures, !gc_stack), TCppVoid)
+      | TObjectDecl
+          [
+            (("fileName", _, _), { eexpr = TConst (TString file) });
+            (("lineNumber", _, _), { eexpr = TConst (TInt line) });
+            (("className", _, _), { eexpr = TConst (TString class_name) });
+            (("methodName", _, _), { eexpr = TConst (TString meth) });
+          ] ->
+          (CppPosition (file, line, class_name, meth), TCppDynamic)
+      | TObjectDecl el -> (
+          let retypedEls =
+            List.map (fun ((v, _, _), e) -> (v, retype TCppDynamic e)) el
+          in
+          match return_type with
+          | TCppVoid -> (CppObjectDecl (retypedEls, false), TCppVoid)
+          | _ -> (CppObjectDecl (retypedEls, false), TCppDynamic))
+      | TVar (v, eo) ->
+          let varType = cpp_type_of v.v_type in
+          let init =
+            match eo with None -> None | Some e -> Some (retype varType e)
+          in
+          Hashtbl.add !declarations v.v_name ();
+          (CppVarDecl (v, init), varType)
+      | TIf (ec, e1, e2) ->
+          let ec = retype (TCppScalar "bool") ec in
+          let blockify =
+            if return_type != TCppVoid then fun e -> e else mk_block
+          in
+          let e1 = retype return_type (blockify e1) in
+          let e2 =
+            match e2 with
+            | None -> None
+            | Some e -> Some (retype return_type (blockify e))
+          in
+          ( CppIf (ec, e1, e2),
+            if return_type = TCppVoid then TCppVoid else cpp_type_of expr.etype
+          )
+      (* Switch internal return - wrap whole thing in block  *)
+      | TSwitch
+          {
+            switch_subject = condition;
+            switch_cases = cases;
+            switch_default = def;
+          } -> (
+          if return_type <> TCppVoid then
+            abort "Value from a switch not handled" expr.epos;
+
+          let conditionType = cpp_type_of condition.etype in
+          let condition = retype conditionType condition in
+          let cppDef =
+            match def with
+            | None -> None
+            | Some e -> Some (retype TCppVoid (mk_block e))
+          in
+          if forCppia then
+            let cases =
+              List.map
+                (fun { case_patterns = el; case_expr = e2 } ->
+                  let cppBlock = retype TCppVoid (mk_block e2) in
+                  (List.map (retype conditionType) el, cppBlock))
+                cases
+            in
+            (CppSwitch (condition, conditionType, cases, cppDef, -1), TCppVoid)
+          else
+            try
+              (match conditionType with
+              | TCppScalar "int" | TCppScalar "bool" -> ()
+              | _ -> raise Not_found);
+              let cases =
+                List.map
+                  (fun { case_patterns = el; case_expr = e2 } ->
+                    (List.map const_int_of el, retype TCppVoid (mk_block e2)))
+                  cases
+              in
+              (CppIntSwitch (condition, cases, cppDef), TCppVoid)
+            with Not_found ->
+              let label = alloc_file_id () in
+              (* do something better maybe ... *)
+              let cases =
+                List.map
+                  (fun { case_patterns = el; case_expr = e2 } ->
+                    let cppBlock = retype TCppVoid (mk_block e2) in
+                    let gotoExpr =
+                      {
+                        cppexpr = CppGoto label;
+                        cpptype = TCppVoid;
+                        cpppos = e2.epos;
+                      }
+                    in
+                    let cppBlock = cpp_append_block cppBlock gotoExpr in
+                    (List.map (retype conditionType) el, cppBlock))
+                  cases
+              in
+              ( CppSwitch (condition, conditionType, cases, cppDef, label),
+                TCppVoid ))
+      | TTry (try_block, catches) ->
+          (* TTry internal return - wrap whole thing in block ? *)
+          if return_type <> TCppVoid then
+            abort "Value from a try-block not handled" expr.epos;
+          let cppBlock = retype TCppVoid try_block in
+          let cppCatches =
+            List.map
+              (fun (tvar, catch_block) ->
+                let old_declarations = Hashtbl.copy !declarations in
+                Hashtbl.add !declarations tvar.v_name ();
+                let cppCatchBlock = retype TCppVoid catch_block in
+                declarations := old_declarations;
+                (tvar, cppCatchBlock))
+              catches
+          in
+          (CppTry (cppBlock, cppCatches), TCppVoid)
+      | TReturn eo ->
+          ( CppReturn
+              (match eo with
+              | None -> None
+              | Some e -> Some (retype !function_return_type e)),
+            TCppVoid )
+      | TCast (base, None) -> (
+          (* Use auto-cast rules *)
+          let return_type = cpp_type_of expr.etype in
+          let baseCpp = retype return_type base in
+          let baseStr = tcpp_to_string baseCpp.cpptype in
+          let returnStr = tcpp_to_string return_type in
+          if baseStr = returnStr then
+            (baseCpp.cppexpr, baseCpp.cpptype (* nothing to do *))
+          else
+            match return_type with
+            | TCppObjC k -> (CppCastObjC (baseCpp, k), return_type)
+            | TCppPointer (_, _)
+            | TCppRawPointer (_, _)
+            | TCppStar _ | TCppInst _ ->
+                (CppCast (baseCpp, return_type), return_type)
+            | TCppString -> (CppCastScalar (baseCpp, "::String"), return_type)
+            | TCppCode t when baseStr <> tcpp_to_string t ->
+                (CppCast (baseCpp, t), t)
+            | TCppNativePointer klass -> (CppCastNative baseCpp, return_type)
+            | TCppObjCBlock (args, ret) ->
+                (CppCastObjCBlock (baseCpp, args, ret), return_type)
+            | TCppProtocol p -> (CppCastProtocol (baseCpp, p), return_type)
+            | TCppDynamic when baseCpp.cpptype = TCppClass ->
+                (CppCast (baseCpp, TCppDynamic), TCppDynamic)
+            | _ -> (baseCpp.cppexpr, baseCpp.cpptype (* use autocasting rules *))
+          )
+      | TCast (base, Some t) -> (
+          let baseCpp = retype (cpp_type_of base.etype) base in
+          let baseStr = tcpp_to_string baseCpp.cpptype in
+          let default_return_type =
+            if return_type = TCppUnchanged then cpp_type_of expr.etype
+            else return_type
+          in
+          let return_type =
+            cpp_type_from_path (t_path t) [] (fun () -> default_return_type)
+          in
+          let returnStr = tcpp_to_string return_type in
+
+          if baseStr = returnStr then
+            (baseCpp.cppexpr, baseCpp.cpptype (* nothing to do *))
+          else
+            match return_type with
+            | TCppNativePointer klass -> (CppCastNative baseCpp, return_type)
+            | TCppVoid ->
+                (CppTCast (baseCpp, cpp_type_of expr.etype), return_type)
+            | TCppDynamic -> (baseCpp.cppexpr, baseCpp.cpptype)
+            | _ -> (CppTCast (baseCpp, return_type), return_type))
+    in
+    let cppExpr = mk_cppexpr retypedExpr retypedType in
+
+    (* Autocast rules... *)
+    if return_type = TCppVoid then mk_cppexpr retypedExpr TCppVoid
+    else if return_type = TCppVarArg then
+      match cpp_variant_type_of cppExpr.cpptype with
+      | TCppVoidStar | TCppScalar _ -> cppExpr
+      | TCppString ->
+          mk_cppexpr
+            (CppVar (VarInternal (cppExpr, ".", "raw_ptr()")))
+            (TCppPointer ("ConstPointer", TCppScalar "char"))
+      | TCppDynamic -> mk_cppexpr (CppCastNative cppExpr) TCppVoidStar
+      | _ ->
+          let toDynamic =
+            mk_cppexpr (CppCast (cppExpr, TCppDynamic)) TCppDynamic
+          in
+          mk_cppexpr (CppCastNative toDynamic) TCppVoidStar
+    else if
+      cppExpr.cpptype = TCppVariant
+      || cppExpr.cpptype = TCppDynamic
+      || cppExpr.cpptype == TCppObject
+    then
+      match return_type with
+      | TCppUnchanged -> cppExpr
+      | TCppInst (t, _) when Meta.has Meta.StructAccess t.cl_meta ->
+          let structType = TCppStruct (TCppInst (t, [])) in
+          let structCast =
+            mk_cppexpr (CppCast (cppExpr, structType)) structType
+          in
+          mk_cppexpr (CppCast (structCast, TCppInst (t, []))) (TCppInst (t, []))
+      | TCppObjectArray _ | TCppScalarArray _ | TCppNativePointer _
+      | TCppDynamicArray | TCppObjectPtr | TCppVarArg | TCppInst _ ->
+          mk_cppexpr (CppCast (cppExpr, return_type)) return_type
+      | TCppObjC k -> mk_cppexpr (CppCastObjC (cppExpr, k)) return_type
+      | TCppObjCBlock (ret, args) ->
+          mk_cppexpr (CppCastObjCBlock (cppExpr, ret, args)) return_type
+      | TCppScalar scalar ->
+          mk_cppexpr (CppCastScalar (cppExpr, scalar)) return_type
+      | TCppString ->
+          mk_cppexpr (CppCastScalar (cppExpr, "::String")) return_type
+      | TCppInterface _ when cppExpr.cpptype = TCppVariant ->
+          mk_cppexpr (CppCastVariant cppExpr) return_type
+      | TCppDynamic when cppExpr.cpptype = TCppVariant ->
+          mk_cppexpr (CppCastVariant cppExpr) return_type
+      | TCppStar (t, const) ->
+          let ptrType =
+            TCppPointer ((if const then "ConstPointer" else "Pointer"), t)
+          in
+          let ptrCast = mk_cppexpr (CppCast (cppExpr, ptrType)) ptrType in
+          mk_cppexpr
+            (CppCast (ptrCast, TCppStar (t, const)))
+            (TCppStar (t, const))
+      | _ -> cppExpr
+    else
+      match (cppExpr.cpptype, return_type) with
+      | _, TCppUnchanged -> cppExpr
+      (*
+        Using the 'typedef hack', where we use typedef X<T> = T, allows the
+        haxe compiler to use these types interchangeably. We then work
+        out the correct way to convert between them when one is expected, but another provided.
+
+        TCppFunction: these do not really interact with the haxe function type, T
+        Since they are implemented with cpp::Function, conversion to/from Dynamic should happen automatically
+          CallableData<T> = T;
+          FunctionData<T,ABI> = T;
+
+        TCppObjCBlock can move in and out of Dyanmic
+          ObjcBlock<T> = T;
+
+        TCppProtocol can move in and out of Dyanmic, via delegate creation
+          Protocol<T /*:interface*/ > = T;
+
+        Explicitly wrapped type - already interacts well with Dynamic and T
+          Struct<T> = T;
+
+        TCppStar, TCppStruct, TCppReference - for interacting with native code
+          Star<T> = T;
+          ConstStar<T> = T;
+          Reference<T> = T;
+          T may be an extern class, with @:structAccess - in which case
+            Dynamic interaction must be handled explicitly
+        These types, plus Dynamic can be used interchangeably by haxe
+        Derived/inherited types may also be mixed in
+      *)
+      | TCppAutoCast, _ | TCppObjC _, TCppDynamic | TCppObjCBlock _, TCppDynamic
+        ->
+          mk_cppexpr (CppCast (cppExpr, return_type)) return_type
+      (* Infer type from right-hand-side for pointer or reference to Dynamic *)
+      | TCppReference TCppDynamic, TCppReference _ -> cppExpr
+      | TCppReference TCppDynamic, t -> mk_cppexpr retypedExpr (TCppReference t)
+      | TCppStar (TCppDynamic, _), TCppStar (_, _) -> cppExpr
+      | TCppStar (TCppDynamic, const), t ->
+          mk_cppexpr retypedExpr (TCppStar (t, const))
+      | TCppStar (t, const), TCppDynamic ->
+          let ptrType =
+            TCppPointer ((if const then "ConstPointer" else "Pointer"), t)
+          in
+          let ptrCast = mk_cppexpr (CppCast (cppExpr, ptrType)) ptrType in
+          mk_cppexpr (CppCast (ptrCast, TCppDynamic)) TCppDynamic
+      | TCppStar (t, const), TCppReference _
+      | TCppStar (t, const), TCppInst _
+      | TCppStar (t, const), TCppStruct _ ->
+          mk_cppexpr (CppDereference cppExpr) return_type
+      | TCppInst (t, _), TCppStar _
+        when is_native_class t
+             &&
+             match cppExpr.cppexpr with
+             | CppCall (FuncNew _, _) -> true
+             | _ -> false ->
+          mk_cppexpr (CppNewNative cppExpr) return_type
+      | TCppInst _, TCppStar (p, const) | TCppStruct _, TCppStar (p, const) ->
+          mk_cppexpr (CppAddressOf cppExpr) return_type
+      | TCppObjectPtr, TCppObjectPtr -> cppExpr
+      | TCppObjectPtr, _ ->
+          mk_cppexpr (CppCast (cppExpr, TCppDynamic)) TCppDynamic
+      | TCppProtocol _, TCppProtocol _ -> cppExpr
+      | t, TCppProtocol protocol ->
+          mk_cppexpr (CppCastProtocol (cppExpr, protocol)) return_type
+      | TCppInst (t, _), TCppDynamic when Meta.has Meta.StructAccess t.cl_meta
+        ->
+          let structType = TCppStruct (TCppInst (t, [])) in
+          let structCast =
+            mk_cppexpr (CppCast (cppExpr, structType)) structType
+          in
+          mk_cppexpr (CppCast (structCast, TCppDynamic)) TCppDynamic
+      | _, TCppObjectPtr ->
+          mk_cppexpr (CppCast (cppExpr, TCppObjectPtr)) TCppObjectPtr
+      | TCppDynamicArray, TCppScalarArray _
+      | TCppDynamicArray, TCppObjectArray _
+      | TCppScalarArray _, TCppDynamicArray
+      | TCppObjectArray _, TCppDynamicArray
+        when forCppia ->
+          mk_cppexpr (CppCast (cppExpr, return_type)) return_type
+      | TCppScalar from, TCppScalar too when from <> too ->
+          mk_cppexpr (CppCastScalar (cppExpr, too)) return_type
+      | _ -> cppExpr
+  in
+  retype request_type expression_tree

+ 188 - 0
src/generators/cpp/cppSourceWriter.ml

@@ -0,0 +1,188 @@
+open Extlib_leftovers
+open Ast
+open Type
+open Error
+open Common
+open Globals
+open CppStrings
+open CppAstTools
+open CppTypeUtils
+
+(*
+  Code for generating source files.
+  It manages creating diretories, indents, blocks and only modifying files
+  when the content changes.
+*)
+
+let get_include_prefix common_ctx with_slash =
+  try
+    Common.defined_value common_ctx Define.IncludePrefix ^ if with_slash then "/" else ""
+  with Not_found -> ""
+
+let should_prefix_include = function
+  | x when is_internal_class x -> false
+  | [], "hxMath" -> true
+  | _ -> false
+
+let verbatim_include file =
+  match String.sub file 0 1 with
+  | "@" -> "@import " ^ String.sub file 1 (String.length file - 1) ^ ";\n"
+  | _ -> "#include \"" ^ file ^ "\"\n"
+
+let guarded_include file =
+  let guard_name = "INCLUDED_" ^ hash64 file in
+  "#ifndef " ^ guard_name ^ "\n" ^ "#define " ^ guard_name ^ "\n" ^ verbatim_include file ^ "#endif\n"
+
+let source_file_extension common_ctx =
+  (* no need to -D file_extension if -D objc is defined *)
+  if Common.defined common_ctx Define.Objc then ".mm"
+  else
+    try
+      "." ^ Common.defined_value common_ctx Define.FileExtension
+    with Not_found -> ".cpp"
+
+class source_writer common_ctx write_header_func write_func close_func =
+  object (this)
+    val indent_str = "\t"
+    val mutable indent = ""
+    val mutable indents = []
+    val mutable just_finished_block = false
+    val mutable headerLines = Hashtbl.create 0
+
+    method close =
+      close_func ();
+      ()
+
+    method write x =
+      write_func x;
+      just_finished_block <- false
+
+    method write_h x =
+      write_header_func x;
+      ()
+
+    method write_h_unique x =
+      if not (Hashtbl.mem headerLines x) then (
+        Hashtbl.add headerLines x ();
+        this#write_h x)
+
+    method indent_one = this#write indent_str
+
+    method push_indent =
+      indents <- indent_str :: indents;
+      indent <- String.concat "" indents
+
+    method pop_indent =
+      match indents with
+      | h :: tail ->
+          indents <- tail;
+          indent <- String.concat "" indents
+      | [] -> indent <- "/*?*/"
+
+    method write_i x = this#write (indent ^ x)
+    method get_indent = indent
+
+    method begin_block =
+      this#write "{\n";
+      this#push_indent
+
+    method end_block =
+      this#pop_indent;
+      this#write_i "}\n";
+      just_finished_block <- true
+
+    method end_block_line =
+      this#pop_indent;
+      this#write_i "}";
+      just_finished_block <- true
+
+    method terminate_line =
+      this#write (if just_finished_block then "" else ";\n")
+
+    method add_big_closures =
+      this#write_h_unique "#include <hx/MacrosJumbo.h>\n"
+
+    method add_include class_path =
+      match class_path with
+      | [ "@verbatim" ], file -> this#write_h_unique (guarded_include file)
+      | _ ->
+          let prefix =
+            if should_prefix_include class_path then ""
+            else get_include_prefix common_ctx true
+          in
+          this#write_h
+            ("#ifndef INCLUDED_" ^ join_class_path class_path "_" ^ "\n");
+          this#write_h
+            ("#include <" ^ prefix ^ join_class_path class_path "/" ^ ".h>\n");
+          this#write_h "#endif\n"
+  end
+
+let read_whole_file chan = Std.input_all chan
+
+(*
+  The cached_source_writer will not write to the file if it has not changed,
+  thus allowing the makefile dependencies to work correctly
+*)
+let cached_source_writer common_ctx filename =
+  let header = Buffer.create 0 in
+  let add_header str = Buffer.add_string header str in
+  let buffer = Buffer.create 0 in
+  let add_buf str = Buffer.add_string buffer str in
+  let close () =
+    Buffer.add_buffer header buffer;
+    let contents = Buffer.contents header in
+    let same =
+      try
+        let in_file = open_in filename in
+        let old_contents = read_whole_file in_file in
+        close_in in_file;
+        contents = old_contents
+      with _ -> false
+    in
+    if not same then (
+      let out_file = open_out filename in
+      output_string out_file contents;
+      close_out out_file)
+  in
+  new source_writer common_ctx add_header add_buf close
+
+let new_source_file common_ctx base_dir sub_dir extension class_path =
+  let include_prefix = get_include_prefix common_ctx true in
+  let full_dir =
+    if sub_dir = "include" && include_prefix <> "" then (
+      let dir =
+        match fst class_path with
+        | [] -> base_dir ^ "/include/" ^ get_include_prefix common_ctx false
+        | path ->
+            base_dir ^ "/include/" ^ include_prefix ^ String.concat "/" path
+      in
+      Path.mkdir_recursive base_dir
+        ([ "include"; include_prefix ] @ fst class_path);
+      dir)
+    else (
+      Path.mkdir_recursive base_dir (sub_dir :: fst class_path);
+      base_dir ^ "/" ^ sub_dir ^ "/" ^ String.concat "/" (fst class_path))
+  in
+  let file =
+    cached_source_writer common_ctx (full_dir ^ "/" ^ snd class_path ^ extension)
+  in
+  Codegen.map_source_header common_ctx (fun s ->
+      file#write_h (Printf.sprintf "// %s\n" s));
+  file
+
+let new_cpp_file common_ctx base_dir =
+  new_source_file common_ctx base_dir "src" (source_file_extension common_ctx)
+
+let new_header_file common_ctx base_dir =
+  new_source_file common_ctx base_dir "include" ".h"
+
+let new_placed_cpp_file common_ctx class_path =
+  let base_dir = common_ctx.file in
+
+  if (Common.defined common_ctx Define.Vcproj ) then begin
+    Path.mkdir_recursive base_dir ("src"::[]);
+    cached_source_writer common_ctx
+      ( base_dir ^ "/src/" ^ ( String.concat "-" (fst class_path) ) ^ "-" ^
+      (snd class_path) ^ (source_file_extension common_ctx) )
+  end else
+    new_cpp_file common_ctx common_ctx.file class_path

+ 131 - 0
src/generators/cpp/cppStrings.ml

@@ -0,0 +1,131 @@
+open Extlib_leftovers
+open Globals
+
+let gen_hash32 seed str =
+  let h = ref (Int32.of_int seed) in
+  let cycle = Int32.of_int 223 in
+  for i = 0 to String.length str - 1 do
+    h :=
+      Int32.add (Int32.mul !h cycle)
+        (Int32.of_int (int_of_char (String.unsafe_get str i)))
+  done;
+  !h
+
+let hash64 s = String.sub (Digest.to_hex (Digest.string s)) 0 16
+let gen_hash seed str = Printf.sprintf "0x%08lx" (gen_hash32 seed str)
+let gen_hash_small seed str = Printf.sprintf "%08lx" (gen_hash32 seed str)
+
+let gen_qstring_hash str =
+  let h = gen_hash32 0 str in
+  Printf.sprintf "%02lx,%02lx,%02lx,%02lx"
+    (Int32.shift_right_logical (Int32.shift_left h 24) 24)
+    (Int32.shift_right_logical (Int32.shift_left h 16) 24)
+    (Int32.shift_right_logical (Int32.shift_left h 8) 24)
+    (Int32.shift_right_logical h 24)
+
+let gen_wqstring_hash str =
+  let h = gen_hash32 0 str in
+  Printf.sprintf "%04lx,%04lx"
+    (Int32.shift_right_logical (Int32.shift_left h 16) 16)
+    (Int32.shift_right_logical h 16)
+
+let special_to_hex s =
+  let l = String.length s in
+  let b = Buffer.create 0 in
+  for i = 0 to l - 1 do
+    match Char.code (String.unsafe_get s i) with
+    | c when c > 127 || c < 32 ->
+        Buffer.add_string b (Printf.sprintf "\\x%02x\"\"" c)
+    | c -> Buffer.add_char b (Char.chr c)
+  done;
+  Buffer.contents b
+
+let strq ctx s =
+  let has_utf8_chars s =
+    let result = ref false in
+    for i = 0 to String.length s - 1 do
+      result := !result || Char.code (String.unsafe_get s i) > 127
+    done;
+    !result
+  in
+
+  let gen_str macro gen s =
+    let rec split s plus =
+      let escaped = StringHelper.s_escape ~hex:false s in
+      let hexed = special_to_hex escaped in
+      if String.length hexed <= 16000 then
+        plus ^ " HX_CSTRING(\"" ^ hexed ^ "\")"
+      else
+        let len = String.length s in
+        let half = len lsr 1 in
+        split (String.sub s 0 half) plus
+        ^ split (String.sub s half (len - half)) "+"
+    in
+    let escaped = StringHelper.s_escape ~hex:false s in
+    let hexed = special_to_hex escaped in
+    if String.length hexed <= 16000 then
+      macro ^ "(\"" ^ hexed ^ "\"," ^ gen s ^ ")"
+    else "(" ^ split s "" ^ ")"
+  in
+
+  if Common.defined ctx Define.HxcppSmartStings && has_utf8_chars s then (
+    let b = Buffer.create 0 in
+
+    let add ichar =
+      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%04x" c)
+      | c when c > 0xFFFF -> Buffer.add_string b (Printf.sprintf "\\U%08x" c)
+      | c -> Buffer.add_char b (Char.chr c)
+    in
+    UTF8.iter (fun c -> add (UCharExt.code c)) s;
+    "HX_W(u\"" ^ Buffer.contents b ^ "\"," ^ gen_wqstring_hash s ^ ")")
+  else gen_str "HX_" gen_qstring_hash s
+
+let escape_command s =
+  let b = Buffer.create 0 in
+  String.iter
+    (fun ch ->
+      if ch == '"' || ch == '\\' then Buffer.add_string b "\\";
+      Buffer.add_char b ch)
+    s;
+  Buffer.contents b
+
+let const_char_star s =
+  let escaped = StringHelper.s_escape ~hex:false s in
+  "\"" ^ special_to_hex escaped ^ "\""
+
+let make_path_absolute path pos =
+   try
+      if (String.sub path 0 2) = "./" then begin
+         let base = if (Filename.is_relative pos.pfile) then
+            Filename.concat (Sys.getcwd()) pos.pfile
+         else
+            pos.pfile
+         in
+         Path.normalize_path (Filename.concat (Filename.dirname base) (String.sub path 2 ((String.length path) -2)))
+      end else
+         path
+   with Invalid_argument _ -> path
+
+let path_of_string path =
+  ["@verbatim"], path
+
+let get_all_meta_string_path meta_list key =
+  let extract_path pos expr =
+    match expr with
+    | Ast.EConst (Ast.String (name, _)), _ -> make_path_absolute name pos
+    | _ -> ""
+  in
+  let extract_meta meta =
+    match meta with
+    | k, exprs, pos when k = key -> Some (extract_path pos (List.hd exprs))
+    | _ -> None
+  in
+  ExtList.List.filter_map extract_meta meta_list

+ 358 - 0
src/generators/cpp/cppTypeUtils.ml

@@ -0,0 +1,358 @@
+(* Various helper functions to run checks on haxe classes and various other ast types *)
+(* functions in here operate on standard haxe ast types, not gencpp ast types *)
+
+open Extlib_leftovers
+open Ast
+open Type
+open Error
+open Common
+open Globals
+
+let follow = Abstract.follow_with_abstracts
+
+let is_native_gen_class class_def =
+   Meta.has Meta.NativeGen class_def.cl_meta ||
+   match class_def.cl_kind with
+   | KAbstractImpl abstract_def -> Meta.has Meta.NativeGen abstract_def.a_meta
+   | _ -> false
+ 
+let is_native_gen_module = function
+   | TClassDecl class_def -> is_native_gen_class class_def
+   | _ -> false
+ 
+let is_extern_class class_def =
+   has_class_flag class_def CExtern ||
+   Meta.has Meta.Extern class_def.cl_meta ||
+   match class_def.cl_kind with
+   | KAbstractImpl abstract_def -> Meta.has Meta.Extern abstract_def.a_meta
+   | _ -> false
+ 
+let is_extern_enum enum_def =
+   has_enum_flag enum_def EnExtern || Meta.has Meta.Extern enum_def.e_meta
+
+(* The internal classes are implemented by the core hxcpp system, so the cpp classes should not be generated *)
+let is_internal_class = function
+   | [], "Int"
+   | [], "Void"
+   | [], "String"
+   | [], "Null"
+   | [], "Float"
+   | [], "Array"
+   | [], "Class"
+   | [], "Enum"
+   | [], "Bool"
+   | [], "Dynamic"
+   | [], "ArrayAccess"
+   | [], "Math"
+   | [], "Single"
+   | [ "cpp" ], "FastIterator"
+   | [ "cpp" ], "Pointer"
+   | [ "cpp" ], "ConstPointer"
+   | [ "cpp" ], "RawPointer"
+   | [ "cpp" ], "RawConstPointer"
+   | [ "cpp" ], "Function"
+   | [ "cpp" ], "VirtualArray"
+   | [ "cpp" ], "Int8"
+   | [ "cpp" ], "UInt8"
+   | [ "cpp" ], "Char"
+   | [ "cpp" ], "Int16"
+   | [ "cpp" ], "UInt16"
+   | [ "cpp" ], "Int32"
+   | [ "cpp" ], "UInt32"
+   | [ "cpp" ], "Int64"
+   | [ "cpp" ], "UInt64"
+   | [ "cpp" ], "Float32"
+   | [ "cpp" ], "Float64" ->
+      true
+   | _ ->
+      false
+
+let is_native_class class_def =
+   (is_extern_class class_def || is_native_gen_class class_def) && not (is_internal_class class_def.cl_path)
+
+let is_interface_type t =
+   match follow t with
+   | TInst (klass,params) -> (has_class_flag klass CInterface)
+   | _ -> false
+
+let rec is_objc_type t =
+   match t with
+   | TInst(cl,_) -> (has_class_flag cl CExtern) && Meta.has Meta.Objc cl.cl_meta
+   | TType(td,_) -> (Meta.has Meta.Objc td.t_meta)
+   | TAbstract (a,_) -> (Meta.has Meta.Objc a.a_meta)
+   | TMono r -> (match r.tm_type with | Some t -> is_objc_type t | _ -> false)
+   | TLazy f -> is_objc_type (lazy_type f)
+   | _ -> false
+
+let is_objc_type t =
+   match t with
+   | TInst(cl,_) -> (has_class_flag cl CExtern) && Meta.has Meta.Objc cl.cl_meta
+   | TType(td,_) -> (Meta.has Meta.Objc td.t_meta)
+   | TAbstract (a,_) -> (Meta.has Meta.Objc a.a_meta)
+   | TMono r -> (match r.tm_type with | Some t -> is_objc_type t | _ -> false)
+   | TLazy f -> is_objc_type (lazy_type f)
+   | _ -> false
+
+let is_dynamic_type_param class_kind =
+   match class_kind with
+   | KTypeParameter _ -> true
+   | _ -> false
+
+let is_dynamic_array_param t =
+   match follow t with
+   | TAbstract ({ a_path = ([],"Dynamic") },[]) -> true
+   | TInst (klass, params) ->
+      (match klass with
+      | { cl_path = ([], "Array") }
+      | { cl_path = ([], "Class") }
+      | { cl_path = (["cpp"], "FastIterator") }
+      | { cl_path = (["cpp"], "RawPointer") }
+      | { cl_path = (["cpp"], "ConstRawPointer") }
+      | { cl_path = (["cpp"], "Pointer") }
+      | { cl_path = (["cpp"], "ConstPointer") }
+      | { cl_path = (["cpp"], "Function") } -> false
+      | { cl_kind = KTypeParameter _ } -> true
+      | _ -> false)
+   | _ -> false
+
+let is_numeric t =
+   match follow t with
+   | TAbstract({ a_path = ([], "Int") }, [])
+   | TAbstract({ a_path = ([], "Float") }, [])
+   | TAbstract({ a_path = ([], "Single") }, [])
+   | TAbstract({ a_path = (["cpp"], "Char") }, [])
+   | TAbstract({ a_path = (["cpp"], "Float32") }, [])
+   | TAbstract({ a_path = (["cpp"], "Float64") }, [])
+   | TAbstract({ a_path = (["cpp"], "Int8") }, [])
+   | TAbstract({ a_path = (["cpp"], "Int16") }, [])
+   | TAbstract({ a_path = (["cpp"], "Int32") }, [])
+   | TAbstract({ a_path = (["cpp"], "Int64") }, [])
+   | TAbstract({ a_path = (["cpp"], "UInt8") }, [])
+   | TAbstract({ a_path = (["cpp"], "UInt16") }, [])
+   | TAbstract({ a_path = (["cpp"], "UInt32") }, [])
+   | TAbstract({ a_path = (["cpp"], "UInt64") }, [])
+      -> true
+   | _
+      -> false
+   
+let is_cpp_function_instance t =
+   match follow t with
+   | TInst ({ cl_path = (["cpp"], "Function") }, _) -> true
+   | _ -> false
+
+let is_objc_class klass =
+   has_class_flag klass CExtern && Meta.has Meta.Objc klass.cl_meta
+
+let is_var_field field =
+   match field.cf_kind with
+   | Var _
+   | Method MethDynamic -> true
+   | _ -> false
+
+let is_pointer haxe_type includeRaw =
+   match follow haxe_type with
+   | TInst (klass,params) ->
+      (match klass.cl_path with
+      | ["cpp"] , "Pointer"
+      | ["cpp"] , "ConstPointer"
+      | ["cpp"] , "Function" -> true
+      | ["cpp"] , "RawPointer" when includeRaw -> true
+      | ["cpp"] , "RawConstPointer" when includeRaw -> true
+      | _ -> false )
+   | TType (type_def,params) ->
+      (match type_def.t_path with
+      | ["cpp"] , "Pointer"
+      | ["cpp"] , "ConstPointer"
+      | ["cpp"] , "Function" -> true
+      | ["cpp"] , "RawPointer" when includeRaw -> true
+      | ["cpp"] , "RawConstPointer" when includeRaw -> true
+      | _ -> false )
+   | _ -> false
+   ;;
+
+let is_array haxe_type =
+   match follow haxe_type with
+   | TInst ({ cl_path = ([], "Array") }, params)
+   | TType ({ t_path = ([], "Array") }, params) ->
+      not (is_dynamic_array_param (List.hd params))
+   | _ -> false
+
+let is_array_or_dyn_array haxe_type =
+   match follow haxe_type with
+   | TInst ({ cl_path = ([], "Array") }, _)
+   | TType ({ t_path = ([], "Array")}, _) -> true
+   | _ -> false
+
+let is_array_implementer haxe_type =
+   match follow haxe_type with
+   | TInst ({ cl_array_access = Some _ }, _) -> true
+   | _ -> false
+
+let rec has_rtti_interface c interface =
+   List.exists (function (t,pl) ->
+      (snd t.cl_path) = interface && (match fst t.cl_path with | ["cpp";"rtti"] -> true | _ -> false )
+   ) c.cl_implements ||
+      (match c.cl_super with None -> false | Some (c,_) -> has_rtti_interface c interface)
+
+let has_field_integer_lookup class_def =
+   has_rtti_interface class_def "FieldIntegerLookup"
+
+let has_field_integer_numeric_lookup class_def =
+   has_rtti_interface class_def "FieldNumericIntegerLookup"
+
+let should_implement_field x = is_physical_field x
+
+let is_scalar_abstract abstract_def =
+   Meta.has Meta.Scalar abstract_def.a_meta && Meta.has Meta.CoreType abstract_def.a_meta
+
+let is_real_function field =
+   match field.cf_kind with
+   | Method MethNormal | Method MethInline-> true
+   | _ -> false
+
+let get_nth_type field index =
+   match follow field.ef_type with
+   | TFun (args,_) ->
+      let rec nth l index = match l with
+      | [] -> raise Not_found
+      | (_,_,t)::rest ->
+            if index = 0 then t
+            else nth rest (index-1)
+      in
+      nth args index
+   | _ -> raise Not_found
+
+let is_dynamic_haxe_method f =
+   match f.cf_expr, f.cf_kind with
+   | Some { eexpr = TFunction _ }, (Var _ | Method MethDynamic) -> true
+   | _ -> false
+
+let has_dynamic_member_functions class_def =
+   List.fold_left (fun result field ->
+      match field.cf_expr with
+      | Some { eexpr = TFunction function_def } when is_dynamic_haxe_method field -> true
+      | _ -> result ) false class_def.cl_ordered_fields
+
+let has_field_init field =
+   match field.cf_expr with
+   (* Function field *)
+   | Some { eexpr = TFunction function_def } -> is_dynamic_haxe_method field
+   (* Data field *)
+   | Some _ -> true
+   | _ -> false
+
+let is_data_member field =
+   match field.cf_kind with
+   | Var _ | Method MethDynamic -> true
+   | _ -> false
+
+let is_override field =
+   has_class_field_flag field CfOverride
+
+let rec unreflective_type t =
+   match follow t with
+   | TInst (klass,_) ->  Meta.has Meta.Unreflective klass.cl_meta
+   | TFun (args,ret) ->
+      List.fold_left (fun result (_,_,t) -> result || (unreflective_type t)) (unreflective_type ret) args;
+   | _ -> false
+
+let reflective class_def field = not (
+   (Meta.has Meta.NativeGen class_def.cl_meta) ||
+   (Meta.has Meta.Unreflective class_def.cl_meta) ||
+   (Meta.has Meta.Unreflective field.cf_meta) ||
+   unreflective_type field.cf_type)
+
+let has_init_field class_def =
+   match TClass.get_cl_init class_def with
+   | Some _ -> true
+   | _ -> false
+
+let is_abstract_impl class_def = match class_def.cl_kind with
+   | KAbstractImpl _ -> true
+   | _ -> false
+
+let variable_field field =
+   match field.cf_expr with
+   | Some { eexpr = TFunction function_def } -> is_dynamic_haxe_method field
+   | None when has_class_field_flag field CfAbstract -> false
+   | _ -> true
+
+let is_readable class_def field =
+   match field.cf_kind with
+   | Var { v_read = AccNever } when not (is_physical_field field) -> false
+   | Var { v_read = AccInline } -> false
+   | Var _ when is_abstract_impl class_def -> false
+   | _ -> true
+
+let is_writable class_def field =
+   match field.cf_kind with
+   | Var { v_write = AccNever } when not (is_physical_field field) -> false
+   | Var { v_read = AccInline } -> false
+   | Var _ when is_abstract_impl class_def -> false
+   | _ -> true
+
+let statics_except_meta class_def = (List.filter (fun static -> static.cf_name <> "__meta__" && static.cf_name <> "__rtti") class_def.cl_ordered_statics);;
+
+let has_set_member_field class_def =
+   let reflect_fields = List.filter (reflective class_def) (class_def.cl_ordered_fields) in
+   let reflect_writable = List.filter (is_writable class_def) reflect_fields in
+   List.exists variable_field reflect_writable
+
+let has_set_static_field class_def =
+   let reflect_fields = List.filter (reflective class_def) (statics_except_meta class_def) in
+   let reflect_writable = List.filter (is_writable class_def) reflect_fields in
+   List.exists variable_field reflect_writable
+
+let has_get_fields class_def =
+   let is_data_field field = (match follow field.cf_type with | TFun _ -> false | _ -> true) in
+   List.exists is_data_field class_def.cl_ordered_fields
+
+let has_get_member_field class_def =
+   let reflect_fields = List.filter (reflective class_def) (class_def.cl_ordered_fields) in
+   List.exists (is_readable class_def) reflect_fields
+
+let has_get_static_field class_def =
+   let reflect_fields = List.filter (reflective class_def) (statics_except_meta class_def) in
+   List.exists (is_readable class_def) reflect_fields
+
+let has_compare_field class_def =
+   List.exists (fun f -> f.cf_name="__compare") class_def.cl_ordered_fields
+
+let has_boot_field class_def =
+   match TClass.get_cl_init class_def with
+   | None -> List.exists has_field_init (List.filter should_implement_field class_def.cl_ordered_statics)
+   | _ -> true
+
+(*
+   Functions are added in reverse order (oldest on right), then list is reversed because this is easier in ocaml
+   The order is important because cppia looks up functions by index
+*)
+let current_virtual_functions_rev clazz base_functions =
+   List.fold_left (fun result elem -> match follow elem.cf_type, elem.cf_kind  with
+      | _, Method MethDynamic -> result
+      | TFun (args,return_type), Method _  ->
+          if (is_override elem ) then
+            if List.exists (fun (e,a,r) -> e.cf_name=elem.cf_name ) result then
+               result
+            else
+               (elem,args,return_type) :: result
+          else
+             (elem,args,return_type) :: result
+      | _,_ -> result
+    ) base_functions clazz.cl_ordered_fields
+
+let all_virtual_functions clazz =
+  let rec all_virtual_functions_rec clazz =
+   current_virtual_functions_rev clazz (match clazz.cl_super with
+       | Some def -> all_virtual_functions_rec (fst def)
+       | _ -> []
+     )
+   in
+   List.rev (all_virtual_functions_rec clazz)
+
+let class_name class_def =
+  let (_, class_path) = class_def.cl_path in
+  let nativeGen       = Meta.has Meta.NativeGen class_def.cl_meta in
+  class_path ^ if nativeGen then "" else "_obj"
+
+let class_pointer class_def = "::hx::ObjectPtr< " ^ class_name class_def ^ " >"

+ 1931 - 0
src/generators/cpp/gen/cppCppia.ml

@@ -0,0 +1,1931 @@
+open Extlib_leftovers
+open Ast
+open Type
+open Error
+open Common
+open Globals
+open CppExprUtils
+open CppTypeUtils
+open CppAst
+open CppAstTools
+open CppSourceWriter
+open CppContext
+
+let cpp_type_of = CppRetyper.cpp_type_of
+
+let script_type t optional = if optional then begin
+  match type_string t with
+  | "::String" -> "String"
+  | _ -> "Object"
+  end else match type_string t with
+  | "bool" -> "Int"
+  | "int" | "::cpp::Int32" -> "Int"
+  | "Float" -> "Float"
+  | "::String" -> "String"
+  | "Null" -> "Void"
+  | "Void" -> "Void"
+  | "float" | "::cpp::Float32" | "::cpp::Float64" -> "Float"
+  | "::cpp::Int64" | "::cpp::UInt64" -> "Object"
+  | _ -> "Object"
+
+let script_signature t optional = match script_type t optional with
+  | "Bool" -> "b"
+  | "Int" -> "i"
+  | "Float" -> "f"
+  | "String" -> "s"
+  | "Void" -> "v"
+  | "void" -> "v"
+  | _ -> "o"
+
+let script_size_type t optional = match script_type t optional with
+  | "Object" -> "void *"
+  | "Int" -> "int"
+  | "Bool" -> "bool"
+  | x -> x
+
+let rec script_type_string haxe_type =
+  match haxe_type with
+  | TAbstract ({ a_path = [], "Null" }, [ t ]) -> (
+      match follow t with
+      | TAbstract ({ a_path = [], "Int" }, _)
+      | TAbstract ({ a_path = [], "Float" }, _)
+      | TAbstract ({ a_path = [], "Bool" }, _) ->
+          "Dynamic"
+      | _ -> script_type_string t)
+  | TInst ({ cl_path = [], "Null" }, [ t ]) -> (
+      match follow t with
+      | TAbstract ({ a_path = [], "Int" }, _)
+      | TAbstract ({ a_path = [], "Float" }, _)
+      | TAbstract ({ a_path = [], "Bool" }, _) ->
+          "Dynamic"
+      | _ -> script_type_string t)
+  | _ -> (
+      match follow haxe_type with
+      | TType ({ t_path = [], "Array" }, params) -> "Array"
+      | TInst ({ cl_path = [], "Array" }, params) -> (
+          match params with
+          | [ t ] -> (
+              match type_string_suff "" t false with
+              | "int" -> "Array.int"
+              | "Float" -> "Array.Float"
+              | "bool" -> "Array.bool"
+              | "::String" -> "Array.String"
+              | "unsigned char" -> "Array.unsigned char"
+              | "::cpp::UInt8" -> "Array.unsigned char"
+              | "Dynamic" -> "Array.Any"
+              | _ -> "Array.Object")
+          | _ -> "Array.Object")
+      | TAbstract (abs, pl) when abs.a_impl <> None ->
+          script_type_string (Abstract.get_underlying_type abs pl)
+      | _ -> type_string_suff "" haxe_type false)
+
+let rec script_cpptype_string cppType =
+  match cppType with
+  | TCppDynamic | TCppUnchanged | TCppWrapped _ | TCppObject -> "Dynamic"
+  | TCppObjectPtr -> ".*.hx.Object*"
+  | TCppReference t -> ".ref." ^ script_cpptype_string t
+  | TCppStruct t -> ".struct." ^ script_cpptype_string t
+  | TCppStar (t, _) -> "*." ^ script_cpptype_string t
+  | TCppVoid -> "void"
+  | TCppVoidStar -> "*.void"
+  | TCppRest _ -> "vaarg_list"
+  | TCppVarArg -> "vararg"
+  | TCppAutoCast -> ".cpp.AutoCast"
+  | TCppVariant -> ".cpp.Variant"
+  | TCppEnum enum -> join_class_path enum.e_path "."
+  | TCppScalar scalar -> scalar
+  | TCppString -> "String"
+  | TCppFastIterator it -> "cpp.FastIterator." ^ script_cpptype_string it
+  | TCppPointer (_, valueType) ->
+      "cpp.Pointer." ^ script_cpptype_string valueType
+  | TCppRawPointer (_, valueType) ->
+      "cpp.RawPointer." ^ script_cpptype_string valueType
+  | TCppFunction _ -> "cpp.Function"
+  | TCppObjCBlock _ -> "cpp.ObjCBlock"
+  | TCppDynamicArray -> "Array.Any"
+  | TCppObjectArray _ -> "Array.Object"
+  | TCppScalarArray value -> "Array." ^ script_cpptype_string value
+  | TCppObjC _ -> "cpp.ObjC"
+  | TCppProtocol _ -> "cpp.ObjC.Protocol"
+  | TCppNativePointer klass ->
+      "cpp.Pointer." ^ join_class_path klass.cl_path "."
+  | TCppInterface klass -> join_class_path klass.cl_path "."
+  | TCppInst (klass, _) -> join_class_path klass.cl_path "."
+  | TCppClass -> "Class"
+  | TCppGlobal -> "?global"
+  | TCppNull -> "null"
+  | TCppCode _ -> "Dynamic"
+
+type array_of =
+  | ArrayInterface of int
+  | ArrayData of string
+  | ArrayObject
+  | ArrayAny
+  | ArrayNone
+
+let is_template_type t = false
+
+type cppia_op =
+  | IaFunction
+  | IaVar
+  | IaToInterface
+  | IaToDynArray
+  | IaToDataArray
+  | IaToInterfaceArray
+  | IaFun
+  | IaCast
+  | IaTCast
+  | IaBlock
+  | IaBreak
+  | IaContinue
+  | IaIsNull
+  | IaNotNull
+  | IaSet
+  | IaCall
+  | IaCallGlobal
+  | IaCallStatic
+  | IaCallMember
+  | IaCallSuper
+  | IaCallThis
+  | IaCallSuperNew
+  | IaCreateEnum
+  | IaADef
+  | IaIf
+  | IaIfElse
+  | IaFStatic
+  | IaFName
+  | IaFThisInst
+  | IaFLink
+  | IaFThisName
+  | IaFEnum
+  | IaThrow
+  | IaArrayI
+  | IaPlusPlus
+  | IaPlusPlusPost
+  | IaMinusMinus
+  | IaMinusMinusPost
+  | IaNeg
+  | IaBitNot
+  | IaLogicNot
+  | IaTVars
+  | IaVarDecl
+  | IaVarDeclI
+  | IaNew
+  | IaReturn
+  | IaRetVal
+  | IaPosInfo
+  | IaObjDef
+  | IaClassOf
+  | IaWhile
+  | IaFor
+  | IaEnumI
+  | IaSwitch
+  | IaTry
+  | IaImplDynamic
+  | IaConstInt
+  | IaConstFloat
+  | IaConstString
+  | IaConstFalse
+  | IaConstTrue
+  | IaConstNull
+  | IaConsThis
+  | IaConstSuper
+  | IaCastInt
+  | IaCastBool
+  | IaInterface
+  | IaClass
+  | IaAccessNormal
+  | IaAccessNot
+  | IaAccessResolve
+  | IaAccessCall
+  | IaEnum
+  | IaInline
+  | IaMain
+  | IaNoMain
+  | IaResources
+  | IaReso
+  | IaNoCast
+  | IaAccessCallNative
+  | IaBinOp of Ast.binop
+
+let cppia_op_info = function
+  | IaFunction -> ("FUNCTION", 1)
+  | IaVar -> ("VAR", 2)
+  | IaToInterface -> ("TOINTERFACE", 3)
+  | IaToDynArray -> ("TODYNARRAY", 4)
+  | IaToDataArray -> ("TODATAARRAY", 5)
+  | IaToInterfaceArray -> ("TOINTERFACEARRAY", 6)
+  | IaFun -> ("FUN", 7)
+  | IaCast -> ("CAST", 8)
+  | IaBlock -> ("BLOCK", 9)
+  | IaBreak -> ("BREAK", 10)
+  | IaContinue -> ("CONTINUE", 11)
+  | IaIsNull -> ("ISNULL", 12)
+  | IaNotNull -> ("NOTNULL", 13)
+  | IaSet -> ("SET", 14)
+  | IaCall -> ("CALL", 15)
+  | IaCallGlobal -> ("CALLGLOBAL", 16)
+  | IaCallStatic -> ("CALLSTATIC", 17)
+  | IaCallMember -> ("CALLMEMBER", 18)
+  | IaCallSuper -> ("CALLSUPER", 19)
+  | IaCallThis -> ("CALLTHIS", 20)
+  | IaCallSuperNew -> ("CALLSUPERNEW", 21)
+  | IaCreateEnum -> ("CREATEENUM", 22)
+  | IaADef -> ("ADEF", 23)
+  | IaIf -> ("IF", 24)
+  | IaIfElse -> ("IFELSE", 25)
+  | IaFName -> ("FNAME", 27)
+  | IaFStatic -> ("FSTATIC", 28)
+  | IaFThisInst -> ("FTHISINST", 29)
+  | IaFLink -> ("FLINK", 30)
+  | IaFThisName -> ("FTHISNAME", 31)
+  | IaFEnum -> ("FENUM", 32)
+  | IaThrow -> ("THROW", 33)
+  | IaArrayI -> ("ARRAYI", 34)
+  | IaPlusPlus -> ("++", 35)
+  | IaPlusPlusPost -> ("+++", 36)
+  | IaMinusMinus -> ("--", 37)
+  | IaMinusMinusPost -> ("---", 38)
+  | IaNeg -> ("NEG", 39)
+  | IaBitNot -> ("~", 40)
+  | IaLogicNot -> ("!", 41)
+  | IaTVars -> ("TVARS", 42)
+  | IaVarDecl -> ("VARDECL", 43)
+  | IaVarDeclI -> ("VARDECLI", 44)
+  | IaNew -> ("NEW", 45)
+  | IaReturn -> ("RETURN", 46)
+  | IaRetVal -> ("RETVAL", 47)
+  | IaPosInfo -> ("POSINFO", 48)
+  | IaObjDef -> ("OBJDEF", 49)
+  | IaClassOf -> ("CLASSOF", 50)
+  | IaWhile -> ("WHILE", 51)
+  | IaFor -> ("FOR", 52)
+  | IaEnumI -> ("ENUMI", 53)
+  | IaSwitch -> ("SWITCH", 54)
+  | IaTry -> ("TRY", 55)
+  | IaImplDynamic -> ("IMPLDYNAMIC", 56)
+  | IaConstInt -> ("i", 57)
+  | IaConstFloat -> ("f", 58)
+  | IaConstString -> ("s", 59)
+  | IaConstFalse -> ("false", 60)
+  | IaConstTrue -> ("true", 61)
+  | IaConstNull -> ("NULL", 62)
+  | IaConsThis -> ("THIS", 63)
+  | IaConstSuper -> ("SUPER", 64)
+  | IaCastInt -> ("CASTINT", 65)
+  | IaCastBool -> ("CASTBOOL", 66)
+  | IaInterface -> ("INTERFACE", 67)
+  | IaClass -> ("CLASS", 68)
+  | IaAccessNormal -> ("N", 69)
+  | IaAccessNot -> ("n", 70)
+  | IaAccessResolve -> ("R", 71)
+  | IaAccessCall -> ("C", 72)
+  | IaEnum -> ("ENUM", 73)
+  | IaInline -> ("INLINE", 74)
+  | IaMain -> ("MAIN", 75)
+  | IaNoMain -> ("NOMAIN", 76)
+  | IaResources -> ("RESOURCES", 77)
+  | IaReso -> ("RESO", 78)
+  | IaNoCast -> ("NOCAST", 79)
+  | IaAccessCallNative -> ("V", 80)
+  | IaBinOp OpAdd -> ("+", 101)
+  | IaBinOp OpMult -> ("*", 102)
+  | IaBinOp OpDiv -> ("/", 103)
+  | IaBinOp OpSub -> ("-", 104)
+  | IaBinOp OpAssign -> ("=", 105)
+  | IaBinOp OpEq -> ("==", 106)
+  | IaBinOp OpNotEq -> ("!=", 107)
+  | IaBinOp OpGte -> (">=", 108)
+  | IaBinOp OpLte -> ("<=", 109)
+  | IaBinOp OpGt -> (">", 110)
+  | IaBinOp OpLt -> ("<", 111)
+  | IaBinOp OpAnd -> ("&", 112)
+  | IaBinOp OpOr -> ("|", 113)
+  | IaBinOp OpXor -> ("^", 114)
+  | IaBinOp OpBoolAnd -> ("&&", 115)
+  | IaBinOp OpBoolOr -> ("||", 116)
+  | IaBinOp OpShr -> (">>", 117)
+  | IaBinOp OpUShr -> (">>>", 118)
+  | IaBinOp OpShl -> ("<<", 119)
+  | IaBinOp OpMod -> ("%", 120)
+  | IaBinOp OpInterval -> ("...", 121)
+  | IaBinOp OpArrow -> ("=>", 122)
+  | IaBinOp OpIn -> (" in ", 123)
+  | IaBinOp OpNullCoal -> ("??", 124)
+  | IaBinOp (OpAssignOp OpAdd) -> ("+=", 201)
+  | IaBinOp (OpAssignOp OpMult) -> ("*=", 202)
+  | IaBinOp (OpAssignOp OpDiv) -> ("/=", 203)
+  | IaBinOp (OpAssignOp OpSub) -> ("-=", 204)
+  | IaBinOp (OpAssignOp OpAnd) -> ("&=", 212)
+  | IaBinOp (OpAssignOp OpOr) -> ("|=", 213)
+  | IaBinOp (OpAssignOp OpXor) -> ("^=", 214)
+  | IaBinOp (OpAssignOp OpBoolAnd) -> ("&&=", 215)
+  | IaBinOp (OpAssignOp OpBoolOr) -> ("||=", 216)
+  | IaBinOp (OpAssignOp OpShr) -> (">>=", 217)
+  | IaBinOp (OpAssignOp OpUShr) -> (">>>=", 218)
+  | IaBinOp (OpAssignOp OpShl) -> ("<<=", 219)
+  | IaBinOp (OpAssignOp OpMod) -> ("%=", 220)
+  | IaBinOp (OpAssignOp OpIn)
+  | IaBinOp (OpAssignOp OpNullCoal)
+  | IaBinOp (OpAssignOp OpInterval)
+  | IaBinOp (OpAssignOp OpAssign)
+  | IaBinOp (OpAssignOp OpEq)
+  | IaBinOp (OpAssignOp OpNotEq)
+  | IaBinOp (OpAssignOp OpGte)
+  | IaBinOp (OpAssignOp OpLte)
+  | IaBinOp (OpAssignOp OpGt)
+  | IaBinOp (OpAssignOp OpLt)
+  | IaBinOp (OpAssignOp (OpAssignOp _))
+  | IaBinOp (OpAssignOp OpArrow) ->
+      die "" __LOC__
+  | IaTCast -> ("TCAST", 221)
+
+let follow = Abstract.follow_with_abstracts
+
+let is_matching_interface_type t0 t1 =
+  match (follow t0, follow t1) with
+  | TInst (k0, _), TInst (k1, _) -> k0 == k1
+  | _ -> false
+
+let rec is_null expr =
+  match expr.eexpr with
+  | TConst TNull -> true
+  | TParenthesis expr | TMeta (_, expr) -> is_null expr
+  | TCast (e, None) -> is_null e
+  | _ -> false
+
+let is_virtual_array expr = type_string expr.etype = "cpp::VirtualArray"
+
+let is_this expression =
+  match (remove_parens expression).eexpr with
+  | TConst TThis -> true
+  | _ -> false
+
+let is_super expression =
+  match (remove_parens expression).eexpr with
+  | TConst TSuper -> true
+  | _ -> false
+
+let is_native_pointer expr =
+  let t = type_string expr.etype in
+  let l = String.length t in
+  l > 1 && String.sub t (l - 1) 1 = "*"
+
+let is_extern_class_instance obj =
+  match follow obj.etype with
+  | TInst (klass, params) -> has_class_flag klass CExtern
+  | _ -> false
+
+let rec is_dynamic_in_cpp ctx expr =
+  let expr_type =
+    type_string
+      (match follow expr.etype with TFun (args, ret) -> ret | _ -> expr.etype)
+  in
+  if expr_type = "Dynamic" || expr_type = "cpp::ArrayBase" then true
+  else
+    let result =
+      match expr.eexpr with
+      | TEnumParameter (obj, _, index) -> true (* TODO? *)
+      | TField (obj, field) ->
+          is_dynamic_member_lookup_in_cpp ctx obj field
+          || is_dynamic_member_return_in_cpp ctx obj field
+      | TArray (obj, index) -> is_dynamic_in_cpp ctx obj || is_virtual_array obj
+      | TTypeExpr _ -> false
+      | TCall (func, args) -> (
+          let is_IaCall =
+            match (remove_parens_cast func).eexpr with
+            | TField ({ eexpr = TIdent "__global__" }, field) -> false
+            | TField (obj, FStatic (class_def, field))
+              when is_real_function field ->
+                false
+            | TField (obj, FInstance (_, _, field))
+              when is_this obj && is_real_function field ->
+                false
+            | TField (obj, FInstance (_, _, field)) when is_super obj -> false
+            | TField (obj, FInstance (_, _, field))
+              when field.cf_name = "_hx_getIndex" ->
+                false
+            | TField (obj, FInstance (_, _, field))
+              when field.cf_name = "__Index"
+                   || (not (is_dynamic_in_cppia ctx obj))
+                      && is_real_function field ->
+                false
+            | TField (obj, FDynamic name)
+              when is_internal_member name
+                   || (type_string obj.etype = "::String" && name = "cca") ->
+                false
+            | TConst TSuper -> false
+            | TField (_, FEnum (enum, field)) -> false
+            | _ -> true
+          in
+          if is_IaCall then true
+          else
+            match follow func.etype with
+            | TFun (args, ret) -> is_dynamic_in_cpp ctx func
+            | _ -> true)
+      | TParenthesis expr | TMeta (_, expr) -> is_dynamic_in_cpp ctx expr
+      | TCast (e, None) -> type_string expr.etype = "Dynamic"
+      | TIdent "__global__" -> false
+      | TConst TNull -> true
+      | _ -> false (* others ? *)
+    in
+    result
+
+and is_dynamic_member_lookup_in_cpp (ctx : context) field_object field =
+  let member = field_name field in
+  if is_internal_member member then false
+  else if is_native_pointer field_object then false
+  else if is_pointer field_object.etype true then false
+  else if match field_object.eexpr with TTypeExpr _ -> true | _ -> false then
+    false
+  else if is_dynamic_in_cpp ctx field_object then true
+  else if is_array field_object.etype then false
+  else
+    let tstr = type_string field_object.etype in
+    match tstr with
+    (* Internal classes have no dynamic members *)
+    | "::String" | "Null" | "::hx::Class" | "::Enum" | "::Math"
+    | "::ArrayAccess" ->
+        false
+    | "Dynamic" -> true
+    | name ->
+        let full_name = name ^ "." ^ member in
+        if Hashtbl.mem ctx.ctx_class_member_types full_name then false
+        else not (is_extern_class_instance field_object)
+
+and is_dynamic_member_return_in_cpp ctx field_object field =
+  let member = field_name field in
+  if is_array field_object.etype then false
+  else if is_pointer field_object.etype true then false
+  else if is_internal_member member then false
+  else
+    match field_object.eexpr with
+    | TTypeExpr t -> (
+        let full_name =
+          "::" ^ join_class_path_remap (t_path t) "::" ^ "." ^ member
+        in
+        try
+          let mem_type = Hashtbl.find ctx.ctx_class_member_types full_name in
+          mem_type = "Dynamic"
+          || mem_type = "cpp::ArrayBase"
+          || mem_type = "cpp::VirtualArray"
+        with Not_found -> true)
+    | _ -> (
+        let tstr = type_string field_object.etype in
+        match tstr with
+        (* Internal classes have no dynamic members *)
+        | "::String" | "Null" | "::hx::Class" | "::Enum" | "::Math"
+        | "::ArrayAccess" ->
+            false
+        | "Dynamic" | "cpp::ArrayBase" | "cpp::VirtualArray" -> true
+        | name -> (
+            let full_name = name ^ "." ^ member in
+            try
+              let mem_type =
+                Hashtbl.find ctx.ctx_class_member_types full_name
+              in
+              mem_type = "Dynamic"
+              || mem_type = "cpp::ArrayBase"
+              || mem_type = "cpp::VirtualArray"
+            with Not_found -> true))
+
+and is_dynamic_in_cppia ctx expr =
+  match expr.eexpr with
+  | TCast (_, None) -> true
+  | _ -> is_dynamic_in_cpp ctx expr
+
+class script_writer ctx filename asciiOut =
+  object (this)
+    val debug = asciiOut
+
+    val doComment =
+      asciiOut && Common.defined ctx.ctx_common Define.AnnotateSource
+
+    val indent_str = if asciiOut then "\t" else ""
+    val mutable indent = ""
+    val mutable indents = []
+    val mutable just_finished_block = false
+    val mutable classCount = 0
+    val mutable return_type = TMono (Monomorph.create ())
+    val buffer = Buffer.create 0
+    val identTable = Hashtbl.create 0
+    val fileTable = Hashtbl.create 0
+    val identBuffer = Buffer.create 0
+    val cppiaAst = not (Common.defined ctx.ctx_common Define.NoCppiaAst)
+
+    method stringId name =
+      try Hashtbl.find identTable name
+      with Not_found ->
+        let size = Hashtbl.length identTable in
+        Hashtbl.add identTable name size;
+        Buffer.add_string identBuffer
+          (string_of_int (String.length name) ^ " " ^ name ^ "\n");
+        size
+
+    method incClasses = classCount <- classCount + 1
+    method stringText name = string_of_int (this#stringId name) ^ " "
+    val typeTable = Hashtbl.create 0
+    val typeBuffer = Buffer.create 0
+
+    method typeId name =
+      let name = if name = "::hx::Class" then "::Class" else name in
+      try Hashtbl.find typeTable name
+      with Not_found ->
+        let size = Hashtbl.length typeTable in
+        Hashtbl.add typeTable name size;
+        Buffer.add_string typeBuffer
+          (string_of_int (String.length name) ^ " " ^ name ^ "\n");
+        size
+
+    method write str =
+      (if asciiOut then Buffer.add_string buffer str
+       else
+         let push i = Buffer.add_char buffer (Char.chr i) in
+         let pushI32 i =
+           push (Int32.to_int (Int32.logand i (Int32.of_int 255)))
+         in
+         List.iter
+           (fun i ->
+             if
+               Int32.compare i Int32.zero >= 0
+               && Int32.compare i (Int32.of_int 254) < 0
+             then pushI32 i
+             else if
+               Int32.compare i Int32.zero >= 0
+               && Int32.compare i (Int32.of_int 65536) < 0
+             then (
+               push 254;
+               pushI32 i;
+               pushI32 (Int32.shift_right i 8))
+             else (
+               push 255;
+               pushI32 i;
+               pushI32 (Int32.shift_right i 8);
+               pushI32 (Int32.shift_right i 16);
+               pushI32 (Int32.shift_right i 24)))
+           (List.map Int32.of_string (Str.split (Str.regexp "[\n\t ]+") str)));
+      just_finished_block <- false
+
+    method comment text = if doComment then this#write ("# " ^ text ^ "\n")
+    method commentOf text = if doComment then " # " ^ text else ""
+    method typeTextString typeName = string_of_int (this#typeId typeName) ^ " "
+
+    method typeText typeT =
+      let tname =
+        if cppiaAst then script_cpptype_string (cpp_type_of typeT)
+        else script_type_string typeT
+      in
+      string_of_int (this#typeId tname) ^ " "
+
+    method astType cppType =
+      string_of_int (this#typeId (script_cpptype_string cppType)) ^ " "
+
+    method writeType typeT = this#write (this#typeText typeT)
+
+    method toCppType etype =
+      string_of_int (this#typeId (script_cpptype_string (cpp_type_of etype)))
+      ^ " "
+
+    method boolText value = if value then "1" else "0"
+    method writeBool value = this#write (if value then "1 " else "0 ")
+    method staticText value = if value then "1" else "0"
+    method writeData str = Buffer.add_string buffer str
+    method wint ival = this#write (string_of_int ival ^ " ")
+    method ident name = this#wint (this#stringId name)
+
+    method cppInstText clazz =
+      match clazz.cl_path with
+      | [], "Array" -> this#typeTextString "Array"
+      | x -> this#typeTextString (join_class_path x ".")
+
+    method instText clazz =
+      match clazz.cl_path with
+      | [], "Array" -> string_of_int (this#typeId "Array< ::Dynamic >") ^ " "
+      | _ -> this#typeText (TInst (clazz, []))
+
+    method instName clazz =
+      this#write
+        (if cppiaAst then this#cppInstText clazz else this#instText clazz)
+
+    method enumText e = this#typeText (TEnum (e, []))
+
+    method close =
+      let out_file = open_out_bin filename in
+      output_string out_file (if asciiOut then "CPPIA\n" else "CPPIB\n");
+      let idents = Buffer.contents identBuffer in
+      output_string out_file (string_of_int (Hashtbl.length identTable) ^ "\n");
+      output_string out_file idents;
+      let types = Buffer.contents typeBuffer in
+      output_string out_file (string_of_int (Hashtbl.length typeTable) ^ "\n");
+      output_string out_file types;
+      output_string out_file (string_of_int classCount ^ "\n");
+      let contents = Buffer.contents buffer in
+      output_string out_file contents;
+      close_out out_file
+
+    method fileId file =
+      try Hashtbl.find fileTable file
+      with Not_found ->
+        let stripped_file = strip_file ctx.ctx_common file in
+        let result = this#stringId stripped_file in
+        Hashtbl.add fileTable file result;
+        result
+
+    method constText c =
+      match c with
+      | TInt i -> this#op IaConstInt ^ Printf.sprintf "%ld " i
+      | TFloat f ->
+          this#op IaConstFloat ^ this#stringText (Texpr.replace_separators f "")
+      | TString s -> this#op IaConstString ^ this#stringText s
+      | TBool true -> this#op IaConstTrue
+      | TBool false -> this#op IaConstFalse
+      | TNull -> this#op IaConstNull
+      | TThis -> this#op IaConsThis
+      | TSuper -> this#op IaConstSuper
+
+    method get_array_type t =
+      match follow t with
+      | TInst ({ cl_path = [], "Array" }, [ param ]) -> (
+          let typeName = type_string_suff "" param false in
+          match typeName with
+          | "::String" -> ArrayData "String"
+          | "int" | "Float" | "bool" | "String" | "unsigned char"
+          | "::cpp::UInt8" ->
+              ArrayData typeName
+          | "cpp::ArrayBase" | "cpp::VirtualArray" | "Dynamic" -> ArrayAny
+          | _ when is_interface_type param ->
+              ArrayInterface (this#typeId (script_type_string param))
+          | _ -> ArrayObject)
+      | TAbstract (abs, pl) when abs.a_impl <> None ->
+          this#get_array_type (Abstract.get_underlying_type abs pl)
+      | _ -> ArrayNone
+
+    method pushReturn inType =
+      let oldReturnType = return_type in
+      return_type <- inType;
+      fun () -> return_type <- oldReturnType
+
+    method fileText file = string_of_int (this#fileId file)
+    method indent_one = this#write indent_str
+
+    method push_indent =
+      indents <- indent_str :: indents;
+      indent <- String.concat "" indents
+
+    method pop_indent =
+      match indents with
+      | h :: tail ->
+          indents <- tail;
+          indent <- String.concat "" indents
+      | [] -> indent <- "/*?*/"
+
+    method write_i x = this#write (indent ^ x)
+    method get_indent = indent
+    method begin_expr = this#push_indent
+
+    method end_expr =
+      if not just_finished_block then this#write "\n";
+      this#pop_indent;
+      just_finished_block <- true
+
+    method op x =
+      match cppia_op_info x with
+      | name, index -> (if debug then name else string_of_int index) ^ " "
+
+    method writeOp o = this#write (this#op o)
+    method writeOpLine o = this#write (this#op o ^ "\n")
+
+    method voidFunc isStatic isDynamic funcName fieldExpression =
+      this#comment funcName;
+      this#write
+        (this#op IaFunction ^ this#staticText isStatic ^ " "
+       ^ this#boolText isDynamic ^ " " ^ this#stringText funcName ^ " ");
+      this#write (this#typeTextString "Void" ^ "0\n");
+      this#gen_expression fieldExpression
+
+    method func isStatic isDynamic funcName ret args isInterface fieldExpression
+        abstractPos =
+      this#comment funcName;
+      this#write
+        (this#op IaFunction ^ this#staticText isStatic ^ " "
+       ^ this#boolText isDynamic ^ " " ^ this#stringText funcName ^ " ");
+      this#write (this#typeText ret ^ string_of_int (List.length args) ^ " ");
+      List.iter
+        (fun (name, opt, typ) ->
+          this#write
+            (this#stringText name ^ this#boolText opt ^ " " ^ this#typeText typ
+           ^ " "))
+        args;
+      this#write "\n";
+      if not isInterface then
+        match fieldExpression with
+        | Some ({ eexpr = TFunction function_def } as e) ->
+            if cppiaAst then (
+              let args = List.map fst function_def.tf_args in
+              let cppExpr =
+                CppRetyper.expression ctx TCppVoid args function_def.tf_type
+                  function_def.tf_expr false
+              in
+              this#begin_expr;
+              this#writePos function_def.tf_expr;
+              this#write
+                (this#op IaFun
+                ^ this#typeText function_def.tf_type
+                ^ string_of_int (List.length args)
+                ^ "\n");
+              let close = this#gen_func_args function_def.tf_args in
+              this#gen_expression_tree cppExpr;
+              this#end_expr;
+              close ())
+            else this#gen_expression e
+        | _ ->
+            (* Abstract function - dummp implementation that (should) not get called *)
+            this#begin_expr;
+            this#wpos abstractPos;
+            this#writeOpLine IaReturn;
+            this#end_expr
+
+    method var readAcc writeAcc isExtern isStatic name varType varExpr =
+      this#write
+        (this#op IaVar ^ this#staticText isStatic ^ " " ^ this#op readAcc
+       ^ this#op writeAcc ^ this#boolText isExtern ^ " " ^ this#stringText name
+       ^ this#typeText varType
+        ^ (match varExpr with Some _ -> "1" | _ -> "0")
+        ^ if doComment then " # " ^ name ^ "\n" else "\n");
+      match varExpr with
+      | Some expression ->
+          if cppiaAst then
+            let varType = cpp_type_of expression.etype in
+            let cppExpr =
+              CppRetyper.expression ctx varType [] t_dynamic expression false
+            in
+            this#gen_expression_tree cppExpr
+          else this#gen_expression expression
+      | _ -> ()
+
+    method implDynamic = this#writeOpLine IaImplDynamic
+
+    method writeVar v =
+      this#ident v.v_name;
+      this#wint v.v_id;
+      this#writeBool (has_var_flag v VCaptured);
+      this#writeType v.v_type
+
+    method writeList prefix len =
+      this#write (prefix ^ " " ^ string_of_int len ^ "\n")
+
+    method wpos p =
+      if debug then
+        this#write
+          (this#fileText p.pfile ^ "\t"
+          ^ string_of_int (Lexer.get_error_line p)
+          ^ indent)
+
+    method writePos expr = this#wpos expr.epos
+    method writeCppPos expr = this#wpos expr.cpppos
+
+    method checkCast toType expr forceCast fromGenExpression =
+      let write_cast text =
+        if not fromGenExpression then this#writePos expr;
+        this#write (text ^ "\n");
+        this#begin_expr;
+        this#gen_expression expr;
+        this#end_expr;
+        true
+      in
+      let was_cast =
+        if is_interface_type toType then
+          if is_dynamic_in_cppia ctx expr then
+            write_cast
+              (this#op IaToInterface ^ this#typeText toType ^ " "
+              ^ this#typeTextString "Dynamic")
+          else if not (is_matching_interface_type toType expr.etype) then
+            write_cast
+              (this#op IaToInterface ^ this#typeText toType ^ " "
+             ^ this#typeText expr.etype)
+          else false
+        else
+          let get_array_expr_type expr =
+            if is_dynamic_in_cppia ctx expr then ArrayNone
+            else this#get_array_type expr.etype
+          in
+          match (this#get_array_type toType, get_array_expr_type expr) with
+          | ArrayAny, _ -> false
+          | ArrayObject, ArrayData _ -> write_cast (this#op IaToDynArray)
+          | ArrayObject, ArrayObject -> false
+          | ArrayObject, ArrayNone | ArrayObject, ArrayAny ->
+              write_cast
+                (this#op IaToDataArray ^ this#typeTextString "Array.Object")
+          | ArrayData t, ArrayNone
+          | ArrayData t, ArrayObject
+          | ArrayData t, ArrayAny ->
+              write_cast
+                (this#op IaToDataArray ^ this#typeTextString ("Array." ^ t))
+          | ArrayInterface t, ArrayNone | ArrayInterface t, ArrayAny ->
+              write_cast (this#op IaToInterfaceArray ^ string_of_int t)
+          | _, _ ->
+              (* a0,a1 ->
+                    let arrayString a =
+                       match a with
+                       | ArrayNone -> "ArrayNone"
+                       | ArrayAny -> "ArrayAny"
+                       | ArrayObject -> "ArrayObject"
+                       | ArrayData _ -> "ArrayData"
+                       | ArrayInterface _ -> "ArrayInterface"
+                 in
+                 this#write ("NOCAST " ^ (arrayString a0) ^ "=" ^ (arrayString a1)); *)
+              false
+      in
+
+      if not was_cast then (
+        (if forceCast then
+           let op =
+             match type_string expr.etype with
+             | "int" -> IaCastInt
+             | "bool" -> IaCastBool
+             | _ when is_interface_type toType -> IaNoCast
+             | _ -> IaCast
+           in
+           this#writeOpLine op);
+        this#gen_expression expr)
+
+    method gen_func_args args =
+      let gen_inits = ref [] in
+      List.iter
+        (fun (arg, init) ->
+          this#write (indent ^ indent_str);
+          this#writeVar arg;
+          match init with
+          | Some { eexpr = TConst TNull } -> this#write "0\n"
+          | Some const ->
+              let argType = cpp_type_of const.etype in
+              if is_cpp_scalar argType || argType == TCppString then (
+                this#write "1 ";
+                this#gen_expression_only const;
+                this#write "\n")
+              else (
+                gen_inits := (arg, const) :: !gen_inits;
+                this#write "0\n")
+          | _ -> this#write "0\n")
+        args;
+
+      if List.length !gen_inits == 0 then fun () -> ()
+      else (
+        this#begin_expr;
+        this#writePos (snd (List.hd !gen_inits));
+        this#writeList (this#op IaBlock) (List.length !gen_inits + 1);
+        List.iter
+          (fun (arg, const) ->
+            let start_expr () =
+              this#begin_expr;
+              this#writePos const
+            in
+            let local_var () =
+              this#begin_expr;
+              this#writePos const;
+              this#write
+                (this#op IaVar ^ string_of_int arg.v_id
+               ^ this#commentOf arg.v_name);
+              this#end_expr
+            in
+
+            start_expr ();
+            this#writeOpLine IaIf;
+            start_expr ();
+            this#writeOpLine IaIsNull;
+            local_var ();
+            this#end_expr;
+            start_expr ();
+            this#writeOpLine IaSet;
+            local_var ();
+            this#gen_expression const;
+            this#end_expr;
+            this#begin_expr)
+          !gen_inits;
+        fun () -> this#end_expr)
+
+    method gen_expression expr =
+      this#begin_expr;
+      this#writePos expr;
+      this#gen_expression_only expr;
+      this#end_expr
+
+    method gen_expression_only expr =
+      (* { *)
+      let expression = remove_parens expr in
+      match expression.eexpr with
+      | TFunction function_def ->
+          this#write
+            (this#op IaFun
+            ^ this#typeText function_def.tf_type
+            ^ string_of_int (List.length function_def.tf_args)
+            ^ "\n");
+          let close = this#gen_func_args function_def.tf_args in
+          let pop = this#pushReturn function_def.tf_type in
+          this#gen_expression function_def.tf_expr;
+          pop ();
+          close ()
+      | TBlock expr_list ->
+          this#writeList (this#op IaBlock) (List.length expr_list);
+          List.iter this#gen_expression expr_list
+      | TConst const -> this#write (this#constText const)
+      | TBreak -> this#writeOp IaBreak
+      | TContinue -> this#writeOp IaContinue
+      | TBinop (op, e1, e2) when op = OpAssign ->
+          this#writeOpLine IaSet;
+          this#gen_expression e1;
+          this#checkCast e1.etype e2 false false
+      | TBinop (OpEq, e1, { eexpr = TConst TNull }) ->
+          this#writeOpLine IaIsNull;
+          this#gen_expression e1
+      | TBinop (OpNotEq, e1, { eexpr = TConst TNull }) ->
+          this#writeOpLine IaNotNull;
+          this#gen_expression e1
+      | TBinop (OpEq, { eexpr = TConst TNull }, e1) ->
+          this#writeOpLine IaIsNull;
+          this#gen_expression e1
+      | TBinop (OpNotEq, { eexpr = TConst TNull }, e1) ->
+          this#writeOpLine IaNotNull;
+          this#gen_expression e1
+      | TBinop (op, e1, e2) ->
+          this#writeOpLine (IaBinOp op);
+          this#gen_expression e1;
+          this#gen_expression e2
+      | TThrow e ->
+          this#writeOpLine IaThrow;
+          this#gen_expression e
+      | TArrayDecl expr_list ->
+          this#write
+            (this#op IaADef
+            ^ this#typeText expression.etype
+            ^ " "
+            ^ string_of_int (List.length expr_list)
+            ^ "\n");
+          List.iter this#gen_expression expr_list
+      | TIf (e, e1, e2) -> (
+          match e2 with
+          | None ->
+              this#writeOpLine IaIf;
+              this#gen_expression e;
+              this#gen_expression e1
+          | Some elze ->
+              this#writeOpLine IaIfElse;
+              this#gen_expression e;
+              this#gen_expression e1;
+              this#gen_expression elze)
+      | TCall (func, arg_list) -> (
+          let argN = string_of_int (List.length arg_list) ^ " " in
+          let gen_call () =
+            (match (remove_parens_cast func).eexpr with
+            | TField ({ eexpr = TIdent "__global__" }, field) ->
+                this#write
+                  (this#op IaCallGlobal
+                  ^ this#stringText (field_name field)
+                  ^ argN
+                  ^ this#commentOf (field_name field)
+                  ^ "\n")
+            | TField (obj, FStatic (class_def, field))
+              when is_real_function field ->
+                this#write
+                  (this#op IaCallStatic ^ this#instText class_def ^ " "
+                  ^ this#stringText field.cf_name
+                  ^ argN
+                  ^ this#commentOf
+                      (join_class_path class_def.cl_path "."
+                      ^ "." ^ field.cf_name)
+                  ^ "\n")
+            | TField (obj, FInstance (_, _, field))
+              when is_this obj && is_real_function field ->
+                this#write
+                  (this#op IaCallThis ^ this#typeText obj.etype ^ " "
+                  ^ this#stringText field.cf_name
+                  ^ argN
+                  ^ this#commentOf field.cf_name
+                  ^ "\n")
+            | TField (obj, FInstance (_, _, field)) when is_super obj ->
+                this#write
+                  (this#op IaCallSuper ^ this#typeText obj.etype ^ " "
+                  ^ this#stringText field.cf_name
+                  ^ argN
+                  ^ this#commentOf field.cf_name
+                  ^ "\n")
+            (* Cppia does not have a "GetEnumIndex" op code - must use IaCallMember ::hx::EnumBase.__Index *)
+            | TField (obj, FInstance (_, _, field))
+              when field.cf_name = "_hx_getIndex"
+                   && script_type_string obj.etype = "::hx::EnumBase" ->
+                this#write
+                  (this#op IaCallMember
+                  ^ this#typeTextString "::hx::EnumBase"
+                  ^ " " ^ this#stringText "__Index" ^ argN
+                  ^ this#commentOf "Enum index"
+                  ^ "\n");
+                this#gen_expression obj
+            | TField (obj, FInstance (_, _, field))
+              when field.cf_name = "__Index"
+                   || (not (is_dynamic_in_cppia ctx obj))
+                      && is_real_function field ->
+                this#write
+                  (this#op IaCallMember ^ this#typeText obj.etype ^ " "
+                  ^ this#stringText field.cf_name
+                  ^ argN
+                  ^ this#commentOf field.cf_name
+                  ^ "\n");
+                this#gen_expression obj
+            | TField (obj, FDynamic name)
+              when is_internal_member name
+                   || (type_string obj.etype = "::String" && name = "cca") ->
+                this#write
+                  (this#op IaCallMember ^ this#typeText obj.etype ^ " "
+                 ^ this#stringText name ^ argN ^ this#commentOf name ^ "\n");
+                this#gen_expression obj
+            | TConst TSuper ->
+                this#write
+                  (this#op IaCallSuperNew ^ this#typeText func.etype ^ " "
+                 ^ argN ^ "\n")
+            | TField (_, FEnum (enum, field)) ->
+                this#write
+                  (this#op IaCreateEnum ^ this#enumText enum ^ " "
+                  ^ this#stringText field.ef_name
+                  ^ argN
+                  ^ this#commentOf field.ef_name
+                  ^ "\n")
+            | _ ->
+                this#write (this#op IaCall ^ argN ^ "\n");
+                this#gen_expression func);
+            let matched_args =
+              match func.etype with
+              | TFun (args, _) -> (
+                  try
+                    List.iter2
+                      (fun (_, _, protoT) arg ->
+                        this#checkCast protoT arg false false)
+                      args arg_list;
+                    true
+                  with Invalid_argument _ ->
+                    (*print_endline "Bad count?";*) false)
+              | _ -> false
+            in
+            if not matched_args then List.iter this#gen_expression arg_list
+          in
+          match (remove_parens_cast func).eexpr with
+          | TField (obj, field)
+            when is_array_or_dyn_array obj.etype && field_name field = "map"
+            -> (
+              match this#get_array_type expression.etype with
+              | ArrayData t ->
+                  this#write
+                    (this#op IaToDataArray
+                    ^ this#typeTextString ("Array." ^ t)
+                    ^ "\n");
+                  this#begin_expr;
+                  this#writePos func;
+                  gen_call ();
+                  this#end_expr
+              | ArrayInterface t ->
+                  this#write
+                    (this#op IaToInterfaceArray ^ string_of_int t ^ "\n");
+                  this#begin_expr;
+                  this#writePos func;
+                  gen_call ();
+                  this#end_expr
+              | _ -> gen_call ())
+          | _ -> gen_call ())
+      | TField (obj, acc) -> (
+          let objType =
+            if is_dynamic_in_cppia ctx obj then "Dynamic"
+            else script_type_string obj.etype
+          in
+          let typeText =
+            if is_dynamic_in_cppia ctx obj then this#typeTextString "Dynamic"
+            else this#typeText obj.etype
+          in
+          match acc with
+          | FDynamic name ->
+              this#write
+                (this#op IaFName ^ typeText ^ " " ^ this#stringText name
+               ^ this#commentOf name ^ "\n");
+              this#gen_expression obj
+          | FStatic (class_def, field) ->
+              this#write
+                (this#op IaFStatic ^ this#instText class_def ^ " "
+                ^ this#stringText field.cf_name
+                ^ this#commentOf field.cf_name)
+          | FInstance (_, _, field) when is_this obj ->
+              this#write
+                (this#op IaFThisInst ^ typeText ^ " "
+                ^ this#stringText field.cf_name
+                ^ this#commentOf field.cf_name)
+          | FInstance (_, _, field) ->
+              this#write
+                (this#op IaFLink ^ typeText ^ " "
+                ^ this#stringText field.cf_name
+                ^ this#commentOf (objType ^ "." ^ field.cf_name)
+                ^ "\n");
+              this#gen_expression obj
+          | FClosure (_, field) when is_this obj ->
+              this#write
+                (this#op IaFThisName ^ typeText ^ " "
+                ^ this#stringText field.cf_name
+                ^ "\n")
+          | FAnon field when is_this obj ->
+              this#write
+                (this#op IaFThisName ^ typeText ^ " "
+                ^ this#stringText field.cf_name
+                ^ this#commentOf field.cf_name
+                ^ "\n")
+          | FClosure (_, field) | FAnon field ->
+              this#write
+                (this#op IaFName ^ typeText ^ " "
+                ^ this#stringText field.cf_name
+                ^ this#commentOf field.cf_name
+                ^ "\n");
+              this#gen_expression obj
+          | FEnum (enum, field) ->
+              this#write
+                (this#op IaFEnum ^ this#enumText enum ^ " "
+                ^ this#stringText field.ef_name
+                ^ this#commentOf field.ef_name))
+      | TArray (e1, e2) ->
+          this#write (this#op IaArrayI ^ this#typeText e1.etype ^ "\n");
+          this#gen_expression e1;
+          this#gen_expression e2
+      | TUnop (op, flag, e) ->
+          this#writeOpLine
+            (match (op, flag) with
+            | Increment, Prefix -> IaPlusPlus
+            | Increment, _ -> IaPlusPlusPost
+            | Decrement, Prefix -> IaMinusMinus
+            | Decrement, _ -> IaMinusMinusPost
+            | Not, _ -> IaLogicNot
+            | Neg, _ -> IaNeg
+            | Spread, _ -> die ~p:e.epos "Unexpected spread operator" __LOC__
+            | NegBits, _ -> IaBitNot);
+          this#gen_expression e
+      (* TODO - lval op-assign local/member/array *)
+      | TLocal var ->
+          this#write
+            (this#op IaVar ^ string_of_int var.v_id ^ this#commentOf var.v_name)
+      | TVar (tvar, optional_init) -> (
+          this#write
+            (this#op IaTVars ^ string_of_int 1
+            ^ this#commentOf (tvar.v_name ^ ":" ^ script_type_string tvar.v_type)
+            ^ "\n");
+          this#write ("\t\t" ^ indent);
+          match optional_init with
+          | None ->
+              this#writeOp IaVarDecl;
+              this#writeVar tvar
+          | Some init ->
+              this#writeOp IaVarDeclI;
+              let init = remove_parens init in
+              this#writeVar tvar;
+              this#write (" " ^ this#typeText init.etype);
+              this#write "\n";
+              this#checkCast tvar.v_type init false false)
+      | TNew (clazz, params, arg_list) -> (
+          this#write
+            (this#op IaNew
+            ^ this#typeText (TInst (clazz, params))
+            ^ string_of_int (List.length arg_list)
+            ^ "\n");
+          try
+            match
+              OverloadResolution.maybe_resolve_constructor_overload clazz params
+                arg_list
+            with
+            | Some (_, { cf_type = TFun (args, _) }, _) ->
+                List.iter2
+                  (fun (_, _, protoT) arg ->
+                    this#checkCast protoT arg false false)
+                  args arg_list
+            | _ -> raise (Invalid_argument "")
+          with Invalid_argument _ -> List.iter this#gen_expression arg_list)
+      | TReturn optval -> (
+          match optval with
+          | None -> this#writeOpLine IaReturn
+          | Some value ->
+              this#write (this#op IaRetVal ^ this#typeText value.etype ^ "\n");
+              this#checkCast return_type value false false)
+      | TObjectDecl
+          [
+            (("fileName", _, _), { eexpr = TConst (TString file) });
+            (("lineNumber", _, _), { eexpr = TConst (TInt line) });
+            (("className", _, _), { eexpr = TConst (TString class_name) });
+            (("methodName", _, _), { eexpr = TConst (TString meth) });
+          ] ->
+          this#write
+            (this#op IaPosInfo ^ this#stringText file
+           ^ Printf.sprintf "%ld" line ^ " " ^ this#stringText class_name ^ " "
+           ^ this#stringText meth)
+      | TObjectDecl values ->
+          this#write (this#op IaObjDef ^ string_of_int (List.length values));
+          this#write " ";
+          List.iter
+            (fun ((name, _, _), _) -> this#write (this#stringText name))
+            values;
+          this#write "\n";
+          List.iter (fun (_, e) -> this#gen_expression e) values
+      | TTypeExpr type_expr ->
+          let klass = "::" ^ join_class_path (t_path type_expr) "::" in
+          this#write (this#op IaClassOf ^ string_of_int (this#typeId klass))
+      | TWhile (e1, e2, flag) ->
+          this#write
+            (this#op IaWhile ^ (if flag = NormalWhile then "1" else "0") ^ "\n");
+          this#gen_expression e1;
+          this#gen_expression e2
+      | TFor (tvar, init, loop) ->
+          this#writeOp IaFor;
+          this#writeVar tvar;
+          this#write "\n";
+          this#gen_expression init;
+          this#gen_expression loop
+      | TEnumParameter (expr, ef, i) ->
+          let enum =
+            match follow ef.ef_type with
+            | TEnum (en, _) | TFun (_, TEnum (en, _)) -> en
+            | _ -> die "" __LOC__
+          in
+          this#write
+            (this#op IaEnumI
+            ^ this#typeText (TEnum (enum, []))
+            ^ string_of_int i ^ "\n");
+          this#gen_expression expr
+      | TEnumIndex expr ->
+          this#write
+            (this#op IaCallMember
+            ^ this#typeTextString "::hx::EnumBase"
+            ^ " " ^ this#stringText "__Index" ^ "0"
+            ^ this#commentOf "Enum index"
+            ^ "\n");
+          this#gen_expression expr
+      | TSwitch
+          {
+            switch_subject = condition;
+            switch_cases = cases;
+            switch_default = optional_default;
+          } -> (
+          this#write
+            (this#op IaSwitch
+            ^ string_of_int (List.length cases)
+            ^ " "
+            ^ (match optional_default with None -> "0" | Some _ -> "1")
+            ^ "\n");
+          this#gen_expression condition;
+          List.iter
+            (fun { case_patterns = cases_list; case_expr = expression } ->
+              this#writeList ("\t\t\t" ^ indent) (List.length cases_list);
+              List.iter (fun value -> this#gen_expression value) cases_list;
+              this#gen_expression expression)
+            cases;
+          match optional_default with
+          | None -> ()
+          | Some expr -> this#gen_expression expr)
+      | TTry (e, catches) ->
+          this#writeList (this#op IaTry) (List.length catches);
+          this#gen_expression e;
+          List.iter
+            (fun (tvar, catch_expr) ->
+              this#write ("\t\t\t" ^ indent);
+              this#writeVar tvar;
+              this#write "\n";
+              this#gen_expression catch_expr)
+            catches
+      | TCast (cast, Some (TClassDecl t)) ->
+          this#write (this#op IaTCast ^ this#typeText (TInst (t, [])) ^ "\n");
+          this#gen_expression cast
+      | TCast (cast, _) -> this#checkCast expression.etype cast true true
+      | TParenthesis _ -> abort "Unexpected parens" expression.epos
+      | TMeta (_, _) -> abort "Unexpected meta" expression.epos
+      | TIdent _ -> abort "Unexpected ident" expression.epos
+
+    (* } *)
+    method gen_expression_tree expression_tree =
+      (* { *)
+      let rec gen_expression expression =
+        this#begin_expr;
+        this#writeCppPos expression;
+        let rec match_expr expression =
+          match expression.cppexpr with
+          | CppBlock (exprs, closures, _) ->
+              this#writeList (this#op IaBlock) (List.length exprs);
+              List.iter gen_expression exprs
+          | CppVarDecl (var, init) -> (
+              let name = CppGen.cpp_var_name_of var in
+              this#write
+                (this#op IaTVars ^ string_of_int 1
+                ^ this#commentOf (name ^ ":" ^ script_type_string var.v_type)
+                ^ "\n");
+              this#write ("\t\t" ^ indent);
+              match init with
+              | None ->
+                  this#writeOp IaVarDecl;
+                  this#writeVar var
+              | Some init ->
+                  this#writeOp IaVarDeclI;
+                  this#writeVar var;
+                  this#write (" " ^ this#astType init.cpptype);
+                  this#write "\n";
+                  gen_expression init)
+          | CppInt i -> this#write (this#op IaConstInt ^ Printf.sprintf "%ld " i)
+          | CppFloat float_as_string ->
+              this#write (this#op IaConstFloat ^ this#stringText float_as_string)
+          | CppString s -> this#write (this#op IaConstString ^ this#stringText s)
+          | CppBool false -> this#writeOp IaConstFalse
+          | CppBool true -> this#writeOp IaConstTrue
+          | CppNull -> this#writeOp IaConstNull
+          | CppNil -> abort "Nil not supported in cppia" expression.cpppos
+          | CppThis _ -> this#writeOp IaConsThis
+          | CppSuper _ -> this#writeOp IaConstSuper
+          | CppBreak -> this#writeOp IaBreak
+          | CppContinue -> this#writeOp IaContinue
+          | CppGoto label ->
+              abort "Goto not supported in cppia" expression.cpppos
+          | CppReturn None -> this#writeOpLine IaReturn
+          | CppReturn (Some value) ->
+              this#write (this#op IaRetVal ^ this#astType value.cpptype ^ "\n");
+              gen_expression value
+          | CppWhile (condition, block, while_flag, _) ->
+              this#write
+                (this#op IaWhile
+                ^ (if while_flag = NormalWhile then "1" else "0")
+                ^ "\n");
+              gen_expression condition;
+              gen_expression block
+          | CppIf (condition, block, None) ->
+              this#writeOpLine IaIf;
+              gen_expression condition;
+              gen_expression block
+          | CppIf (condition, block, Some elze) ->
+              this#writeOpLine IaIfElse;
+              gen_expression condition;
+              gen_expression block;
+              gen_expression elze
+          | CppBinop (op, left, right) ->
+              this#writeOpLine (IaBinOp op);
+              gen_expression left;
+              gen_expression right
+          | CppVar var -> gen_var_loc var
+          | CppExtern (name, _) ->
+              abort
+                ("Unexpected global '" ^ name ^ "' in cppia")
+                expression.cpppos
+          | CppSet (lvalue, rvalue) ->
+              this#writeOpLine IaSet;
+              gen_lvalue lvalue expression.cpppos;
+              gen_expression rvalue
+          | CppCall (func, args) ->
+              let argN = string_of_int (List.length args) ^ " " in
+              (match func with
+              | FuncThis (field, inst) ->
+                  let name = field.cf_name in
+                  this#write
+                    (this#op IaCallThis ^ this#astType inst ^ " "
+                   ^ this#stringText name ^ argN ^ this#commentOf name ^ "\n")
+              | FuncInstance (expr, _, field) | FuncInterface (expr, _, field)
+                ->
+                  this#write
+                    (this#op IaCallMember ^ this#astType expr.cpptype ^ " "
+                    ^ this#stringText field.cf_name
+                    ^ argN
+                    ^ this#commentOf field.cf_name
+                    ^ "\n");
+                  gen_expression expr
+              | FuncStatic (class_def, _, field) ->
+                  this#write
+                    (this#op IaCallStatic ^ this#cppInstText class_def ^ " "
+                    ^ this#stringText field.cf_name
+                    ^ argN
+                    ^ this#commentOf
+                        (join_class_path class_def.cl_path "."
+                        ^ "." ^ field.cf_name)
+                    ^ "\n")
+              | FuncTemplate _ ->
+                  abort "Templated function call not supported in cppia"
+                    expression.cpppos
+              | FuncFromStaticFunction ->
+                  abort "Unexpected FuncFromStaticFunction" expression.cpppos
+              | FuncEnumConstruct (enum, field) ->
+                  this#write
+                    (this#op IaCreateEnum ^ this#enumText enum ^ " "
+                    ^ this#stringText field.ef_name
+                    ^ argN
+                    ^ this#commentOf field.ef_name
+                    ^ "\n")
+              | FuncSuperConstruct (TCppInst (klass, _))
+                when is_native_gen_class klass && is_native_class klass ->
+                  abort "Unsupported super for native class constructor"
+                    expression.cpppos
+              | FuncSuperConstruct childType ->
+                  this#write
+                    (this#op IaCallSuperNew ^ this#astType childType ^ " "
+                   ^ argN ^ "\n")
+              | FuncSuper (_, TCppInst (klass, _), _)
+                when is_native_gen_class klass && is_native_class klass ->
+                  abort "Unsupported super for native class method"
+                    expression.cpppos
+              | FuncSuper (_, objType, field) ->
+                  this#write
+                    (this#op IaCallSuper ^ this#astType objType ^ " "
+                    ^ this#stringText field.cf_name
+                    ^ argN
+                    ^ this#commentOf field.cf_name
+                    ^ "\n")
+              | FuncExtern (name, _) ->
+                  this#write
+                    (this#op IaCallGlobal ^ this#stringText name ^ argN
+                   ^ this#commentOf name ^ "\n")
+              | FuncNew newType ->
+                  this#write (this#op IaNew ^ this#astType newType ^ argN ^ "\n")
+              | FuncInternal (obj, "cca", ".") when obj.cpptype = TCppString ->
+                  this#write
+                    (this#op IaCallMember ^ this#astType obj.cpptype ^ " "
+                   ^ this#stringText "cca" ^ argN ^ this#commentOf "cca" ^ "\n"
+                    );
+                  gen_expression obj
+              | FuncInternal (obj, name, join) ->
+                  (* abort ("Internal function call '" ^ name ^ "' not supported in cppia") expression.cpppos; *)
+                  this#write
+                    (this#op IaCallMember ^ this#astType obj.cpptype ^ " "
+                   ^ this#stringText name ^ argN ^ this#commentOf name ^ "\n");
+                  gen_expression obj
+              | FuncExpression expr ->
+                  this#write (this#op IaCall ^ argN ^ "\n");
+                  gen_expression expr);
+              List.iter gen_expression args
+          | CppFunction (func, _) -> (
+              match func with
+              | FuncThis (field, inst) ->
+                  this#write
+                    (this#op IaFThisName ^ this#astType inst ^ " "
+                    ^ this#stringText field.cf_name
+                    ^ this#commentOf
+                        (script_cpptype_string inst ^ "." ^ field.cf_name))
+              | FuncInternal (expr, name, _) ->
+                  this#write
+                    (this#op IaFLink ^ this#astType expr.cpptype ^ " "
+                   ^ this#stringText name
+                    ^ this#commentOf
+                        ("Internal "
+                        ^ script_cpptype_string expr.cpptype
+                        ^ "." ^ name)
+                    ^ "\n");
+                  gen_expression expr
+              | FuncInstance (expr, _, field) | FuncInterface (expr, _, field)
+                ->
+                  this#write
+                    (this#op IaFName ^ this#astType expr.cpptype ^ " "
+                    ^ this#stringText field.cf_name
+                    ^ this#commentOf
+                        (script_cpptype_string expr.cpptype
+                        ^ "." ^ field.cf_name)
+                    ^ "\n");
+                  gen_expression expr
+              | FuncStatic (class_def, _, field) ->
+                  this#write
+                    (this#op IaFStatic ^ this#cppInstText class_def ^ " "
+                    ^ this#stringText field.cf_name
+                    ^ this#commentOf field.cf_name)
+              | FuncExpression expr -> match_expr expr
+              | FuncExtern (name, _) ->
+                  abort
+                    ("Can't create extern " ^ name ^ " closure")
+                    expression.cpppos
+              | FuncSuper _ | FuncSuperConstruct _ ->
+                  abort "Can't create super closure" expression.cpppos
+              | FuncNew _ -> abort "Can't create new closure" expression.cpppos
+              | FuncEnumConstruct _ ->
+                  abort "Enum constructor outside of CppCall" expression.cpppos
+              | FuncFromStaticFunction ->
+                  abort "Can't create cpp.Function.fromStaticFunction closure"
+                    expression.cpppos
+              | FuncTemplate _ ->
+                  abort "Can't create template function closure"
+                    expression.cpppos)
+          | CppPosition (file, line, class_name, meth) ->
+              this#write
+                (this#op IaPosInfo ^ this#stringText file
+               ^ Printf.sprintf "%ld" line ^ " " ^ this#stringText class_name
+               ^ " " ^ this#stringText meth)
+          | CppNullCompare ("IsNull", e) ->
+              this#writeOpLine IaIsNull;
+              gen_expression e
+          | CppNullCompare (_, e) ->
+              this#writeOpLine IaNotNull;
+              gen_expression e
+          | CppCompare (_, left, right, op) ->
+              this#writeOpLine (IaBinOp op);
+              gen_expression left;
+              gen_expression right
+          | CppArray arrayLoc -> gen_array arrayLoc expression.cpppos
+          | CppArrayDecl exprList ->
+              this#write
+                (this#op IaADef
+                ^ this#astType expression.cpptype
+                ^ " "
+                ^ string_of_int (List.length exprList)
+                ^ "\n");
+              List.iter gen_expression exprList
+          | CppEnumField (enum, field) ->
+              this#write
+                (this#op IaFEnum ^ this#enumText enum ^ " "
+                ^ this#stringText field.ef_name
+                ^ this#commentOf field.ef_name)
+          | CppEnumIndex obj ->
+              (* Cppia does not have a "GetEnumIndex" op code - must use IaCallMember ::hx::EnumBase.__Index *)
+              this#write
+                (this#op IaCallMember
+                ^ this#typeTextString "::hx::EnumBase"
+                ^ " " ^ this#stringText "__Index" ^ "0"
+                ^ this#commentOf "Enum index"
+                ^ "\n");
+              gen_expression obj
+          | CppDynamicField (obj, name) ->
+              this#write
+                (this#op IaFName
+                ^ this#typeTextString "Dynamic"
+                ^ " " ^ this#stringText name ^ this#commentOf name ^ "\n");
+              gen_expression obj
+          | CppClassOf (path, native) ->
+              let klass = join_class_path path "." in
+              this#write
+                (this#op IaClassOf ^ this#typeTextString klass
+               ^ this#commentOf klass)
+          | CppEnumParameter (obj, field, index) ->
+              this#write
+                (this#op IaEnumI
+                ^ this#typeTextString "Dynamic"
+                ^ string_of_int index ^ "\n");
+              gen_expression obj
+          | CppClosure closure ->
+              this#write
+                (this#op IaFun
+                ^ this#astType closure.close_type
+                ^ string_of_int (List.length closure.close_args)
+                ^ "\n");
+              let close = this#gen_func_args closure.close_args in
+              gen_expression closure.close_expr;
+              close ()
+          | CppObjectDecl (values, isStruct) ->
+              this#write (this#op IaObjDef ^ string_of_int (List.length values));
+              this#write " ";
+              List.iter
+                (fun (name, _) -> this#write (this#stringText name))
+                values;
+              this#write "\n";
+              List.iter (fun (_, e) -> gen_expression e) values
+          | CppCrement (incFlag, preFlag, lvalue) ->
+              let op =
+                match (incFlag, preFlag) with
+                | CppIncrement, Prefix -> IaPlusPlus
+                | CppIncrement, Postfix -> IaPlusPlusPost
+                | CppDecrement, Prefix -> IaMinusMinus
+                | CppDecrement, Postfix -> IaMinusMinusPost
+              in
+              this#writeOpLine op;
+              gen_lvalue lvalue expression.cpppos
+          | CppModify (op, lvalue, rvalue) ->
+              this#writeOpLine (IaBinOp (OpAssignOp op));
+              gen_lvalue lvalue expression.cpppos;
+              gen_expression rvalue
+          | CppUnop (op, expr) ->
+              let op =
+                match op with
+                | CppNot -> IaLogicNot
+                | CppNeg -> IaNeg
+                | CppNegBits -> IaBitNot
+              in
+              this#writeOpLine op;
+              gen_expression expr
+          | CppThrow value ->
+              this#writeOpLine IaThrow;
+              gen_expression value
+          | CppTry (block, catches) ->
+              this#writeList (this#op IaTry) (List.length catches);
+              gen_expression block;
+              List.iter
+                (fun (tvar, catch_expr) ->
+                  this#write ("\t\t\t" ^ indent);
+                  this#writeVar tvar;
+                  this#write "\n";
+                  gen_expression catch_expr)
+                catches
+          | CppIntSwitch _ ->
+              abort "CppIntSwitch not supported in cppia" expression.cpppos
+          | CppSwitch (condition, _, cases, optional_default, _) -> (
+              this#write
+                (this#op IaSwitch
+                ^ string_of_int (List.length cases)
+                ^ " "
+                ^ (match optional_default with None -> "0" | Some _ -> "1")
+                ^ "\n");
+              gen_expression condition;
+              List.iter
+                (fun (cases_list, expression) ->
+                  this#writeList ("\t\t\t" ^ indent) (List.length cases_list);
+                  List.iter (fun value -> gen_expression value) cases_list;
+                  gen_expression expression)
+                cases;
+              match optional_default with
+              | None -> ()
+              | Some expr -> gen_expression expr)
+          | CppTCast (expr, toType) ->
+              this#write (this#op IaTCast ^ this#astType toType ^ "\n");
+              gen_expression expr
+          | CppCast (expr, toType) -> (
+              match toType with
+              | TCppDynamicArray ->
+                  this#write (this#op IaToDynArray ^ "\n");
+                  gen_expression expr
+              | TCppObjectArray _ ->
+                  this#write
+                    (this#op IaToDataArray
+                    ^ this#typeTextString "Array.Object"
+                    ^ "\n");
+                  gen_expression expr
+              | TCppScalarArray t ->
+                  this#write
+                    (this#op IaToDataArray
+                    ^ this#typeTextString ("Array." ^ script_cpptype_string t)
+                    ^ "\n");
+                  gen_expression expr
+              | _ -> match_expr expr)
+          | CppCastScalar (expr, "bool") ->
+              this#writeOpLine IaCastBool;
+              gen_expression expr
+          | CppCastScalar (expr, "int") ->
+              this#writeOpLine IaCastInt;
+              gen_expression expr
+          | CppCastScalar (expr, "Float") ->
+              this#write
+                (this#op IaTCast ^ this#astType (TCppScalar "Float") ^ "\n");
+              gen_expression expr
+          | CppCastScalar (expr, _) -> match_expr expr
+          | CppCastVariant expr -> match_expr expr
+          | CppCastStatic (expr, _) -> match_expr expr
+          | CppNullAccess ->
+              this#writeOpLine IaThrow;
+              this#begin_expr;
+              this#writeCppPos expression;
+              this#write (this#op IaConstString ^ this#stringText "Null access");
+              this#end_expr
+          | CppCode _ | CppFunctionAddress _ | CppNewNative _ | CppDereference _
+          | CppAddressOf _ | CppFor _ | CppCastObjC _ | CppCastObjCBlock _
+          | CppCastProtocol _ | CppCastNative _ ->
+              abort
+                ("Unsupported operation in cppia :" ^ s_tcpp expression.cppexpr)
+                expression.cpppos
+          (*| x -> print_endline ("Unknown cppexpr " ^ (s_tcpp x) );*)
+        in
+
+        match_expr expression;
+        this#end_expr
+      and gen_array arrayLoc pos =
+        match arrayLoc with
+        | ArrayObject (arrayObj, index, _) | ArrayTyped (arrayObj, index, _) ->
+            this#write (this#op IaArrayI ^ this#astType arrayObj.cpptype ^ "\n");
+            gen_expression arrayObj;
+            gen_expression index
+        | ArrayPointer (_, _) | ArrayRawPointer (_, _) ->
+            abort "Unvalid array access in cppia" pos
+        | ArrayVirtual (arrayObj, index)
+        | ArrayImplements (_, arrayObj, index)
+        | ArrayDynamic (arrayObj, index) ->
+            this#write (this#op IaArrayI ^ this#astType arrayObj.cpptype ^ "\n");
+            gen_expression arrayObj;
+            gen_expression index
+      and gen_lvalue lvalue pos =
+        this#begin_expr;
+        this#wpos pos;
+        (match lvalue with
+        | CppVarRef varLoc -> gen_var_loc varLoc
+        | CppArrayRef arrayLoc -> gen_array arrayLoc pos
+        | CppExternRef (name, _) ->
+            abort ("Unsupported extern '" ^ name ^ "' in cppia") pos
+        | CppDynamicRef (expr, name) ->
+            let typeText = this#typeTextString "Dynamic" in
+            this#write
+              (this#op IaFName ^ typeText ^ " " ^ this#stringText name
+             ^ this#commentOf name ^ "\n");
+            gen_expression expr);
+        this#end_expr
+      and gen_var_loc loc =
+        match loc with
+        | VarClosure var | VarLocal var ->
+            this#write
+              (this#op IaVar ^ string_of_int var.v_id
+             ^ this#commentOf var.v_name)
+        | VarStatic (class_def, _, field) ->
+            this#write
+              (this#op IaFStatic ^ this#cppInstText class_def ^ " "
+              ^ this#stringText field.cf_name
+              ^ this#commentOf field.cf_name)
+        | VarThis (field, thisType) ->
+            this#write
+              (this#op IaFThisInst ^ this#astType thisType ^ " "
+              ^ this#stringText field.cf_name
+              ^ this#commentOf field.cf_name)
+        | VarInstance (obj, field, _, _) | VarInterface (obj, field) ->
+            let objType = script_cpptype_string obj.cpptype in
+            this#write
+              (this#op IaFLink ^ this#astType obj.cpptype ^ " "
+              ^ this#stringText field.cf_name
+              ^ this#commentOf (objType ^ "." ^ field.cf_name)
+              ^ "\n");
+            gen_expression obj
+        | VarInternal (obj, _, name) ->
+            let objType = script_cpptype_string obj.cpptype in
+            this#write
+              (this#op IaFLink ^ this#astType obj.cpptype ^ " "
+             ^ this#stringText name
+              ^ this#commentOf (objType ^ "." ^ name)
+              ^ "\n");
+            gen_expression obj
+        (*
+      and get_array_type elem =
+         this#stringText (script_cpptype_string elem.cpptype);
+      *)
+      in
+      gen_expression expression_tree
+  end
+
+let generate_script_class common_ctx script class_def =
+  script#incClasses;
+  let classText = join_class_path class_def.cl_path "." in
+  script#comment ("Class " ^ classText);
+  script#writeOp
+    (if has_class_flag class_def CInterface then IaInterface else IaClass);
+  script#instName class_def;
+  (match class_def.cl_super with
+  | None -> script#ident ""
+  | Some (c, _) -> script#instName c);
+  script#wint (List.length class_def.cl_implements);
+  List.iter (fun (c, _) -> script#instName c) class_def.cl_implements;
+  script#write "\n";
+  (* Looks like some map impl classes have their bodies discarded - not sure best way to filter *)
+  let non_dodgy_function allow_empty field =
+    has_class_flag class_def CInterface
+    ||
+    match (field.cf_kind, field.cf_expr) with
+    | Var _, _ -> true
+    | Method MethDynamic, _ -> true
+    | Method MethNormal, None when allow_empty -> true
+    | Method _, Some _ -> true
+    | _ -> false
+  in
+  let ordered_statics =
+    List.filter (non_dodgy_function false) class_def.cl_ordered_statics
+  in
+  let ordered_fields =
+    List.filter (non_dodgy_function true) class_def.cl_ordered_fields
+  in
+  script#write
+    (string_of_int
+       (List.length ordered_fields
+       + List.length ordered_statics
+       + (match class_def.cl_constructor with Some _ -> 1 | _ -> 0)
+       + match TClass.get_cl_init class_def with Some _ -> 1 | _ -> 0)
+    ^ "\n");
+
+  let generate_field isStatic field =
+    match (field.cf_kind, follow field.cf_type) with
+    | Var { v_read = AccInline; v_write = AccNever }, _ ->
+        script#writeOpLine IaInline
+    | Var v, _ ->
+        let mode_code mode =
+          match mode with
+          | AccNormal | AccCtor -> IaAccessNormal
+          | AccNo -> IaAccessNot
+          | AccNever -> IaAccessNot
+          | AccCall ->
+              if
+                Meta.has Meta.NativeProperty class_def.cl_meta
+                || Meta.has Meta.NativeProperty field.cf_meta
+                || Common.defined common_ctx Define.ForceNativeProperty
+              then IaAccessCallNative
+              else IaAccessCall
+          | AccInline -> IaAccessNormal
+          | AccRequire (_, _) -> IaAccessNormal
+        in
+        let isExtern = not (is_physical_field field) in
+        script#var (mode_code v.v_read) (mode_code v.v_write) isExtern isStatic
+          field.cf_name field.cf_type field.cf_expr
+    | Method MethDynamic, TFun (args, ret) ->
+        script#func isStatic true field.cf_name ret args
+          (has_class_flag class_def CInterface)
+          field.cf_expr field.cf_pos
+    | Method _, TFun (args, ret) when field.cf_name = "new" ->
+        script#func true false "new"
+          (TInst (class_def, []))
+          args false field.cf_expr field.cf_pos
+    | Method _, TFun (args, ret) ->
+        script#func isStatic false field.cf_name ret args
+          (has_class_flag class_def CInterface)
+          field.cf_expr field.cf_pos
+    | Method _, _ ->
+        print_endline
+          ("Unknown method type "
+          ^ join_class_path class_def.cl_path "."
+          ^ "." ^ field.cf_name)
+  in
+  (match class_def.cl_constructor with
+  | Some field -> generate_field true field
+  | _ -> ());
+  (match TClass.get_cl_init class_def with
+  | Some expression -> script#voidFunc true false "__init__" expression
+  | _ -> ());
+
+  List.iter (generate_field false) ordered_fields;
+  List.iter (generate_field true) ordered_statics;
+  script#write "\n"
+
+let generate_script_enum script enum_def meta =
+  script#incClasses;
+  let sorted_items =
+    List.sort
+      (fun f1 f2 -> f1.ef_index - f2.ef_index)
+      (pmap_values enum_def.e_constrs)
+  in
+  script#writeList
+    (script#op IaEnum ^ script#enumText enum_def)
+    (List.length sorted_items);
+
+  List.iter
+    (fun constructor ->
+      let name = script#stringText constructor.ef_name in
+      match constructor.ef_type with
+      | TFun (args, _) ->
+          script#write (name ^ " " ^ string_of_int (List.length args));
+          List.iter
+            (fun (arg, _, t) ->
+              script#write
+                (" " ^ script#stringText arg ^ " " ^ script#typeText t))
+            args;
+          script#write "\n"
+      | _ -> script#write (name ^ " 0\n"))
+    sorted_items;
+
+  match meta with
+  | Some expr ->
+      script#write "1\n";
+      script#gen_expression expr
+  | _ ->
+      script#write "0\n";
+      script#write "\n"
+
+let generate_cppia ctx =
+  let common_ctx = ctx.ctx_common in
+  let debug = ctx.ctx_debug_level in
+  Path.mkdir_from_path common_ctx.file;
+  let script = new script_writer ctx common_ctx.file common_ctx.debug in
+  ignore (script#stringId "");
+  ignore (script#typeId "");
+
+  List.iter
+    (fun object_def ->
+      match object_def with
+      | TClassDecl class_def when has_class_flag class_def CExtern ->
+          () (*if (gen_externs) then gen_extern_class common_ctx class_def;*)
+      | TClassDecl class_def ->
+          let is_internal = is_internal_class class_def.cl_path in
+          if is_internal || Meta.has Meta.Macro class_def.cl_meta then (
+            if debug >= 4 then
+              print_endline
+                (" internal class " ^ join_class_path class_def.cl_path "."))
+          else generate_script_class common_ctx script class_def
+      | TEnumDecl enum_def when has_enum_flag enum_def EnExtern -> ()
+      | TEnumDecl enum_def ->
+          let is_internal = is_internal_class enum_def.e_path in
+          if is_internal then (
+            if debug >= 4 then
+              print_endline
+                (" internal enum " ^ join_class_path enum_def.e_path "."))
+          else
+            let meta = Texpr.build_metadata common_ctx.basic object_def in
+            if has_enum_flag enum_def EnExtern then
+              if debug >= 4 then
+                print_endline
+                  ("external enum " ^ join_class_path enum_def.e_path ".");
+            generate_script_enum script enum_def meta
+      | TTypeDecl _ | TAbstractDecl _ -> (* already done *) ())
+    common_ctx.types;
+
+  (match common_ctx.main.main_expr with
+  | None -> script#writeOpLine IaNoMain
+  | Some e ->
+      script#writeOpLine IaMain;
+      script#gen_expression e);
+
+  script#write
+    (script#op IaResources
+    ^ string_of_int (Hashtbl.length common_ctx.resources)
+    ^ "\n");
+  Hashtbl.iter
+    (fun name data ->
+      script#write
+        (script#op IaReso ^ script#stringText name
+        ^ string_of_int (String.length data)
+        ^ "\n"))
+    common_ctx.resources;
+  Hashtbl.iter (fun _ data -> script#writeData data) common_ctx.resources;
+
+  script#close

+ 2048 - 0
src/generators/cpp/gen/cppGen.ml

@@ -0,0 +1,2048 @@
+open Ast
+open Type
+open Error
+open Common
+open Globals
+open CppStrings
+open CppExprUtils
+open CppTypeUtils
+open CppAst
+open CppAstTools
+open CppSourceWriter
+open CppContext
+
+type tinject = {
+  inj_prologue : bool -> unit;
+  inj_setvar : string;
+  inj_tail : string;
+}
+
+let cpp_type_of = CppRetyper.cpp_type_of
+let cpp_type_of_null = CppRetyper.cpp_type_of_null
+let cpp_instance_type = CppRetyper.cpp_instance_type
+let type_to_string haxe_type = tcpp_to_string (cpp_type_of haxe_type)
+
+let type_cant_be_null haxe_type =
+  match cpp_type_of haxe_type with TCppScalar _ -> true | _ -> false
+
+let type_arg_to_string name default_val arg_type prefix =
+  let remap_name = keyword_remap name in
+  let type_str = type_to_string arg_type in
+  match default_val with
+  | Some { eexpr = TConst TNull } -> (type_str, remap_name)
+  | Some constant when type_cant_be_null arg_type ->
+      ("::hx::Null< " ^ type_str ^ " > ", prefix ^ remap_name)
+  | Some constant -> (type_str, prefix ^ remap_name)
+  | _ -> (type_str, remap_name)
+
+let cpp_var_name_of var =
+  match get_meta_string var.v_meta Meta.Native with
+  | Some n -> n
+  | None -> keyword_remap var.v_name
+
+let cpp_var_debug_name_of v =
+  match get_meta_string v.v_meta Meta.RealPath with
+  | Some n -> n
+  | None -> v.v_name
+
+(* Generate prototype text, including allowing default values to be null *)
+let print_arg name default_val arg_type prefix =
+  let n, t = type_arg_to_string name default_val arg_type prefix in
+  n ^ " " ^ t
+
+(* Generate prototype text, including allowing default values to be null *)
+let print_arg_name name default_val arg_type prefix =
+  let n, _ = type_arg_to_string name default_val arg_type prefix in
+  n
+
+let print_arg_list arg_list prefix =
+  String.concat ","
+    (List.map (fun (v, o) -> print_arg v.v_name o v.v_type prefix) arg_list)
+
+let print_arg_list_name arg_list prefix =
+  String.concat ","
+    (List.map
+       (fun (v, o) -> print_arg_name v.v_name o v.v_type prefix)
+       arg_list)
+
+let print_arg_names args =
+  String.concat "," (List.map (fun (name, _, _) -> keyword_remap name) args)
+
+let rec print_tfun_arg_list include_names arg_list =
+  let oType o arg_type =
+    let type_str = type_to_string arg_type in
+    (* type_str may have already converted Null<X> to Dynamic because of NotNull tag ... *)
+    if o && type_cant_be_null arg_type && type_str <> "Dynamic" then
+      "::hx::Null< " ^ type_str ^ " > "
+    else type_str
+  in
+  match arg_list with
+  | [] -> ""
+  | [ (name, o, arg_type) ] ->
+      oType o arg_type ^ if include_names then " " ^ keyword_remap name else ""
+  | (name, o, arg_type) :: remaining ->
+      oType o arg_type
+      ^ (if include_names then " " ^ keyword_remap name else "")
+      ^ ","
+      ^ print_tfun_arg_list include_names remaining
+
+let has_new_gc_references class_def =
+  let is_gc_reference field =
+    should_implement_field field
+    && is_data_member field
+    && not (type_cant_be_null field.cf_type)
+  in
+  List.exists is_gc_reference class_def.cl_ordered_fields
+
+let rec has_gc_references class_def =
+  (match class_def.cl_super with
+  | Some def when has_gc_references (fst def) -> true
+  | _ -> false)
+  || has_new_gc_references class_def
+
+let rec find_next_super_iteration class_def =
+  match class_def.cl_super with
+  | Some (klass, params) when has_new_gc_references klass ->
+      tcpp_to_string_suffix "_obj" (cpp_instance_type klass params)
+  | Some (klass, _) -> find_next_super_iteration klass
+  | _ -> ""
+
+let cpp_member_name_of member =
+  match get_meta_string member.cf_meta Meta.Native with
+  | Some n -> n
+  | None -> keyword_remap member.cf_name
+
+let function_signature include_names tfun abi =
+  match follow tfun with
+  | TFun (args, ret) ->
+      type_to_string ret ^ " " ^ abi ^ "("
+      ^ print_tfun_arg_list include_names args
+      ^ ")"
+  | _ -> "void *"
+
+let cpp_no_debug_synbol ctx var =
+  ctx.ctx_debug_level <= 1
+  || (match var.v_kind with VUser _ -> false | _ -> true)
+  ||
+  match cpp_type_of var.v_type with
+  | TCppStar _ | TCppReference _ -> true
+  | TCppInst (class_def, _) when Meta.has Meta.StructAccess class_def.cl_meta ->
+      true
+  | TCppInst (class_def, _) when Meta.has Meta.Unreflective class_def.cl_meta ->
+      true
+  | _ ->
+      let name = cpp_var_debug_name_of var in
+      String.length name > 4 && String.sub name 0 4 = "_hx_"
+
+let cpp_debug_name_of var = keyword_remap var.v_name
+let cpp_debug_var_visible ctx var = not (cpp_no_debug_synbol ctx (fst var))
+let cpp_var_type_of var = tcpp_to_string (cpp_type_of var.v_type)
+
+let mk_injection prologue set_var tail =
+  Some { inj_prologue = prologue; inj_setvar = set_var; inj_tail = tail }
+
+let tvar_arg_to_string tvar default_val prefix =
+  let remap_name = cpp_var_name_of tvar in
+  let type_str = cpp_var_type_of tvar in
+  match default_val with
+  | Some { eexpr = TConst TNull } ->
+      (tcpp_to_string (cpp_type_of_null tvar.v_type), remap_name)
+  | Some constant ->
+      (tcpp_to_string (cpp_type_of_null tvar.v_type), prefix ^ remap_name)
+  | _ -> (type_str, remap_name)
+
+(* Generate prototype text, including allowing default values to be null *)
+let cpp_arg_string tvar default_val prefix =
+  let t, n = tvar_arg_to_string tvar default_val prefix in
+  t ^ " " ^ n
+
+let cpp_arg_list args prefix =
+  String.concat "," (List.map (fun (v, o) -> cpp_arg_string v o prefix) args)
+
+let gen_type ctx haxe_type = ctx.ctx_output (type_to_string haxe_type)
+
+let cpp_macro_var_type_of var =
+  let t = tcpp_to_string (cpp_type_of var.v_type) in
+  if String.contains t ',' then
+    Str.global_replace (Str.regexp ",") " HX_COMMA " t
+  else t
+
+let cpp_class_name klass =
+  let globalNamespace =
+    match get_meta_string klass.cl_meta Meta.Native with
+    | Some _ -> ""
+    | None -> "::"
+  in
+  let path = globalNamespace ^ join_class_path_remap klass.cl_path "::" in
+  if is_native_class klass || path = "::String" then path else path ^ "_obj"
+
+let rec implements_native_interface class_def =
+  List.exists
+    (fun (intf_def, _) ->
+      is_native_gen_class intf_def || implements_native_interface intf_def)
+    class_def.cl_implements
+  ||
+  match class_def.cl_super with
+  | Some (i, _) -> implements_native_interface i
+  | _ -> false
+
+let can_quick_alloc klass =
+  (not (is_native_class klass)) && not (implements_native_interface klass)
+
+let only_stack_access haxe_type =
+  match cpp_type_of haxe_type with
+  | TCppInst (klass, _) -> Meta.has Meta.StackOnly klass.cl_meta
+  | _ -> false
+
+let cpp_is_static_extension member =
+  Meta.has Meta.NativeStaticExtension member.cf_meta
+
+let cpp_enum_name_of field =
+  match get_meta_string field.ef_meta Meta.Native with
+  | Some n -> n
+  | None -> keyword_remap field.ef_name
+
+let string_of_path path = "::" ^ join_class_path_remap path "::" ^ "_obj"
+
+let default_value_string ctx value =
+  match value.eexpr with
+  | TConst (TInt i) -> Printf.sprintf "%ld" i
+  | TConst (TFloat float_as_string) ->
+      "((Float)" ^ Texpr.replace_separators float_as_string "" ^ ")"
+  | TConst (TString s) -> strq ctx s
+  | TConst (TBool b) -> if b then "true" else "false"
+  | TConst TNull -> "null()"
+  | TField (_, FEnum (enum, field)) ->
+      string_of_path enum.e_path ^ "::" ^ cpp_enum_name_of field ^ "_dyn()"
+  | _ -> "/* Hmmm " ^ s_expr_kind value ^ " */"
+
+let cpp_gen_default_values ctx args prefix =
+  List.iter
+    (fun (tvar, o) ->
+      let vtype = cpp_type_of tvar.v_type in
+      let not_null =
+        type_has_meta_key Meta.NotNull tvar.v_type || is_cpp_scalar vtype
+      in
+      match o with
+      | Some { eexpr = TConst TNull } -> ()
+      | Some const ->
+          let name = cpp_var_name_of tvar in
+          let spacer =
+            if ctx.ctx_debug_level > 0 then "            \t" else ""
+          in
+          let pname = prefix ^ name in
+          ctx.ctx_output
+            (spacer ^ "\t" ^ tcpp_to_string vtype ^ " " ^ name ^ " = " ^ pname);
+          ctx.ctx_output
+            (if not_null then
+               ".Default(" ^ default_value_string ctx.ctx_common const ^ ");\n"
+             else
+               ";\n" ^ spacer ^ "\tif (::hx::IsNull(" ^ pname ^ ")) " ^ name
+               ^ " = "
+               ^ default_value_string ctx.ctx_common const
+               ^ ";\n")
+      | _ -> ())
+    args
+
+let ctx_default_values ctx args prefix = cpp_gen_default_values ctx args prefix
+
+let cpp_class_hash interface =
+  gen_hash 0 (join_class_path interface.cl_path "::")
+
+let cpp_template_param path native =
+  let path = "::" ^ join_class_path_remap path "::" in
+  if native then path
+  else
+    match path with
+    | "::Array" -> "::hx::ArrayBase"
+    | "::Int" -> "int"
+    | "::Bool" -> "bool"
+    | x -> x
+
+let rec is_constant_zero expr =
+  match expr.cppexpr with
+  | CppFloat x when float_of_string x = 0.0 -> true
+  | CppInt i when i = Int32.zero -> true
+  | CppCastScalar (expr, _) -> is_constant_zero expr
+  | _ -> false
+
+let cpp_is_const_scalar_array arrayType expressions =
+  List.length expressions > 0
+  &&
+  match arrayType with
+  | TCppScalarArray _ ->
+      List.for_all
+        (fun expr ->
+          match expr.cppexpr with
+          | CppInt _ | CppFloat _ | CppString _ | CppBool _ -> true
+          | _ -> false)
+        expressions
+  | _ -> false
+
+let list_num l = string_of_int (List.length l)
+
+(* This gets the class include order correct.  In the header files, we forward declare
+   the class types so the header file does not have any undefined variables.
+   In the cpp files, we include all the required header files, providing the actual
+   types for everything.  This way there is no problem with circular class references.
+*)
+let gen_forward_decl writer class_path isNative =
+  let output = writer#write in
+  match class_path with
+  | [ "@verbatim" ], file -> writer#write (guarded_include file)
+  | _ ->
+      let name = fst (remap_class_path class_path) in
+      output
+        ((if isNative then "HX_DECLARE_NATIVE" else "HX_DECLARE_CLASS")
+        ^ list_num name ^ "(");
+      List.iter (fun package_part -> output (package_part ^ ",")) name;
+      output (snd class_path ^ ")\n")
+
+let format_code code = String.concat "\n" (ExtString.String.nsplit code "\r\n")
+
+let get_meta_string_full_filename meta key =
+  let rec loop = function
+    | [] -> ""
+    | (k, _, pos) :: _ when k = key ->
+        if Filename.is_relative pos.pfile then
+          Path.normalize_path (Filename.concat (Sys.getcwd ()) pos.pfile)
+        else pos.pfile
+    | _ :: l -> loop l
+  in
+  loop meta
+
+let get_meta_string_full_dirname meta key =
+  let name = get_meta_string_full_filename meta key in
+  try Path.normalize_path (Filename.dirname name)
+  with Invalid_argument _ -> ""
+
+let get_code meta key =
+  let code = get_meta_string meta key |> Option.default "" in
+  let magic_var = "${GENCPP_SOURCE_DIRECTORY}" in
+  let code =
+    if ExtString.String.exists code magic_var then
+      let source_directory = get_meta_string_full_dirname meta key in
+      let _, code = ExtString.String.replace code magic_var source_directory in
+      code
+    else code
+  in
+  if code <> "" then format_code code ^ "\n" else code
+
+let get_class_code class_def key =
+  match class_def.cl_kind with
+  | KAbstractImpl abstract_def ->
+      let value = get_code abstract_def.a_meta key in
+      value
+  | _ -> get_code class_def.cl_meta key
+
+let with_debug ctx metadata run =
+  let old_debug = ctx.ctx_debug_level in
+  let no_debug = Meta.has Meta.NoDebug metadata in
+  if no_debug then ctx.ctx_debug_level <- 0;
+  run no_debug;
+  ctx.ctx_debug_level <- old_debug
+
+let hx_stack_push ctx output clazz func_name pos gc_stack =
+  if ctx.ctx_debug_level > 0 then (
+    let stripped_file = strip_file ctx.ctx_common pos.pfile in
+    let esc_file = StringHelper.s_escape stripped_file in
+    ctx.ctx_file_info := PMap.add stripped_file pos.pfile !(ctx.ctx_file_info);
+    let full_name =
+      clazz ^ "." ^ func_name
+      ^
+      if clazz = "*" then
+        " (" ^ esc_file ^ ":" ^ string_of_int (Lexer.get_error_line pos) ^ ")"
+      else ""
+    in
+
+    let hash_class_func = gen_hash 0 (clazz ^ "." ^ func_name) in
+    let hash_file = gen_hash 0 stripped_file in
+
+    let lineName = string_of_int (Lexer.get_error_line pos) in
+    incr ctx.ctx_file_id;
+    let classId = hash64 (clazz ^ "." ^ stripped_file) in
+    let varName = "_hx_pos_" ^ classId ^ "_" ^ lineName ^ "_" ^ func_name in
+    let decl =
+      varName ^ ",\"" ^ clazz ^ "\",\"" ^ func_name ^ "\"," ^ hash_class_func
+      ^ ",\"" ^ full_name ^ "\",\"" ^ esc_file ^ "\"," ^ lineName ^ ","
+      ^ hash_file
+    in
+    if ctx.ctx_is_header then
+      ctx.ctx_writer#write_h_unique
+        ("HX_DECLARE_STACK_FRAME" ^ "(" ^ varName ^ ")\n")
+    else
+      ctx.ctx_writer#write_h_unique
+        ((if func_name = "new" then "HX_DEFINE_STACK_FRAME"
+          else "HX_LOCAL_STACK_FRAME")
+        ^ "(" ^ decl ^ ")\n");
+    output
+      ((if gc_stack then "HX_GC_STACKFRAME" else "HX_STACKFRAME")
+      ^ "(&" ^ varName ^ ")\n"))
+  else if gc_stack then output "HX_JUST_GC_STACKFRAME\n"
+
+(* Add include to source code *)
+let add_include writer class_path = writer#add_include class_path
+
+let real_interfaces =
+  List.filter (function t, pl ->
+      (match (t, pl) with
+      | { cl_path = [ "cpp"; "rtti" ], _ }, [] -> false
+      | _ -> true))
+
+let native_field_name_remap is_static field =
+  let remap_name = keyword_remap field.cf_name in
+  if not is_static then remap_name
+  else
+    match get_meta_string field.cf_meta Meta.Native with
+    | Some nativeImpl ->
+        let r = Str.regexp "^[a-zA-Z_0-9]+$" in
+        if Str.string_match r remap_name 0 then "_hx_" ^ remap_name
+        else "_hx_f" ^ gen_hash 0 remap_name
+    | None -> remap_name
+
+let rec is_dynamic_accessor name acc field class_def =
+  acc ^ "_" ^ field.cf_name = name
+  && (not (List.exists (fun f -> f.cf_name = name) class_def.cl_ordered_fields))
+  &&
+  match class_def.cl_super with
+  | None -> true
+  | Some (parent, _) -> is_dynamic_accessor name acc field parent
+
+(* Builds inheritance tree, so header files can include parents defs.  *)
+let create_super_dependencies common_ctx =
+  let result = Hashtbl.create 0 in
+  let real_non_native_interfaces =
+    List.filter (function t, pl ->
+        (match (t, pl) with
+        | { cl_path = [ "cpp"; "rtti" ], _ }, [] -> false
+        | _ -> not (is_native_gen_class t)))
+  in
+  let iterator object_def =
+    match object_def with
+    | TClassDecl class_def when not (has_class_flag class_def CExtern) ->
+        let deps = ref [] in
+        (match class_def.cl_super with
+        | Some super ->
+            if not (has_class_flag (fst super) CExtern) then
+              deps := (fst super).cl_path :: !deps
+        | _ -> ());
+        List.iter
+          (fun imp ->
+            if not (has_class_flag (fst imp) CExtern) then
+              deps := (fst imp).cl_path :: !deps)
+          (real_non_native_interfaces class_def.cl_implements);
+        Hashtbl.add result class_def.cl_path !deps
+    | TEnumDecl enum_def when not (has_enum_flag enum_def EnExtern) ->
+        Hashtbl.add result enum_def.e_path []
+    | _ -> ()
+  in
+  List.iter iterator common_ctx.types;
+  result
+
+let can_inline_constructor baseCtx class_def super_deps constructor_deps =
+  match class_def.cl_constructor with
+  | Some { cf_expr = Some super_func } ->
+      let is_simple = ref true in
+      let rec check_simple e =
+        (match e.eexpr with
+        | TReturn _ -> is_simple := false
+        | TArrayDecl e when List.length e > 0 -> is_simple := false
+        | _ -> ());
+        if !is_simple then Type.iter check_simple e
+      in
+      check_simple super_func;
+      !is_simple
+      &&
+      let rec known_classes class_def so_far =
+        match class_def.cl_super with
+        | Some super -> known_classes (fst super) ((fst super).cl_path :: so_far)
+        | _ -> so_far
+      in
+      let allowed = known_classes class_def [ class_def.cl_path ] in
+      (* Check to see if all the types required by the constructor are already in the header *)
+      (* This is quite restrictive, since most classes are forward-declared *)
+      let deps, _ =
+        CppReferences.find_referenced_types_flags baseCtx (TClassDecl class_def)
+          "new" super_deps constructor_deps false false true
+      in
+      List.for_all (fun dep -> List.mem dep allowed) deps
+  | _ -> true
+
+let create_constructor_dependencies common_ctx =
+  let result = Hashtbl.create 0 in
+  List.iter
+    (fun object_def ->
+      match object_def with
+      | TClassDecl class_def when not (has_class_flag class_def CExtern) -> (
+          match class_def.cl_constructor with
+          | Some func_def -> Hashtbl.add result class_def.cl_path func_def
+          | _ -> ())
+      | _ -> ())
+    common_ctx.types;
+  result
+
+let begin_namespace output class_path =
+  List.iter
+    (fun namespace -> output ("namespace " ^ namespace ^ "{\n"))
+    (List.map keyword_remap (fst class_path))
+
+let end_namespace output class_path =
+  List.iter
+    (fun namespace -> output ("}" ^ " // end namespace " ^ namespace ^ "\n"))
+    (fst class_path)
+
+let begin_header_file output_h def_string nativeGen =
+  output_h ("#ifndef INCLUDED_" ^ def_string ^ "\n");
+  output_h ("#define INCLUDED_" ^ def_string ^ "\n\n");
+  output_h "#ifndef HXCPP_H\n";
+  if nativeGen then (
+    output_h "#ifdef HXCPP_API_LEVEL\n";
+    output_h "#include <hxcpp.h>\n";
+    output_h "#else\n";
+    output_h "#include <hx/Native.h>\n";
+    output_h "#endif\n")
+  else output_h "#include <hxcpp.h>\n";
+  output_h "#endif\n\n"
+
+let end_header_file output_h def_string =
+  output_h ("\n#endif /* INCLUDED_" ^ def_string ^ " */ \n")
+
+let cpp_tfun_signature include_names args return_type =
+  let argList = print_tfun_arg_list include_names args in
+  let returnType = type_to_string return_type in
+  "( " ^ returnType ^ " (::hx::Object::*)(" ^ argList ^ "))"
+
+exception FieldFound of tclass_field
+
+let find_class_implementation class_def name interface =
+  let rec find def =
+    List.iter
+      (fun f -> if f.cf_name = name then raise (FieldFound f))
+      def.cl_ordered_fields;
+    match def.cl_super with Some (def, _) -> find def | _ -> ()
+  in
+  try
+    find class_def;
+    abort
+      ("Could not find implementation of " ^ name ^ " in "
+      ^ join_class_path class_def.cl_path "."
+      ^ " required by "
+      ^ join_class_path interface.cl_path ".")
+      class_def.cl_pos
+  with FieldFound field -> (
+    match (follow field.cf_type, field.cf_kind) with
+    | _, Method MethDynamic -> ""
+    | TFun (args, return_type), Method _ ->
+        cpp_tfun_signature false args return_type
+    | _, _ -> "")
+
+let gen_gc_name class_path =
+  let class_name_text = join_class_path class_path "." in
+  const_char_star class_name_text
+
+(* All interfaces (and sub-interfaces) implemented *)
+let implementations class_def =
+  let implemented_hash = Hashtbl.create 0 in
+  let native_implemented = Hashtbl.create 0 in
+
+  let cpp_interface_impl_name interface =
+    "_hx_" ^ join_class_path interface.cl_path "_"
+  in
+  let iterator impl =
+    let rec descend_interface interface =
+      let intf_def = fst interface in
+      let interface_name = cpp_interface_impl_name intf_def in
+      let hash =
+        if is_native_gen_class intf_def then native_implemented
+        else implemented_hash
+      in
+      if not (Hashtbl.mem hash interface_name) then (
+        Hashtbl.replace hash interface_name intf_def;
+        List.iter descend_interface intf_def.cl_implements);
+      match intf_def.cl_super with
+      | Some (interface, params) -> descend_interface (interface, params)
+      | _ -> ()
+    in
+    descend_interface impl
+  in
+
+  List.iter iterator (real_interfaces class_def.cl_implements);
+  (implemented_hash, native_implemented)
+
+let needed_interface_functions implemented_instance_fields
+    native_implementations =
+  let have =
+    List.map (fun field -> (field.cf_name, ())) implemented_instance_fields
+    |> List.to_seq |> Hashtbl.of_seq
+  in
+  let want = ref [] in
+  Hashtbl.iter
+    (fun _ intf_def ->
+      List.iter
+        (fun field ->
+          if not (Hashtbl.mem have field.cf_name) then (
+            Hashtbl.replace have field.cf_name ();
+            want := field :: !want))
+        intf_def.cl_ordered_fields)
+    native_implementations;
+  !want
+
+let gen_cpp_ast_expression_tree ctx class_name func_name function_args
+    function_type injection tree =
+  let writer = ctx.ctx_writer in
+  let out = ctx.ctx_output in
+  let lastLine = ref (-1) in
+  let tempId = ref 0 in
+  let strq = strq ctx.ctx_common in
+
+  let spacer = if ctx.ctx_debug_level > 0 then "            \t" else "" in
+  let output_i value =
+    out spacer;
+    writer#write_i value
+  in
+
+  let output_p expr value =
+    if ctx.ctx_debug_level > 0 then (
+      let line = Lexer.get_error_line expr.cpppos in
+      let lineName = Printf.sprintf "%4d" line in
+      let macro = if line != !lastLine then "HXLINE" else "HXDLIN" in
+      out (macro ^ "(" ^ lineName ^ ")\t");
+      lastLine := line);
+    writer#write_i value
+  in
+
+  let forInjection =
+    match injection with Some inject -> inject.inj_setvar <> "" | _ -> false
+  in
+
+  let cppTree =
+    CppRetyper.expression ctx TCppVoid function_args function_type tree
+      forInjection
+  in
+  let label_name i = Printf.sprintf "_hx_goto_%i" i in
+  let class_hash = gen_hash_small 0 class_name in
+
+  let rec gen_with_injection injection expr new_line =
+    (match expr.cppexpr with
+    | CppBlock (exprs, closures, gc_stack) ->
+        writer#begin_block;
+        List.iter gen_closure closures;
+        (match injection with
+        | Some inject -> inject.inj_prologue gc_stack
+        | _ -> ());
+        let remaining = ref (List.length exprs) in
+        lastLine := Lexer.get_error_line tree.epos;
+        List.iter
+          (fun e ->
+            output_p e "";
+            (if !remaining = 1 then
+               match injection with
+               | Some inject -> out inject.inj_setvar
+               | _ -> ());
+            gen e;
+            decr remaining;
+            writer#terminate_line)
+          exprs;
+        (match injection with Some inject -> out inject.inj_tail | _ -> ());
+        out spacer;
+        if new_line then writer#end_block else writer#end_block_line
+    | CppInt i ->
+        out
+          (Printf.sprintf
+             (if i > Int32.of_int (-1000000000) && i < Int32.of_int 1000000000
+              then "%ld"
+              else "(int)%ld")
+             i)
+    | CppFloat float_as_string -> out ("((Float)" ^ float_as_string ^ ")")
+    | CppString s -> out (strq s)
+    | CppBool b -> out (if b then "true" else "false")
+    | CppNull -> out "null()"
+    | CppNil -> out "nil"
+    | CppThis ThisReal -> out "::hx::ObjectPtr<OBJ_>(this)"
+    | CppThis _ -> out "__this"
+    | CppSuper thiscall ->
+        out
+          ("::hx::ObjectPtr<super>("
+          ^ (if thiscall = ThisReal then "this" else "__this.mPtr")
+          ^ ")")
+    | CppBreak -> out "break"
+    | CppContinue -> out "continue"
+    | CppGoto label -> out ("goto " ^ label_name label)
+    | CppVarDecl (var, init) -> (
+        let name = cpp_var_name_of var in
+        (if cpp_no_debug_synbol ctx var then
+           out (cpp_var_type_of var ^ " " ^ name)
+         else
+           let dbgName = cpp_var_debug_name_of var in
+           let macro = if init = None then "HX_VAR" else "HX_VARI" in
+           let varType = cpp_macro_var_type_of var in
+           if name <> dbgName then
+             out
+               (macro ^ "_NAME( " ^ varType ^ "," ^ name ^ ",\"" ^ dbgName
+              ^ "\")")
+           else out (macro ^ "( " ^ varType ^ "," ^ name ^ ")"));
+        match init with
+        | Some init ->
+            out " = ";
+            gen init
+        | _ -> ())
+    | CppEnumIndex obj ->
+        gen obj;
+        if cpp_is_dynamic_type obj.cpptype then
+          out ".StaticCast< ::hx::EnumBase >()";
+        out "->_hx_getIndex()"
+    | CppNullAccess -> out ("::hx::Throw(" ^ strq "Null access" ^ ")")
+    | CppFunction (func, _) -> (
+        match func with
+        | FuncThis (field, _) ->
+            out ("this->" ^ cpp_member_name_of field ^ "_dyn()")
+        | FuncInstance (expr, inst, field) ->
+            gen expr;
+            out
+              ((if expr.cpptype = TCppString || inst = InstStruct then "."
+                else "->")
+              ^ cpp_member_name_of field ^ "_dyn()")
+        | FuncInterface (expr, _, field) ->
+            gen expr;
+            out ("->__Field(" ^ strq field.cf_name ^ ", ::hx::paccDynamic)")
+        | FuncStatic (clazz, _, field) -> (
+            match get_meta_string field.cf_meta Meta.Native with
+            | Some n -> out n
+            | None ->
+                out (cpp_class_name clazz);
+                out ("::" ^ cpp_member_name_of field ^ "_dyn()"))
+        | FuncExpression expr -> gen expr
+        | FuncExtern (name, isGlobal) ->
+            if isGlobal then out " ::";
+            out name
+        | FuncInternal (expr, name, _) ->
+            gen expr;
+            out ("->__Field(" ^ strq name ^ ",::hx::paccDynamic)")
+        | FuncSuper _ | FuncSuperConstruct _ ->
+            abort "Can't create super closure" expr.cpppos
+        | FuncNew _ -> abort "Can't create new closure" expr.cpppos
+        | FuncEnumConstruct _ ->
+            abort "Enum constructor outside of CppCall" expr.cpppos
+        | FuncFromStaticFunction ->
+            abort "Can't create cpp.Function.fromStaticFunction closure"
+              expr.cpppos
+        | FuncTemplate _ ->
+            abort "Can't create template function closure" expr.cpppos)
+    | CppCall (FuncInterface (expr, clazz, field), args)
+      when not (is_native_gen_class clazz) ->
+        out (cpp_class_name clazz ^ "::" ^ cpp_member_name_of field ^ "(");
+        gen expr;
+        List.iter
+          (fun arg ->
+            out ",";
+            gen arg)
+          args;
+        out ")"
+    | CppCall ((FuncStatic (_, true, field) as func), arg_list)
+    | CppCall ((FuncInstance (_, InstObjC, field) as func), arg_list) ->
+        out "[ ";
+        (match func with
+        | FuncStatic (cl, _, _) -> out (join_class_path_remap cl.cl_path "::")
+        | FuncInstance (expr, _, _) -> gen expr
+        | _ -> ());
+
+        let names = ExtString.String.nsplit field.cf_name ":" in
+        let field_name, arg_names =
+          match names with
+          | name :: args -> (name, args)
+          | _ -> die "" __LOC__ (* per nsplit specs, this should never happen *)
+        in
+        out (" " ^ field_name);
+        (try
+           match (arg_list, arg_names) with
+           | [], _ -> ()
+           | [ single_arg ], _ ->
+               out ": ";
+               gen single_arg
+           | first_arg :: args, arg_names ->
+               out ": ";
+               gen first_arg;
+               List.iter2
+                 (fun arg arg_name ->
+                   out (" " ^ arg_name ^ ": ");
+                   gen arg)
+                 args arg_names
+         with Invalid_argument _ ->
+           (* not all arguments names are known *)
+           abort
+             ("The function called here with name " ^ String.concat ":" names
+            ^ " does not contain the right amount of arguments' names as \
+               required" ^ " by the objective-c calling / naming convention:"
+            ^ " expected "
+             ^ string_of_int (List.length arg_list)
+             ^ " and found "
+             ^ string_of_int (List.length arg_names))
+             expr.cpppos);
+        out " ]"
+    | CppCall (FuncNew (TCppInst (klass, p)), args) when can_quick_alloc klass
+      ->
+        out (cpp_class_path_of klass p ^ "_obj::__alloc( HX_CTX ");
+        List.iter
+          (fun arg ->
+            out ",";
+            gen arg)
+          args;
+        out ")"
+    | CppCall (func, args) ->
+        let doCall = ref true in
+        let closeCall = ref "" in
+        let argsRef = ref args in
+        (match func with
+        | FuncThis (field, _) -> out ("this->" ^ cpp_member_name_of field)
+        | FuncInstance (expr, inst, field) ->
+            let operator =
+              if expr.cpptype = TCppString || inst = InstStruct then "."
+              else "->"
+            in
+            gen expr;
+            out (operator ^ cpp_member_name_of field)
+        | FuncInterface (expr, _, field) ->
+            gen expr;
+            out ("->" ^ cpp_member_name_of field)
+        | FuncStatic (clazz, false, field) when cpp_is_static_extension field
+          -> (
+            match args with
+            | fst :: remaining ->
+                argsRef := remaining;
+                gen fst;
+                out ("->" ^ cpp_member_name_of field)
+            | _ ->
+                abort "Native static extensions must have at least 1 argument"
+                  expr.cpppos)
+        | FuncStatic (clazz, _, field) -> (
+            match get_meta_string field.cf_meta Meta.Native with
+            | Some rename ->
+                (* This is the case if you use @:native('new foo').  c++ wil group the space undesirably *)
+                if String.contains rename ' ' then (
+                  out "(";
+                  closeCall := ")");
+                out rename
+            | None ->
+                out (cpp_class_name clazz);
+                out ("::" ^ cpp_member_name_of field))
+        | FuncTemplate (clazz, field, tpath, native) ->
+            (match get_meta_string field.cf_meta Meta.Native with
+            | Some rename ->
+                (* This is the case if you use @:native('new foo').  c++ wil group the space undesirably *)
+                if String.contains rename ' ' then (
+                  out "(";
+                  closeCall := ")");
+                out rename
+            | None ->
+                out (cpp_class_name clazz);
+                out ("::" ^ cpp_member_name_of field));
+            out ("< " ^ cpp_template_param tpath native ^ "  >")
+        | FuncFromStaticFunction ->
+            abort "Unexpected FuncFromStaticFunction" expr.cpppos
+        | FuncEnumConstruct (enum, field) ->
+            out (string_of_path enum.e_path ^ "::" ^ cpp_enum_name_of field)
+        | FuncSuperConstruct (TCppInst (klass, _)) when is_native_class klass ->
+            doCall := false
+        | FuncSuperConstruct _ ->
+            out
+              ((if not ctx.ctx_real_this_ptr then "__this->" else "")
+              ^ "super::__construct")
+        | FuncSuper (_, TCppInst (klass, p), field) when is_native_class klass
+          ->
+            out (cpp_class_path_of klass p ^ "::" ^ cpp_member_name_of field)
+        | FuncSuper (this, _, field) ->
+            out
+              ((if this == ThisReal then "this->" else "__->")
+              ^ "super::" ^ cpp_member_name_of field)
+        | FuncNew newType ->
+            let objName =
+              match newType with
+              | TCppString -> "::String"
+              | TCppDynamicArray -> "::cpp::VirtualArray_obj::__new"
+              | TCppObjectArray _ -> "::Array_obj< ::Dynamic>::__new"
+              | TCppScalarArray value ->
+                  "::Array_obj< " ^ tcpp_to_string value ^ " >::__new"
+              | TCppObjC klass -> cpp_class_path_of klass [] ^ "_obj::__new"
+              | TCppNativePointer klass -> "new " ^ cpp_class_path_of klass []
+              | TCppInst (klass, p) when is_native_class klass ->
+                  cpp_class_path_of klass p
+              | TCppInst (klass, p) -> cpp_class_path_of klass p ^ "_obj::__new"
+              | TCppClass -> "::hx::Class_obj::__new"
+              | TCppFunction _ -> tcpp_to_string newType
+              | _ ->
+                  abort
+                    ("Unknown 'new' target " ^ tcpp_to_string newType)
+                    expr.cpppos
+            in
+            out objName
+        | FuncInternal (func, name, join) ->
+            gen func;
+            out (join ^ name)
+        | FuncExtern (name, isGlobal) ->
+            if isGlobal then out " ::";
+            out name
+        | FuncExpression expr -> gen expr);
+        if !doCall then (
+          let sep = ref "" in
+          out "(";
+          List.iter
+            (fun arg ->
+              out !sep;
+              sep := ",";
+              gen arg)
+            !argsRef;
+          out (")" ^ !closeCall))
+    | CppNewNative e ->
+        out "new ";
+        gen e
+    | CppAddressOf e ->
+        out "&(";
+        gen e;
+        out ")"
+    | CppDereference e ->
+        out "(*(";
+        gen e;
+        out "))"
+    | CppFunctionAddress (klass, member) ->
+        let signature = function_signature false member.cf_type "" in
+        let name = cpp_member_name_of member in
+        (*let void_cast = has_meta_key field.cf_meta Meta.Void in*)
+        out ("::cpp::Function< " ^ signature ^ ">(::hx::AnyCast(");
+        out ("&::" ^ join_class_path_remap klass.cl_path "::" ^ "_obj::" ^ name);
+        out " ))"
+    | CppExtern (name, isGlobal) ->
+        if isGlobal then out " ::";
+        out name
+    | CppDynamicField (obj, name) ->
+        gen obj;
+        out ("->__Field(" ^ strq name ^ ",::hx::paccDynamic)")
+    | CppArray arrayLoc -> (
+        match arrayLoc with
+        | ArrayTyped (arrayObj, index, _) ->
+            gen arrayObj;
+            out "->__get(";
+            gen index;
+            out ")"
+        | ArrayPointer (arrayObj, index) ->
+            gen arrayObj;
+            out ".ptr[";
+            gen index;
+            out "]"
+        | ArrayRawPointer (arrayObj, index) ->
+            gen arrayObj;
+            out "[";
+            gen index;
+            out "]"
+        | ArrayObject (arrayObj, index, elem) ->
+            let close =
+              if cpp_is_dynamic_type elem then ""
+              else if elem = TCppDynamicArray then (
+                out (tcpp_to_string elem ^ "( ");
+                ")")
+              else ".StaticCast< " ^ tcpp_to_string elem ^ " >()"
+            in
+            gen arrayObj;
+            out "->__get(";
+            gen index;
+            out (")" ^ close)
+        | ArrayVirtual (arrayObj, index) ->
+            gen arrayObj;
+            out "->__get(";
+            gen index;
+            out ")"
+        | ArrayDynamic (arrayObj, index) ->
+            gen arrayObj;
+            out "->__GetItem(";
+            gen index;
+            out ")"
+        | ArrayImplements (_, arrayObj, index) ->
+            gen arrayObj;
+            out "->__get(";
+            gen index;
+            out ")")
+    | CppSet (lvalue, rvalue) ->
+        let close =
+          if expr.cpptype = TCppVoid then ""
+          else (
+            out "(";
+            ")")
+        in
+        (match lvalue with
+        | CppVarRef (VarClosure var)
+          when is_gc_element ctx (cpp_type_of var.v_type) ->
+            out ("this->_hx_set_" ^ cpp_var_name_of var ^ "(HX_CTX, ");
+            gen rvalue;
+            out ")"
+        | CppVarRef (VarThis (member, _))
+          when is_gc_element ctx (cpp_type_of member.cf_type) ->
+            out ("this->_hx_set_" ^ cpp_member_name_of member ^ "(HX_CTX, ");
+            gen rvalue;
+            out ")"
+        | CppVarRef (VarInstance (obj, member, _, "->"))
+          when is_gc_element ctx (cpp_type_of member.cf_type) ->
+            gen obj;
+            out ("->_hx_set_" ^ cpp_member_name_of member ^ "(HX_CTX, ");
+            gen rvalue;
+            out ")"
+        | CppVarRef (VarInternal (obj, operator, member)) ->
+            gen obj;
+            out (operator ^ member)
+        | CppVarRef varLoc ->
+            gen_val_loc varLoc true;
+            out " = ";
+            gen rvalue
+        | CppArrayRef arrayLoc -> (
+            match arrayLoc with
+            | ArrayObject (arrayObj, index, _)
+              when is_gc_element ctx TCppDynamic ->
+                gen arrayObj;
+                out "->setCtx( HX_CTX, ";
+                gen index;
+                out ",";
+                gen rvalue;
+                out ")"
+            | ArrayTyped (arrayObj, index, t) when is_gc_element ctx t ->
+                gen arrayObj;
+                out "->setCtx( HX_CTX, ";
+                gen index;
+                out ",";
+                gen rvalue;
+                out ")"
+            | ArrayObject (arrayObj, index, _)
+            | ArrayTyped (arrayObj, index, _)
+            | ArrayRawPointer (arrayObj, index) ->
+                gen arrayObj;
+                out "[";
+                gen index;
+                out "] = ";
+                gen rvalue
+            | ArrayPointer (arrayObj, index) ->
+                gen arrayObj;
+                out ".ptr[";
+                gen index;
+                out "] = ";
+                gen rvalue
+            | ArrayVirtual (arrayObj, index) ->
+                gen arrayObj;
+                out "->set(";
+                gen index;
+                out ",";
+                gen rvalue;
+                out ")"
+            | ArrayDynamic (arrayObj, index) ->
+                gen arrayObj;
+                out "->__SetItem(";
+                gen index;
+                out ",";
+                gen rvalue;
+                out ")"
+            | ArrayImplements (_, arrayObj, index) ->
+                gen arrayObj;
+                out "->__set(";
+                gen index;
+                out ",";
+                gen rvalue;
+                out ")")
+        | CppDynamicRef (expr, name) ->
+            gen expr;
+            out ("->__SetField(" ^ strq name ^ ",");
+            gen rvalue;
+            out ",::hx::paccDynamic)"
+        | CppExternRef (name, isGlobal) ->
+            if isGlobal then out " ::";
+            out (name ^ " = "));
+        out close
+    | CppCrement (incFlag, preFlag, lvalue) ->
+        let op = if incFlag == CppIncrement then "++" else "--" in
+        if preFlag == Prefix then out op;
+        gen_lvalue lvalue;
+        if preFlag == Postfix then out op
+    | CppModify (op, lvalue, rvalue) ->
+        out (string_of_op_eq op expr.cpppos);
+        out "(";
+        gen_lvalue lvalue;
+        out ",";
+        gen rvalue;
+        out ")"
+    | CppPosition (name, line, clazz, func) ->
+        out
+          ("::hx::SourceInfo(" ^ strq name ^ ","
+          ^ string_of_int (Int32.to_int line)
+          ^ "," ^ strq clazz ^ "," ^ strq func ^ ")")
+    | CppClassOf (path, native) ->
+        let path = "::" ^ join_class_path_remap path "::" in
+        let path =
+          match path with "::Int" -> "int" | "::Bool" -> "bool" | x -> x
+        in
+        if native then out "null()"
+        else if path = "::Array" then out "::hx::ArrayBase::__mClass"
+        else out ("::hx::ClassOf< " ^ path ^ " >()")
+    | CppVar loc -> gen_val_loc loc false
+    | CppClosure closure ->
+        out
+          (" ::Dynamic(new _hx_Closure_" ^ string_of_int closure.close_id ^ "(");
+        let separator = ref "" in
+        (match closure.close_this with
+        | Some this ->
+            out (if this = ThisReal then "this" else "__this");
+            separator := ","
+        | _ -> ());
+
+        Hashtbl.iter
+          (fun name value ->
+            out !separator;
+            separator := ",";
+            out (keyword_remap name))
+          closure.close_undeclared;
+        out "))"
+    | CppObjectDecl (values, isStruct) ->
+        let length = List.length values in
+        let lengthStr = string_of_int length in
+        if expr.cpptype != TCppVoid then out " ::Dynamic(";
+        if isStruct && length > 0 && length <= 5 then (
+          out
+            ("::hx::AnonStruct" ^ lengthStr ^ "_obj< "
+            ^ String.concat ","
+                (List.map
+                   (fun (_, value) -> tcpp_to_string value.cpptype)
+                   values)
+            ^ " >::Create(");
+          let sep = ref "" in
+          List.iter
+            (fun (name, value) ->
+              out (!sep ^ strq name ^ ",");
+              sep := ",";
+              gen value)
+            values;
+          out ")")
+        else (
+          out ("::hx::Anon_obj::Create(" ^ lengthStr ^ ")");
+          let sorted =
+            List.sort
+              (fun (_, _, h0) (_, _, h1) -> Int32.compare h0 h1)
+              (List.map
+                 (fun (name, value) -> (name, value, gen_hash32 0 name))
+                 values)
+          in
+          writer#push_indent;
+          ExtList.List.iteri
+            (fun idx (name, value, _) ->
+              out ("\n" ^ spacer);
+              writer#write_i
+                ("->setFixed(" ^ string_of_int idx ^ "," ^ strq name ^ ",");
+              gen value;
+              out ")")
+            sorted);
+        if expr.cpptype != TCppVoid then out ")";
+        writer#pop_indent
+    | CppArrayDecl exprList when cpp_is_const_scalar_array expr.cpptype exprList
+      ->
+        let arrayType =
+          match expr.cpptype with
+          | TCppScalarArray value -> value
+          | _ -> assert false
+        in
+        let typeName = tcpp_to_string arrayType in
+        incr ctx.ctx_file_id;
+
+        let id =
+          "_hx_array_data_" ^ class_hash ^ "_"
+          ^ string_of_int !(ctx.ctx_file_id)
+        in
+
+        let out_top = ctx.ctx_writer#write_h in
+        out_top ("static const " ^ typeName ^ " " ^ id ^ "[] = {\n\t");
+        List.iter
+          (fun expr ->
+            match expr.cppexpr with
+            | CppInt i -> out_top (Printf.sprintf "(%s)%ld," typeName i)
+            | CppFloat f -> out_top (f ^ ",")
+            | CppString s -> out_top (strq s ^ ",")
+            | CppBool b -> out_top (if b then "1," else "0,")
+            | _ -> die "" __LOC__)
+          exprList;
+        out_top "\n};\n";
+        out
+          ("::Array_obj< " ^ typeName ^ " >::fromData( " ^ id ^ ","
+         ^ list_num exprList ^ ")")
+    | CppArrayDecl exprList ->
+        let count = List.length exprList in
+        let countStr = string_of_int count in
+        let arrayType, close =
+          match expr.cpptype with
+          | TCppObjectArray _ -> ("::Array_obj< ::Dynamic>", "")
+          | TCppScalarArray value ->
+              ("::Array_obj< " ^ tcpp_to_string value ^ " >", "")
+          | TCppDynamicArray -> ("::cpp::VirtualArray_obj", "")
+          | _ -> (" ::Dynamic( ::cpp::VirtualArray_obj", ")")
+        in
+        out (arrayType ^ "::__new(" ^ countStr ^ ")");
+        ExtList.List.iteri
+          (fun idx elem ->
+            out ("->init(" ^ string_of_int idx ^ ",");
+            gen elem;
+            out ")")
+          exprList;
+        out close
+    | CppBinop (Ast.OpUShr, left, right) ->
+        out "::hx::UShr(";
+        gen left;
+        out ",";
+        gen right;
+        out ")"
+    | CppBinop (Ast.OpMod, left, right) ->
+        if is_constant_zero right then (
+          out "::hx::Mod(";
+          gen left;
+          out ",(double)( ";
+          gen right;
+          out " ))")
+        else (
+          out "::hx::Mod(";
+          gen left;
+          out ",";
+          gen right;
+          out ")")
+    | CppBinop (Ast.OpDiv, left, right) when is_constant_zero right ->
+        out "::hx::DivByZero(";
+        gen left;
+        out ")"
+    | CppBinop (op, left, right) ->
+        let op = string_of_op op expr.cpppos in
+        out "(";
+        gen left;
+        out (" " ^ op ^ " ");
+        gen right;
+        out ")"
+    | CppCompare (opName, left, right, _) ->
+        out ("::hx::" ^ opName ^ "( ");
+        gen left;
+        out ",";
+        gen right;
+        out " )"
+    | CppNullCompare (op, left) ->
+        out ("::hx::" ^ op ^ "( ");
+        gen left;
+        out " )"
+    | CppThrow value ->
+        out "HX_STACK_DO_THROW(";
+        gen value;
+        out ")"
+    | CppReturn None -> out "return"
+    | CppReturn (Some value) ->
+        out "return ";
+        gen value
+    | CppEnumField (enum, field) ->
+        out
+          (string_of_path enum.e_path ^ "::" ^ cpp_enum_name_of field ^ "_dyn()")
+    | CppEnumParameter (obj, field, index) -> (
+        let valueType = cpp_type_of (get_nth_type field index) in
+        let baseType = enum_getter_type valueType in
+        gen obj;
+        if cpp_is_dynamic_type obj.cpptype then
+          out ".StaticCast< ::hx::EnumBase >()";
+        out ("->_hx_get" ^ baseType ^ "(" ^ string_of_int index ^ ")");
+        match valueType with
+        | TCppObjectArray _ | TCppScalarArray _ | TCppDynamicArray | TCppClass
+        | TCppEnum _ | TCppInst _ ->
+            out (".StaticCast< " ^ tcpp_to_string valueType ^ " >()")
+        | _ -> ())
+    | CppIntSwitch (condition, cases, defVal) ->
+        out "switch((int)(";
+        gen condition;
+        out "))";
+        writer#begin_block;
+        List.iter
+          (fun (values, expr) ->
+            out spacer;
+            writer#write_i "";
+            List.iter
+              (fun value ->
+                out ("case (int)" ^ Printf.sprintf "%ld" value ^ ": "))
+              values;
+            gen expr;
+            out spacer;
+            writer#write_i "break;\n")
+          cases;
+        (match defVal with
+        | Some expr ->
+            output_i "default:";
+            gen expr
+        | _ -> ());
+        out spacer;
+        writer#end_block
+    | CppSwitch (condition, conditionType, cases, optional_default, label) ->
+        let tmp_name = "_hx_switch_" ^ string_of_int !tempId in
+        incr tempId;
+        out (tcpp_to_string conditionType ^ " " ^ tmp_name ^ " = ");
+        gen condition;
+        out ";\n";
+        List.iter
+          (fun (cases, expression) ->
+            output_i "if ( ";
+            let or_str = ref "" in
+            List.iter
+              (fun value ->
+                out (!or_str ^ " (" ^ tmp_name ^ "==");
+                gen value;
+                out ")";
+                or_str := " || ")
+              cases;
+            out " )";
+            gen expression)
+          cases;
+        (match optional_default with
+        | None -> ()
+        | Some default ->
+            output_i "/* default */";
+            gen default);
+        output_i (label_name label ^ ":")
+    | CppUnop (unop, value) ->
+        out
+          (match unop with CppNot -> "!" | CppNeg -> "-" | CppNegBits -> "~");
+        out "(";
+        gen value;
+        out ")"
+    | CppWhile (condition, block, while_flag, loop_id) ->
+        (match while_flag with
+        | NormalWhile ->
+            out "while(";
+            gen condition;
+            out ")";
+            lastLine := -1;
+            gen block
+        | DoWhile ->
+            out "do ";
+            lastLine := -1;
+            gen_with_injection None block false;
+            out " while(";
+            gen condition;
+            out ");\n");
+        if loop_id > -1 then output_i (label_name loop_id ^ ":")
+    | CppIf (condition, block, None) ->
+        out "if (";
+        gen condition;
+        out ") ";
+        gen block
+    | CppIf (condition, block, Some elze) when expr.cpptype = TCppVoid ->
+        out "if (";
+        gen condition;
+        out ") ";
+        gen block;
+        output_i "else ";
+        gen elze
+    | CppIf (condition, block, Some elze) ->
+        gen condition;
+        out " ? ";
+        gen block;
+        out " : ";
+        gen elze
+    | CppFor (tvar, init, loop) ->
+        let varType = cpp_var_type_of tvar in
+        out
+          ("for(::cpp::FastIterator_obj< " ^ varType
+         ^ " > *__it = ::cpp::CreateFastIterator< " ^ varType ^ " >(");
+        gen init;
+        out ");  __it->hasNext(); )";
+        let prologue _ =
+          output_i (varType ^ " " ^ cpp_var_name_of tvar ^ " = __it->next();\n")
+        in
+        gen_with_injection (mk_injection prologue "" "") loop true
+    | CppTry (block, catches) ->
+        let prologue = function
+          | _ ->
+              ExtList.List.iteri
+                (fun idx (v, _) ->
+                  output_i
+                    ("HX_STACK_CATCHABLE(" ^ cpp_macro_var_type_of v ^ ", "
+                   ^ string_of_int idx ^ ");\n"))
+                catches
+        in
+        out "try ";
+        gen_with_injection
+          (mk_injection prologue "" "")
+          block
+          (List.length catches < 0);
+        if List.length catches > 0 then (
+          out " catch( ::Dynamic _hx_e) ";
+          writer#begin_block;
+
+          let seen_dynamic = ref false in
+          let else_str = ref "" in
+          List.iter
+            (fun (v, catch) ->
+              let type_name = cpp_var_type_of v in
+              (match cpp_type_of v.v_type with
+              | TCppInterface klass ->
+                  let hash = cpp_class_hash klass in
+                  output_i
+                    (!else_str ^ "if (::hx::TIsInterface< (int)" ^ hash
+                   ^ " >(_hx_e.mPtr))")
+              | TCppString ->
+                  output_i
+                    (!else_str
+                   ^ "if (_hx_e.IsClass< ::String >() && \
+                      _hx_e->toString()!=null() )")
+              | _ ->
+                  if type_name = "Dynamic" then (
+                    seen_dynamic := true;
+                    output_i !else_str)
+                  else
+                    output_i
+                      (!else_str ^ "if (_hx_e.IsClass< " ^ type_name ^ " >() )"));
+
+              let prologue = function
+                | _ ->
+                    output_i "HX_STACK_BEGIN_CATCH\n";
+                    output_i
+                      (type_name ^ " " ^ cpp_var_name_of v ^ " = _hx_e;\n")
+              in
+              gen_with_injection (mk_injection prologue "" "") catch true;
+              else_str := "else ")
+            catches;
+
+          if not !seen_dynamic then (
+            output_i "else {\n";
+            output_i "\tHX_STACK_DO_THROW(_hx_e);\n";
+            output_i "}\n");
+          out spacer;
+          writer#end_block)
+    | CppCode (value, exprs) ->
+        Codegen.interpolate_code ctx.ctx_common (format_code value) exprs out
+          (fun e -> gen e)
+          expr.cpppos
+    | CppTCast (expr, cppType) -> (
+        match cppType with
+        | TCppInterface i ->
+            out " ::hx::interface_check(";
+            gen expr;
+            out ("," ^ cpp_class_hash i ^ ")")
+        | _ ->
+            let toType = tcpp_to_string cppType in
+            if toType = "Dynamic" then (
+              out " ::Dynamic(";
+              gen expr;
+              out ")")
+            else (
+              out ("::hx::TCast< " ^ toType ^ " >::cast(");
+              gen expr;
+              out ")"))
+    | CppCastStatic (expr, toType) ->
+        let close =
+          match expr.cpptype with
+          | TCppDynamic -> ""
+          | _ ->
+              out "Dynamic( ";
+              ")"
+        in
+        gen expr;
+        out (close ^ ".StaticCast< " ^ tcpp_to_string toType ^ " >()")
+    | CppCast (expr, toType) -> (
+        match (expr.cppexpr, expr.cpptype, toType) with
+        | CppCall (FuncInternal _, _), _, _ ->
+            gen expr;
+            out (".StaticCast< " ^ tcpp_to_string toType ^ " >()")
+        | _, TCppObjC _, _ | _, TCppObjCBlock _, _ ->
+            out ("( (" ^ tcpp_to_string toType ^ ")((id) ( ");
+            gen expr;
+            out ") ))"
+        | _, _, TCppObjectPtr ->
+            out "::hx::DynamicPtr(";
+            gen expr;
+            out ")"
+        | _, TCppPointer (_, _), TCppStar (_, _)
+        | _, TCppPointer (_, _), TCppRawPointer (_, _) ->
+            out ("( (" ^ tcpp_to_string toType ^ ")( (");
+            gen expr;
+            out ").get_raw()) )"
+        | _ ->
+            out ("( (" ^ tcpp_to_string toType ^ ")(");
+            gen expr;
+            out ") )")
+    | CppCastScalar (expr, scalar) ->
+        out ("( (" ^ scalar ^ ")(");
+        gen expr;
+        out ") )"
+    | CppCastVariant expr ->
+        out " ::Dynamic(";
+        gen expr;
+        out ")"
+    | CppCastObjC (expr, klass) ->
+        let path = join_class_path_remap klass.cl_path "::" in
+        let toType =
+          if has_class_flag klass CInterface then "id < " ^ path ^ ">"
+          else path ^ " *"
+        in
+        out ("( (" ^ toType ^ ") (id) (");
+        gen expr;
+        out ") )"
+    | CppCastObjCBlock (expr, args, ret) ->
+        out (tcpp_objc_block_struct args ret ^ "::create( ");
+        gen expr;
+        out ")"
+    | CppCastProtocol (expr, klass) ->
+        out (join_class_path_remap klass.cl_path "::" ^ "_obj::_hx_toProtocol( ");
+        gen expr;
+        out ")"
+    | CppCastNative expr ->
+        out "(";
+        gen expr;
+        out ").mPtr");
+    if ctx.ctx_debug_level >= 3 then
+      out
+        ("/* " ^ s_tcpp expr.cppexpr ^ ":" ^ tcpp_to_string expr.cpptype ^ " */")
+  and gen expr = gen_with_injection None expr true
+  and gen_lvalue lvalue =
+    match lvalue with
+    | CppVarRef varLoc -> gen_val_loc varLoc true
+    | CppArrayRef arrayLoc -> (
+        match arrayLoc with
+        | ArrayObject (arrayObj, index, _) ->
+            out "::hx::IndexRef(";
+            gen arrayObj;
+            out ".mPtr,";
+            gen index;
+            out ")"
+        | ArrayTyped (arrayObj, index, _) ->
+            gen arrayObj;
+            out "[";
+            gen index;
+            out "]"
+        | ArrayPointer (arrayObj, index) ->
+            gen arrayObj;
+            out ".ptr[";
+            gen index;
+            out "]"
+        | ArrayRawPointer (arrayObj, index) ->
+            gen arrayObj;
+            out "[";
+            gen index;
+            out "]"
+        | ArrayVirtual (arrayObj, index) | ArrayDynamic (arrayObj, index) ->
+            out "::hx::IndexRef(";
+            gen arrayObj;
+            out ".mPtr,";
+            gen index;
+            out ")"
+        | ArrayImplements (_, arrayObj, index) ->
+            out "::hx::__ArrayImplRef(";
+            gen arrayObj;
+            out ",";
+            gen index;
+            out ")")
+    | CppExternRef (name, isGlobal) ->
+        if isGlobal then out " ::";
+        out name
+    | CppDynamicRef (expr, name) ->
+        let objPtr =
+          match expr.cpptype with TCppVariant -> "getObject()" | _ -> ".mPtr"
+        in
+        out "::hx::FieldRef((";
+        gen expr;
+        out (")" ^ objPtr ^ "," ^ strq name ^ ")")
+  and gen_val_loc loc lvalue =
+    match loc with
+    | VarClosure var -> out (cpp_var_name_of var)
+    | VarLocal local -> out (cpp_var_name_of local)
+    | VarStatic (clazz, objc, member) -> (
+        match get_meta_string member.cf_meta Meta.Native with
+        | Some n -> out n
+        | None ->
+            if objc then (
+              out (join_class_path_remap clazz.cl_path "::");
+              out ("." ^ cpp_member_name_of member))
+            else (
+              out (cpp_class_name clazz);
+              out ("::" ^ cpp_member_name_of member)))
+    | VarThis (member, _) -> out ("this->" ^ cpp_member_name_of member)
+    | VarInstance (obj, member, _, operator) ->
+        gen obj;
+        out (operator ^ cpp_member_name_of member)
+    | VarInternal (obj, operator, member) ->
+        gen obj;
+        out (operator ^ member)
+    | VarInterface (obj, member) ->
+        gen obj;
+        out ("->" ^ cpp_member_name_of member ^ "_get()")
+  and string_of_op_eq op pos =
+    match op with
+    | OpAdd -> "::hx::AddEq"
+    | OpMult -> "::hx::MultEq"
+    | OpDiv -> "::hx::DivEq"
+    | OpSub -> "::hx::SubEq"
+    | OpAnd -> "::hx::AndEq"
+    | OpOr -> "::hx::OrEq"
+    | OpXor -> "::hx::XorEq"
+    | OpShl -> "::hx::ShlEq"
+    | OpShr -> "::hx::ShrEq"
+    | OpUShr -> "::hx::UShrEq"
+    | OpMod -> "::hx::ModEq"
+    | _ -> abort "Bad assign op" pos
+  and string_of_op op pos =
+    match op with
+    | OpAdd -> "+"
+    | OpMult -> "*"
+    | OpDiv -> "/"
+    | OpSub -> "-"
+    | OpEq -> "=="
+    | OpNotEq -> "!="
+    | OpGt -> ">"
+    | OpGte -> ">="
+    | OpLt -> "<"
+    | OpLte -> "<="
+    | OpAnd -> "&"
+    | OpOr -> "|"
+    | OpXor -> "^"
+    | OpBoolAnd -> "&&"
+    | OpBoolOr -> "||"
+    | OpShl -> "<<"
+    | OpShr -> ">>"
+    | OpUShr -> ">>>"
+    | OpMod -> "%"
+    | OpInterval -> "..."
+    | OpArrow -> "->"
+    | OpIn -> " in "
+    | OpNullCoal -> "??"
+    | OpAssign | OpAssignOp _ -> abort "Unprocessed OpAssign" pos
+  and gen_closure closure =
+    let argc = Hashtbl.length closure.close_undeclared in
+    let size = string_of_int argc in
+    if argc >= 62 then
+      (* Limited by c++ macro size of 128 args *)
+      abort "Too many capture variables" closure.close_expr.cpppos;
+    if argc >= 20 || List.length closure.close_args >= 20 then
+      writer#add_big_closures;
+    let argsCount = list_num closure.close_args in
+    output_i ("HX_BEGIN_LOCAL_FUNC_S" ^ size ^ "(");
+    out
+      (if closure.close_this != None then "::hx::LocalThisFunc,"
+       else "::hx::LocalFunc,");
+    out ("_hx_Closure_" ^ string_of_int closure.close_id);
+    Hashtbl.iter
+      (fun name var ->
+        out ("," ^ cpp_macro_var_type_of var ^ "," ^ keyword_remap name))
+      closure.close_undeclared;
+    out (") HXARGC(" ^ argsCount ^ ")\n");
+
+    let func_type = tcpp_to_string closure.close_type in
+    output_i
+      (func_type ^ " _hx_run(" ^ cpp_arg_list closure.close_args "__o_" ^ ")");
+
+    let prologue = function
+      | gc_stack ->
+          cpp_gen_default_values ctx closure.close_args "__o_";
+          hx_stack_push ctx output_i class_name func_name
+            closure.close_expr.cpppos gc_stack;
+          if ctx.ctx_debug_level >= 2 then (
+            if closure.close_this != None then
+              output_i "HX_STACK_THIS(__this.mPtr)\n";
+            List.iter
+              (fun (v, _) ->
+                output_i
+                  ("HX_STACK_ARG(" ^ cpp_var_name_of v ^ ",\""
+                 ^ cpp_debug_name_of v ^ "\")\n"))
+              (List.filter (cpp_debug_var_visible ctx) closure.close_args);
+
+            let line = Lexer.get_error_line closure.close_expr.cpppos in
+            let lineName = Printf.sprintf "%4d" line in
+            out ("HXLINE(" ^ lineName ^ ")\n"))
+    in
+    gen_with_injection (mk_injection prologue "" "") closure.close_expr true;
+
+    let return =
+      match closure.close_type with TCppVoid -> "(void)" | _ -> "return"
+    in
+
+    output_i ("HX_END_LOCAL_FUNC" ^ argsCount ^ "(" ^ return ^ ")\n\n")
+  in
+
+  gen_with_injection injection cppTree true
+
+let gen_cpp_init ctx dot_name func_name var_name expr =
+  let output = ctx.ctx_output in
+  let prologue = function
+    | gc_stack ->
+        let spacer =
+          if ctx.ctx_debug_level > 0 then "            \t" else "\t"
+        in
+        let output_i s = output (spacer ^ s) in
+        hx_stack_push ctx output_i dot_name func_name expr.epos gc_stack
+  in
+  let injection = mk_injection prologue var_name "" in
+  gen_cpp_ast_expression_tree ctx dot_name func_name [] t_dynamic injection
+    (mk_block expr)
+
+let generate_main_header output_main =
+  output_main "#include <hxcpp.h>\n\n";
+  output_main "#include <stdio.h>\n\n";
+  output_main "extern \"C\" void __hxcpp_main();\n\n";
+  output_main "extern \"C\" void __hxcpp_lib_main();\n\n"
+
+let generate_main_footer1 output_main = output_main "void __hxcpp_main() {\n"
+
+let generate_main_footer2 output_main =
+  output_main "\t}\n\n";
+  output_main "void __hxcpp_lib_main() {\n";
+  output_main "\tHX_TOP_OF_STACK\n";
+  output_main "\t::hx::Boot();\n";
+  output_main "\t__boot_all();\n";
+  output_main "\t__hxcpp_main();\n";
+  output_main "\t}\n"
+
+let generate_main ctx super_deps class_def =
+  let common_ctx = ctx.ctx_common in
+  (* main routine should be a single static function *)
+  let main_expression =
+    match class_def.cl_ordered_statics with
+    | [ { cf_expr = Some expression } ] -> expression
+    | _ -> die "" __LOC__
+  in
+  CppReferences.find_referenced_types ctx (TClassDecl class_def) super_deps
+    (Hashtbl.create 0) false false false
+  |> ignore;
+  let depend_referenced =
+    CppReferences.find_referenced_types ctx (TClassDecl class_def) super_deps
+      (Hashtbl.create 0) false true false
+  in
+  let generate_startup filename is_main =
+    (*make_class_directories base_dir ( "src" :: []);*)
+    let cpp_file = new_cpp_file common_ctx common_ctx.file ([], filename) in
+    let output_main = cpp_file#write in
+
+    generate_main_header cpp_file#write_h;
+
+    List.iter (add_include cpp_file) depend_referenced;
+    output_main "\n\n";
+
+    if is_main then output_main "\n#include <hx/HxcppMain.h>\n\n";
+
+    generate_main_footer1 output_main;
+
+    let ctx = file_context ctx cpp_file 1 false in
+    gen_cpp_init ctx "hxcpp" "__hxcpp_main" "" main_expression;
+
+    generate_main_footer2 output_main;
+    cpp_file#close
+  in
+  generate_startup "__main__" true;
+  generate_startup "__lib__" false
+
+let generate_dummy_main common_ctx =
+  let generate_startup filename is_main =
+    let main_file = new_cpp_file common_ctx common_ctx.file ([], filename) in
+    let output_main = main_file#write in
+    generate_main_header main_file#write_h;
+    if is_main then output_main "\n#include <hx/HxcppMain.h>\n\n";
+    generate_main_footer1 output_main;
+    generate_main_footer2 output_main;
+    main_file#close
+  in
+  generate_startup "__main__" true;
+  generate_startup "__lib__" false
+
+let generate_boot ctx boot_enums boot_classes nonboot_classes init_classes =
+  let common_ctx = ctx.ctx_common in
+  (* Write boot class too ... *)
+  let base_dir = common_ctx.file in
+  let boot_file = new_cpp_file common_ctx base_dir ([], "__boot__") in
+  let output_boot = boot_file#write in
+  boot_file#write_h "#include <hxcpp.h>\n\n";
+
+  List.iter
+    (fun class_path -> boot_file#add_include class_path)
+    (boot_enums @ boot_classes @ nonboot_classes);
+
+  let newScriptable = Common.defined common_ctx Define.Scriptable in
+  if newScriptable then (
+    output_boot "#include <hx/Scriptable.h>\n";
+    let funcs =
+      hash_iterate !(ctx.ctx_interface_slot) (fun name id -> (name, id))
+    in
+    let sorted = List.sort (fun (_, id1) (_, id2) -> id1 - id2) funcs in
+    output_boot
+      "static const char *scriptableInterfaceFuncs[] = {\n\t0,\n\t0,\n";
+    List.iter
+      (fun (name, id) ->
+        output_boot ("\t\"" ^ name ^ "\", //" ^ string_of_int (-id) ^ "\n"))
+      sorted;
+    output_boot "};\n");
+
+  output_boot "\nvoid __files__boot();\n";
+  output_boot "\nvoid __boot_all()\n{\n";
+  output_boot "__files__boot();\n";
+  output_boot "::hx::RegisterResources( ::hx::GetResources() );\n";
+  if newScriptable then
+    output_boot
+      ("::hx::ScriptableRegisterNameSlots(scriptableInterfaceFuncs,"
+      ^ string_of_int !(ctx.ctx_interface_slot_count)
+      ^ ");\n");
+
+  List.iter
+    (fun class_path ->
+      output_boot
+        ("::" ^ join_class_path_remap class_path "::" ^ "_obj::__register();\n"))
+    (boot_enums @ boot_classes @ nonboot_classes);
+
+  let dump_boot =
+    List.iter (fun class_path ->
+        output_boot
+          ("::" ^ join_class_path_remap class_path "::" ^ "_obj::__boot();\n"))
+  in
+
+  dump_boot boot_enums;
+
+  List.iter
+    (fun class_path ->
+      output_boot
+        ("::" ^ join_class_path_remap class_path "::" ^ "_obj::__init__();\n"))
+    (List.rev init_classes);
+
+  let is_cpp_class = function
+    | "cpp" :: _, _ -> true
+    | [], "EReg" -> true
+    | [ "haxe" ], "Log" -> true
+    | _ -> false
+  in
+
+  dump_boot
+    (List.filter (fun path -> is_cpp_class path) (List.rev boot_classes));
+  dump_boot
+    (List.filter (fun path -> not (is_cpp_class path)) (List.rev boot_classes));
+
+  output_boot "}\n\n";
+  boot_file#close
+
+let generate_files common_ctx file_info =
+  (* Write __files__ class too ... *)
+  let base_dir = common_ctx.file in
+  let files_file = new_cpp_file common_ctx base_dir ([], "__files__") in
+  let output_files = files_file#write in
+  let types = common_ctx.types in
+  files_file#write_h "#include <hxcpp.h>\n\n";
+  output_files "namespace hx {\n";
+  output_files "const char *__hxcpp_all_files[] = {\n";
+  output_files "#ifdef HXCPP_DEBUGGER\n";
+  List.iter
+    (fun file -> output_files (const_char_star file ^ ",\n"))
+    (List.sort String.compare (pmap_keys !file_info));
+  output_files "#endif\n";
+  output_files " 0 };\n";
+  output_files "\n";
+
+  output_files "const char *__hxcpp_all_files_fullpath[] = {\n";
+  output_files "#ifdef HXCPP_DEBUGGER\n";
+  List.iter
+    (fun file ->
+      output_files
+        (const_char_star
+           (Path.get_full_path
+              (try Common.find_file common_ctx file with Not_found -> file))
+        ^ ",\n"))
+    (List.sort String.compare (pmap_keys !file_info));
+  output_files "#endif\n";
+  output_files " 0 };\n";
+  output_files "\n";
+
+  output_files "const char *__hxcpp_all_classes[] = {\n";
+  output_files "#ifdef HXCPP_DEBUGGER\n";
+  List.iter
+    (fun object_def ->
+      match object_def with
+      | TClassDecl class_def when is_extern_class class_def -> ()
+      | TClassDecl class_def when has_class_flag class_def CInterface -> ()
+      | TClassDecl class_def ->
+          output_files
+            (const_char_star (join_class_path class_def.cl_path ".") ^ ",\n")
+      | _ -> ())
+    types;
+  output_files "#endif\n";
+  output_files " 0 };\n";
+
+  output_files "} // namespace hx\n";
+  output_files
+    "void __files__boot() { \
+     __hxcpp_set_debugger_info(::hx::__hxcpp_all_classes, \
+     ::hx::__hxcpp_all_files_fullpath); }\n";
+
+  files_file#close
+
+let gen_cpp_function_body ctx clazz is_static func_name function_def head_code
+    tail_code no_debug =
+  let output = ctx.ctx_output in
+  let dot_name = join_class_path clazz.cl_path "." in
+  if no_debug then ctx.ctx_debug_level <- 0;
+  let prologue = function
+    | gc_stack ->
+        let spacer = if no_debug then "\t" else "            \t" in
+        let output_i s = output (spacer ^ s) in
+        ctx_default_values ctx function_def.tf_args "__o_";
+        hx_stack_push ctx output_i dot_name func_name function_def.tf_expr.epos
+          gc_stack;
+        if ctx.ctx_debug_level >= 2 then (
+          if not is_static then
+            output_i
+              ("HX_STACK_THIS("
+              ^ (if ctx.ctx_real_this_ptr then "this" else "__this")
+              ^ ")\n");
+          List.iter
+            (fun (v, _) ->
+              if not (cpp_no_debug_synbol ctx v) then
+                output_i
+                  ("HX_STACK_ARG(" ^ cpp_var_name_of v ^ ",\"" ^ v.v_name
+                 ^ "\")\n"))
+            function_def.tf_args;
+
+          let line = Lexer.get_error_line function_def.tf_expr.epos in
+          let lineName = Printf.sprintf "%4d" line in
+          output ("HXLINE(" ^ lineName ^ ")\n"));
+        if head_code <> "" then output_i (head_code ^ "\n")
+  in
+  let args = List.map fst function_def.tf_args in
+
+  let injection = mk_injection prologue "" tail_code in
+  gen_cpp_ast_expression_tree ctx dot_name func_name args function_def.tf_type
+    injection
+    (mk_block function_def.tf_expr)
+
+let constructor_arg_var_list class_def =
+  match class_def.cl_constructor with
+  | Some definition -> (
+      match definition.cf_expr with
+      | Some { eexpr = TFunction function_def } ->
+          List.map
+            (fun (v, o) ->
+              (v.v_name, type_arg_to_string v.v_name o v.v_type "__o_"))
+            function_def.tf_args
+      | _ -> (
+          match follow definition.cf_type with
+          | TFun (args, _) ->
+              List.map (fun (a, _, t) -> (a, (type_to_string t, a))) args
+          | _ -> []))
+  | _ -> []
+
+let generate_constructor ctx out class_def isHeader =
+  let class_name = class_name class_def in
+  let ptr_name = class_pointer class_def in
+  let can_quick_alloc = can_quick_alloc class_def in
+  let gcName = gen_gc_name class_def.cl_path in
+  let isContainer = if has_gc_references class_def then "true" else "false" in
+  let cargs = constructor_arg_var_list class_def in
+  let constructor_type_var_list = List.map snd cargs in
+  let constructor_type_args =
+    String.concat ","
+      (List.map (fun (t, a) -> t ^ " " ^ a) constructor_type_var_list)
+  in
+  let constructor_var_list = List.map snd constructor_type_var_list in
+  let constructor_args = String.concat "," constructor_var_list in
+
+  let classScope = if isHeader then "" else class_name ^ "::" in
+  let staticHead = if isHeader then "inline static " else "" in
+  out
+    (staticHead ^ ptr_name ^ " " ^ classScope ^ "__new(" ^ constructor_type_args
+   ^ ") {\n");
+  out ("\t" ^ ptr_name ^ " __this = new " ^ class_name ^ "();\n");
+  out ("\t__this->__construct(" ^ constructor_args ^ ");\n");
+  out "\treturn __this;\n";
+  out "}\n\n";
+
+  if can_quick_alloc then (
+    out
+      (staticHead ^ ptr_name ^ " " ^ classScope ^ "__alloc(::hx::Ctx *_hx_ctx"
+      ^ (if constructor_type_args = "" then "" else "," ^ constructor_type_args)
+      ^ ") {\n");
+    out
+      ("\t" ^ class_name ^ " *__this = (" ^ class_name
+     ^ "*)(::hx::Ctx::alloc(_hx_ctx, sizeof(" ^ class_name ^ "), " ^ isContainer
+     ^ ", " ^ gcName ^ "));\n");
+    out ("\t*(void **)__this = " ^ class_name ^ "::_hx_vtable;\n");
+    let rec dump_dynamic class_def =
+      if has_dynamic_member_functions class_def then
+        out
+          ("\t"
+          ^ join_class_path_remap class_def.cl_path "::"
+          ^ "_obj::__alloc_dynamic_functions(_hx_ctx,__this);\n")
+      else
+        match class_def.cl_super with
+        | Some super -> dump_dynamic (fst super)
+        | _ -> ()
+    in
+    dump_dynamic class_def;
+
+    if isHeader then
+      match class_def.cl_constructor with
+      | Some
+          ({ cf_expr = Some { eexpr = TFunction function_def } } as definition)
+        ->
+          with_debug ctx definition.cf_meta (fun no_debug ->
+              ctx.ctx_real_this_ptr <- false;
+              gen_cpp_function_body ctx class_def false "new" function_def "" ""
+                no_debug;
+              out "\n")
+      | _ -> ()
+    else out ("\t__this->__construct(" ^ constructor_args ^ ");\n");
+
+    out "\treturn __this;\n";
+    out "}\n\n")
+
+let generate_native_constructor ctx out class_def isHeader =
+  let cargs = constructor_arg_var_list class_def in
+  let constructor_type_var_list = List.map snd cargs in
+  let constructor_type_args =
+    String.concat ","
+      (List.map (fun (t, a) -> t ^ " " ^ a) constructor_type_var_list)
+  in
+  let class_name = class_name class_def in
+
+  match class_def.cl_constructor with
+  | Some ({ cf_expr = Some { eexpr = TFunction function_def } } as definition)
+    ->
+      if isHeader then
+        out ("\t\t" ^ class_name ^ "(" ^ constructor_type_args ^ ");\n\n")
+      else
+        with_debug ctx definition.cf_meta (fun no_debug ->
+            ctx.ctx_real_this_ptr <- true;
+            out
+              (class_name ^ "::" ^ class_name ^ "(" ^ constructor_type_args
+             ^ ")");
+
+            (match class_def.cl_super with
+            | Some (klass, _) -> (
+                let rec find_super_args = function
+                  | TCall ({ eexpr = TConst TSuper }, args) :: _ -> Some args
+                  | (TParenthesis e | TMeta (_, e) | TCast (e, None)) :: rest ->
+                      find_super_args (e.eexpr :: rest)
+                  | TBlock e :: rest ->
+                      find_super_args (List.map (fun e -> e.eexpr) e @ rest)
+                  | _ :: rest -> find_super_args rest
+                  | _ -> None
+                in
+                match find_super_args [ function_def.tf_expr.eexpr ] with
+                | Some args ->
+                    out ("\n:" ^ cpp_class_path_of klass [] ^ "(");
+                    let sep = ref "" in
+                    List.iter
+                      (fun arg ->
+                        out !sep;
+                        sep := ",";
+                        gen_cpp_ast_expression_tree ctx "" "" [] t_dynamic None
+                          arg)
+                      args;
+                    out ")\n"
+                | _ -> ())
+            | _ -> ());
+
+            let head_code = get_code definition.cf_meta Meta.FunctionCode in
+            let tail_code = get_code definition.cf_meta Meta.FunctionTailCode in
+            gen_cpp_function_body ctx class_def false "new" function_def
+              head_code tail_code no_debug)
+  | _ -> ()
+
+let dynamic_functions class_def =
+  List.fold_left
+    (fun result field ->
+      match field.cf_expr with
+      | Some { eexpr = TFunction function_def }
+        when is_dynamic_haxe_method field ->
+          keyword_remap field.cf_name :: result
+      | _ -> result)
+    [] class_def.cl_ordered_fields

+ 553 - 0
src/generators/cpp/gen/cppGenClassHeader.ml

@@ -0,0 +1,553 @@
+open Ast
+open Type
+open Error
+open Common
+open Globals
+open CppStrings
+open CppExprUtils
+open CppTypeUtils
+open CppAst
+open CppAstTools
+open CppSourceWriter
+open CppContext
+open CppGen
+
+let gen_member_def ctx class_def is_static is_interface field =
+  let output = ctx.ctx_output in
+  let remap_name = keyword_remap field.cf_name in
+  let nativeGen = Meta.has Meta.NativeGen class_def.cl_meta in
+
+  if is_interface then
+    match (follow field.cf_type, field.cf_kind) with
+    | _, Method MethDynamic -> ()
+    | TFun (args, return_type), Method _ ->
+        let gen_args = print_tfun_arg_list true in
+        if is_static || nativeGen then (
+          output
+            ((if not is_static then "\t\tvirtual " else "\t\t")
+            ^ type_to_string return_type);
+          output (" " ^ remap_name ^ "( ");
+          output (gen_args args);
+          output (if not is_static then ")=0;\n" else ");\n");
+          if reflective class_def field then
+            if Common.defined ctx.ctx_common Define.DynamicInterfaceClosures
+            then
+              output
+                ("\t\tinline ::Dynamic " ^ remap_name
+               ^ "_dyn() { return __Field( "
+                ^ strq ctx.ctx_common field.cf_name
+                ^ ", ::hx::paccDynamic); }\n")
+            else output ("\t\tvirtual ::Dynamic " ^ remap_name ^ "_dyn()=0;\n"))
+        else
+          let argList = gen_args args in
+          let returnType = type_to_string return_type in
+          let returnStr = if returnType = "void" then "" else "return " in
+          let commaArgList = if argList = "" then argList else "," ^ argList in
+          let cast =
+            "::hx::interface_cast< ::"
+            ^ join_class_path_remap class_def.cl_path "::"
+            ^ "_obj *>"
+          in
+          output
+            ("\t\t" ^ returnType ^ " (::hx::Object :: *_hx_" ^ remap_name ^ ")("
+           ^ argList ^ "); \n");
+          output
+            ("\t\tstatic inline " ^ returnType ^ " " ^ remap_name
+           ^ "( ::Dynamic _hx_" ^ commaArgList ^ ") {\n");
+          output "\t\t\t#ifdef HXCPP_CHECK_POINTER\n";
+          output
+            "\t\t\tif (::hx::IsNull(_hx_)) ::hx::NullReference(\"Object\", \
+             false);\n";
+          output "\t\t\t#ifdef HXCPP_GC_CHECK_POINTER\n";
+          output "\t\t\t\tGCCheckPointer(_hx_.mPtr);\n";
+          output "\t\t\t#endif\n";
+          output "\t\t\t#endif\n";
+          output
+            ("\t\t\t" ^ returnStr ^ "(_hx_.mPtr->*( " ^ cast
+           ^ "(_hx_.mPtr->_hx_getInterface(" ^ cpp_class_hash class_def
+           ^ ")))->_hx_" ^ remap_name ^ ")(" ^ print_arg_names args
+           ^ ");\n\t\t}\n")
+    | _ -> ()
+  else
+    let nonVirtual = Meta.has Meta.NonVirtual field.cf_meta in
+    let doDynamic =
+      (nonVirtual || not (is_override field)) && reflective class_def field
+    in
+    let decl = get_meta_string field.cf_meta Meta.Decl in
+    let has_decl = match decl with Some _ -> true | None -> false in
+    if has_decl then output ("      typedef " ^ (decl |> Option.get) ^ ";\n");
+    output (if is_static then "\t\tstatic " else "\t\t");
+    match field.cf_expr with
+    | Some { eexpr = TFunction function_def } ->
+        (if is_dynamic_haxe_method field then (
+           if doDynamic then (
+             output ("::Dynamic " ^ remap_name ^ ";\n");
+             if (not is_static) && is_gc_element ctx TCppDynamic then
+               output
+                 ("\t\tinline ::Dynamic _hx_set_" ^ remap_name
+                ^ "(::hx::StackContext *_hx_ctx,::Dynamic _hx_v) { \
+                   HX_OBJ_WB(this,_hx_v.mPtr) return " ^ remap_name
+                ^ "=_hx_v; }\n");
+             output (if is_static then "\t\tstatic " else "\t\t");
+             output
+               ("inline ::Dynamic &" ^ remap_name ^ "_dyn() " ^ "{return "
+              ^ remap_name ^ "; }\n")))
+         else
+           let return_type = type_to_string function_def.tf_type in
+           (if (not is_static) && not nonVirtual then
+              let scriptable =
+                Common.defined ctx.ctx_common Define.Scriptable
+              in
+              if (not (is_internal_member field.cf_name)) && not scriptable then
+                let key =
+                  join_class_path class_def.cl_path "." ^ "." ^ field.cf_name
+                in
+                try output (Hashtbl.find ctx.ctx_class_member_types key)
+                with Not_found -> ()
+              else output "virtual ");
+           output (if return_type = "Void" then "void" else return_type);
+
+           let remap_name = native_field_name_remap is_static field in
+           output (" " ^ remap_name ^ "(");
+           output (print_arg_list function_def.tf_args "");
+           output ");\n";
+           if doDynamic then (
+             output (if is_static then "\t\tstatic " else "\t\t");
+             output ("::Dynamic " ^ remap_name ^ "_dyn();\n")));
+        output "\n"
+    | _ when has_class_field_flag field CfAbstract ->
+        let ctx_arg_list ctx arg_list prefix =
+          let get_default_value name =
+            try
+              match Meta.get Meta.Value field.cf_meta with
+              | _, [ (EObjectDecl decls, _) ], _ ->
+                  Some
+                    (List.find (fun ((n, _, _), _) -> n = name) decls
+                    |> snd
+                    |> type_constant_value ctx.ctx_common.basic)
+              | _ -> None
+            with Not_found -> None
+          in
+
+          String.concat ","
+            (List.map
+               (fun (n, o, t) -> print_arg n (get_default_value n) t prefix)
+               arg_list)
+        in
+        let tl, tr =
+          match follow field.cf_type with
+          | TFun (tl, tr) -> (tl, tr)
+          | _ -> die "" __LOC__
+        in
+        let return_type = type_to_string tr in
+        let remap_name = native_field_name_remap is_static field in
+        output "virtual ";
+        output (if return_type = "Void" then "void" else return_type);
+        output (" " ^ remap_name ^ "(");
+        output (ctx_arg_list ctx tl "");
+        output
+          (") "
+          ^ (if return_type = "void" then "{}" else "{ return 0; }")
+          ^ "\n");
+        if doDynamic then output ("\t\t::Dynamic " ^ remap_name ^ "_dyn();\n")
+    | _ when has_decl -> output (remap_name ^ "_decl " ^ remap_name ^ ";\n")
+    (* Variable access *)
+    | _ -> (
+        (* Variable access *)
+        let tcpp = cpp_type_of field.cf_type in
+        let tcppStr = tcpp_to_string tcpp in
+        if (not is_static) && only_stack_access field.cf_type then
+          abort
+            ("Variables of type " ^ tcppStr ^ " may not be used as members")
+            field.cf_pos;
+
+        output (tcppStr ^ " " ^ remap_name ^ ";\n");
+        (if (not is_static) && is_gc_element ctx tcpp then
+           let getPtr =
+             match tcpp with TCppString -> ".raw_ref()" | _ -> ".mPtr"
+           in
+           output
+             ("\t\tinline " ^ tcppStr ^ " _hx_set_" ^ remap_name
+            ^ "(::hx::StackContext *_hx_ctx," ^ tcppStr
+            ^ " _hx_v) { HX_OBJ_WB(this,_hx_v" ^ getPtr ^ ") return "
+            ^ remap_name ^ "=_hx_v; }\n"));
+
+        (* Add a "dyn" function for variable to unify variable/function access *)
+        match follow field.cf_type with
+        | _ when nativeGen -> ()
+        | TFun (_, _) ->
+            output (if is_static then "\t\tstatic " else "\t\t");
+            output
+              ("Dynamic " ^ remap_name ^ "_dyn() { return " ^ remap_name
+             ^ ";}\n")
+        | _ -> (
+            (match field.cf_kind with
+            | Var { v_read = AccCall }
+              when (not is_static)
+                   && is_dynamic_accessor ("get_" ^ field.cf_name) "get" field
+                        class_def ->
+                output ("\t\tDynamic get_" ^ field.cf_name ^ ";\n")
+            | _ -> ());
+            match field.cf_kind with
+            | Var { v_write = AccCall }
+              when (not is_static)
+                   && is_dynamic_accessor ("set_" ^ field.cf_name) "set" field
+                        class_def ->
+                output ("\t\tDynamic set_" ^ field.cf_name ^ ";\n")
+            | _ -> ()))
+
+let generate baseCtx class_def =
+  let common_ctx = baseCtx.ctx_common in
+  let class_path = class_def.cl_path in
+  let nativeGen = Meta.has Meta.NativeGen class_def.cl_meta in
+  let smart_class_name = snd class_path in
+  let scriptable =
+    Common.defined common_ctx Define.Scriptable && not class_def.cl_private
+  in
+  let class_name = class_name class_def in
+  let ptr_name = class_pointer class_def in
+  let can_quick_alloc = can_quick_alloc class_def in
+  let gcName = gen_gc_name class_def.cl_path in
+  let isContainer = if has_gc_references class_def then "true" else "false" in
+  let cargs = constructor_arg_var_list class_def in
+  let constructor_type_var_list = List.map snd cargs in
+  let constructor_type_args =
+    String.concat ","
+      (List.map (fun (t, a) -> t ^ " " ^ a) constructor_type_var_list)
+  in
+
+  (*let cpp_file = new_cpp_file common_ctx.file class_path in*)
+  let debug =
+    if
+      Meta.has Meta.NoDebug class_def.cl_meta
+      || Common.defined baseCtx.ctx_common Define.NoDebug
+    then 0
+    else 1
+  in
+
+  let h_file = new_header_file common_ctx common_ctx.file class_path in
+  let ctx = file_context baseCtx h_file debug true in
+  let strq = strq ctx.ctx_common in
+
+  let parent, super =
+    match class_def.cl_super with
+    | Some (klass, params) ->
+        let name =
+          tcpp_to_string_suffix "_obj" (cpp_instance_type klass params)
+        in
+        ( (if has_class_flag class_def CInterface && nativeGen then "virtual "
+           else "")
+          ^ name,
+          name )
+    | None when nativeGen && has_class_flag class_def CInterface ->
+        ("virtual ::hx::NativeInterface", "::hx::NativeInterface")
+    | None when has_class_flag class_def CInterface -> ("", "::hx::Object")
+    | None when nativeGen -> ("", "")
+    | None -> ("::hx::Object", "::hx::Object")
+  in
+  let output_h = h_file#write in
+  let def_string = join_class_path class_path "_" in
+
+  begin_header_file h_file#write_h def_string nativeGen;
+
+  (* Include the real header file for the super class *)
+  (match class_def.cl_super with
+  | Some super ->
+      let klass = fst super in
+      let include_files = get_all_meta_string_path klass.cl_meta Meta.Include in
+      if List.length include_files > 0 then
+        List.iter
+          (fun inc -> h_file#add_include (path_of_string inc))
+          include_files
+      else h_file#add_include klass.cl_path
+  | _ -> ());
+
+  (* And any interfaces ... *)
+  List.iter
+    (fun imp ->
+      let interface = fst imp in
+      let include_files =
+        get_all_meta_string_path interface.cl_meta Meta.Include
+      in
+      if List.length include_files > 0 then
+        List.iter
+          (fun inc -> h_file#add_include (path_of_string inc))
+          include_files
+      else h_file#add_include interface.cl_path)
+    (real_interfaces class_def.cl_implements);
+
+  (* Only need to forward-declare classes that are mentioned in the header file
+     (ie, not the implementation) *)
+  let super_deps = create_super_dependencies common_ctx in
+  let header_referenced, header_flags =
+    CppReferences.find_referenced_types_flags ctx (TClassDecl class_def) "*"
+      super_deps (Hashtbl.create 0) true false scriptable
+  in
+  List.iter2
+    (fun r f -> gen_forward_decl h_file r f)
+    header_referenced header_flags;
+  output_h "\n";
+
+  output_h (get_class_code class_def Meta.HeaderCode);
+  let includes =
+    get_all_meta_string_path class_def.cl_meta Meta.HeaderInclude
+  in
+  let printer inc = output_h ("#include \"" ^ inc ^ "\"\n") in
+  List.iter printer includes;
+
+  begin_namespace output_h class_path;
+  output_h "\n\n";
+  output_h (get_class_code class_def Meta.HeaderNamespaceCode);
+
+  let extern_class = Common.defined common_ctx Define.DllExport in
+  let attribs =
+    "HXCPP_" ^ (if extern_class then "EXTERN_" else "") ^ "CLASS_ATTRIBUTES"
+  in
+
+  let dump_native_interfaces () =
+    List.iter
+      (fun (c, params) ->
+        output_h (" , public virtual " ^ join_class_path c.cl_path "::"))
+      (List.filter
+         (fun (t, _) -> is_native_gen_class t)
+         class_def.cl_implements)
+  in
+
+  if has_class_flag class_def CInterface && not nativeGen then (
+    output_h ("class " ^ attribs ^ " " ^ class_name ^ " {\n");
+    output_h "\tpublic:\n";
+    output_h ("\t\ttypedef " ^ super ^ " super;\n"))
+  else if super = "" then (
+    output_h ("class " ^ attribs ^ " " ^ class_name);
+    dump_native_interfaces ();
+    output_h "\n{\n\tpublic:\n")
+  else (
+    output_h ("class " ^ attribs ^ " " ^ class_name ^ " : public " ^ parent);
+    dump_native_interfaces ();
+    output_h "\n{\n\tpublic:\n";
+    if not nativeGen then (
+      output_h ("\t\ttypedef " ^ super ^ " super;\n");
+      output_h ("\t\ttypedef " ^ class_name ^ " OBJ_;\n")));
+
+  let classId =
+    try Hashtbl.find baseCtx.ctx_type_ids (class_text class_def.cl_path)
+    with Not_found -> Int32.zero
+  in
+  let classIdTxt = Printf.sprintf "0x%08lx" classId in
+
+  if (not (has_class_flag class_def CInterface)) && not nativeGen then (
+    output_h ("\t\t" ^ class_name ^ "();\n");
+    output_h "\n\tpublic:\n";
+    output_h ("\t\tenum { _hx_ClassId = " ^ classIdTxt ^ " };\n\n");
+    output_h ("\t\tvoid __construct(" ^ constructor_type_args ^ ");\n");
+    output_h
+      ("\t\tinline void *operator new(size_t inSize, bool inContainer="
+     ^ isContainer ^ ",const char *inName=" ^ gcName ^ ")\n");
+    output_h
+      "\t\t\t{ return ::hx::Object::operator new(inSize,inContainer,inName); }\n";
+    output_h "\t\tinline void *operator new(size_t inSize, int extra)\n";
+    output_h
+      ("\t\t\t{ return ::hx::Object::operator new(inSize+extra," ^ isContainer
+     ^ "," ^ gcName ^ "); }\n");
+    if has_class_flag class_def CAbstract then output_h "\n"
+    else if
+      can_inline_constructor baseCtx class_def super_deps
+        (create_constructor_dependencies common_ctx)
+    then (
+      output_h "\n";
+      CppGen.generate_constructor ctx
+        (fun str -> output_h ("\t\t" ^ str))
+        class_def true)
+    else (
+      output_h
+        ("\t\tstatic " ^ ptr_name ^ " __new(" ^ constructor_type_args ^ ");\n");
+      if can_quick_alloc then
+        output_h
+          ("\t\tstatic " ^ ptr_name ^ " __alloc(::hx::Ctx *_hx_ctx"
+          ^ (if constructor_type_args = "" then ""
+             else "," ^ constructor_type_args)
+          ^ ");\n"));
+    if not (has_class_flag class_def CAbstract) then (
+      output_h "\t\tstatic void * _hx_vtable;\n";
+      output_h "\t\tstatic Dynamic __CreateEmpty();\n";
+      output_h "\t\tstatic Dynamic __Create(::hx::DynamicArray inArgs);\n");
+    if List.length (CppGen.dynamic_functions class_def) > 0 then
+      output_h
+        ("\t\tstatic void __alloc_dynamic_functions(::hx::Ctx *_hx_alloc,"
+       ^ class_name ^ " *_hx_obj);\n");
+    if scriptable then
+      output_h "\t\tstatic ::hx::ScriptFunction __script_construct;\n";
+    output_h ("\t\t//~" ^ class_name ^ "();\n\n");
+    output_h "\t\tHX_DO_RTTI_ALL;\n";
+    if has_get_member_field class_def then
+      output_h
+        "\t\t::hx::Val __Field(const ::String &inString, ::hx::PropertyAccess \
+         inCallProp);\n";
+    if has_get_static_field class_def then
+      output_h
+        "\t\tstatic bool __GetStatic(const ::String &inString, Dynamic \
+         &outValue, ::hx::PropertyAccess inCallProp);\n";
+    if has_set_member_field class_def then
+      output_h
+        "\t\t::hx::Val __SetField(const ::String &inString,const ::hx::Val \
+         &inValue, ::hx::PropertyAccess inCallProp);\n";
+    if has_set_static_field class_def then
+      output_h
+        "\t\tstatic bool __SetStatic(const ::String &inString, Dynamic \
+         &ioValue, ::hx::PropertyAccess inCallProp);\n";
+    if has_get_fields class_def then
+      output_h "\t\tvoid __GetFields(Array< ::String> &outFields);\n";
+
+    if has_compare_field class_def then
+      output_h
+        ("\t\tint __Compare(const ::hx::Object *inRHS) const { "
+       ^ "return const_cast<" ^ class_name
+       ^ " *>(this)->__compare(Dynamic((::hx::Object *)inRHS)); }\n");
+
+    output_h "\t\tstatic void __register();\n";
+    let native_gen = Meta.has Meta.NativeGen class_def.cl_meta in
+    let needs_gc_funcs = (not native_gen) && has_new_gc_references class_def in
+    if needs_gc_funcs then (
+      output_h "\t\tvoid __Mark(HX_MARK_PARAMS);\n";
+      output_h "\t\tvoid __Visit(HX_VISIT_PARAMS);\n");
+
+    let haxe_implementations, native_implementations =
+      CppGen.implementations class_def
+    in
+    let implements_haxe = Hashtbl.length haxe_implementations > 0 in
+    let implements_native = Hashtbl.length native_implementations > 0 in
+
+    if implements_native then (
+      let implemented_instance_fields =
+        List.filter should_implement_field class_def.cl_ordered_fields
+      in
+      let neededInterfaceFunctions =
+        match implements_native with
+        | true ->
+            CppGen.needed_interface_functions implemented_instance_fields
+              native_implementations
+        | false -> []
+      in
+
+      output_h "\n\t\tHX_NATIVE_IMPLEMENTATION\n";
+      List.iter
+        (fun field ->
+          match (follow field.cf_type, field.cf_kind) with
+          | _, Method MethDynamic -> ()
+          | TFun (args, return_type), _ ->
+              let retVal = type_to_string return_type in
+              let ret = if retVal = "void" then "" else "return " in
+              let name = keyword_remap field.cf_name in
+              let argNames =
+                List.map (fun (name, _, _) -> keyword_remap name) args
+              in
+              output_h
+                ("\t\t" ^ retVal ^ " " ^ name ^ "( "
+                ^ print_tfun_arg_list true args
+                ^ ") {\n");
+              output_h
+                ("\t\t\t" ^ ret ^ "super::" ^ name ^ "( "
+               ^ String.concat "," argNames ^ ");\n\t\t}\n")
+          | _ -> ())
+        neededInterfaceFunctions;
+      output_h "\n");
+
+    output_h "\t\tbool _hx_isInstanceOf(int inClassId);\n";
+    if implements_haxe then (
+      output_h "\t\tvoid *_hx_getInterface(int inHash);\n";
+      (* generate header glue *)
+      let alreadyGlued = Hashtbl.create 0 in
+      Hashtbl.iter
+        (fun interface_name src ->
+          let rec check_interface interface =
+            let check_field field =
+              match (follow field.cf_type, field.cf_kind) with
+              | _, Method MethDynamic -> ()
+              | TFun (args, return_type), Method _ ->
+                  let cast = cpp_tfun_signature false args return_type in
+                  let class_implementation =
+                    find_class_implementation class_def field.cf_name interface
+                  in
+                  let realName = cpp_member_name_of field in
+                  let castKey = realName ^ "::" ^ cast in
+                  let castKey =
+                    if interface_name = "_hx_haxe_IMap" && realName = "set" then
+                      castKey ^ "*"
+                    else castKey
+                  in
+                  let implementationKey =
+                    realName ^ "::" ^ class_implementation
+                  in
+                  if castKey <> implementationKey then
+                    let glue =
+                      Printf.sprintf "%s_%08lx" field.cf_name
+                        (gen_hash32 0 cast)
+                    in
+                    if not (Hashtbl.mem alreadyGlued castKey) then (
+                      Hashtbl.replace alreadyGlued castKey ();
+                      let argList = print_tfun_arg_list true args in
+                      let returnType = type_to_string return_type in
+                      let headerCode =
+                        "\t\t" ^ returnType ^ " " ^ glue ^ "(" ^ argList
+                        ^ ");\n"
+                      in
+                      output_h headerCode;
+                      output_h "\n")
+              | _ -> ()
+            in
+            (match interface.cl_super with
+            | Some (super, _) -> check_interface super
+            | _ -> ());
+            List.iter check_field interface.cl_ordered_fields
+          in
+          check_interface src)
+        haxe_implementations);
+
+    if has_init_field class_def then output_h "\t\tstatic void __init__();\n\n";
+    output_h
+      ("\t\t::String __ToString() const { return " ^ strq smart_class_name
+     ^ "; }\n\n"))
+  else if not nativeGen then output_h "\t\tHX_DO_INTERFACE_RTTI;\n\n"
+  else (
+    CppGen.generate_native_constructor ctx output_h class_def true;
+    (* native interface *) ());
+
+  if has_boot_field class_def then output_h "\t\tstatic void __boot();\n";
+
+  (match class_def.cl_array_access with
+  | Some t -> output_h ("\t\ttypedef " ^ type_string t ^ " __array_access;\n")
+  | _ -> ());
+
+  List.iter
+    (gen_member_def ctx class_def true (has_class_flag class_def CInterface))
+    (List.filter should_implement_field class_def.cl_ordered_statics);
+
+  let not_toString (field, args, _) =
+    field.cf_name <> "toString" || has_class_flag class_def CInterface
+  in
+  let functions = List.filter not_toString (all_virtual_functions class_def) in
+  if has_class_flag class_def CInterface then
+    List.iter
+      (fun (field, _, _) -> gen_member_def ctx class_def false true field)
+      functions
+  else
+    List.iter
+      (gen_member_def ctx class_def false false)
+      (List.filter should_implement_field class_def.cl_ordered_fields);
+
+  (if has_class_flag class_def CInterface then
+     match get_meta_string class_def.cl_meta Meta.ObjcProtocol with
+     | Some protocol ->
+         output_h
+           ("\t\tstatic id<" ^ protocol
+          ^ "> _hx_toProtocol(Dynamic inImplementation);\n")
+     | None -> ());
+
+  output_h (get_class_code class_def Meta.HeaderClassCode);
+  output_h "};\n\n";
+
+  end_namespace output_h class_path;
+
+  end_header_file output_h def_string;
+
+  h_file#close

+ 1393 - 0
src/generators/cpp/gen/cppGenClassImplementation.ml

@@ -0,0 +1,1393 @@
+open Ast
+open Type
+open Error
+open Common
+open Globals
+open CppStrings
+open CppExprUtils
+open CppTypeUtils
+open CppAst
+open CppAstTools
+open CppSourceWriter
+open CppContext
+open CppGen
+
+let gen_field ctx class_def class_name is_static field =
+  ctx.ctx_real_this_ptr <- not is_static;
+
+  let output = ctx.ctx_output in
+  let remap_name = keyword_remap field.cf_name in
+  let decl = get_meta_string field.cf_meta Meta.Decl in
+  let has_decl = match decl with Some _ -> true | None -> false in
+  match field.cf_expr with
+  (* Function field *)
+  | Some { eexpr = TFunction function_def } ->
+      let return_type_str = type_to_string function_def.tf_type in
+      let nargs = string_of_int (List.length function_def.tf_args) in
+      let return_type = cpp_type_of function_def.tf_type in
+      let is_void = return_type = TCppVoid in
+      let ret = if is_void then "(void)" else "return " in
+
+      let needsWrapper t =
+        match t with
+        | TCppStar _ -> true
+        | TCppInst (t, _) -> Meta.has Meta.StructAccess t.cl_meta
+        | _ -> false
+      in
+      let orig_debug = ctx.ctx_debug_level in
+      let no_debug = Meta.has Meta.NoDebug field.cf_meta in
+
+      if not (is_dynamic_haxe_method field) then (
+        (* The actual function definition *)
+        let remap_name = native_field_name_remap is_static field in
+        output (if is_void then "void" else return_type_str);
+        output (" " ^ class_name ^ "::" ^ remap_name ^ "(");
+        output (print_arg_list function_def.tf_args "__o_");
+        output ")";
+        ctx.ctx_real_this_ptr <- true;
+        let code = get_code field.cf_meta Meta.FunctionCode in
+        let tail_code = get_code field.cf_meta Meta.FunctionTailCode in
+
+        match get_meta_string field.cf_meta Meta.Native with
+        | Some nativeImpl when is_static ->
+            output " {\n";
+            output
+              ("\t" ^ ret ^ "::" ^ nativeImpl ^ "("
+              ^ print_arg_list_name function_def.tf_args "__o_"
+              ^ ");\n");
+            output "}\n\n"
+        | _ ->
+            gen_cpp_function_body ctx class_def is_static field.cf_name
+              function_def code tail_code no_debug;
+
+            output "\n\n";
+            let nonVirtual = Meta.has Meta.NonVirtual field.cf_meta in
+            let doDynamic =
+              (nonVirtual || not (is_override field))
+              && reflective class_def field
+            in
+            (* generate dynamic version too ... *)
+            if doDynamic then
+              let tcpp_args =
+                List.map
+                  (fun (v, _) -> cpp_type_of v.v_type)
+                  function_def.tf_args
+              in
+              let wrap =
+                needsWrapper return_type || List.exists needsWrapper tcpp_args
+              in
+              if wrap then (
+                let wrapName = "_hx_wrap" ^ class_name ^ "_" ^ remap_name in
+                output ("static ::Dynamic " ^ wrapName ^ "( ");
+                let sep = ref " " in
+                if not is_static then (
+                  output "::hx::Object *obj";
+                  sep := ",");
+                ExtList.List.iteri
+                  (fun i _ ->
+                    output (!sep ^ "const Dynamic &a" ^ string_of_int i);
+                    sep := ",")
+                  tcpp_args;
+                output ") {\n\t";
+                (if not is_void then
+                   match return_type with
+                   | TCppStar _ -> output "return (cpp::Pointer<const void *>) "
+                   | TCppInst (t, _) when Meta.has Meta.StructAccess t.cl_meta
+                     ->
+                       output
+                         ("return (cpp::Struct< " ^ tcpp_to_string return_type
+                        ^ " >) ")
+                   | _ -> output "return ");
+
+                if is_static then output (class_name ^ "::" ^ remap_name ^ "(")
+                else
+                  output
+                    ("reinterpret_cast< " ^ class_name ^ " *>(obj)->"
+                   ^ remap_name ^ "(");
+
+                sep := "";
+                ExtList.List.iteri
+                  (fun i arg ->
+                    output !sep;
+                    sep := ",";
+                    (match arg with
+                    | TCppStar (t, const) ->
+                        output
+                          ("(cpp::"
+                          ^ (if const then "Const" else "")
+                          ^ "Pointer<" ^ tcpp_to_string t ^ " >) ")
+                    | TCppInst (t, _) when Meta.has Meta.StructAccess t.cl_meta
+                      ->
+                        output ("(cpp::Struct< " ^ tcpp_to_string arg ^ " >) ")
+                    | _ -> ());
+                    output ("a" ^ string_of_int i))
+                  tcpp_args;
+
+                output ");\n";
+
+                if is_void then output "\treturn null();\n";
+                output "}\n";
+                let nName = string_of_int (List.length tcpp_args) in
+                output
+                  ("::Dynamic " ^ class_name ^ "::" ^ remap_name
+                 ^ "_dyn() {\n\treturn ");
+                if is_static then
+                  output
+                    ("::hx::CreateStaticFunction" ^ nName ^ "(\"" ^ remap_name
+                   ^ "\"," ^ wrapName ^ ");")
+                else
+                  output
+                    ("::hx::CreateMemberFunction" ^ nName ^ "(\"" ^ remap_name
+                   ^ "\",this," ^ wrapName ^ ");");
+                output "}\n")
+              else (
+                if is_static then output "STATIC_";
+                output
+                  ("HX_DEFINE_DYNAMIC_FUNC" ^ nargs ^ "(" ^ class_name ^ ","
+                 ^ remap_name ^ "," ^ ret ^ ")\n\n")))
+      else (
+        ctx.ctx_real_this_ptr <- false;
+        let func_name = "__default_" ^ remap_name in
+        output ("HX_BEGIN_DEFAULT_FUNC(" ^ func_name ^ "," ^ class_name ^ ")\n");
+        output return_type_str;
+        output
+          (" _hx_run(" ^ print_arg_list function_def.tf_args "__o_" ^ ")");
+        gen_cpp_function_body ctx class_def is_static func_name function_def ""
+          "" no_debug;
+
+        output ("HX_END_LOCAL_FUNC" ^ nargs ^ "(" ^ ret ^ ")\n");
+        output "HX_END_DEFAULT_FUNC\n\n";
+
+        if is_static then
+          output ("::Dynamic " ^ class_name ^ "::" ^ remap_name ^ ";\n\n"));
+      ctx.ctx_debug_level <- orig_debug
+  (* Data field *)
+  | _ when has_decl ->
+      if is_static then (
+        output (class_name ^ "::" ^ remap_name ^ "_decl ");
+        output (" " ^ class_name ^ "::" ^ remap_name ^ ";\n\n"))
+  | _ ->
+      if is_static && is_physical_field field then (
+        gen_type ctx field.cf_type;
+        output (" " ^ class_name ^ "::" ^ remap_name ^ ";\n\n"))
+      else if has_class_field_flag field CfAbstract then
+        let tl, tr =
+          match follow field.cf_type with
+          | TFun (tl, tr) -> (tl, tr)
+          | _ -> die "" __LOC__
+        in
+        let nargs = string_of_int (List.length tl) in
+        let return_type = cpp_type_of tr in
+        let is_void = return_type = TCppVoid in
+        let ret = if is_void then "(void)" else "return " in
+        output
+          ("HX_DEFINE_DYNAMIC_FUNC" ^ nargs ^ "(" ^ class_name ^ ","
+         ^ remap_name ^ "," ^ ret ^ ")\n\n")
+
+let gen_field_init ctx class_def field =
+  let dot_name = join_class_path class_def.cl_path "." in
+  let output = ctx.ctx_output in
+  let remap_name = keyword_remap field.cf_name in
+
+  match field.cf_expr with
+  (* Function field *)
+  | Some { eexpr = TFunction function_def } ->
+      if is_dynamic_haxe_method field then
+        let func_name = "__default_" ^ remap_name in
+        output ("\t" ^ remap_name ^ " = new " ^ func_name ^ ";\n\n")
+  (* Data field *)
+  | Some expr ->
+      let var_name =
+        match remap_name with
+        | "__meta__" -> "__mClass->__meta__"
+        | "__rtti" -> "__mClass->__rtti__"
+        | _ -> remap_name
+      in
+
+      gen_cpp_init ctx dot_name "boot" (var_name ^ " = ") expr
+  | _ -> ()
+
+let cpp_get_interface_slot ctx name =
+  try Hashtbl.find !(ctx.ctx_interface_slot) name
+  with Not_found ->
+    let result = !(ctx.ctx_interface_slot_count) in
+    Hashtbl.replace !(ctx.ctx_interface_slot) name result;
+    ctx.ctx_interface_slot_count := !(ctx.ctx_interface_slot_count) + 1;
+    result
+
+let generate_protocol_delegate ctx class_def output =
+  let protocol =
+    get_meta_string class_def.cl_meta Meta.ObjcProtocol |> Option.default ""
+  in
+  let full_class_name =
+    ("::" ^ join_class_path_remap class_def.cl_path "::") ^ "_obj"
+  in
+  let name = "_hx_" ^ protocol ^ "_delegate" in
+  output ("@interface " ^ name ^ " : NSObject<" ^ protocol ^ "> {\n");
+  output "\t::hx::Object *haxeObj;\n";
+  output "}\n";
+  output "@end\n\n";
+  output ("@implementation " ^ name ^ "\n");
+  output "- (id)initWithImplementation:( ::hx::Object *)inInplemnetation {\n";
+  output "  if (self = [super init]) {\n";
+  output "     self->haxeObj = inInplemnetation;\n";
+  output "     GCAddRoot(&self->haxeObj);\n";
+  output "  }\n";
+  output "  return self;\n";
+  output "}\n";
+  output "- (void)dealloc {\n";
+  output "   GCRemoveRoot(&self->haxeObj);\n";
+  output "   #ifndef OBJC_ARC\n";
+  output "   [super dealloc];\n";
+  output "   #endif\n";
+  output "}\n\n";
+
+  let dump_delegate field =
+    match field.cf_type with
+    | TFun (args, ret) ->
+        let retStr = type_to_string ret in
+        let fieldName, argNames =
+          match get_meta_string field.cf_meta Meta.ObjcProtocol with
+          | Some nativeName ->
+              let parts = ExtString.String.nsplit nativeName ":" in
+              (List.hd parts, parts)
+          | None -> (field.cf_name, List.map (fun (n, _, _) -> n) args)
+        in
+        output ("- (" ^ retStr ^ ") " ^ fieldName);
+
+        let first = ref true in
+        (try
+           List.iter2
+             (fun (name, _, argType) signature_name ->
+               if !first then
+                 output (" :(" ^ type_to_string argType ^ ")" ^ name)
+               else
+                 output
+                   (" " ^ signature_name ^ ":(" ^ type_to_string argType ^ ")"
+                  ^ name);
+               first := false)
+             args argNames
+         with Invalid_argument _ ->
+           abort
+             (let argString =
+                String.concat "," (List.map (fun (name, _, _) -> name) args)
+              in
+              "Invalid arg count in delegate in " ^ field.cf_name ^ " '"
+              ^ field.cf_name ^ "," ^ argString ^ "' != '"
+              ^ String.concat "," argNames ^ "'")
+             field.cf_pos);
+        output " {\n";
+        output "\t::hx::NativeAttach _hx_attach;\n";
+        output
+          ((if retStr = "void" then "\t" else "\treturn ")
+          ^ full_class_name ^ "::"
+          ^ keyword_remap field.cf_name
+          ^ "(haxeObj");
+        List.iter (fun (name, _, _) -> output ("," ^ name)) args;
+        output ");\n}\n\n"
+    | _ -> ()
+  in
+  List.iter dump_delegate class_def.cl_ordered_fields;
+
+  output "@end\n\n"
+
+let generate baseCtx class_def =
+  let common_ctx = baseCtx.ctx_common in
+  let class_path = class_def.cl_path in
+  let debug = baseCtx.ctx_debug_level in
+  let cpp_file = new_placed_cpp_file baseCtx.ctx_common class_path in
+  let cpp_ctx = file_context baseCtx cpp_file debug false in
+  let ctx = cpp_ctx in
+  let output_cpp = cpp_file#write in
+  let strq = strq ctx.ctx_common in
+  let scriptable =
+    Common.defined common_ctx Define.Scriptable && not class_def.cl_private
+  in
+
+  let class_super_name =
+    match class_def.cl_super with
+    | Some (klass, params) ->
+        tcpp_to_string_suffix "_obj" (cpp_instance_type klass params)
+    | _ -> ""
+  in
+  if debug > 1 then
+    print_endline
+      ("Found class definition:" ^ join_class_path class_def.cl_path "::");
+
+  cpp_file#write_h "#include <hxcpp.h>\n\n";
+
+  let constructor_deps = create_constructor_dependencies common_ctx in
+  let super_deps = create_super_dependencies common_ctx in
+  let all_referenced =
+    CppReferences.find_referenced_types ctx (TClassDecl class_def) super_deps
+      constructor_deps false false scriptable
+  in
+  List.iter (add_include cpp_file) all_referenced;
+
+  if scriptable then cpp_file#write_h "#include <hx/Scriptable.h>\n";
+
+  cpp_file#write_h "\n";
+
+  output_cpp (get_class_code class_def Meta.CppFileCode);
+  let includes = get_all_meta_string_path class_def.cl_meta Meta.CppInclude in
+  let printer inc = output_cpp ("#include \"" ^ inc ^ "\"\n") in
+  List.iter printer includes;
+
+  begin_namespace output_cpp class_path;
+  output_cpp "\n";
+
+  output_cpp (get_class_code class_def Meta.CppNamespaceCode);
+
+  let nativeGen = Meta.has Meta.NativeGen class_def.cl_meta in
+  let class_name = class_name class_def in
+  let cargs = constructor_arg_var_list class_def in
+  let constructor_type_var_list = List.map snd cargs in
+  let constructor_var_list = List.map snd constructor_type_var_list in
+  let constructor_type_args =
+    String.concat ","
+      (List.map (fun (t, a) -> t ^ " " ^ a) constructor_type_var_list)
+  in
+  let haxe_implementations, native_implementations =
+    implementations class_def
+  in
+
+  if (not (has_class_flag class_def CInterface)) && not nativeGen then (
+    output_cpp
+      ("void " ^ class_name ^ "::__construct(" ^ constructor_type_args ^ ")");
+    (match class_def.cl_constructor with
+    | Some ({ cf_expr = Some { eexpr = TFunction function_def } } as definition)
+      ->
+        with_debug ctx definition.cf_meta (fun no_debug ->
+            gen_cpp_function_body ctx class_def false "new" function_def "" ""
+              no_debug;
+            output_cpp "\n")
+    | _ -> output_cpp " { }\n\n");
+
+    (* Destructor goes in the cpp file so we can "see" the full definition of the member vars *)
+    if not (has_class_flag class_def CAbstract) then (
+      let ptr_name = class_pointer class_def in
+      let array_arg_list inList =
+        (* Convert an array to a comma separated list of values *)
+        let i = ref (0 - 1) in
+        String.concat ","
+          (List.map
+             (fun _ ->
+               incr i;
+               "inArgs[" ^ string_of_int !i ^ "]")
+             inList)
+      in
+
+      output_cpp
+        ("Dynamic " ^ class_name ^ "::__CreateEmpty() { return new "
+       ^ class_name ^ "; }\n\n");
+      output_cpp ("void *" ^ class_name ^ "::_hx_vtable = 0;\n\n");
+
+      output_cpp
+        ("Dynamic " ^ class_name ^ "::__Create(::hx::DynamicArray inArgs)\n");
+      output_cpp
+        ("{\n\t" ^ ptr_name ^ " _hx_result = new " ^ class_name ^ "();\n");
+      output_cpp
+        ("\t_hx_result->__construct("
+        ^ array_arg_list constructor_var_list
+        ^ ");\n");
+      output_cpp "\treturn _hx_result;\n}\n\n");
+    let rec addParent cls others =
+      match cls.cl_super with
+      | Some (super, _) -> (
+          try
+            let parentId =
+              Hashtbl.find ctx.ctx_type_ids (class_text super.cl_path)
+            in
+            addParent super (parentId :: others)
+          with Not_found -> others)
+      | _ -> others
+    in
+    let classId =
+      try Hashtbl.find baseCtx.ctx_type_ids (class_text class_def.cl_path)
+      with Not_found -> Int32.zero
+    in
+    let implemented_classes = addParent class_def [ classId; Int32.of_int 1 ] in
+    let implemented_classes = List.sort compare implemented_classes in
+
+    output_cpp ("bool " ^ class_name ^ "::_hx_isInstanceOf(int inClassId) {\n");
+    let txt cId = Printf.sprintf "0x%08lx" cId in
+    let rec dump_classes indent classes =
+      match classes with
+      | [] -> ()
+      | [ c ] -> output_cpp (indent ^ "return inClassId==(int)" ^ txt c ^ ";\n")
+      | [ c; c1 ] ->
+          output_cpp
+            (indent ^ "return inClassId==(int)" ^ txt c ^ " || inClassId==(int)"
+           ^ txt c1 ^ ";\n")
+      | _ ->
+          let len = List.length classes in
+          let mid = List.nth classes (len / 2) in
+          let low, high = List.partition (fun e -> e <= mid) classes in
+          output_cpp (indent ^ "if (inClassId<=(int)" ^ txt mid ^ ") {\n");
+          dump_classes (indent ^ "\t") low;
+          output_cpp (indent ^ "} else {\n");
+          dump_classes (indent ^ "\t") high;
+          output_cpp (indent ^ "}\n")
+    in
+    dump_classes "\t" implemented_classes;
+    output_cpp "}\n\n";
+
+    let implements_haxe_keys = hash_keys haxe_implementations in
+    let implements_haxe = Hashtbl.length haxe_implementations > 0 in
+
+    if implements_haxe then (
+      let alreadyGlued = Hashtbl.create 0 in
+      let cname = "_hx_" ^ join_class_path class_def.cl_path "_" in
+      let implname = cpp_class_name class_def in
+      let cpp_glue = ref [] in
+      List.iter
+        (fun interface_name ->
+          try
+            let interface = Hashtbl.find haxe_implementations interface_name in
+            output_cpp
+              ("static " ^ cpp_class_name interface ^ " " ^ cname ^ "_"
+             ^ interface_name ^ "= {\n");
+            let rec gen_interface_funcs interface =
+              let gen_field field =
+                match (follow field.cf_type, field.cf_kind) with
+                | _, Method MethDynamic -> ()
+                | TFun (args, return_type), Method _ ->
+                    let cast = cpp_tfun_signature false args return_type in
+                    let class_implementation =
+                      find_class_implementation class_def field.cf_name
+                        interface
+                    in
+                    let realName = cpp_member_name_of field in
+                    let castKey = realName ^ "::" ^ cast in
+                    (* C++ can't work out which function it needs to take the addrss of
+                       when the implementation is overloaded - currently the map-set functions.
+                       Change the castKey to force a glue function in this case (could double-cast the pointer, but it is ugly)
+                    *)
+                    let castKey =
+                      if interface_name = "_hx_haxe_IMap" && realName = "set"
+                      then castKey ^ "*"
+                      else castKey
+                    in
+                    let implementationKey =
+                      realName ^ "::" ^ class_implementation
+                    in
+                    if castKey <> implementationKey then (
+                      let glue =
+                        Printf.sprintf "%s_%08lx" field.cf_name
+                          (gen_hash32 0 cast)
+                      in
+                      if not (Hashtbl.mem alreadyGlued castKey) then (
+                        Hashtbl.replace alreadyGlued castKey ();
+                        let argList = print_tfun_arg_list true args in
+                        let returnType = type_to_string return_type in
+                        let returnStr =
+                          if returnType = "void" then "" else "return "
+                        in
+                        let cppCode =
+                          returnType ^ " " ^ class_name ^ "::" ^ glue ^ "("
+                          ^ argList ^ ") {\n" ^ "\t\t\t" ^ returnStr ^ realName
+                          ^ "(" ^ print_arg_names args ^ ");\n}\n"
+                        in
+                        (* let headerCode = "\t\t" ^ returnType ^ " " ^ glue ^ "(" ^ argList ^ ");\n" in *)
+                        (* header_glue := headerCode :: !header_glue; *)
+                        cpp_glue := cppCode :: !cpp_glue);
+                      output_cpp
+                        ("\t" ^ cast ^ "&" ^ implname ^ "::" ^ glue ^ ",\n"))
+                    else
+                      output_cpp
+                        ("\t" ^ cast ^ "&" ^ implname ^ "::" ^ realName ^ ",\n")
+                | _ -> ()
+              in
+              (match interface.cl_super with
+              | Some super -> gen_interface_funcs (fst super)
+              | _ -> ());
+              List.iter gen_field interface.cl_ordered_fields
+            in
+            gen_interface_funcs interface;
+            output_cpp "};\n\n"
+          with Not_found -> ())
+        implements_haxe_keys;
+
+      output_cpp (String.concat "\n" !cpp_glue);
+
+      output_cpp ("void *" ^ class_name ^ "::_hx_getInterface(int inHash) {\n");
+      output_cpp "\tswitch(inHash) {\n";
+      List.iter
+        (fun interface_name ->
+          try
+            let interface = Hashtbl.find haxe_implementations interface_name in
+            output_cpp
+              ("\t\tcase (int)" ^ cpp_class_hash interface ^ ": return &"
+             ^ cname ^ "_" ^ interface_name ^ ";\n")
+          with Not_found -> ())
+        implements_haxe_keys;
+
+      output_cpp "\t}\n";
+
+      if class_super_name = "" then (
+        output_cpp "\t#ifdef HXCPP_SCRIPTABLE\n";
+        output_cpp "\treturn super::_hx_getInterface(inHash);\n";
+        output_cpp "\t#else\n";
+        output_cpp "\treturn 0;\n";
+        output_cpp "\t#endif\n")
+      else output_cpp "\treturn super::_hx_getInterface(inHash);\n";
+      output_cpp "}\n\n"));
+
+  (match TClass.get_cl_init class_def with
+  | Some expression ->
+      let ctx = file_context baseCtx cpp_file debug false in
+      output_cpp ("void " ^ class_name ^ "::__init__()");
+      gen_cpp_init ctx (cpp_class_name class_def) "__init__" ""
+        (mk_block expression);
+      output_cpp "\n\n"
+  | _ -> ());
+
+  let dump_field_name field = output_cpp ("\t" ^ strq field.cf_name ^ ",\n") in
+  let statics_except_meta = statics_except_meta class_def in
+  let implemented_fields =
+    List.filter should_implement_field statics_except_meta
+  in
+
+  List.iter
+    (gen_field ctx class_def class_name false)
+    class_def.cl_ordered_fields;
+  List.iter (gen_field ctx class_def class_name true) statics_except_meta;
+  output_cpp "\n";
+
+  let dynamic_functions = dynamic_functions class_def in
+  if List.length dynamic_functions > 0 then (
+    output_cpp
+      ("void " ^ class_name ^ "::__alloc_dynamic_functions(::hx::Ctx *_hx_ctx,"
+     ^ class_name ^ " *_hx_obj) {\n");
+    List.iter
+      (fun name ->
+        output_cpp
+          ("\tif (!_hx_obj->" ^ name ^ ".mPtr) _hx_obj->" ^ name
+         ^ " = new __default_" ^ name ^ "(_hx_obj);\n"))
+      dynamic_functions;
+    (match class_def.cl_super with
+    | Some super ->
+        let rec find_super class_def =
+          if has_dynamic_member_functions class_def then
+            let super_name =
+              join_class_path_remap class_def.cl_path "::" ^ "_obj"
+            in
+            output_cpp
+              ("\t" ^ super_name
+             ^ "::__alloc_dynamic_functions(_hx_ctx,_hx_obj);\n")
+          else
+            match class_def.cl_super with
+            | Some super -> find_super (fst super)
+            | _ -> ()
+        in
+        find_super (fst super)
+    | _ -> ());
+    output_cpp "}\n");
+
+  let inline_constructor =
+    can_inline_constructor baseCtx class_def super_deps
+      (create_constructor_dependencies common_ctx)
+  in
+  if
+    (not (has_class_flag class_def CInterface))
+    && (not nativeGen) && (not inline_constructor)
+    && not (has_class_flag class_def CAbstract)
+  then generate_constructor ctx output_cpp class_def false
+  else if nativeGen then
+    generate_native_constructor ctx output_cpp class_def false;
+
+  let reflect_member_fields =
+    List.filter (reflective class_def) class_def.cl_ordered_fields
+  in
+  let reflect_static_fields =
+    List.filter (reflective class_def) statics_except_meta
+  in
+
+  (* Initialise non-static variables *)
+  if (not (has_class_flag class_def CInterface)) && not nativeGen then (
+    output_cpp (class_name ^ "::" ^ class_name ^ "()\n{\n");
+    List.iter
+      (fun name ->
+        output_cpp ("\t" ^ name ^ " = new __default_" ^ name ^ "(this);\n"))
+      dynamic_functions;
+    output_cpp "}\n\n";
+
+    let dump_field_iterator macro field =
+      if is_data_member field then (
+        let remap_name = keyword_remap field.cf_name in
+        output_cpp
+          ("\t" ^ macro ^ "(" ^ remap_name ^ ",\"" ^ field.cf_name ^ "\");\n");
+
+        (match field.cf_kind with
+        | Var { v_read = AccCall }
+          when is_dynamic_accessor ("get_" ^ field.cf_name) "get" field
+                 class_def ->
+            let name = "get_" ^ field.cf_name in
+            output_cpp ("\t" ^ macro ^ "(" ^ name ^ "," ^ "\"" ^ name ^ "\");\n")
+        | _ -> ());
+        match field.cf_kind with
+        | Var { v_write = AccCall }
+          when is_dynamic_accessor ("set_" ^ field.cf_name) "set" field
+                 class_def ->
+            let name = "set_" ^ field.cf_name in
+            output_cpp ("\t" ^ macro ^ "(" ^ name ^ "," ^ "\"" ^ name ^ "\");\n")
+        | _ -> ())
+    in
+
+    let implemented_instance_fields =
+      List.filter should_implement_field class_def.cl_ordered_fields
+    in
+
+    let override_iteration =
+      (not nativeGen) && has_new_gc_references class_def
+    in
+    if override_iteration then (
+      let super_needs_iteration = find_next_super_iteration class_def in
+      let smart_class_name = snd class_path in
+      (* MARK function - explicitly mark all child pointers *)
+      output_cpp ("void " ^ class_name ^ "::__Mark(HX_MARK_PARAMS)\n{\n");
+      output_cpp ("\tHX_MARK_BEGIN_CLASS(" ^ smart_class_name ^ ");\n");
+      List.iter
+        (dump_field_iterator "HX_MARK_MEMBER_NAME")
+        implemented_instance_fields;
+      (match super_needs_iteration with
+      | "" -> ()
+      | super -> output_cpp ("\t" ^ super ^ "::__Mark(HX_MARK_ARG);\n"));
+      output_cpp "\tHX_MARK_END_CLASS();\n";
+      output_cpp "}\n\n";
+
+      (* Visit function - explicitly visit all child pointers *)
+      output_cpp ("void " ^ class_name ^ "::__Visit(HX_VISIT_PARAMS)\n{\n");
+      List.iter
+        (dump_field_iterator "HX_VISIT_MEMBER_NAME")
+        implemented_instance_fields;
+      (match super_needs_iteration with
+      | "" -> ()
+      | super -> output_cpp ("\t" ^ super ^ "::__Visit(HX_VISIT_ARG);\n"));
+      output_cpp "}\n\n");
+
+    let dump_quick_field_test fields =
+      if List.length fields > 0 then (
+        let len = function _, l, _ -> l in
+        let sfields = List.sort (fun f1 f2 -> len f1 - len f2) fields in
+        let len_case = ref (-1) in
+        output_cpp "\tswitch(inName.length) {\n";
+        List.iter
+          (fun (field, l, result) ->
+            if l <> !len_case then (
+              if !len_case >= 0 then output_cpp "\t\tbreak;\n";
+              output_cpp ("\tcase " ^ string_of_int l ^ ":\n");
+              len_case := l);
+            output_cpp
+              ("\t\tif (HX_FIELD_EQ(inName,\""
+              ^ StringHelper.s_escape field
+              ^ "\") ) { " ^ result ^ " }\n"))
+          sfields;
+        output_cpp "\t}\n")
+    in
+
+    let checkPropCall field =
+      if
+        Meta.has Meta.NativeProperty class_def.cl_meta
+        || Meta.has Meta.NativeProperty field.cf_meta
+        || Common.defined common_ctx Define.ForceNativeProperty
+      then "inCallProp != ::hx::paccNever"
+      else "inCallProp == ::hx::paccAlways"
+    in
+
+    let toCommon t f value =
+      t ^ "( "
+      ^ (match cpp_type_of f.cf_type with
+        | TCppInst (t, _) as inst when Meta.has Meta.StructAccess t.cl_meta ->
+            "cpp::Struct< " ^ tcpp_to_string inst ^ " >( " ^ value ^ " )"
+        | TCppStar (t, _) -> "cpp::Pointer<void *>( " ^ value ^ " )"
+        | _ -> value)
+      ^ " )"
+    in
+    let toVal f value = toCommon "::hx::Val" f value in
+    let toDynamic f value = toCommon "" f value in
+
+    if has_get_member_field class_def then (
+      (* Dynamic "Get" Field function - string version *)
+      output_cpp
+        ("::hx::Val " ^ class_name
+       ^ "::__Field(const ::String &inName,::hx::PropertyAccess inCallProp)\n\
+          {\n");
+      let get_field_dat =
+        List.map (fun f ->
+            ( f.cf_name,
+              String.length f.cf_name,
+              match f.cf_kind with
+              | Var { v_read = AccCall } when not (is_physical_field f) ->
+                  "if (" ^ checkPropCall f ^ ") return "
+                  ^ toVal f (keyword_remap ("get_" ^ f.cf_name) ^ "()")
+                  ^ ";"
+              | Var { v_read = AccCall } ->
+                  "return "
+                  ^ toVal f
+                      (checkPropCall f ^ " ? "
+                      ^ keyword_remap ("get_" ^ f.cf_name)
+                      ^ "() : " ^ keyword_remap f.cf_name
+                      ^ if variable_field f then "" else "_dyn()")
+                  ^ ";"
+              | _ ->
+                  "return "
+                  ^ toVal f
+                      (keyword_remap f.cf_name
+                      ^ if variable_field f then "" else "_dyn()")
+                  ^ ";" ))
+      in
+      let reflect_member_readable =
+        List.filter (is_readable class_def) reflect_member_fields
+      in
+      dump_quick_field_test (get_field_dat reflect_member_readable);
+      output_cpp "\treturn super::__Field(inName,inCallProp);\n}\n\n");
+
+    if has_get_static_field class_def then (
+      output_cpp
+        ("bool " ^ class_name
+       ^ "::__GetStatic(const ::String &inName, Dynamic &outValue, \
+          ::hx::PropertyAccess inCallProp)\n\
+          {\n");
+      let get_field_dat =
+        List.map (fun f ->
+            ( f.cf_name,
+              String.length f.cf_name,
+              match f.cf_kind with
+              | Var { v_read = AccCall } when not (is_physical_field f) ->
+                  "if (" ^ checkPropCall f ^ ") { outValue = "
+                  ^ toDynamic f (keyword_remap ("get_" ^ f.cf_name) ^ "()")
+                  ^ "; return true; }"
+              | Var { v_read = AccCall } ->
+                  "outValue = "
+                  ^ toDynamic f
+                      (checkPropCall f ^ " ? "
+                      ^ keyword_remap ("get_" ^ f.cf_name)
+                      ^ "() : " ^ keyword_remap f.cf_name
+                      ^ if variable_field f then "" else "_dyn()")
+                  ^ "; return true;"
+              | _ when variable_field f ->
+                  "outValue = "
+                  ^ toDynamic f (keyword_remap f.cf_name)
+                  ^ "; return true;"
+              | _ ->
+                  "outValue = "
+                  ^ native_field_name_remap true f
+                  ^ "_dyn(); return true;" ))
+      in
+      let reflect_static_readable =
+        List.filter (is_readable class_def) reflect_static_fields
+      in
+      dump_quick_field_test (get_field_dat reflect_static_readable);
+      output_cpp "\treturn false;\n}\n\n");
+
+    let castable f =
+      match cpp_type_of f.cf_type with
+      | TCppInst (t, _) as inst when Meta.has Meta.StructAccess t.cl_meta ->
+          "cpp::Struct< " ^ tcpp_to_string inst ^ " > "
+      | TCppStar (t, _) -> "cpp::Pointer< " ^ tcpp_to_string t ^ " >"
+      | _ -> type_to_string f.cf_type
+    in
+
+    (* Dynamic "Set" Field function *)
+    if has_set_member_field class_def then (
+      output_cpp
+        ("::hx::Val " ^ class_name
+       ^ "::__SetField(const ::String &inName,const ::hx::Val \
+          &inValue,::hx::PropertyAccess inCallProp)\n\
+          {\n");
+
+      let set_field_dat =
+        List.map (fun f ->
+            let default_action =
+              if is_gc_element ctx (cpp_type_of f.cf_type) then
+                "_hx_set_" ^ keyword_remap f.cf_name
+                ^ "(HX_CTX_GET,inValue.Cast< " ^ castable f ^ " >());"
+                ^ " return inValue;"
+              else
+                keyword_remap f.cf_name ^ "=inValue.Cast< " ^ castable f
+                ^ " >();" ^ " return inValue;"
+            in
+            ( f.cf_name,
+              String.length f.cf_name,
+              match f.cf_kind with
+              | Var { v_write = AccCall } ->
+                  let inVal = "(inValue.Cast< " ^ castable f ^ " >())" in
+                  let setter = keyword_remap ("set_" ^ f.cf_name) in
+                  "if (" ^ checkPropCall f ^ ") return "
+                  ^ toVal f (setter ^ inVal)
+                  ^ ";"
+                  ^ if not (is_physical_field f) then "" else default_action
+              | _ -> default_action ))
+      in
+
+      let reflect_member_writable =
+        List.filter (is_writable class_def) reflect_member_fields
+      in
+      let reflect_write_member_variables =
+        List.filter variable_field reflect_member_writable
+      in
+      dump_quick_field_test (set_field_dat reflect_write_member_variables);
+      output_cpp "\treturn super::__SetField(inName,inValue,inCallProp);\n}\n\n");
+
+    if has_set_static_field class_def then (
+      output_cpp
+        ("bool " ^ class_name
+       ^ "::__SetStatic(const ::String &inName,Dynamic \
+          &ioValue,::hx::PropertyAccess inCallProp)\n\
+          {\n");
+
+      let set_field_dat =
+        List.map (fun f ->
+            let default_action =
+              keyword_remap f.cf_name ^ "=ioValue.Cast< " ^ castable f
+              ^ " >(); return true;"
+            in
+            ( f.cf_name,
+              String.length f.cf_name,
+              match f.cf_kind with
+              | Var { v_write = AccCall } ->
+                  let inVal = "(ioValue.Cast< " ^ castable f ^ " >())" in
+                  let setter = keyword_remap ("set_" ^ f.cf_name) in
+                  "if (" ^ checkPropCall f ^ ")  ioValue = "
+                  ^ toDynamic f (setter ^ inVal)
+                  ^ ";"
+                  ^
+                  if not (is_physical_field f) then ""
+                  else " else " ^ default_action
+              | _ -> default_action ))
+      in
+
+      let reflect_static_writable =
+        List.filter (is_writable class_def) reflect_static_fields
+      in
+      let reflect_write_static_variables =
+        List.filter variable_field reflect_static_writable
+      in
+      dump_quick_field_test (set_field_dat reflect_write_static_variables);
+      output_cpp "\treturn false;\n}\n\n");
+
+    (* For getting a list of data members (eg, for serialization) *)
+    if has_get_fields class_def then (
+      let append_field field =
+        output_cpp ("\toutFields->push(" ^ strq field.cf_name ^ ");\n")
+      in
+      let is_data_field field =
+        match follow field.cf_type with TFun _ -> false | _ -> true
+      in
+
+      output_cpp
+        ("void " ^ class_name
+       ^ "::__GetFields(Array< ::String> &outFields)\n{\n");
+      List.iter append_field
+        (List.filter is_data_field class_def.cl_ordered_fields);
+      output_cpp "\tsuper::__GetFields(outFields);\n";
+      output_cpp "};\n\n");
+
+    let storage field =
+      match cpp_type_of field.cf_type with
+      | TCppScalar "bool" -> "::hx::fsBool"
+      | TCppScalar "int" -> "::hx::fsInt"
+      | TCppScalar "Float" -> "::hx::fsFloat"
+      | TCppString -> "::hx::fsString"
+      | o when is_object_element o ->
+          "::hx::fsObject" ^ " /* " ^ tcpp_to_string o ^ " */ "
+      | u -> "::hx::fsUnknown" ^ " /* " ^ tcpp_to_string u ^ " */ "
+    in
+    let dump_member_storage field =
+      output_cpp
+        ("\t{" ^ storage field ^ ",(int)offsetof(" ^ class_name ^ ","
+        ^ keyword_remap field.cf_name
+        ^ ")," ^ strq field.cf_name ^ "},\n")
+    in
+    let dump_static_storage field =
+      output_cpp
+        ("\t{" ^ storage field ^ ",(void *) &" ^ class_name ^ "::"
+        ^ keyword_remap field.cf_name
+        ^ "," ^ strq field.cf_name ^ "},\n")
+    in
+
+    output_cpp "#ifdef HXCPP_SCRIPTABLE\n";
+
+    let stored_fields =
+      List.filter is_data_member implemented_instance_fields
+    in
+    if List.length stored_fields > 0 then (
+      output_cpp
+        ("static ::hx::StorageInfo " ^ class_name
+       ^ "_sMemberStorageInfo[] = {\n");
+      List.iter dump_member_storage stored_fields;
+      output_cpp "\t{ ::hx::fsUnknown, 0, null()}\n};\n")
+    else
+      output_cpp
+        ("static ::hx::StorageInfo *" ^ class_name
+       ^ "_sMemberStorageInfo = 0;\n");
+
+    let stored_statics = List.filter is_data_member implemented_fields in
+    if List.length stored_statics > 0 then (
+      output_cpp
+        ("static ::hx::StaticInfo " ^ class_name ^ "_sStaticStorageInfo[] = {\n");
+      List.iter dump_static_storage stored_statics;
+      output_cpp "\t{ ::hx::fsUnknown, 0, null()}\n};\n")
+    else
+      output_cpp
+        ("static ::hx::StaticInfo *" ^ class_name ^ "_sStaticStorageInfo = 0;\n");
+
+    output_cpp "#endif\n\n");
+
+  (* cl_interface *)
+  let implemented_instance_fields =
+    List.filter should_implement_field class_def.cl_ordered_fields
+  in
+  let reflective_members =
+    List.filter (reflective class_def) implemented_instance_fields
+  in
+  let sMemberFields =
+    match reflective_members with
+    | [] -> "0 /* sMemberFields */"
+    | _ ->
+        let memberFields = class_name ^ "_sMemberFields" in
+        output_cpp ("static ::String " ^ memberFields ^ "[] = {\n");
+        List.iter dump_field_name reflective_members;
+        output_cpp "\t::String(null()) };\n\n";
+        memberFields
+  in
+
+  let hasMarkFunc =
+    (not nativeGen) && List.exists is_data_member implemented_fields
+  in
+
+  if hasMarkFunc then (
+    (* Mark static variables as used *)
+    output_cpp
+      ("static void " ^ class_name ^ "_sMarkStatics(HX_MARK_PARAMS) {\n");
+    List.iter
+      (fun field ->
+        if is_data_member field then
+          output_cpp
+            ("\tHX_MARK_MEMBER_NAME(" ^ class_name ^ "::"
+            ^ keyword_remap field.cf_name
+            ^ ",\"" ^ field.cf_name ^ "\");\n"))
+      implemented_fields;
+    output_cpp "};\n\n";
+
+    (* Visit static variables *)
+    output_cpp "#ifdef HXCPP_VISIT_ALLOCS\n";
+    output_cpp
+      ("static void " ^ class_name ^ "_sVisitStatics(HX_VISIT_PARAMS) {\n");
+    List.iter
+      (fun field ->
+        if is_data_member field then
+          output_cpp
+            ("\tHX_VISIT_MEMBER_NAME(" ^ class_name ^ "::"
+            ^ keyword_remap field.cf_name
+            ^ ",\"" ^ field.cf_name ^ "\");\n"))
+      implemented_fields;
+    output_cpp "};\n\n";
+    output_cpp "#endif\n\n");
+
+  let generate_script_function isStatic field scriptName callName =
+    match follow field.cf_type with
+    | TFun (args, return_type) when not (is_data_member field) ->
+        let isTemplated =
+          (not isStatic) && not (has_class_flag class_def CInterface)
+        in
+        if isTemplated then output_cpp "\ntemplate<bool _HX_SUPER=false>";
+        output_cpp
+          ("\nstatic void CPPIA_CALL " ^ scriptName
+         ^ "(::hx::CppiaCtx *ctx) {\n");
+        let ret =
+          match cpp_type_of return_type with
+          | TCppScalar "bool" -> "b"
+          | _ -> CppCppia.script_signature return_type false
+        in
+        if ret <> "v" then
+          output_cpp
+            ("ctx->return" ^ CppCppia.script_type return_type false ^ "(");
+
+        let dump_call cast =
+          if has_class_flag class_def CInterface then
+            output_cpp
+              (class_name ^ "::" ^ callName ^ "(ctx->getThis()"
+              ^ if List.length args > 0 then "," else "")
+          else if isStatic then output_cpp (class_name ^ "::" ^ callName ^ "(")
+          else
+            output_cpp
+              ("((" ^ class_name ^ "*)ctx->getThis())->" ^ cast ^ callName ^ "(");
+
+          let signature, _, _ =
+            List.fold_left
+              (fun (signature, sep, size) (_, opt, t) ->
+                output_cpp
+                  (sep ^ "ctx->get" ^ CppCppia.script_type t opt ^ "(" ^ size
+                 ^ ")");
+                ( signature ^ CppCppia.script_signature t opt,
+                  ",",
+                  size ^ "+sizeof(" ^ CppCppia.script_size_type t opt ^ ")" ))
+              (ret, "", "sizeof(void*)") args
+          in
+          output_cpp ")";
+          signature
+        in
+        let signature =
+          if isTemplated then (
+            output_cpp " _HX_SUPER ? ";
+            ignore (dump_call (class_name ^ "::"));
+            output_cpp " : ";
+            dump_call "")
+          else dump_call ""
+        in
+
+        if ret <> "v" then output_cpp ")";
+        output_cpp ";\n}\n";
+        signature
+    | _ -> ""
+  in
+
+  let newInteface = has_class_flag class_def CInterface in
+
+  if scriptable && not nativeGen then (
+    let delegate = "this->" in
+    let dump_script_field idx (field, f_args, return_t) =
+      let args = print_tfun_arg_list true f_args in
+      let names = List.map (fun (n, _, _) -> keyword_remap n) f_args in
+      let return_type = type_to_string return_t in
+      let ret =
+        if return_type = "Void" || return_type = "void" then " " else "return "
+      in
+      let name = keyword_remap field.cf_name in
+      let vtable = "__scriptVTable[" ^ string_of_int (idx + 1) ^ "] " in
+      let args_varray =
+        List.fold_left
+          (fun l n -> l ^ ".Add(" ^ n ^ ")")
+          "Array<Dynamic>()" names
+      in
+
+      output_cpp ("\t" ^ return_type ^ " " ^ name ^ "( " ^ args ^ " ) {\n");
+      if newInteface then (
+        output_cpp "\t\t::hx::CppiaCtx *__ctx = ::hx::CppiaCtx::getCurrent();\n";
+        output_cpp "\t\t::hx::AutoStack __as(__ctx);\n";
+        output_cpp "\t\t__ctx->pushObject(this);\n";
+        List.iter
+          (fun (name, opt, t) ->
+            output_cpp
+              ("\t\t__ctx->push" ^ CppCppia.script_type t opt ^ "("
+             ^ keyword_remap name ^ ");\n"))
+          f_args;
+        let interfaceSlot = string_of_int (-cpp_get_interface_slot ctx name) in
+        output_cpp
+          ("\t\t" ^ ret ^ "__ctx->run"
+          ^ CppCppia.script_type return_t false
+          ^ "(__GetScriptVTable()[" ^ interfaceSlot ^ "]);\n");
+        output_cpp "\t}\n")
+      else (
+        output_cpp ("\tif (" ^ vtable ^ ") {\n");
+        output_cpp "\t\t::hx::CppiaCtx *__ctx = ::hx::CppiaCtx::getCurrent();\n";
+        output_cpp "\t\t::hx::AutoStack __as(__ctx);\n";
+        output_cpp
+          ("\t\t__ctx->pushObject("
+          ^ (if has_class_flag class_def CInterface then "mDelegate.mPtr"
+             else "this")
+          ^ ");\n");
+        List.iter
+          (fun (name, opt, t) ->
+            output_cpp
+              ("\t\t__ctx->push" ^ CppCppia.script_type t opt ^ "("
+             ^ keyword_remap name ^ ");\n"))
+          f_args;
+        output_cpp
+          ("\t\t" ^ ret ^ "__ctx->run"
+          ^ CppCppia.script_type return_t false
+          ^ "(" ^ vtable ^ ");\n");
+        output_cpp ("\t}  else " ^ ret);
+
+        if has_class_flag class_def CInterface then (
+          output_cpp
+            (" " ^ delegate ^ "__Field(HX_CSTRING(\"" ^ field.cf_name
+           ^ "\"), ::hx::paccNever)");
+          if List.length names <= 5 then
+            output_cpp ("->__run(" ^ String.concat "," names ^ ");")
+          else output_cpp ("->__Run(" ^ args_varray ^ ");"))
+        else
+          output_cpp
+            (class_name ^ "::" ^ name ^ "(" ^ String.concat "," names ^ ");");
+        if return_type <> "void" then output_cpp "return null();";
+        output_cpp "}\n";
+        let dynamic_interface_closures =
+          Common.defined baseCtx.ctx_common Define.DynamicInterfaceClosures
+        in
+        if has_class_flag class_def CInterface && not dynamic_interface_closures
+        then
+          output_cpp
+            ("\tDynamic " ^ name
+           ^ "_dyn() { return mDelegate->__Field(HX_CSTRING(\"" ^ field.cf_name
+           ^ "\"), ::hx::paccNever); }\n\n"))
+    in
+
+    let new_sctipt_functions =
+      if newInteface then all_virtual_functions class_def
+      else List.rev (current_virtual_functions_rev class_def [])
+    in
+    let sctipt_name = class_name ^ "__scriptable" in
+
+    if newInteface then (
+      output_cpp ("class " ^ sctipt_name ^ " : public ::hx::Object {\n");
+      output_cpp "public:\n")
+    else (
+      output_cpp ("class " ^ sctipt_name ^ " : public " ^ class_name ^ " {\n");
+      output_cpp ("   typedef " ^ sctipt_name ^ " __ME;\n");
+      output_cpp ("   typedef " ^ class_name ^ " super;\n");
+      let field_arg_count field =
+        match (follow field.cf_type, field.cf_kind) with
+        | _, Method MethDynamic -> -1
+        | TFun (args, return_type), Method _ -> List.length args
+        | _, _ -> -1
+      in
+      let has_funky_toString =
+        List.exists
+          (fun f -> f.cf_name = "toString")
+          class_def.cl_ordered_statics
+        || List.exists
+             (fun f -> f.cf_name = "toString" && field_arg_count f <> 0)
+             class_def.cl_ordered_fields
+      in
+      let super_string =
+        if has_funky_toString then class_name ^ "::super" else class_name
+      in
+      output_cpp ("   typedef " ^ super_string ^ " __superString;\n");
+      if has_class_flag class_def CInterface then
+        output_cpp "   HX_DEFINE_SCRIPTABLE_INTERFACE\n"
+      else (
+        output_cpp
+          ("   HX_DEFINE_SCRIPTABLE(HX_ARR_LIST"
+          ^ string_of_int (List.length constructor_var_list)
+          ^ ")\n");
+        output_cpp "\tHX_DEFINE_SCRIPTABLE_DYNAMIC;\n"));
+
+    let list_iteri func in_list =
+      let idx = ref 0 in
+      List.iter
+        (fun elem ->
+          func !idx elem;
+          idx := !idx + 1)
+        in_list
+    in
+
+    let not_toString (field, args, _) =
+      field.cf_name <> "toString" || has_class_flag class_def CInterface
+    in
+    let functions =
+      List.filter not_toString (all_virtual_functions class_def)
+    in
+    list_iteri dump_script_field functions;
+    output_cpp "};\n\n";
+
+    let sigs = Hashtbl.create 0 in
+
+    let static_functions =
+      List.filter (fun f -> not (is_data_member f)) reflect_static_fields
+    in
+    let all_script_functions =
+      List.map (fun (f, _, _) -> f) new_sctipt_functions @ static_functions
+    in
+
+    if List.length all_script_functions > 0 then (
+      List.iter
+        (fun (f, _, _) ->
+          let s =
+            generate_script_function false f ("__s_" ^ f.cf_name)
+              (keyword_remap f.cf_name)
+          in
+          Hashtbl.add sigs f.cf_name s)
+        new_sctipt_functions;
+
+      let dump_script_static f =
+        let s =
+          generate_script_function true f ("__s_" ^ f.cf_name)
+            (keyword_remap f.cf_name)
+        in
+        Hashtbl.add sigs f.cf_name s
+      in
+      List.iter dump_script_static class_def.cl_ordered_statics;
+
+      output_cpp "#ifndef HXCPP_CPPIA_SUPER_ARG\n";
+      output_cpp "#define HXCPP_CPPIA_SUPER_ARG(x)\n";
+      output_cpp "#endif\n";
+      output_cpp
+        "static ::hx::ScriptNamedFunction __scriptableFunctions[] = {\n";
+      let dump_func f isStaticFlag =
+        let s = try Hashtbl.find sigs f.cf_name with Not_found -> "v" in
+        output_cpp
+          ("  ::hx::ScriptNamedFunction(\"" ^ f.cf_name ^ "\",__s_" ^ f.cf_name
+         ^ ",\"" ^ s ^ "\", " ^ isStaticFlag ^ " ");
+        let superCall =
+          if isStaticFlag = "true" || has_class_flag class_def CInterface then
+            "0"
+          else "__s_" ^ f.cf_name ^ "<true>"
+        in
+        output_cpp ("HXCPP_CPPIA_SUPER_ARG(" ^ superCall ^ ")");
+        output_cpp " ),\n"
+      in
+      List.iter (fun (f, _, _) -> dump_func f "false") new_sctipt_functions;
+      List.iter (fun f -> dump_func f "true") static_functions;
+      output_cpp
+        "  ::hx::ScriptNamedFunction(0,0,0 HXCPP_CPPIA_SUPER_ARG(0) ) };\n")
+    else
+      output_cpp
+        "static ::hx::ScriptNamedFunction *__scriptableFunctions = 0;\n";
+
+    if newInteface then (
+      output_cpp ("\n\n" ^ class_name ^ " " ^ class_name ^ "_scriptable = {\n");
+      List.iter
+        (fun (f, args, return_type) ->
+          let cast = cpp_tfun_signature true args return_type in
+          output_cpp
+            ("\t" ^ cast ^ "&" ^ sctipt_name ^ "::" ^ keyword_remap f.cf_name
+           ^ ",\n"))
+        new_sctipt_functions;
+      output_cpp "};\n"));
+
+  let class_name_text = join_class_path class_path "." in
+
+  (* Initialise static in boot function ... *)
+  if (not (has_class_flag class_def CInterface)) && not nativeGen then (
+    (* Remap the specialised "extern" classes back to the generic names *)
+    output_cpp ("::hx::Class " ^ class_name ^ "::__mClass;\n\n");
+    (if scriptable then
+       match class_def.cl_constructor with
+       | Some field ->
+           let signature =
+             generate_script_function false field "__script_construct_func"
+               "__construct"
+           in
+           output_cpp
+             ("::hx::ScriptFunction " ^ class_name
+            ^ "::__script_construct(__script_construct_func,\"" ^ signature
+            ^ "\");\n")
+       | _ ->
+           output_cpp
+             ("::hx::ScriptFunction " ^ class_name
+            ^ "::__script_construct(0,0);\n"));
+
+    let reflective_statics =
+      List.filter (reflective class_def) implemented_fields
+    in
+    let sStaticFields =
+      if List.length reflective_statics > 0 then (
+        output_cpp ("static ::String " ^ class_name ^ "_sStaticFields[] = {\n");
+        List.iter dump_field_name reflective_statics;
+        output_cpp "\t::String(null())\n};\n\n";
+        class_name ^ "_sStaticFields")
+      else "0 /* sStaticFields */"
+    in
+
+    output_cpp ("void " ^ class_name ^ "::__register()\n{\n");
+    if not (has_class_flag class_def CAbstract) then (
+      output_cpp ("\t" ^ class_name ^ " _hx_dummy;\n");
+      output_cpp ("\t" ^ class_name ^ "::_hx_vtable = *(void **)&_hx_dummy;\n"));
+    output_cpp "\t::hx::Static(__mClass) = new ::hx::Class_obj();\n";
+    output_cpp ("\t__mClass->mName = " ^ strq class_name_text ^ ";\n");
+    output_cpp "\t__mClass->mSuper = &super::__SGetClass();\n";
+    if not (has_class_flag class_def CAbstract) then (
+      output_cpp "\t__mClass->mConstructEmpty = &__CreateEmpty;\n";
+      output_cpp "\t__mClass->mConstructArgs = &__Create;\n");
+    output_cpp
+      ("\t__mClass->mGetStaticField = &"
+      ^
+      if has_get_static_field class_def then class_name ^ "::__GetStatic;\n"
+      else "::hx::Class_obj::GetNoStaticField;\n");
+    output_cpp
+      ("\t__mClass->mSetStaticField = &"
+      ^
+      if has_set_static_field class_def then class_name ^ "::__SetStatic;\n"
+      else "::hx::Class_obj::SetNoStaticField;\n");
+    if hasMarkFunc then
+      output_cpp ("\t__mClass->mMarkFunc = " ^ class_name ^ "_sMarkStatics;\n");
+    output_cpp
+      ("\t__mClass->mStatics = ::hx::Class_obj::dupFunctions(" ^ sStaticFields
+     ^ ");\n");
+    output_cpp
+      ("\t__mClass->mMembers = ::hx::Class_obj::dupFunctions(" ^ sMemberFields
+     ^ ");\n");
+    output_cpp ("\t__mClass->mCanCast = ::hx::TCanCast< " ^ class_name ^ " >;\n");
+    if hasMarkFunc then
+      output_cpp
+        ("#ifdef HXCPP_VISIT_ALLOCS\n\t__mClass->mVisitFunc = " ^ class_name
+       ^ "_sVisitStatics;\n#endif\n");
+    output_cpp
+      ("#ifdef HXCPP_SCRIPTABLE\n\t__mClass->mMemberStorageInfo = " ^ class_name
+     ^ "_sMemberStorageInfo;\n#endif\n");
+    output_cpp
+      ("#ifdef HXCPP_SCRIPTABLE\n\t__mClass->mStaticStorageInfo = " ^ class_name
+     ^ "_sStaticStorageInfo;\n#endif\n");
+    output_cpp "\t::hx::_hx_RegisterClass(__mClass->mName, __mClass);\n";
+    if scriptable then
+      output_cpp
+        ("  HX_SCRIPTABLE_REGISTER_CLASS(\"" ^ class_name_text ^ "\","
+       ^ class_name ^ ");\n");
+    Hashtbl.iter
+      (fun _ intf_def ->
+        output_cpp
+          ("\tHX_REGISTER_VTABLE_OFFSET( " ^ class_name ^ ","
+          ^ join_class_path_remap intf_def.cl_path "::"
+          ^ ");\n"))
+      native_implementations;
+    output_cpp "}\n\n")
+  else if not nativeGen then (
+    output_cpp ("::hx::Class " ^ class_name ^ "::__mClass;\n\n");
+
+    output_cpp ("void " ^ class_name ^ "::__register()\n{\n");
+
+    output_cpp "\t::hx::Static(__mClass) = new ::hx::Class_obj();\n";
+    output_cpp ("\t__mClass->mName = " ^ strq class_name_text ^ ";\n");
+    output_cpp "\t__mClass->mSuper = &super::__SGetClass();\n";
+    if hasMarkFunc then
+      output_cpp ("\t__mClass->mMarkFunc = " ^ class_name ^ "_sMarkStatics;\n");
+    output_cpp
+      ("\t__mClass->mMembers = ::hx::Class_obj::dupFunctions(" ^ sMemberFields
+     ^ ");\n");
+    output_cpp
+      ("\t__mClass->mCanCast = ::hx::TIsInterface< (int)"
+     ^ cpp_class_hash class_def ^ " >;\n");
+    if hasMarkFunc then
+      output_cpp
+        ("#ifdef HXCPP_VISIT_ALLOCS\n\t__mClass->mVisitFunc = " ^ class_name
+       ^ "_sVisitStatics;\n#endif\n");
+    output_cpp "\t::hx::_hx_RegisterClass(__mClass->mName, __mClass);\n";
+    if scriptable then
+      output_cpp
+        ("  HX_SCRIPTABLE_REGISTER_INTERFACE(\"" ^ class_name_text ^ "\","
+       ^ class_name ^ ");\n");
+    output_cpp "}\n\n");
+
+  if has_boot_field class_def then (
+    output_cpp ("void " ^ class_name ^ "::__boot()\n{\n");
+
+    List.iter
+      (gen_field_init ctx class_def)
+      (List.filter should_implement_field class_def.cl_ordered_statics);
+
+    output_cpp "}\n\n");
+
+  end_namespace output_cpp class_path;
+
+  if
+    has_class_flag class_def CInterface
+    && Meta.has Meta.ObjcProtocol class_def.cl_meta
+  then (
+    let full_class_name =
+      ("::" ^ join_class_path_remap class_path "::") ^ "_obj"
+    in
+    let protocol =
+      get_meta_string class_def.cl_meta Meta.ObjcProtocol |> Option.default ""
+    in
+    generate_protocol_delegate ctx class_def output_cpp;
+    output_cpp
+      ("id<" ^ protocol ^ "> " ^ full_class_name
+     ^ "::_hx_toProtocol(Dynamic inImplementation) {\n");
+    output_cpp
+      ("\treturn [ [_hx_" ^ protocol
+     ^ "_delegate alloc] initWithImplementation:inImplementation.mPtr];\n");
+    output_cpp "}\n\n");
+
+  cpp_file#close

+ 212 - 0
src/generators/cpp/gen/cppGenEnum.ml

@@ -0,0 +1,212 @@
+open Ast
+open Type
+open Error
+open Common
+open Globals
+open CppStrings
+open CppExprUtils
+open CppTypeUtils
+open CppAst
+open CppAstTools
+open CppSourceWriter
+open CppContext
+open CppGen
+
+let generate baseCtx enum_def =
+  let common_ctx       = baseCtx.ctx_common in
+  let class_path       = enum_def.e_path in
+  let just_class_name  = (snd class_path) in
+  let class_name       = just_class_name ^ "_obj" in
+  let remap_class_name = ("::" ^ (join_class_path_remap class_path "::") )  in
+  let cpp_file         = new_placed_cpp_file common_ctx class_path in
+  let output_cpp       = (cpp_file#write) in
+  let debug            = if (Meta.has Meta.NoDebug enum_def.e_meta) || ( Common.defined common_ctx Define.NoDebug) then 0 else 1 in
+
+  let ctx = file_context baseCtx cpp_file debug false in
+  let strq = strq ctx.ctx_common in
+
+  let classId = try Hashtbl.find baseCtx.ctx_type_ids (class_text enum_def.e_path) with Not_found -> Int32.zero in
+  let classIdTxt = Printf.sprintf "0x%08lx" classId in
+
+  if (debug>1) then
+      print_endline ("Found enum definition:" ^ (join_class_path  class_path "::" ));
+
+  cpp_file#write_h "#include <hxcpp.h>\n\n";
+
+  let super_deps       = create_super_dependencies common_ctx in
+  let referenced,flags = CppReferences.find_referenced_types_flags ctx (TEnumDecl enum_def) "*" super_deps (Hashtbl.create 0) false false false in
+  List.iter (add_include cpp_file) referenced;
+
+  begin_namespace output_cpp class_path;
+  output_cpp "\n";
+
+  PMap.iter (fun _ constructor ->
+      let name = keyword_remap constructor.ef_name in
+      match constructor.ef_type with
+      | TFun (args,_) ->
+        output_cpp (remap_class_name ^ " " ^ class_name ^ "::" ^ name ^ "(" ^
+            (print_tfun_arg_list true args) ^")\n");
+
+        output_cpp ("{\n\treturn ::hx::CreateEnum< " ^ class_name ^ " >(" ^ (strq name) ^ "," ^
+            (string_of_int constructor.ef_index) ^ "," ^ (string_of_int (List.length args)) ^  ")" );
+          ExtList.List.iteri (fun i (arg,_,_) -> output_cpp ("->_hx_init(" ^ (string_of_int i) ^ "," ^ (keyword_remap arg) ^ ")")) args;
+        output_cpp ";\n}\n\n"
+      | _ ->
+        output_cpp ( remap_class_name ^ " " ^ class_name ^ "::" ^ name ^ ";\n\n" )
+  ) enum_def.e_constrs;
+
+
+  let constructor_arg_count constructor =
+      (match constructor.ef_type with | TFun(args,_) -> List.length args | _ -> 0 )
+  in
+
+  output_cpp ("bool " ^ class_name ^ "::__GetStatic(const ::String &inName, ::Dynamic &outValue, ::hx::PropertyAccess inCallProp)\n{\n");
+  PMap.iter (fun _ constructor ->
+      let name = constructor.ef_name in
+      let dyn = if constructor_arg_count constructor > 0 then "_dyn()" else "" in
+      output_cpp ("\tif (inName==" ^ strq name ^ ") { outValue = " ^ class_name ^ "::" ^ keyword_remap name ^ dyn ^ "; return true; }\n" );
+  ) enum_def.e_constrs;
+  output_cpp ("\treturn super::__GetStatic(inName, outValue, inCallProp);\n}\n\n");
+
+  output_cpp ("HX_DEFINE_CREATE_ENUM(" ^ class_name ^ ")\n\n");
+
+  output_cpp ("bool " ^ class_name ^ "::_hx_isInstanceOf(int inClassId) {\n");
+  output_cpp ("\treturn inClassId == (int)0x00000001 || inClassId == ::hx::EnumBase_obj::_hx_ClassId || inClassId == _hx_ClassId;\n");
+  output_cpp ("}\n");
+
+  output_cpp ("int " ^ class_name ^ "::__FindIndex(::String inName)\n{\n");
+  PMap.iter (fun _ constructor ->
+      let name = constructor.ef_name in
+      let idx = string_of_int constructor.ef_index in
+      output_cpp ("\tif (inName==" ^ (strq name) ^ ") return " ^ idx ^ ";\n") ) enum_def.e_constrs;
+  output_cpp ("\treturn super::__FindIndex(inName);\n");
+  output_cpp ("}\n\n");
+
+  (* Dynamic versions of constructors *)
+  let dump_dynamic_constructor _ constr =
+      let count = constructor_arg_count constr in
+      if (count>0) then begin
+        let nargs = string_of_int count in
+        output_cpp ("STATIC_HX_DEFINE_DYNAMIC_FUNC" ^ nargs ^ "(" ^ class_name ^ "," ^
+              (keyword_remap constr.ef_name) ^ ",return)\n\n");
+      end
+  in
+  PMap.iter dump_dynamic_constructor enum_def.e_constrs;
+
+
+  output_cpp ("int " ^ class_name ^ "::__FindArgCount(::String inName)\n{\n");
+  PMap.iter (fun _ constructor ->
+      let name = constructor.ef_name in
+      let count = string_of_int (constructor_arg_count constructor) in
+      output_cpp ("\tif (inName==" ^ (strq name) ^ ") return " ^ count ^ ";\n") ) enum_def.e_constrs;
+      output_cpp ("\treturn super::__FindArgCount(inName);\n");
+      output_cpp ("}\n\n");
+
+  (* Dynamic "Get" Field function - string version *)
+  output_cpp ("::hx::Val " ^ class_name ^ "::__Field(const ::String &inName,::hx::PropertyAccess inCallProp)\n{\n");
+  let dump_constructor_test _ constr =
+      output_cpp ("\tif (inName==" ^ (strq constr.ef_name) ^ ") return " ^
+                  (keyword_remap constr.ef_name) );
+      if ( (constructor_arg_count constr) > 0 ) then output_cpp "_dyn()";
+      output_cpp (";\n")
+  in
+  PMap.iter dump_constructor_test enum_def.e_constrs;
+  output_cpp ("\treturn super::__Field(inName,inCallProp);\n}\n\n");
+
+  output_cpp ("static ::String " ^ class_name ^ "_sStaticFields[] = {\n");
+  let sorted =
+      List.sort (fun f1 f2 -> (PMap.find f1 enum_def.e_constrs ).ef_index -
+              (PMap.find f2 enum_def.e_constrs ).ef_index )
+        (pmap_keys enum_def.e_constrs) in
+
+    List.iter (fun name -> output_cpp ("\t" ^ (strq name) ^ ",\n") ) sorted;
+
+  output_cpp "\t::String(null())\n};\n\n";
+
+  (* ENUM - Mark static as used by GC - they are const now, so no marking*)
+  (* ENUM - Visit static as used by GC - none *)
+
+  output_cpp ("::hx::Class " ^ class_name ^ "::__mClass;\n\n");
+
+  output_cpp ("Dynamic __Create_" ^ class_name ^ "() { return new " ^ class_name ^ "; }\n\n");
+
+  output_cpp ("void " ^ class_name ^ "::__register()\n{\n");
+  let text_name = strq (join_class_path class_path ".") in
+  output_cpp ("\n::hx::Static(__mClass) = ::hx::_hx_RegisterClass(" ^ text_name ^
+              ", ::hx::TCanCast< " ^ class_name ^ " >," ^ class_name ^ "_sStaticFields,0,\n");
+  output_cpp ("\t&__Create_" ^ class_name ^ ", &__Create,\n");
+  output_cpp ("\t&super::__SGetClass(), &Create" ^ class_name ^ ", 0\n");
+  output_cpp("#ifdef HXCPP_VISIT_ALLOCS\n    , 0\n#endif\n");
+  output_cpp ("#ifdef HXCPP_SCRIPTABLE\n    , 0\n#endif\n");
+      output_cpp (");\n");
+  output_cpp ("\t__mClass->mGetStaticField = &" ^ class_name ^"::__GetStatic;\n");
+  output_cpp "}\n\n";
+
+  output_cpp ("void " ^ class_name ^ "::__boot()\n{\n");
+  (match Texpr.build_metadata common_ctx.basic (TEnumDecl enum_def) with
+      | Some expr ->
+        let ctx = file_context ctx cpp_file 1 false in
+        gen_cpp_init ctx class_name "boot" "__mClass->__meta__ = " expr
+      | _ -> () );
+  PMap.iter (fun _ constructor ->
+      let name = constructor.ef_name in
+      match constructor.ef_type with
+      | TFun (_,_) -> ()
+      | _ ->
+        output_cpp ( (keyword_remap name) ^ " = ::hx::CreateConstEnum< " ^ class_name ^ " >(" ^ (strq name) ^  "," ^
+            (string_of_int constructor.ef_index) ^ ");\n" )
+  ) enum_def.e_constrs;
+  output_cpp ("}\n\n");
+
+  output_cpp "\n";
+  end_namespace output_cpp class_path;
+  cpp_file#close;
+
+  let h_file = new_header_file common_ctx common_ctx.file class_path in
+  let super = "::hx::EnumBase_obj" in
+  let output_h = (h_file#write) in
+  let def_string = join_class_path class_path "_"  in
+
+  begin_header_file (h_file#write_h) def_string false;
+
+  List.iter2 (fun r f -> gen_forward_decl h_file r f) referenced flags;
+
+  output_h ( get_code enum_def.e_meta Meta.HeaderCode );
+
+  begin_namespace output_h class_path;
+
+  output_h "\n\n";
+  output_h ("class " ^ class_name ^ " : public " ^ super ^ "\n");
+  output_h ("{\n\ttypedef " ^ super ^ " super;\n");
+  output_h ("\t\ttypedef " ^ class_name ^ " OBJ_;\n");
+  output_h "\n\tpublic:\n";
+  output_h ("\t\tenum { _hx_ClassId = " ^ classIdTxt ^ " };\n\n");
+  output_h ("\t\t" ^ class_name ^ "() {};\n");
+  output_h ("\t\tHX_DO_ENUM_RTTI;\n");
+  output_h ("\t\tstatic void __boot();\n");
+  output_h ("\t\tstatic void __register();\n");
+  output_h ("\t\tstatic bool __GetStatic(const ::String &inName, Dynamic &outValue, ::hx::PropertyAccess inCallProp);\n");
+  output_h ("\t\t::String GetEnumName( ) const { return " ^ (strq (join_class_path class_path "."))  ^ "; }\n" );
+  output_h ("\t\t::String __ToString() const { return " ^ (strq (just_class_name ^ ".") )^ " + _hx_tag; }\n");
+  output_h ("\t\tbool _hx_isInstanceOf(int inClassId);\n\n");
+
+
+  PMap.iter (fun _ constructor ->
+      let name = keyword_remap constructor.ef_name in
+      output_h ( "\t\tstatic " ^  remap_class_name ^ " " ^ name );
+      match constructor.ef_type with
+      | TFun (args,_) ->
+        output_h ( "(" ^ (print_tfun_arg_list true args) ^");\n");
+        output_h ( "\t\tstatic ::Dynamic " ^ name ^ "_dyn();\n");
+      | _ ->
+        output_h ";\n";
+        output_h ( "\t\tstatic inline " ^  remap_class_name ^ " " ^ name ^
+                  "_dyn() { return " ^name ^ "; }\n" );
+  ) enum_def.e_constrs;
+
+  output_h "};\n\n";
+
+  end_namespace output_h class_path;
+
+  end_header_file output_h def_string;
+  h_file#close 

+ 236 - 0
src/generators/cpp/gen/cppReferences.ml

@@ -0,0 +1,236 @@
+open Ast
+open Type
+open Error
+open Common
+open Globals
+open CppStrings
+open CppExprUtils
+open CppTypeUtils
+open CppAst
+open CppAstTools
+open CppSourceWriter
+open CppContext
+
+(*
+   Get a list of all classes referred to by the class/enum definition
+   These are used for "#include"ing the appropriate header files,
+   or for building the dependencies in the Build.xml file
+*)
+let find_referenced_types_flags ctx obj field_name super_deps constructor_deps header_only for_depends include_super_args =
+  let types = ref PMap.empty in
+  (if for_depends then
+     let include_files =
+       get_all_meta_string_path (t_infos obj).mt_meta Meta.Depend
+     in
+     let include_adder inc =
+       types := PMap.add (path_of_string inc) true !types
+     in
+     List.iter include_adder include_files);
+  let rec add_type_flag isNative in_path =
+    if not (PMap.mem in_path !types) then (
+      types := PMap.add in_path isNative !types;
+      try List.iter (add_type_flag isNative) (Hashtbl.find super_deps in_path)
+      with Not_found -> ())
+  and add_type in_path = add_type_flag false in_path in
+  let add_extern_type decl =
+    let tinfo = t_infos decl in
+    let include_files =
+      get_all_meta_string_path tinfo.mt_meta
+        (if for_depends then Meta.Depend else Meta.Include)
+    in
+    if List.length include_files > 0 then
+      List.iter (fun inc -> add_type (path_of_string inc)) include_files
+    else if (not for_depends) && Meta.has Meta.Include tinfo.mt_meta then
+      add_type tinfo.mt_path
+  in
+
+  let add_extern_class klass = add_extern_type (TClassDecl klass) in
+  let add_extern_enum enum = add_extern_type (TEnumDecl enum) in
+  let add_native_gen_class klass =
+    let include_files =
+      get_all_meta_string_path klass.cl_meta
+        (if for_depends then Meta.Depend else Meta.Include)
+    in
+    if List.length include_files > 0 then
+      List.iter (fun inc -> add_type (path_of_string inc)) include_files
+    else if for_depends then add_type klass.cl_path
+    else
+      let path = klass.cl_path in
+      if not (has_class_flag klass CInterface) then
+        (* Always include native struct headers directly ... *)
+        add_type (path_of_string (join_class_path path "/" ^ ".h"))
+      else add_type_flag true klass.cl_path
+  in
+  let visited = ref [] in
+  let rec visit_type in_type =
+    if not (List.exists (fun t2 -> Type.fast_eq in_type t2) !visited) then (
+      visited := in_type :: !visited;
+      (match follow in_type with
+      | TMono r -> ( match r.tm_type with None -> () | Some t -> visit_type t)
+      | TEnum (enum, _) -> (
+          match is_extern_enum enum with
+          | true -> add_extern_enum enum
+          | false -> add_type enum.e_path)
+      (* If a class has a template parameter, then we treat it as dynamic - except
+         for the Array, Class, FastIterator or Pointer classes, for which we do a fully typed object *)
+      | TInst (klass, params) -> (
+          match klass.cl_path with
+          | [], "Array"
+          | [], "Class"
+          | [ "cpp" ], "FastIterator"
+          | [ "cpp" ], "Pointer"
+          | [ "cpp" ], "ConstPointer"
+          | [ "cpp" ], "Function"
+          | [ "cpp" ], "RawPointer"
+          | [ "cpp" ], "RawConstPointer" ->
+              List.iter visit_type params
+          | _ when is_native_gen_class klass -> add_native_gen_class klass
+          | _ when is_extern_class klass ->
+              add_extern_class klass;
+              List.iter visit_type params
+          | _ -> (
+              match klass.cl_kind with
+              | KTypeParameter _ -> ()
+              | _ -> add_type klass.cl_path))
+      | TAbstract (a, params) when is_scalar_abstract a ->
+          add_extern_type (TAbstractDecl a)
+      | TFun (args, haxe_type) ->
+          visit_type haxe_type;
+          List.iter (fun (_, _, t) -> visit_type t) args
+      | _ -> ());
+      visited := List.tl !visited)
+  in
+  let visit_params expression =
+    let rec visit_expression expression =
+      (* Expand out TTypeExpr (ie, the name of a class, as used for static access etc ... *)
+      (match expression.eexpr with
+      | TTypeExpr type_def -> (
+          match type_def with
+          | TClassDecl class_def when is_native_gen_class class_def ->
+              add_native_gen_class class_def
+          | TClassDecl class_def when is_extern_class class_def ->
+              add_extern_class class_def
+          | TEnumDecl enum_def when is_extern_enum enum_def ->
+              add_extern_enum enum_def
+          | _ -> add_type (t_path type_def))
+      (* Must visit the types, Type.iter will visit the expressions ... *)
+      | TTry (e, catches) ->
+          List.iter (fun (v, _) -> visit_type v.v_type) catches
+      (* Must visit type too, Type.iter will visit the expressions ... *)
+      | TNew (klass, params, _) -> (
+          visit_type (TInst (klass, params));
+          try
+            let construct_type = Hashtbl.find constructor_deps klass.cl_path in
+            visit_type construct_type.cf_type
+          with Not_found -> ())
+      (* Must visit type too, Type.iter will visit the expressions ... *)
+      | TVar (v, _) -> visit_type v.v_type
+      (* Must visit enum type too, Type.iter will visit the expressions ... *)
+      | TEnumParameter (_, ef, _) -> visit_type (follow ef.ef_type)
+      (* Must visit args too, Type.iter will visit the expressions ... *)
+      | TFunction func_def ->
+          List.iter (fun (v, _) -> visit_type v.v_type) func_def.tf_args
+      | TField (obj, field) -> (
+          match field with
+          | FInstance (clazz, params, _) | FClosure (Some (clazz, params), _) ->
+              visit_type (TInst (clazz, params))
+          | _ -> ())
+      | TConst TSuper -> (
+          match follow expression.etype with
+          | TInst (klass, params) -> (
+              try
+                let construct_type =
+                  Hashtbl.find constructor_deps klass.cl_path
+                in
+                visit_type construct_type.cf_type
+              with Not_found -> ())
+          | _ ->
+              print_endline
+                ("TSuper : Odd etype ?"
+                ^ (CppRetyper.cpp_type_of expression.etype |> tcpp_to_string)))
+      | _ -> ());
+      Type.iter visit_expression expression;
+      visit_type (follow expression.etype)
+    in
+    visit_expression expression
+  in
+  let visit_field field =
+    (* Add the type of the expression ... *)
+    visit_type field.cf_type;
+    if not header_only then
+      match field.cf_expr with
+      | Some expression -> visit_params expression
+      | _ -> ()
+  in
+  let visit_class class_def =
+    let fields =
+      List.append class_def.cl_ordered_fields class_def.cl_ordered_statics
+    in
+    let fields_and_constructor =
+      List.append fields
+        (match class_def.cl_constructor with Some expr -> [ expr ] | _ -> [])
+    in
+    let fields_and_constructor =
+      if field_name = "*" then fields_and_constructor
+      else List.filter (fun f -> f.cf_name = field_name) fields_and_constructor
+    in
+    List.iter visit_field fields_and_constructor;
+    if include_super_args then
+      List.iter visit_field
+        (List.map (fun (a, _, _) -> a) (all_virtual_functions class_def));
+
+    (* Add super & interfaces *)
+    if is_native_gen_class class_def then add_native_gen_class class_def
+    else add_type class_def.cl_path
+  in
+  let visit_enum enum_def =
+    add_type enum_def.e_path;
+    PMap.iter
+      (fun _ constructor ->
+        match constructor.ef_type with
+        | TFun (args, _) -> List.iter (fun (_, _, t) -> visit_type t) args
+        | _ -> ())
+      enum_def.e_constrs;
+    if not header_only then
+      let meta =
+        Texpr.build_metadata ctx.ctx_common.basic (TEnumDecl enum_def)
+      in
+      match meta with Some expr -> visit_params expr | _ -> ()
+  in
+  let inc_cmp i1 i2 =
+    String.compare (join_class_path i1 ".") (join_class_path i2 ".")
+  in
+
+  (* Body of main function *)
+  (match obj with
+  | TClassDecl class_def -> (
+      visit_class class_def;
+      match TClass.get_cl_init class_def with
+      | Some expression -> visit_params expression
+      | _ -> ())
+  | TEnumDecl enum_def -> visit_enum enum_def
+  | TTypeDecl _ | TAbstractDecl _ -> (* These are expanded *) ());
+
+  (*
+        The internal header files are also defined in the hx/Object.h file, so you do
+        #include them separately. However, Math classes has its
+        own header file (under the hxcpp tree) so these should be included
+    *)
+  let include_class_header = function
+    | [], "@Main" -> false
+    | [], "Math" -> true
+    | path -> not (is_internal_class path)
+  in
+  let deps =
+    List.sort inc_cmp
+      (List.filter (fun path -> include_class_header path) (pmap_keys !types))
+  in
+  let flags = List.map (fun dep -> PMap.find dep !types) deps in
+  (deps, flags)
+
+let find_referenced_types ctx obj super_deps constructor_deps header_only for_depends include_super_args =
+  let deps, _ =
+    find_referenced_types_flags ctx obj "*" super_deps constructor_deps
+      header_only for_depends include_super_args
+  in
+  deps

Різницю між файлами не показано, бо вона завелика
+ 22 - 6902
src/generators/gencpp.ml


Деякі файли не було показано, через те що забагато файлів було змінено