Merge: Rewrite the coloration for properties and types.
authorJean Privat <jean@pryen.org>
Tue, 31 Mar 2015 03:21:57 +0000 (10:21 +0700)
committerJean Privat <jean@pryen.org>
Tue, 31 Mar 2015 03:21:57 +0000 (10:21 +0700)
This introduce a new colorer, `POSetGroupColorer` that colors elements introduced by the classes in a class-hierarchy.
The advantage of this new colorer is that it uses conflict graphs of classes to colorize elements.
By comparison, the previously used colorers work with conflict graphs of elements; that are therefore much larger.

An other advantage is that only the introductions of elements are needed by the colorer, so filling the information from the model is far more easier.

The construction of the poset of types is also removed.
Instead, subtyping tables are computed with a more standard way:

* target cast types are grouped by classes: a map class->types is created
* the map is colored: a table layout by class is computed
* for each live type, the table layout of the associated class is used to build the type table

Results are so good that most of the time of the coloring is removed.

For nitc/nitc/nit

Before:

user: 0m6.044s
total: 15096 MIr
do_property_coloring: 1420 MIr
do_type_coloring: 2600 MIr

After:

user: 0m5.608s (-7%)
total: 12800 MIr (-15%)
do_property_coloring: 452 MIr (-68%)
do_type_coloring: 895 MIr (-65%)

note that in the previous numbers, most of the time is done in the model to inherit or resolve things.
Pure coloring algorithm is now negligible:

conflict_graph: 34 MIr (<1% of the total Ir)
coloring: 60 MIr (<1% of the total Ir)

Unfortunately, with types the coloring can degenerate and produce big tables. If this is an issue, the options `--type-poset` use the previous coloring method for types

Pull-Request: #1215
Reviewed-by: Alexandre Terrasa <alexandre@moz-code.org>
Reviewed-by: Lucas Bajolet <r4pass@hotmail.com>
Reviewed-by: Romain Chanoir <chanoir.romain@courrier.uqam.ca>
Reviewed-by: Alexis Laferrière <alexis.laf@xymus.net>

40 files changed:
benchmarks/bench_engines.sh
lib/geometry/points_and_lines.nit
lib/ios/app.nit [new file with mode: 0644]
lib/ios/examples/hello_ios.nit [new file with mode: 0644]
lib/ios/ios.nit [new file with mode: 0644]
lib/ios/platform.nit [new file with mode: 0644]
lib/privileges/privileges.nit [moved from lib/privileges.nit with 100% similarity]
lib/standard/collection/abstract_collection.nit
lib/standard/collection/sorter.nit
lib/standard/exec.nit
lib/standard/exec_nit.c
lib/standard/file.nit
lib/standard/string.nit
lib/string_experimentations/utf8_noindex.nit
lib/template/examples/tmpl_composer.nit
src/astbuilder.nit
src/compiler/abstract_compiler.nit
src/compiler/compiler.nit
src/compiler/global_compiler.nit
src/compiler/separate_compiler.nit
src/compiler/separate_erasure_compiler.nit
src/interpreter/naive_interpreter.nit
src/model/model.nit
src/nit.nit
src/nitvm.nit
src/platform/android.nit
src/platform/emscripten.nit
src/platform/ios.nit [new file with mode: 0644]
src/platform/pnacl.nit
src/platform/xcode_templates.nit [new file with mode: 0644]
src/rapid_type_analysis.nit
src/transform.nit
src/vm/variables_numbering.nit [moved from src/variables_numbering.nit with 99% similarity]
src/vm/virtual_machine.nit [moved from src/vm.nit with 99% similarity]
src/vm/vm.nit [new file with mode: 0644]
src/vm/vm_optimizations.nit [moved from src/vm_optimizations.nit with 99% similarity]
tests/Linux.skip
tests/sav/nitg-e/error_needed_method_alt4.res [deleted file]
tests/sav/test_parser_args1.res
tests/test_platform_ios.nit [new file with mode: 0644]

index 4d4c6c6..62d6a9c 100755 (executable)
@@ -43,7 +43,10 @@ function run_compiler()
 {
        local title=$1
        shift
-       if test -n "$fast"; then
+       if test "$fast" = truetrue; then
+               run_command "$@" ../examples/hello_world.nit -o "hello.$title.bin"
+               bench_command "hello" "hello_world" "./hello.$title.bin"
+       elif test -n "$fast"; then
                run_command "$@" ../src/nitc.nit -o "nitc.$title.bin"
                bench_command "nitc-g" "nitc --global ../src/test_parser.nit" "./nitc.$title.bin" -v --global --no-cc ../src/test_parser.nit
                run_command "$@" ../src/nit.nit -o "nit.$title.bin"
@@ -77,7 +80,7 @@ function run_compiler()
                bench_command "nitmd" "markdown" "./nitmd.$title.bin" markdown/benches/out/mixed.md 80
        fi
 
-       rm -r *.bin .nit_compile out
+       rm -r *.bin .nit_compile out 2> /dev/null
 }
 
 ## HANDLE OPTIONS ##
@@ -100,7 +103,7 @@ while [ "$stop" = false ]; do
                -h) usage; exit;;
                -n) count="$2"; shift; shift;;
                --dry) dry_run=true; shift;;
-               --fast) fast=true; shift;;
+               --fast) fast=true$fast; shift;;
                --html) html="index.html"; echo >"$html" "<html><head></head><body>"; shift;;
                *) stop=true
        esac
@@ -155,109 +158,70 @@ function bench_steps()
 }
 bench_steps
 
-# $#: options to compare
-function bench_nitc-g_options()
+# Simple script to compare various options on nitc
+#
+# usage: *name* *common* [NOALL] *options*...
+#
+# * *name*, the name of the bench
+# * *common*, options to use on each case
+# * NOALL, optional flag to avoid a last case including all additional options
+# * *options*, sequences of options, one for each case
+#
+# Example: `bench_nitc_options "foo" "-a -b" -c "-d -e"` generates 4 cases:
+#
+# * only *common*: `nitc -a -b`
+# * *common* and first *options*: `nitc -a -b -c`
+# * *common* and second *options*: `nitc -a -b -d -e`
+# * all: *common* and all *options*: `nitc -a -b -c -d -e`
+function bench_nitc_options()
 {
        tag=$1
        shift
-       name="$FUNCNAME-$tag"
+       common=$1
+       shift
+       name="$FUNCNAME-$tag$common"
+       name=${name// /}
        skip_test "$name" && return
-       prepare_res "$name.dat" "no options" "nitc-g without options"
-       run_compiler "nitc-g" ./nitc --global
+       prepare_res "$name.dat" "no options" "nitc $common without more options"
+       run_compiler "nitc-$name" ./nitc $common
 
        if test "$1" = NOALL; then
+               withall=
                shift
-       elif test -n "$2"; then
-               prepare_res "$name-all.dat" "all" "nitc-g with all options $@"
-               run_compiler "nitc-g-$tag" ./nitc --global $@
+       else
+               withall=true
        fi
 
        for opt in "$@"; do
-               ot=${opt// /+}
+               ot=${opt// /}
                prepare_res "$name$ot.dat" "$opt" "nitc-g with option $opt"
-               run_compiler "nitc-g$ot" ./nitc --global $opt
+               run_compiler "nitc-$name" ./nitc $common $opt
        done
 
-       plot "$name.gnu"
-}
-bench_nitc-g_options "slower" --hardening --no-shortcut-range
-bench_nitc-g_options "nocheck" --no-check-null --no-check-autocast --no-check-attr-isset --no-check-covariance --no-check-assert
-
-function bench_nitc-s_options()
-{
-       tag=$1
-       shift
-       name="$FUNCNAME-$tag"
-       skip_test "$name" && return
-       prepare_res "$name.dat" "no options" "nitc-s without options"
-       run_compiler "nitc-s" ./nitc --separate
-
-       if test "$1" = NOALL; then
-               shift
-       elif test -n "$2"; then
-               prepare_res "$name-all.dat" "all" "nitc-s with all options $@"
-               run_compiler "nitc-s-$tag" ./nitc --separate $@
+       if test -n "$2" -a -n "$withall"; then
+               prepare_res "$name-all.dat" "all" "nitc-g with all options $@"
+               run_compiler "nitc-$name" ./nitc $common $@
        fi
 
-       for opt in "$@"; do
-               ot=${opt// /+}
-               prepare_res "$name-$ot.dat" "$opt" "nitc-s with option $opt"
-               run_compiler "nitc-s$ot" ./nitc --separate $opt
-       done
-
        plot "$name.gnu"
 }
-bench_nitc-s_options "slower" --hardening --no-shortcut-equal --no-union-attribute --no-shortcut-range --no-inline-intern "--no-gcc-directive likely --no-gcc-directive noreturn" "--no-tag-primitives"
-bench_nitc-s_options "nocheck" --no-check-null --no-check-autocast --no-check-attr-isset --no-check-covariance --no-check-assert
-bench_nitc-s_options "faster" --skip-dead-methods --inline-coloring-numbers --inline-some-methods --direct-call-monomorph "--inline-some-methods --direct-call-monomorph" ""
 
-function bench_nitc-e_options()
-{
-       tag=$1
-       shift
-       name="$FUNCNAME-$tag"
-       skip_test "$name" && return
-       prepare_res "$name.dat" "no options" "nitc-e without options"
-       run_compiler "nitc-e" ./nitc --erasure
+bench_nitc_options "slower" --global --hardening --no-shortcut-range
+bench_nitc_options "nocheck" --global --no-check-null --no-check-autocast --no-check-attr-isset --no-check-covariance --no-check-assert
 
-       if test "$1" = NOALL; then
-               shift
-       elif test -n "$2"; then
-               prepare_res "$name-all.dat" "all" "nitc-e with all options $@"
-               run_compiler "nitc-e-$tag" ./nitc --erasure $@
-       fi
+bench_nitc_options "slower" --separate --hardening --no-shortcut-equal --no-union-attribute --no-shortcut-range --no-inline-intern "--no-gcc-directive likely --no-gcc-directive noreturn" "--no-tag-primitives"
+bench_nitc_options "nocheck" --separate --no-check-null --no-check-autocast --no-check-attr-isset --no-check-covariance --no-check-assert
+bench_nitc_options "faster" --separate --skip-dead-methods --inline-coloring-numbers --inline-some-methods --direct-call-monomorph "--inline-some-methods --direct-call-monomorph"
 
-       for opt in "$@"; do
-               ot=${opt// /+}
-               prepare_res "$name$ot.dat" "$opt" "nitc-e with option $opt"
-               run_compiler "nitc-e$ot" ./nitc --erasure $opt
-       done
+bench_nitc_options "slower" --erasure --hardening --no-shortcut-equal --no-union-attribute --no-shortcut-range --no-inline-intern
+bench_nitc_options "nocheck" --erasure --no-check-null --no-check-autocast --no-check-attr-isset --no-check-covariance --no-check-assert --no-check-erasure-cast
+bench_nitc_options "faster" --erasure --skip-dead-methods --inline-coloring-numbers --inline-some-methods --direct-call-monomorph --rta
 
-       plot "$name.gnu"
-}
-bench_nitc-e_options "slower" --hardening --no-shortcut-equal --no-union-attribute --no-shortcut-range --no-inline-intern
-bench_nitc-e_options "nocheck" --no-check-null --no-check-autocast --no-check-attr-isset --no-check-covariance --no-check-assert --no-check-erasure-cast
-bench_nitc-e_options "faster" --skip-dead-methods --inline-coloring-numbers --inline-some-methods --direct-call-monomorph --rta
-
-function bench_engines()
-{
-       name="$FUNCNAME"
-       skip_test "$name" && return
-       prepare_res "$name-nitc-s.dat" "nitc-s" "nitc with --separate"
-       run_compiler "nitc-s" ./nitc --separate
-       prepare_res "$name-nitc-e.dat" "nitc-e" "nitc with --erasure"
-       run_compiler "nitc-e" ./nitc --erasure
-       prepare_res "$name-nitc-sg.dat" "nitc-sg" "nitc with --separate --semi-global"
-       run_compiler "nitc-sg" ./nitc --separate --semi-global
-       prepare_res "$name-nitc-eg.dat" "nitc-eg" "nitc with --erasure --semi-global"
-       run_compiler "nitc-eg" ./nitc --erasure --semi-global
-       prepare_res "$name-nitc-egt.dat" "nitc-egt" "nitc with --erasure --semi-global --rta"
-       run_compiler "nitc-egt" ./nitc --erasure --semi-global --rta
-       prepare_res "$name-nitc-g.dat" "nitc-g" "nitc with --global"
-       run_compiler "nitc-g" ./nitc --global
-       plot "$name.gnu"
-}
-bench_engines
+bench_nitc_options "engine" "" NOALL "--separate" "--erasure" "--separate --semi-global" "--erasure --semi-global" "--erasure --semi-global --rta" "--global"
+bench_nitc_options "policy" "" NOALL "--separate" "--erasure" "--separate --no-check-covariance" "--erasure --no-check-covariance --no-check-erasure-cast"
+bench_nitc_options "nullables" "" "--no-check-attr-isset" "--no-union-attribute"
+bench_nitc_options "linkboost" "" NOALL --trampoline-call --colors-are-symbols "--colors-are-symbols --trampoline-call" "--separate --link-boost" "--separate --colors-are-symbols --guard-call" "--separate --colors-are-symbols --direct-call-monomorph0"
+bench_nitc_options "monomorph" "" --direct-call-monomorph0 --direct-call-monomorph
 
 function bench_nitc-e_gc()
 {
@@ -287,38 +251,6 @@ function bench_cc_nitc-e()
 }
 bench_cc_nitc-e
 
-function bench_policy()
-{
-       name="$FUNCNAME"
-       skip_test "$name" && return
-       prepare_res "$name-nitc-s.dat" "nitc-s" "nitc with --separate"
-       run_compiler "nitc-s" ./nitc --separate
-       prepare_res "$name-nitc-e.dat" "nitc-e" "nitc with --erasure"
-       run_compiler "nitc-e" ./nitc --erasure
-       prepare_res "$name-nitc-su.dat" "nitc-su" "nitc with --separate --no-check-covariance"
-       run_compiler "nitc-su" ./nitc --separate --no-check-covariance
-       prepare_res "$name-nitc-eu.dat" "nitc-eu" "nitc with --erasure --no-check-covariance --no-check-erasure-cast"
-       run_compiler "nitc-eu" ./nitc --erasure --no-check-covariance --no-check-erasure-cast
-       plot "$name.gnu"
-}
-bench_policy
-
-function bench_nullables()
-{
-       name="$FUNCNAME"
-       skip_test "$name" && return
-       prepare_res "$name-nitc.dat" "nitc" "nitc no options"
-       run_compiler "nitc" ./nitc --separate
-       prepare_res "$name-nitc-ni.dat" "nitc-ni" "nitc --no-check-attr-isset"
-       run_compiler "nitc" ./nitc --separate --no-check-attr-isset
-       prepare_res "$name-nitc-nu.dat" "nitc-nu" "nitc --no-union-attribute"
-       run_compiler "nitc" ./nitc --separate --no-union-attribute
-       prepare_res "$name-nitc-nu-ni.dat" "nitc-nu-ni" "nitc --no-union-attribute --no-check-attr-isset"
-       run_compiler "nitc" ./nitc --separate --no-union-attribute --no-check-attr-isset
-       plot "$name.gnu"
-}
-bench_nullables
-
 function bench_compilation_time
 {
        name="$FUNCNAME"
@@ -339,44 +271,6 @@ function bench_compilation_time
 }
 bench_compilation_time
 
-function bench_linkboost()
-{
-       name="$FUNCNAME"
-       skip_test "$name" && return
-       prepare_res "$name-nitc-st.dat" "nitc-st" "nitc with --separate --trampoline-call"
-       run_compiler "nitc-st" ./nitc --separate --trampoline-call
-       prepare_res "$name-nitc-s.dat" "nitc-s" "nitc with --separate"
-       run_compiler "nitc-s" ./nitc --separate
-       prepare_res "$name-nitc-sc.dat" "nitc-sc" "nitc with --separate --colors-are-symbols"
-       run_compiler "nitc-sc" ./nitc --separate --colors-are-symbols
-       prepare_res "$name-nitc-sct.dat" "nitc-sct" "nitc with --separate --colors-are-symbols --trampoline-call"
-       run_compiler "nitc-sct" ./nitc --separate --colors-are-symbols --trampoline-call
-       prepare_res "$name-nitc-sl.dat" "nitc-sl" "nitc with --separate --link-boost"
-       run_compiler "nitc-scts" ./nitc --separate --link-boost
-       prepare_res "$name-nitc-scgc.dat" "nitc-scgc" "nitc with --separate --colors-are-symbols --guard-call"
-       run_compiler "nitc-scgc" ./nitc --separate --colors-are-symbols --guard-call
-       prepare_res "$name-nitc-scd.dat" "nitc-scd" "nitc with --separate --colors-are-symbols --direct-call-monomorph0"
-       run_compiler "nitc-scd" ./nitc --separate --colors-are-symbols --direct-call-monomorph0
-       plot "$name.gnu"
-}
-bench_linkboost
-
-function bench_call_monomorph()
-{
-       name="$FUNCNAME"
-       skip_test "$name" && return
-       prepare_res "$name-nitc.dat" "nitc" "nitc with --separate"
-       run_compiler "nitc" ./nitc
-       prepare_res "$name-nitc-d0.dat" "nitc-d0" "nitc with --separate --direct-call-monomorph0"
-       run_compiler "nitc-d0" ./nitc --direct-call-monomorph0
-       prepare_res "$name-nitc-d1.dat" "nitc-d" "nitc with --separate --direct-call-monomorph"
-       run_compiler "nitc-d1" ./nitc --direct-call-monomorph
-       prepare_res "$name-nitc-d2.dat" "nitc-d2" "nitc with --separate --direct-call-monomorph2"
-       run_compiler "nitc-d2" ./nitc --direct-call-monomorph --direct-call-monomorph0
-       plot "$name.gnu"
-}
-bench_call_monomorph
-
 if test -n "$html"; then
        echo >>"$html" "</body></html>"
 fi
index 14a67fb..303aae1 100644 (file)
@@ -55,9 +55,13 @@ end
 
 # An abstract 2d line segment
 interface ILine[N: Numeric]
+       # The type of points that ends the segment
        type P: IPoint[N]
 
+       # The point that is the left-end of the segment
        fun point_left: P is abstract
+
+       # The point that is the right-end of the segment
        fun point_right: P is abstract
 
        redef fun to_s do return "{point_left}--{point_right}"
diff --git a/lib/ios/app.nit b/lib/ios/app.nit
new file mode 100644 (file)
index 0000000..0e337bc
--- /dev/null
@@ -0,0 +1,191 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Basic structure for Nit apps on iOS
+module app
+
+import platform
+import ::app
+
+in "ObjC Header" `{
+       #import <UIKit/UIKit.h>
+
+       // Our interface to the iOS system
+       @interface AppDelegate: UIResponder <UIApplicationDelegate>
+
+               // The main window
+               @property (strong, nonatomic) UIWindow *window;
+       @end
+`}
+
+in "ObjC" `{
+
+       // Global reference to the App from app.nit
+       App app_nit_ios_app;
+
+       // Our own C argc and argv
+       int app_nit_ios_argc;
+       char **app_nit_ios_argv;
+
+       @implementation AppDelegate
+
+       - (BOOL)application:(UIApplication *)application
+               willFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
+
+               // Set aside `application` to be used from Nit
+               App_ui_application__assign(app_nit_ios_app, application);
+               App_app_delegate__assign(app_nit_ios_app, self);
+
+               // Complete the callback
+               return App_will_finish_launching_with_options(app_nit_ios_app);
+       }
+
+       - (BOOL)application:(UIApplication *)application
+               didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
+
+               return App_did_finish_launching_with_options(app_nit_ios_app);
+       }
+
+       - (void)applicationWillResignActive:(UIApplication *)application {
+               App_will_resign_active(app_nit_ios_app);
+       }
+
+       - (void)applicationDidEnterBackground:(UIApplication *)application {
+               App_did_enter_background(app_nit_ios_app);
+       }
+
+       - (void)applicationWillEnterForeground:(UIApplication *)application {
+               App_will_enter_foreground(app_nit_ios_app);
+       }
+
+       - (void)applicationDidBecomeActive:(UIApplication *)application {
+               App_did_become_active(app_nit_ios_app);
+       }
+
+       - (void)applicationWillTerminate:(UIApplication *)application {
+               App_will_terminate(app_nit_ios_app);
+       }
+
+       @end
+`}
+
+# Application interface to the iOS system
+extern class AppDelegate in "ObjC" `{ AppDelegate * `}
+end
+
+# Graphical application to which events are sent
+extern class UIApplication in "ObjC" `{ UIApplication * `}
+end
+
+redef class App
+
+       # Main graphical application
+       var ui_application: UIApplication
+
+       # Application interface to the iOS system
+       var app_delegate: AppDelegate
+
+       # Copy back to C the command line arguments
+       #
+       # Nit extracts the first arguments from the `args` sequence,
+       # so we need to add it back. That's why Nit's `args` is smaller than in C.
+       private fun register_args(program_name: NativeString, argc: Int,
+       argv: Sequence[String]) import Sequence[String].[], String.to_cstring in "ObjC" `{
+               app_nit_ios_argc = argc+1;
+
+               // TODO copy or pin the strings when needed
+               app_nit_ios_argv = malloc(argc * sizeof(char*));
+               app_nit_ios_argv[0] = program_name;
+               for (int i = 0; i < argc; i ++) {
+                       String arg = Sequence_of_String__index(argv, i);
+                       app_nit_ios_argv[i+1] = String_to_cstring(arg);
+               }
+       `}
+
+       # Register `self` globally in C so it can be retrieved from iOS callbacks
+       private fun register_globally in "ObjC" `{
+               App_incr_ref(recv);
+               app_nit_ios_app = recv;
+       `}
+
+       # Entry point to the iOS framework
+       private fun ui_application_main: Bool import did_finish_launching_with_options,
+       will_finish_launching_with_options,
+       will_resign_active, did_enter_background, will_enter_foreground,
+       did_become_active, will_terminate, ui_application=, app_delegate= in "ObjC" `{
+
+               @autoreleasepool {
+                       return UIApplicationMain(app_nit_ios_argc, app_nit_ios_argv,
+                               nil, NSStringFromClass([AppDelegate class]));
+               }
+       `}
+
+       # The application is about to launch
+       #
+       # Redef this method to set the very first custom code to be executed.
+       fun will_finish_launching_with_options: Bool do return true
+
+       # The application just launched but is not yet displayed to the user
+       #
+       # Redef this method to customize the behavior.
+       fun did_finish_launching_with_options: Bool do return true
+
+       # The application is about to move from active to inactive state
+       #
+       # This can occur for certain types of temporary interruptions
+       # (such as an incoming phone call or SMS message) or when the
+       # user quits the application and it begins the transition to
+       # the background state.
+       #
+       # Redef this method to pause ongoing tasks, disable timers, and
+       # throttle down OpenGL ES frame rates. Games should use this
+       # method to pause.
+       fun will_resign_active do end
+
+       # The application just left foreground it can be suspended at any time
+       #
+       # Redef this method to release shared resources, save user data,
+       # invalidate timers, and store application state to restore your
+       # application to its current state in case it is terminated later.
+       #
+       # If your application supports background execution, this method
+       # is called instead of `will_terminate` when the user quits.
+       fun did_enter_background do end
+
+       # The application will enter the foreground
+       #
+       # Called as part of the transition from the background to the
+       # inactive state.
+       #
+       # Redef to und changes made on entering the background.
+       fun will_enter_foreground do end
+
+       # The application just became active
+       #
+       # Redef to restart any tasks that were paused (or not yet started) while
+       # the application was inactive. If the application was previously
+       # in the background, optionally refresh the user interface.
+       fun did_become_active do end
+
+       # The application is about to terminate (not suspended)
+       #
+       # Redef to save data if appropriate.
+       fun will_terminate do end
+end
+
+app.register_args(program_name.to_cstring, args.length, args)
+app.register_globally
+
+var ret = app.ui_application_main
+exit if ret then 0 else 1
diff --git a/lib/ios/examples/hello_ios.nit b/lib/ios/examples/hello_ios.nit
new file mode 100644 (file)
index 0000000..d11ffcb
--- /dev/null
@@ -0,0 +1,50 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Simple iOS app with a single label
+module hello_ios
+
+import ios
+
+redef class App
+       redef fun did_finish_launching_with_options
+       do
+               return app_delegate.hello_world
+       end
+end
+
+redef class AppDelegate
+
+       # Print and show "Hello World!"
+       private fun hello_world: Bool in "ObjC" `{
+
+               // Print to the console
+               NSLog(@"Hello World!");
+
+               // Display "Hello world!" on the screen
+               recv.window = [[UIWindow alloc] initWithFrame:
+               [[UIScreen mainScreen] bounds]];
+               recv.window.backgroundColor = [UIColor whiteColor];
+
+               UILabel *label = [[UILabel alloc] init];
+               label.text = @"Hello World!";
+               label.center = CGPointMake(100, 100);
+               [label sizeToFit];
+
+               [recv.window addSubview: label];
+               [recv.window makeKeyAndVisible];
+
+               return YES;
+       `}
+end
diff --git a/lib/ios/ios.nit b/lib/ios/ios.nit
new file mode 100644 (file)
index 0000000..9d5079d
--- /dev/null
@@ -0,0 +1,19 @@
+# This file is part of NIT (http://www.nitlanguage.org).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# iOS services for Nit app on iOS
+module ios
+
+import platform
+import app
diff --git a/lib/ios/platform.nit b/lib/ios/platform.nit
new file mode 100644 (file)
index 0000000..47d1536
--- /dev/null
@@ -0,0 +1,16 @@
+# This file is part of NIT (http://www.nitlanguage.org).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Triggers compilation for the iOS platform
+module platform is platform "ios"
index d51e710..1b1e71e 100644 (file)
@@ -66,6 +66,14 @@ interface Collection[E]
        #     assert [1..1[.is_empty   == true
        fun is_empty: Bool do return length == 0
 
+       # Alias for `not is_empty`.
+       #
+       # Some people prefer to have conditions grammatically easier to read.
+       #
+       #     assert [1,2,3].not_empty  == true
+       #     assert [1..1[.not_empty   == false
+       fun not_empty: Bool do return not self.is_empty
+
        # Number of items in the collection.
        #
        #     assert [10,20,30].length == 3
index cfcc376..00c45a3 100644 (file)
@@ -250,6 +250,81 @@ interface Comparator
 
 end
 
+redef class MapRead[K,V]
+       # Return an array of all values sorted with their keys using `comparator`.
+       #
+       # ~~~
+       # var map = new HashMap[Int, String]
+       # map[10] = "ten"
+       # map[2]  = "two"
+       # map[1]  = "one"
+       # assert map.values_sorted_by_key(default_comparator) == ["one", "two", "ten"]
+       # assert map.values_sorted_by_key(alpha_comparator) == ["one", "ten", "two"]
+       # ~~~
+       fun values_sorted_by_key(comparator: Comparator): Array[V]
+       do
+               var keys = self.keys.to_a
+               comparator.sort(keys)
+               return [for k in keys do self[k]]
+       end
+
+       # Return an array of all keys sorted with their values using `comparator`.
+       #
+       # ~~~
+       # var map = new HashMap[String, Int]
+       # map["ten"] = 10
+       # map["two"] = 2
+       # map["one"] = 1
+       # assert map.keys_sorted_by_values(default_comparator) == ["one", "two", "ten"]
+       # assert map.keys_sorted_by_values(alpha_comparator) == ["one", "ten", "two"]
+       # ~~~
+       #
+       # See: `to_map_comparator` to get the comparator used internally.
+       fun keys_sorted_by_values(comparator: Comparator): Array[K]
+       do
+               var keys = self.keys.to_a
+               var map_cmp = to_map_comparator(comparator)
+               map_cmp.sort(keys)
+               return keys
+       end
+
+       # A comparator that compares things with their values in self.
+       #
+       # See `MapComparator` for details.
+       fun to_map_comparator(comparator: Comparator): MapComparator[K, V] do return new MapComparator[K,V](self, comparator)
+end
+
+# A comparator that compares things with their values in a map.
+#
+# ~~~
+# var map = new HashMap[String, Int]
+# map["ten"] = 10
+# map["two"] = 2
+# map["one"] = 1
+#
+# var map_cmp = map.to_map_comparator(default_comparator)
+# var a = ["ten", "one", "two"]
+# map_cmp.sort(a)
+# assert a == ["one", "two", "ten"]
+# map_cmp = map.to_map_comparator(alpha_comparator)
+# map_cmp.sort(a)
+# assert a == ["one", "ten", "two"]
+# ~~~
+class MapComparator[K,V]
+       super Comparator
+
+       # What is compared are the keys of the values
+       redef type COMPARED: K
+
+       # The map that associates compared elements to the value used to compare them
+       var map: MapRead[K,V]
+
+       # The comparator used to compare values
+       var comparator: Comparator
+
+       redef fun compare(a,b) do return comparator.compare(map[a], map[b])
+end
+
 # This comparator uses the operator `<=>` to compare objects.
 # see `default_comparator` for an easy-to-use general stateless default comparator.
 class DefaultComparator
index d9a0704..8cd2f70 100644 (file)
@@ -129,7 +129,9 @@ class ProcessWriter
        redef fun execute
        do
                super
-               stream_out = new FileWriter.from_fd(data.in_fd)
+               var out = new FileWriter.from_fd(data.in_fd)
+               out.set_buffering_mode(0, sys.buffer_mode_none)
+               stream_out = out
        end
 end
 
index 5b5ffbb..93f5f9b 100644 (file)
@@ -149,4 +149,5 @@ int string_NativeString_NativeString_system_0(const char *cmd) {
                // cmd exited on SIGINT: in my opinion the user wants the main to be discontinued
                kill(getpid(), SIGINT);
        }
+       return status;
 }
index 4d77955..81341ad 100644 (file)
@@ -47,6 +47,21 @@ abstract class FileStream
        # File descriptor of this file
        fun fd: Int do return _file.fileno
 
+       redef fun close
+       do
+               if _file == null then return
+               if _file.address_is_null then
+                       if last_error != null then return
+                       last_error = new IOError("Cannot close unopened file")
+                       return
+               end
+               var i = _file.io_close
+               if i != 0 then
+                       last_error = new IOError("Close failed due to error {sys.errno.strerror}")
+               end
+               _file = null
+       end
+
        # Sets the buffering mode for the current FileStream
        #
        # If the buf_size is <= 0, its value will be 512 by default
@@ -89,11 +104,9 @@ class FileReader
 
        redef fun close
        do
-               if _file == null or _file.address_is_null then return
-               var i = _file.io_close
+               super
                _buffer.clear
                end_reached = true
-               _file = null
        end
 
        redef fun fill_buffer
@@ -122,6 +135,7 @@ class FileReader
                end
        end
 
+       # Creates a new File stream from a file descriptor
        init from_fd(fd: Int) do
                self.path = ""
                prepare_buffer(1)
@@ -150,24 +164,12 @@ class FileWriter
                else
                        for i in s.substrings do write_native(i.to_cstring, i.length)
                end
-               _file.flush
        end
 
        redef fun close
        do
-               if _file == null then return
-               if _file.address_is_null then
-                       if last_error != null then return
-                       last_error = new IOError("Cannot close unopened write stream")
-                       _is_writable = false
-                       return
-               end
-               var i = _file.io_close
-               if i != 0 then
-                       last_error = new IOError("Close failed due to error {sys.errno.strerror}")
-               end
+               super
                _is_writable = false
-               _file = null
        end
        redef var is_writable = false
 
index 63c8226..e974d91 100644 (file)
@@ -531,6 +531,14 @@ abstract class Text
        #
        #     assert "abAB12<>&".escape_to_c         == "abAB12<>&"
        #     assert "\n\"'\\".escape_to_c         == "\\n\\\"\\'\\\\"
+       #
+       # Most non-printable characters (bellow ASCII 32) are escaped to an octal form `\nnn`.
+       # Three digits are always used to avoid following digits to be interpreted as an element
+       # of the octal sequence.
+       #
+       #     assert "{0.ascii}{1.ascii}{8.ascii}{31.ascii}{32.ascii}".escape_to_c == "\\000\\001\\010\\037 "
+       #
+       # The exceptions are the common `\t` and `\n`.
        fun escape_to_c: String
        do
                var b = new FlatBuffer
@@ -538,8 +546,10 @@ abstract class Text
                        var c = chars[i]
                        if c == '\n' then
                                b.append("\\n")
+                       else if c == '\t' then
+                               b.append("\\t")
                        else if c == '\0' then
-                               b.append("\\0")
+                               b.append("\\000")
                        else if c == '"' then
                                b.append("\\\"")
                        else if c == '\'' then
@@ -547,7 +557,17 @@ abstract class Text
                        else if c == '\\' then
                                b.append("\\\\")
                        else if c.ascii < 32 then
-                               b.append("\\{c.ascii.to_base(8, false)}")
+                               b.add('\\')
+                               var oct = c.ascii.to_base(8, false)
+                               # Force 3 octal digits since it is the
+                               # maximum allowed in the C specification
+                               if oct.length == 1 then
+                                       b.add('0')
+                                       b.add('0')
+                               else if oct.length == 2 then
+                                       b.add('0')
+                               end
+                               b.append(oct)
                        else
                                b.add(c)
                        end
@@ -2184,6 +2204,48 @@ redef class Array[E]
        end
 end
 
+redef class NativeArray[E]
+       # Join all the elements using `to_s`
+       #
+       # REQUIRE: `self isa NativeArray[String]`
+       # REQUIRE: all elements are initialized
+       fun native_to_s: String
+       do
+               assert self isa NativeArray[String]
+               var l = length
+               var na = self
+               var i = 0
+               var sl = 0
+               var mypos = 0
+               while i < l do
+                       sl += na[i].length
+                       i += 1
+                       mypos += 1
+               end
+               var ns = new NativeString(sl + 1)
+               ns[sl] = '\0'
+               i = 0
+               var off = 0
+               while i < mypos do
+                       var tmp = na[i]
+                       var tpl = tmp.length
+                       if tmp isa FlatString then
+                               tmp.items.copy_to(ns, tpl, tmp.index_from, off)
+                               off += tpl
+                       else
+                               for j in tmp.substrings do
+                                       var s = j.as(FlatString)
+                                       var slen = s.length
+                                       s.items.copy_to(ns, slen, s.index_from, off)
+                                       off += slen
+                               end
+                       end
+                       i += 1
+               end
+               return ns.to_s_with_length(sl)
+       end
+end
+
 redef class Map[K,V]
        # Concatenate couple of 'key value'.
        # key and value are separated by `couple_sep`.
index 0fa637a..2875a8b 100644 (file)
@@ -231,7 +231,7 @@ class FlatStringIter
 
        private var it: UnicodeChar
 
-       private var is_created: Bool
+       private var is_created = false
 
        init(s: FlatString) do from(s, 0)
 
index 6160b1a..3150c5f 100644 (file)
@@ -55,8 +55,6 @@ class TmplComposer
        # Short name
        var name: String
 
-       init(name: String) do self.name = name
-
        redef fun rendering do add "- {name}\n"
 end
 
@@ -69,13 +67,6 @@ class TmplComposerDetail
        var birth: Int
        var death: Int
 
-       init(firstname, lastname: String, birth, death: Int) do
-               self.firstname = firstname
-               self.lastname = lastname
-               self.birth = birth
-               self.death = death
-       end
-
        redef fun rendering do add """
 
 COMPOSER: {{{firstname}}} {{{lastname}}}
index 8aa0295..99c8763 100644 (file)
@@ -32,7 +32,7 @@ class ASTBuilder
        # Make a new Int literal
        fun make_int(value: Int): AIntExpr
        do
-               return new ADecIntExpr.make(value, mmodule.get_primitive_class("Int").mclass_type)
+               return new ADecIntExpr.make(value, mmodule.int_type)
        end
 
        # Make a new instatiation
index 72e6447..43fd172 100644 (file)
@@ -122,20 +122,30 @@ redef class ModelBuilder
        protected fun write_and_make(compiler: AbstractCompiler)
        do
                var platform = compiler.target_platform
-               var toolchain = platform.toolchain(toolcontext)
+               var toolchain = platform.toolchain(toolcontext, compiler)
                compile_dir = toolchain.compile_dir
-               toolchain.write_and_make compiler
+               toolchain.write_and_make
        end
 end
 
 redef class Platform
        # The specific tool-chain associated to the platform
-       fun toolchain(toolcontext: ToolContext): Toolchain do return new MakefileToolchain(toolcontext)
+       fun toolchain(toolcontext: ToolContext, compiler: AbstractCompiler): Toolchain
+       do
+               return new MakefileToolchain(toolcontext, compiler)
+       end
 end
 
+# Build toolchain for a specific target program, varies per `Platform`
 class Toolchain
+
+       # Toolcontext
        var toolcontext: ToolContext
 
+       # Compiler of the target program
+       var compiler: AbstractCompiler
+
+       # Directory where to generate all C files
        fun compile_dir: String
        do
                var compile_dir = toolcontext.opt_compile_dir.value
@@ -143,13 +153,15 @@ class Toolchain
                return compile_dir
        end
 
-       fun write_and_make(compiler: AbstractCompiler) is abstract
+       # Write all C files and compile them
+       fun write_and_make is abstract
 end
 
+# Default toolchain using a Makefile
 class MakefileToolchain
        super Toolchain
 
-       redef fun write_and_make(compiler)
+       redef fun write_and_make
        do
                var compile_dir = compile_dir
 
@@ -162,11 +174,11 @@ class MakefileToolchain
                compile_dir.mkdir
 
                var cfiles = new Array[String]
-               write_files(compiler, compile_dir, cfiles)
+               write_files(compile_dir, cfiles)
 
                # Generate the Makefile
 
-               write_makefile(compiler, compile_dir, cfiles)
+               write_makefile(compile_dir, cfiles)
 
                var time1 = get_time
                self.toolcontext.info("*** END WRITING C: {time1-time0} ***", 2)
@@ -178,13 +190,14 @@ class MakefileToolchain
                time0 = time1
                self.toolcontext.info("*** COMPILING C ***", 1)
 
-               compile_c_code(compiler, compile_dir)
+               compile_c_code(compile_dir)
 
                time1 = get_time
                self.toolcontext.info("*** END COMPILING C: {time1-time0} ***", 2)
        end
 
-       fun write_files(compiler: AbstractCompiler, compile_dir: String, cfiles: Array[String])
+       # Write all source files to the `compile_dir`
+       fun write_files(compile_dir: String, cfiles: Array[String])
        do
                var platform = compiler.target_platform
                if self.toolcontext.opt_stacktrace.value == "nitstack" and platform.supports_libunwind then compiler.build_c_to_nit_bindings
@@ -281,10 +294,14 @@ class MakefileToolchain
                self.toolcontext.info("Total C source files to compile: {cfiles.length}", 2)
        end
 
-       fun makefile_name(mainmodule: MModule): String do return "{mainmodule.c_name}.mk"
+       # Get the name of the Makefile to use
+       fun makefile_name: String do return "{compiler.mainmodule.c_name}.mk"
 
-       fun default_outname(mainmodule: MModule): String
+       # Get the default name of the executable to produce
+       fun default_outname: String
        do
+               var mainmodule = compiler.mainmodule
+
                # Search a non fictive module
                var res = mainmodule.name
                while mainmodule.is_fictive do
@@ -299,13 +316,14 @@ class MakefileToolchain
        do
                var res = self.toolcontext.opt_output.value
                if res != null then return res
-               res = default_outname(mainmodule)
+               res = default_outname
                var dir = self.toolcontext.opt_dir.value
                if dir != null then return dir.join_path(res)
                return res
        end
 
-       fun write_makefile(compiler: AbstractCompiler, compile_dir: String, cfiles: Array[String])
+       # Write the Makefile
+       fun write_makefile(compile_dir: String, cfiles: Array[String])
        do
                var mainmodule = compiler.mainmodule
                var platform = compiler.target_platform
@@ -320,7 +338,7 @@ class MakefileToolchain
                        # 2. copy the binary at the right place in the `all` goal.
                        outpath = mainmodule.c_name
                end
-               var makename = makefile_name(mainmodule)
+               var makename = makefile_name
                var makepath = "{compile_dir}/{makename}"
                var makefile = new FileWriter.open(makepath)
 
@@ -445,9 +463,10 @@ endif
                makepath.file_copy_to "{compile_dir}/Makefile"
        end
 
-       fun compile_c_code(compiler: AbstractCompiler, compile_dir: String)
+       # The C code is generated, compile it to an executable
+       fun compile_c_code(compile_dir: String)
        do
-               var makename = makefile_name(compiler.mainmodule)
+               var makename = makefile_name
 
                var makeflags = self.toolcontext.opt_make_flags.value
                if makeflags == null then makeflags = ""
@@ -590,6 +609,8 @@ abstract class AbstractCompiler
                self.header.add_decl("#include <stdlib.h>")
                self.header.add_decl("#include <stdio.h>")
                self.header.add_decl("#include <string.h>")
+               self.header.add_decl("#include <sys/types.h>\n")
+               self.header.add_decl("#include <unistd.h>\n")
                self.header.add_decl("#include \"gc_chooser.h\"")
                self.header.add_decl("#ifdef ANDROID")
                self.header.add_decl("  #include <android/log.h>")
@@ -1078,9 +1099,6 @@ abstract class AbstractCompilerVisitor
                self.writer = new CodeWriter(compiler.files.last)
        end
 
-       # Force to get the primitive class named `name` or abort
-       fun get_class(name: String): MClass do return self.compiler.mainmodule.get_primitive_class(name)
-
        # Force to get the primitive property named `name` in the instance `recv` or abort
        fun get_property(name: String, recv: MType): MMethod
        do
@@ -1122,6 +1140,14 @@ abstract class AbstractCompilerVisitor
 
        fun native_array_def(pname: String, ret_type: nullable MType, arguments: Array[RuntimeVariable]) is abstract
 
+       # Return an element of a native array.
+       # The method is unsafe and is just a direct wrapper for the specific implementation of native arrays
+       fun native_array_get(native_array: RuntimeVariable, index: Int): RuntimeVariable is abstract
+
+       # Store an element in a native array.
+       # The method is unsafe and is just a direct wrapper for the specific implementation of native arrays
+       fun native_array_set(native_array: RuntimeVariable, index: Int, value: RuntimeVariable) is abstract
+
        # Evaluate `args` as expressions in the call of `mpropdef` on `recv`.
        # This method is used to manage varargs in signatures and returns the real array
        # of runtime variables to use in the call.
@@ -1395,7 +1421,7 @@ abstract class AbstractCompilerVisitor
                var recv
                var ctype = mtype.ctype
                assert mtype.mclass.name != "NativeArray"
-               if ctype == "val*" then
+               if not mtype.is_c_primitive then
                        recv = init_instance(mtype)
                else if ctype == "char*" then
                        recv = new_expr("NULL/*special!*/", mtype)
@@ -1416,37 +1442,64 @@ abstract class AbstractCompilerVisitor
                end
        end
 
+       # The currently processed module
+       #
+       # alias for `compiler.mainmodule`
+       fun mmodule: MModule do return compiler.mainmodule
+
        # Generate an integer value
        fun int_instance(value: Int): RuntimeVariable
        do
-               var res = self.new_var(self.get_class("Int").mclass_type)
-               self.add("{res} = {value};")
+               var t = mmodule.int_type
+               var res = new RuntimeVariable("{value.to_s}l", t, t)
+               return res
+       end
+
+       # Generate a char value
+       fun char_instance(value: Char): RuntimeVariable
+       do
+               var t = mmodule.char_type
+               var res = new RuntimeVariable("'{value.to_s.escape_to_c}'", t, t)
+               return res
+       end
+
+       # Generate a float value
+       #
+       # FIXME pass a Float, not a string
+       fun float_instance(value: String): RuntimeVariable
+       do
+               var t = mmodule.float_type
+               var res = new RuntimeVariable("{value}", t, t)
                return res
        end
 
        # Generate an integer value
        fun bool_instance(value: Bool): RuntimeVariable
        do
-               var res = self.new_var(self.get_class("Bool").mclass_type)
-               if value then
-                       self.add("{res} = 1;")
-               else
-                       self.add("{res} = 0;")
-               end
+               var s = if value then "1" else "0"
+               var res = new RuntimeVariable(s, bool_type, bool_type)
+               return res
+       end
+
+       # Generate the `null` value
+       fun null_instance: RuntimeVariable
+       do
+               var t = compiler.mainmodule.model.null_type
+               var res = new RuntimeVariable("((val*)NULL)", t, t)
                return res
        end
 
        # Generate a string value
        fun string_instance(string: String): RuntimeVariable
        do
-               var mtype = self.get_class("String").mclass_type
+               var mtype = mmodule.string_type
                var name = self.get_name("varonce")
                self.add_decl("static {mtype.ctype} {name};")
                var res = self.new_var(mtype)
                self.add("if (likely({name}!=NULL)) \{")
                self.add("{res} = {name};")
                self.add("\} else \{")
-               var native_mtype = self.get_class("NativeString").mclass_type
+               var native_mtype = mmodule.native_string_type
                var nat = self.new_var(native_mtype)
                self.add("{nat} = \"{string.escape_to_c}\";")
                var length = self.int_instance(string.length)
@@ -1754,12 +1807,16 @@ redef class MType
 
        # Short name of the `ctype` to use in unions
        fun ctypename: String do return "val"
+
+       # Is the associated C type a primitive one?
+       #
+       # ENSURE `result == (ctype != "val*")`
+       fun is_c_primitive: Bool do return false
 end
 
 redef class MClassType
 
-       redef fun ctype: String
-       do
+       redef var ctype is lazy do
                if mclass.name == "Int" then
                        return "long"
                else if mclass.name == "Bool" then
@@ -1777,6 +1834,8 @@ redef class MClassType
                end
        end
 
+       redef var is_c_primitive is lazy do return ctype != "val*"
+
        redef fun ctype_extern: String
        do
                if mclass.kind == extern_kind then
@@ -2266,7 +2325,7 @@ redef class AAttrPropdef
                        if is_lazy then
                                var set
                                var ret = self.mpropdef.static_mtype
-                               var useiset = ret.ctype == "val*" and not ret isa MNullableType
+                               var useiset = not ret.is_c_primitive and not ret isa MNullableType
                                var guard = self.mlazypropdef.mproperty
                                if useiset then
                                        set = v.isset_attribute(self.mpropdef.mproperty, recv)
@@ -2281,7 +2340,7 @@ redef class AAttrPropdef
 
                                v.assign(res, value)
                                if not useiset then
-                                       var true_v = v.new_expr("1", v.bool_type)
+                                       var true_v = v.bool_instance(true)
                                        v.write_attribute(guard, arguments.first, true_v)
                                end
                                v.add("\}")
@@ -2294,9 +2353,9 @@ redef class AAttrPropdef
                        v.write_attribute(self.mpropdef.mproperty, arguments.first, arguments[1])
                        if is_lazy then
                                var ret = self.mpropdef.static_mtype
-                               var useiset = ret.ctype == "val*" and not ret isa MNullableType
+                               var useiset = not ret.is_c_primitive and not ret isa MNullableType
                                if not useiset then
-                                       v.write_attribute(self.mlazypropdef.mproperty, arguments.first, v.new_expr("1", v.bool_type))
+                                       v.write_attribute(self.mlazypropdef.mproperty, arguments.first, v.bool_instance(true))
                                end
                        end
                else
@@ -2694,15 +2753,15 @@ redef class AOrElseExpr
 end
 
 redef class AIntExpr
-       redef fun expr(v) do return v.new_expr("{self.value.to_s}", self.mtype.as(not null))
+       redef fun expr(v) do return v.int_instance(self.value.as(not null))
 end
 
 redef class AFloatExpr
-       redef fun expr(v) do return v.new_expr("{self.n_float.text}", self.mtype.as(not null)) # FIXME use value, not n_float
+       redef fun expr(v) do return v.float_instance("{self.n_float.text}") # FIXME use value, not n_float
 end
 
 redef class ACharExpr
-       redef fun expr(v) do return v.new_expr("'{self.value.to_s.escape_to_c}'", self.mtype.as(not null))
+       redef fun expr(v) do return v.char_instance(self.value.as(not null))
 end
 
 redef class AArrayExpr
@@ -2730,14 +2789,64 @@ end
 redef class ASuperstringExpr
        redef fun expr(v)
        do
-               var array = new Array[RuntimeVariable]
+               var type_string = mtype.as(not null)
+
+               # Collect elements of the superstring
+               var array = new Array[AExpr]
                for ne in self.n_exprs do
+                       # Drop literal empty string.
+                       # They appears in things like "{a}" that is ["", a, ""]
                        if ne isa AStringFormExpr and ne.value == "" then continue # skip empty sub-strings
-                       var i = v.expr(ne, null)
-                       array.add(i)
+                       array.add(ne)
+               end
+
+               # Store the allocated native array in a static variable
+               # For reusing later
+               var varonce = v.get_name("varonce")
+               v.add("if (unlikely({varonce}==NULL)) \{")
+
+               # The native array that will contains the elements to_s-ized.
+               # For fast concatenation.
+               var a = v.native_array_instance(type_string, v.int_instance(array.length))
+
+               v.add_decl("static {a.mtype.ctype} {varonce};")
+
+               # Pre-fill the array with the literal string parts.
+               # So they do not need to be filled again when reused
+               for i in [0..array.length[ do
+                       var ne = array[i]
+                       if not ne isa AStringFormExpr then continue
+                       var e = v.expr(ne, null)
+                       v.native_array_set(a, i, e)
                end
-               var a = v.array_instance(array, v.object_type)
-               var res = v.send(v.get_property("to_s", a.mtype), [a])
+
+               v.add("\} else \{")
+               # Take the native-array from the store.
+               # The point is to prevent that some recursive execution use (and corrupt) the same native array
+               # WARNING: not thread safe! (FIXME?)
+               v.add("{a} = {varonce};")
+               v.add("{varonce} = NULL;")
+               v.add("\}")
+
+               # Stringify the elements and put them in the native array
+               var to_s_method = v.get_property("to_s", v.object_type)
+               for i in [0..array.length[ do
+                       var ne = array[i]
+                       if ne isa AStringFormExpr then continue
+                       var e = v.expr(ne, null)
+                       # Skip the `to_s` if the element is already a String
+                       if not e.mcasttype.is_subtype(v.compiler.mainmodule, null, type_string) then
+                               e = v.send(to_s_method, [e]).as(not null)
+                       end
+                       v.native_array_set(a, i, e)
+               end
+
+               # Fast join the native string to get the result
+               var res = v.send(v.get_property("native_to_s", a.mtype), [a])
+
+               # We finish to work with the native array,
+               # so store it so that it can be reused
+               v.add("{varonce} = {a};")
                return res
        end
 end
@@ -2767,15 +2876,15 @@ redef class AOrangeExpr
 end
 
 redef class ATrueExpr
-       redef fun expr(v) do return v.new_expr("1", self.mtype.as(not null))
+       redef fun expr(v) do return v.bool_instance(true)
 end
 
 redef class AFalseExpr
-       redef fun expr(v) do return v.new_expr("0", self.mtype.as(not null))
+       redef fun expr(v) do return v.bool_instance(false)
 end
 
 redef class ANullExpr
-       redef fun expr(v) do return v.new_expr("NULL", self.mtype.as(not null))
+       redef fun expr(v) do return v.null_instance
 end
 
 redef class AIsaExpr
@@ -2803,7 +2912,7 @@ redef class AAsNotnullExpr
                var i = v.expr(self.n_expr, null)
                if v.compiler.modelbuilder.toolcontext.opt_no_check_assert.value then return i
 
-               if i.mtype.ctype != "val*" then return i
+               if i.mtype.is_c_primitive then return i
 
                v.add("if (unlikely({i} == NULL)) \{")
                v.add_abort("Cast failed")
index ea60c0a..c013295 100644 (file)
@@ -22,3 +22,4 @@ import compiler_ffi
 import platform::android
 import platform::pnacl
 import platform::emscripten
+import platform::ios
index a01e362..1aff84e 100644 (file)
@@ -86,7 +86,7 @@ class GlobalCompiler
                self.header = new CodeWriter(file)
                self.live_primitive_types = new Array[MClassType]
                for t in runtime_type_analysis.live_types do
-                       if t.ctype != "val*" or t.mclass.name == "Pointer" then
+                       if t.is_c_primitive or t.mclass.name == "Pointer" then
                                self.live_primitive_types.add(t)
                        end
                end
@@ -109,7 +109,7 @@ class GlobalCompiler
 
                # Init instance code (allocate and init-arguments)
                for t in runtime_type_analysis.live_types do
-                       if t.ctype == "val*" then
+                       if not t.is_c_primitive then
                                compiler.generate_init_instance(t)
                                if t.mclass.kind == extern_kind then
                                        compiler.generate_box_instance(t)
@@ -228,7 +228,7 @@ class GlobalCompiler
        fun generate_init_instance(mtype: MClassType)
        do
                assert self.runtime_type_analysis.live_types.has(mtype)
-               assert mtype.ctype == "val*"
+               assert not mtype.is_c_primitive
                var v = self.new_visitor
 
                var is_native_array = mtype.mclass.name == "NativeArray"
@@ -303,11 +303,11 @@ class GlobalCompilerVisitor
        do
                if value.mtype == mtype then
                        return value
-               else if value.mtype.ctype == "val*" and mtype.ctype == "val*" then
+               else if not value.mtype.is_c_primitive and not mtype.is_c_primitive then
                        return value
-               else if value.mtype.ctype == "val*" then
+               else if not value.mtype.is_c_primitive then
                        return self.new_expr("((struct {mtype.c_name}*){value})->value; /* autounbox from {value.mtype} to {mtype} */", mtype)
-               else if mtype.ctype == "val*" then
+               else if not mtype.is_c_primitive then
                        var valtype = value.mtype.as(MClassType)
                        var res = self.new_var(mtype)
                        if not compiler.runtime_type_analysis.live_types.has(valtype) then
@@ -402,11 +402,24 @@ class GlobalCompilerVisitor
 
        redef fun native_array_instance(elttype: MType, length: RuntimeVariable): RuntimeVariable
        do
-               var ret_type = self.get_class("NativeArray").get_mtype([elttype])
+               var ret_type = mmodule.native_array_type(elttype)
                ret_type = anchor(ret_type).as(MClassType)
                return self.new_expr("NEW_{ret_type.c_name}({length})", ret_type)
        end
 
+       redef fun native_array_get(nat, i)
+       do
+               var recv = "((struct {nat.mcasttype.c_name}*){nat})->values"
+               var ret_type = nat.mcasttype.as(MClassType).arguments.first
+               return self.new_expr("{recv}[{i}]", ret_type)
+       end
+
+       redef fun native_array_set(nat, i, val)
+       do
+               var recv = "((struct {nat.mcasttype.c_name}*){nat})->values"
+               self.add("{recv}[{i}]={val};")
+       end
+
        redef fun calloc_array(ret_type, arguments)
        do
                self.ret(self.new_expr("NEW_{ret_type.c_name}({arguments[1]})", ret_type))
@@ -426,7 +439,7 @@ class GlobalCompilerVisitor
                end
 
                self.add("/* send {m} on {args.first.inspect} */")
-               if args.first.mtype.ctype != "val*" then
+               if args.first.mtype.is_c_primitive then
                        var mclasstype = args.first.mtype.as(MClassType)
                        if not self.compiler.runtime_type_analysis.live_types.has(mclasstype) then
                                self.add("/* skip, no method {m} */")
@@ -477,7 +490,7 @@ class GlobalCompilerVisitor
                var defaultpropdef: nullable MMethodDef = null
                for t in types do
                        var propdef = m.lookup_first_definition(self.compiler.mainmodule, t)
-                       if propdef.mclassdef.mclass.name == "Object" and t.ctype == "val*" then
+                       if propdef.mclassdef.mclass.name == "Object" and not t.is_c_primitive then
                                defaultpropdef = propdef
                                continue
                        end
@@ -552,7 +565,7 @@ class GlobalCompilerVisitor
                end
 
                self.add("/* super {m} on {args.first.inspect} */")
-               if args.first.mtype.ctype != "val*" then
+               if args.first.mtype.is_c_primitive then
                        var mclasstype = args.first.mtype.as(MClassType)
                        if not self.compiler.runtime_type_analysis.live_types.has(mclasstype) then
                                self.add("/* skip, no method {m} */")
@@ -628,7 +641,7 @@ class GlobalCompilerVisitor
 
        fun bugtype(recv: RuntimeVariable)
        do
-               if recv.mtype.ctype != "val*" then return
+               if recv.mtype.is_c_primitive then return
                self.add("PRINT_ERROR(\"BTD BUG: Dynamic type is %s, static type is %s\\n\", class_names[{recv}->classid], \"{recv.mcasttype}\");")
                self.add("fatal_exit(1);")
        end
@@ -659,7 +672,7 @@ class GlobalCompilerVisitor
                        ta = self.resolve_for(ta, recv2)
                        var attr = self.new_expr("((struct {t.c_name}*){recv})->{a.intro.c_name}", ta)
                        if not ta isa MNullableType then
-                               if ta.ctype == "val*" then
+                               if not ta.is_c_primitive then
                                        self.add("{res} = ({attr} != NULL);")
                                else
                                        self.add("{res} = 1; /*NOTYET isset on primitive attributes*/")
@@ -705,7 +718,7 @@ class GlobalCompilerVisitor
                        ta = self.resolve_for(ta, recv2)
                        var res2 = self.new_expr("((struct {t.c_name}*){recv})->{a.intro.c_name}", ta)
                        if not ta isa MNullableType and not self.compiler.modelbuilder.toolcontext.opt_no_check_attr_isset.value then
-                               if ta.ctype == "val*" then
+                               if not ta.is_c_primitive then
                                        self.add("if ({res2} == NULL) \{")
                                        self.add_abort("Uninitialized attribute {a.name}")
                                        self.add("\}")
@@ -781,7 +794,7 @@ class GlobalCompilerVisitor
                var res = self.new_var(bool_type)
 
                self.add("/* isa {mtype} on {value.inspect} */")
-               if value.mtype.ctype != "val*" then
+               if value.mtype.is_c_primitive then
                        if value.mtype.is_subtype(self.compiler.mainmodule, null, mtype) then
                                self.add("{res} = 1;")
                        else
@@ -816,14 +829,14 @@ class GlobalCompilerVisitor
        redef fun is_same_type_test(value1, value2)
        do
                var res = self.new_var(bool_type)
-               if value2.mtype.ctype == "val*" then
-                       if value1.mtype.ctype == "val*" then
+               if not value2.mtype.is_c_primitive then
+                       if not value1.mtype.is_c_primitive then
                                self.add "{res} = {value1}->classid == {value2}->classid;"
                        else
                                self.add "{res} = {self.compiler.classid(value1.mtype.as(MClassType))} == {value2}->classid;"
                        end
                else
-                       if value1.mtype.ctype == "val*" then
+                       if not value1.mtype.is_c_primitive then
                                self.add "{res} = {value1}->classid == {self.compiler.classid(value2.mtype.as(MClassType))};"
                        else if value1.mcasttype == value2.mcasttype then
                                self.add "{res} = 1;"
@@ -838,7 +851,7 @@ class GlobalCompilerVisitor
        do
                var res = self.get_name("var_class_name")
                self.add_decl("const char* {res};")
-               if value.mtype.ctype == "val*" then
+               if not value.mtype.is_c_primitive then
                        self.add "{res} = class_names[{value}->classid];"
                else
                        self.add "{res} = class_names[{self.compiler.classid(value.mtype.as(MClassType))}];"
@@ -849,15 +862,15 @@ class GlobalCompilerVisitor
        redef fun equal_test(value1, value2)
        do
                var res = self.new_var(bool_type)
-               if value2.mtype.ctype != "val*" and value1.mtype.ctype == "val*" then
+               if value2.mtype.is_c_primitive and not value1.mtype.is_c_primitive then
                        var tmp = value1
                        value1 = value2
                        value2 = tmp
                end
-               if value1.mtype.ctype != "val*" then
+               if value1.mtype.is_c_primitive then
                        if value2.mtype == value1.mtype then
                                self.add("{res} = {value1} == {value2};")
-                       else if value2.mtype.ctype != "val*" then
+                       else if value2.mtype.is_c_primitive then
                                self.add("{res} = 0; /* incompatible types {value1.mtype} vs. {value2.mtype}*/")
                        else
                                var mtype1 = value1.mtype.as(MClassType)
@@ -894,10 +907,10 @@ class GlobalCompilerVisitor
        redef fun array_instance(array, elttype)
        do
                elttype = self.anchor(elttype)
-               var arraytype = self.get_class("Array").get_mtype([elttype])
+               var arraytype = mmodule.array_type(elttype)
                var res = self.init_instance(arraytype)
                self.add("\{ /* {res} = array_instance Array[{elttype}] */")
-               var nat = self.new_var(self.get_class("NativeArray").get_mtype([elttype]))
+               var nat = self.new_var(mmodule.native_array_type(elttype))
                nat.is_exact = true
                self.add("{nat} = NEW_{nat.mtype.c_name}({array.length});")
                for i in [0..array.length[ do
@@ -991,7 +1004,7 @@ private class CustomizedRuntimeFunction
                for i in [0..mmethoddef.msignature.arity[ do
                        var mtype = mmethoddef.msignature.mparameters[i].mtype
                        if i == mmethoddef.msignature.vararg_rank then
-                               mtype = v.get_class("Array").get_mtype([mtype])
+                               mtype = v.mmodule.array_type(mtype)
                        end
                        mtype = v.resolve_for(mtype, selfvar)
                        comment.append(", {mtype}")
index 555c220..dab4a79 100644 (file)
@@ -269,7 +269,7 @@ class SeparateCompiler
                if mclass.mclass_type.ctype_extern == "val*" then
                        return 0
                else if mclass.kind == extern_kind and mclass.name != "NativeString" then
-                       return self.box_kinds[self.mainmodule.get_primitive_class("Pointer")]
+                       return self.box_kinds[self.mainmodule.pointer_type.mclass]
                else
                        return self.box_kinds[mclass]
                end
@@ -817,7 +817,7 @@ class SeparateCompiler
                var v = new_visitor
 
                var rta = runtime_type_analysis
-               var is_dead = rta != null and not rta.live_classes.has(mclass) and mtype.ctype == "val*" and mclass.name != "NativeArray" and mclass.name != "Pointer"
+               var is_dead = rta != null and not rta.live_classes.has(mclass) and not mtype.is_c_primitive and mclass.name != "NativeArray" and mclass.name != "Pointer"
 
                v.add_decl("/* runtime class {c_name} */")
 
@@ -847,7 +847,7 @@ class SeparateCompiler
                        v.add_decl("\};")
                end
 
-               if mtype.ctype != "val*" or mtype.mclass.name == "Pointer" then
+               if mtype.is_c_primitive or mtype.mclass.name == "Pointer" then
                        # Is a primitive type or the Pointer class, not any other extern class
 
                        if mtype.is_tagged then return
@@ -1185,9 +1185,9 @@ class SeparateCompilerVisitor
        do
                if value.mtype == mtype then
                        return value
-               else if value.mtype.ctype == "val*" and mtype.ctype == "val*" then
+               else if not value.mtype.is_c_primitive and not mtype.is_c_primitive then
                        return value
-               else if value.mtype.ctype == "val*" then
+               else if not value.mtype.is_c_primitive then
                        if mtype.is_tagged then
                                if mtype.name == "Int" then
                                        return self.new_expr("(long)({value})>>2", mtype)
@@ -1200,7 +1200,7 @@ class SeparateCompilerVisitor
                                end
                        end
                        return self.new_expr("((struct instance_{mtype.c_name}*){value})->value; /* autounbox from {value.mtype} to {mtype} */", mtype)
-               else if mtype.ctype == "val*" then
+               else if not mtype.is_c_primitive then
                        if value.mtype.is_tagged then
                                if value.mtype.name == "Int" then
                                        return self.new_expr("(val*)({value}<<2|1)", mtype)
@@ -1280,7 +1280,7 @@ class SeparateCompilerVisitor
        # Thus the expression can be used as a condition.
        fun extract_tag(value: RuntimeVariable): String
        do
-               assert value.mtype.ctype == "val*"
+               assert not value.mtype.is_c_primitive
                return "((long){value}&3)" # Get the two low bits
        end
 
@@ -1288,7 +1288,7 @@ class SeparateCompilerVisitor
        # The point of the method is to work also with primitive types.
        fun class_info(value: RuntimeVariable): String
        do
-               if value.mtype.ctype == "val*" then
+               if not value.mtype.is_c_primitive then
                        if can_be_primitive(value) and not compiler.modelbuilder.toolcontext.opt_no_tag_primitives.value then
                                var tag = extract_tag(value)
                                return "({tag}?class_info[{tag}]:{value}->class)"
@@ -1305,7 +1305,7 @@ class SeparateCompilerVisitor
        # The point of the method is to work also with primitive types.
        fun type_info(value: RuntimeVariable): String
        do
-               if value.mtype.ctype == "val*" then
+               if not value.mtype.is_c_primitive then
                        if can_be_primitive(value) and not compiler.modelbuilder.toolcontext.opt_no_tag_primitives.value then
                                var tag = extract_tag(value)
                                return "({tag}?type_info[{tag}]:{value}->type)"
@@ -1354,7 +1354,7 @@ class SeparateCompilerVisitor
        end
        redef fun send(mmethod, arguments)
        do
-               if arguments.first.mcasttype.ctype != "val*" then
+               if arguments.first.mcasttype.is_c_primitive then
                        # In order to shortcut the primitive, we need to find the most specific method
                        # Howverr, because of performance (no flattening), we always work on the realmainmodule
                        var m = self.compiler.mainmodule
@@ -1561,7 +1561,7 @@ class SeparateCompilerVisitor
 
        redef fun supercall(m: MMethodDef, recvtype: MClassType, arguments: Array[RuntimeVariable]): nullable RuntimeVariable
        do
-               if arguments.first.mcasttype.ctype != "val*" then
+               if arguments.first.mcasttype.is_c_primitive then
                        # In order to shortcut the primitive, we need to find the most specific method
                        # However, because of performance (no flattening), we always work on the realmainmodule
                        var main = self.compiler.mainmodule
@@ -1612,7 +1612,7 @@ class SeparateCompilerVisitor
                        self.add("{res} = {recv}->attrs[{a.const_color}] != NULL; /* {a} on {recv.inspect}*/")
                else
 
-                       if mtype.ctype == "val*" then
+                       if not mtype.is_c_primitive and not mtype.is_tagged then
                                self.add("{res} = {recv}->attrs[{a.const_color}].val != NULL; /* {a} on {recv.inspect} */")
                        else
                                self.add("{res} = 1; /* NOT YET IMPLEMENTED: isset of primitives: {a} on {recv.inspect} */")
@@ -1664,7 +1664,7 @@ class SeparateCompilerVisitor
                        self.add("{res} = {recv}->attrs[{a.const_color}].{ret.ctypename}; /* {a} on {recv.inspect} */")
 
                        # Check for Uninitialized attribute
-                       if ret.ctype == "val*" and not ret isa MNullableType and not self.compiler.modelbuilder.toolcontext.opt_no_check_attr_isset.value then
+                       if not ret.is_c_primitive and not ret isa MNullableType and not self.compiler.modelbuilder.toolcontext.opt_no_check_attr_isset.value then
                                self.add("if (unlikely({res} == NULL)) \{")
                                self.add_abort("Uninitialized attribute {a.name}")
                                self.add("\}")
@@ -1693,7 +1693,11 @@ class SeparateCompilerVisitor
                self.require_declaration(a.const_color)
                if self.compiler.modelbuilder.toolcontext.opt_no_union_attribute.value then
                        var attr = "{recv}->attrs[{a.const_color}]"
-                       if mtype.ctype != "val*" then
+                       if mtype.is_tagged then
+                               # The attribute is not primitive, thus store it as tagged
+                               var tv = autobox(value, compiler.mainmodule.object_type)
+                               self.add("{attr} = {tv}; /* {a} on {recv.inspect} */")
+                       else if mtype.is_c_primitive then
                                assert mtype isa MClassType
                                # The attribute is primitive, thus we store it in a box
                                # The trick is to create the box the first time then resuse the box
@@ -1845,15 +1849,15 @@ class SeparateCompilerVisitor
        do
                var res = self.new_var(bool_type)
                # Swap values to be symetric
-               if value2.mtype.ctype != "val*" and value1.mtype.ctype == "val*" then
+               if value2.mtype.is_c_primitive and not value1.mtype.is_c_primitive then
                        var tmp = value1
                        value1 = value2
                        value2 = tmp
                end
-               if value1.mtype.ctype != "val*" then
+               if value1.mtype.is_c_primitive then
                        if value2.mtype == value1.mtype then
                                self.add("{res} = 1; /* is_same_type_test: compatible types {value1.mtype} vs. {value2.mtype} */")
-                       else if value2.mtype.ctype != "val*" then
+                       else if value2.mtype.is_c_primitive then
                                self.add("{res} = 0; /* is_same_type_test: incompatible types {value1.mtype} vs. {value2.mtype}*/")
                        else
                                var mtype1 = value1.mtype.as(MClassType)
@@ -1870,7 +1874,7 @@ class SeparateCompilerVisitor
        do
                var res = self.get_name("var_class_name")
                self.add_decl("const char* {res};")
-               if value.mtype.ctype == "val*" then
+               if not value.mtype.is_c_primitive then
                        self.add "{res} = {value} == NULL ? \"null\" : {type_info(value)}->name;"
                else if value.mtype isa MClassType and value.mtype.as(MClassType).mclass.kind == extern_kind and
                        value.mtype.as(MClassType).name != "NativeString" then
@@ -1885,15 +1889,15 @@ class SeparateCompilerVisitor
        redef fun equal_test(value1, value2)
        do
                var res = self.new_var(bool_type)
-               if value2.mtype.ctype != "val*" and value1.mtype.ctype == "val*" then
+               if value2.mtype.is_c_primitive and not value1.mtype.is_c_primitive then
                        var tmp = value1
                        value1 = value2
                        value2 = tmp
                end
-               if value1.mtype.ctype != "val*" then
+               if value1.mtype.is_c_primitive then
                        if value2.mtype == value1.mtype then
                                self.add("{res} = {value1} == {value2};")
-                       else if value2.mtype.ctype != "val*" then
+                       else if value2.mtype.is_c_primitive then
                                self.add("{res} = 0; /* incompatible types {value1.mtype} vs. {value2.mtype}*/")
                        else if value1.mtype.is_tagged then
                                self.add("{res} = ({value2} != NULL) && ({self.autobox(value2, value1.mtype)} == {value1});")
@@ -1926,11 +1930,11 @@ class SeparateCompilerVisitor
 
                var incompatible = false
                var primitive
-               if t1.ctype != "val*" then
+               if t1.is_c_primitive then
                        primitive = t1
                        if t1 == t2 then
                                # No need to compare class
-                       else if t2.ctype != "val*" then
+                       else if t2.is_c_primitive then
                                incompatible = true
                        else if can_be_primitive(value2) then
                                if t1.is_tagged then
@@ -1944,7 +1948,7 @@ class SeparateCompilerVisitor
                        else
                                incompatible = true
                        end
-               else if t2.ctype != "val*" then
+               else if t2.is_c_primitive then
                        primitive = t2
                        if can_be_primitive(value1) then
                                if t2.is_tagged then
@@ -2005,7 +2009,7 @@ class SeparateCompilerVisitor
                var t = value.mcasttype.as_notnullable
                if not t isa MClassType then return false
                var k = t.mclass.kind
-               return k == interface_kind or t.ctype != "val*"
+               return k == interface_kind or t.is_c_primitive
        end
 
        fun maybe_null(value: RuntimeVariable): Bool
@@ -2016,8 +2020,8 @@ class SeparateCompilerVisitor
 
        redef fun array_instance(array, elttype)
        do
-               var nclass = self.get_class("NativeArray")
-               var arrayclass = self.get_class("Array")
+               var nclass = mmodule.native_array_class
+               var arrayclass = mmodule.array_class
                var arraytype = arrayclass.get_mtype([elttype])
                var res = self.init_instance(arraytype)
                self.add("\{ /* {res} = array_instance Array[{elttype}] */")
@@ -2034,7 +2038,7 @@ class SeparateCompilerVisitor
 
        redef fun native_array_instance(elttype: MType, length: RuntimeVariable): RuntimeVariable
        do
-               var mtype = self.get_class("NativeArray").get_mtype([elttype])
+               var mtype = mmodule.native_array_type(elttype)
                self.require_declaration("NEW_{mtype.mclass.c_name}")
                assert mtype isa MGenericType
                var compiler = self.compiler
@@ -2054,7 +2058,7 @@ class SeparateCompilerVisitor
        redef fun native_array_def(pname, ret_type, arguments)
        do
                var elttype = arguments.first.mtype
-               var nclass = self.get_class("NativeArray")
+               var nclass = mmodule.native_array_class
                var recv = "((struct instance_{nclass.c_name}*){arguments[0]})->values"
                if pname == "[]" then
                        # Because the objects are boxed, return the box to avoid unnecessary (or broken) unboxing/reboxing
@@ -2075,12 +2079,20 @@ class SeparateCompilerVisitor
                end
        end
 
-       redef fun calloc_array(ret_type, arguments)
+       redef fun native_array_get(nat, i)
+       do
+               var nclass = mmodule.native_array_class
+               var recv = "((struct instance_{nclass.c_name}*){nat})->values"
+               # Because the objects are boxed, return the box to avoid unnecessary (or broken) unboxing/reboxing
+               var res = self.new_expr("{recv}[{i}]", compiler.mainmodule.object_type)
+               return res
+       end
+
+       redef fun native_array_set(nat, i, val)
        do
-               var mclass = self.get_class("ArrayCapable")
-               var ft = mclass.mparameters.first
-               var res = self.native_array_instance(ft, arguments[1])
-               self.ret(res)
+               var nclass = mmodule.native_array_class
+               var recv = "((struct instance_{nclass.c_name}*){nat})->values"
+               self.add("{recv}[{i}]={val};")
        end
 
        fun link_unresolved_type(mclassdef: MClassDef, mtype: MType) do
@@ -2193,7 +2205,7 @@ class SeparateRuntimeFunction
                for i in [0..called_signature.arity[ do
                        var mtype = called_signature.mparameters[i].mtype
                        if i == called_signature.vararg_rank then
-                               mtype = mmethoddef.mclassdef.mmodule.get_primitive_class("Array").get_mtype([mtype])
+                               mtype = mmethoddef.mclassdef.mmodule.array_type(mtype)
                        end
                        sig.append(", {mtype.ctype} p{i}")
                end
@@ -2232,7 +2244,7 @@ class SeparateRuntimeFunction
                for i in [0..msignature.arity[ do
                        var mtype = msignature.mparameters[i].mtype
                        if i == msignature.vararg_rank then
-                               mtype = v.get_class("Array").get_mtype([mtype])
+                               mtype = v.mmodule.array_type(mtype)
                        end
                        comment.append(", {mtype}")
                        var argvar = new RuntimeVariable("p{i}", mtype, mtype)
@@ -2279,14 +2291,14 @@ class SeparateRuntimeFunction
                var selfvar = arguments.first
                var ret = called_signature.return_mtype
 
-               if mmethoddef.is_intro and recv.ctype == "val*" then
+               if mmethoddef.is_intro and not recv.is_c_primitive then
                        var m = mmethoddef.mproperty
                        var n2 = "CALL_" + m.const_color
                        compiler.provide_declaration(n2, "{c_ret} {n2}{c_sig};")
                        var v2 = compiler.new_visitor
                        v2.add "{c_ret} {n2}{c_sig} \{"
                        v2.require_declaration(m.const_color)
-                       var call = "(({c_funptrtype})({selfvar}->class->vft[{m.const_color}]))({arguments.join(", ")});"
+                       var call = "(({c_funptrtype})({v2.class_info(selfvar)}->vft[{m.const_color}]))({arguments.join(", ")});"
                        if ret != null then
                                v2.add "return {call}"
                        else
@@ -2296,14 +2308,14 @@ class SeparateRuntimeFunction
                        v2.add "\}"
 
                end
-               if mmethoddef.has_supercall and recv.ctype == "val*" then
+               if mmethoddef.has_supercall and not recv.is_c_primitive then
                        var m = mmethoddef
                        var n2 = "CALL_" + m.const_color
                        compiler.provide_declaration(n2, "{c_ret} {n2}{c_sig};")
                        var v2 = compiler.new_visitor
                        v2.add "{c_ret} {n2}{c_sig} \{"
                        v2.require_declaration(m.const_color)
-                       var call = "(({c_funptrtype})({selfvar}->class->vft[{m.const_color}]))({arguments.join(", ")});"
+                       var call = "(({c_funptrtype})({v2.class_info(selfvar)}->vft[{m.const_color}]))({arguments.join(", ")});"
                        if ret != null then
                                v2.add "return {call}"
                        else
@@ -2344,3 +2356,14 @@ redef class AMethPropdef
                return super
        end
 end
+
+redef class AAttrPropdef
+       redef fun init_expr(v, recv)
+       do
+               super
+               if is_lazy and v.compiler.modelbuilder.toolcontext.opt_no_union_attribute.value then
+                       var guard = self.mlazypropdef.mproperty
+                       v.write_attribute(guard, recv, v.bool_instance(false))
+               end
+       end
+end
index d1c67e5..b8b2c0a 100644 (file)
@@ -204,7 +204,7 @@ class SeparateErasureCompiler
 
                var rta = runtime_type_analysis
                var is_dead = false # mclass.kind == abstract_kind or mclass.kind == interface_kind
-               if not is_dead and rta != null and not rta.live_classes.has(mclass) and mtype.ctype == "val*" and mclass.name != "NativeArray" then
+               if not is_dead and rta != null and not rta.live_classes.has(mclass) and not mtype.is_c_primitive and mclass.name != "NativeArray" then
                        is_dead = true
                end
 
@@ -263,7 +263,7 @@ class SeparateErasureCompiler
                v.add_decl("\}")
                v.add_decl("\};")
 
-               if mtype.ctype != "val*" or mtype.mclass.name == "Pointer" then
+               if mtype.is_c_primitive or mtype.mclass.name == "Pointer" then
                        #Build instance struct
                        self.header.add_decl("struct instance_{c_name} \{")
                        self.header.add_decl("const struct class *class;")
@@ -535,7 +535,7 @@ class SeparateErasureCompilerVisitor
                end
 
                var class_ptr
-               if value.mtype.ctype == "val*" then
+               if not value.mtype.is_c_primitive then
                        class_ptr = "{value}->class->"
                else
                        var mclass = value.mtype.as(MClassType).mclass
@@ -554,7 +554,7 @@ class SeparateErasureCompilerVisitor
                else if mtype isa MVirtualType then
                        var recv = self.frame.arguments.first
                        var recv_ptr
-                       if recv.mtype.ctype == "val*" then
+                       if not recv.mtype.is_c_primitive then
                                recv_ptr = "{recv}->class->"
                        else
                                var mclass = recv.mtype.as(MClassType).mclass
@@ -638,7 +638,7 @@ class SeparateErasureCompilerVisitor
        do
                var res = self.get_name("var_class_name")
                self.add_decl("const char* {res};")
-               if value.mtype.ctype == "val*" then
+               if not value.mtype.is_c_primitive then
                        self.add "{res} = {value} == NULL ? \"null\" : {value}->class->name;"
                else
                        self.require_declaration("class_{value.mtype.c_name}")
@@ -649,7 +649,7 @@ class SeparateErasureCompilerVisitor
 
        redef fun native_array_instance(elttype, length)
        do
-               var nclass = self.get_class("NativeArray")
+               var nclass = mmodule.native_array_class
                var mtype = nclass.get_mtype([elttype])
                var res = self.new_var(mtype)
                res.is_exact = true
index 311d917..aba779a 100644 (file)
@@ -201,8 +201,8 @@ class NaiveInterpreter
        # Return the integer instance associated with `val`.
        fun int_instance(val: Int): Instance
        do
-               var ic = get_primitive_class("Int")
-               var instance = new PrimitiveInstance[Int](ic.mclass_type, val)
+               var t = mainmodule.int_type
+               var instance = new PrimitiveInstance[Int](t, val)
                init_instance_primitive(instance)
                return instance
        end
@@ -210,8 +210,8 @@ class NaiveInterpreter
        # Return the char instance associated with `val`.
        fun char_instance(val: Char): Instance
        do
-               var ic = get_primitive_class("Char")
-               var instance = new PrimitiveInstance[Char](ic.mclass_type, val)
+               var t = mainmodule.char_type
+               var instance = new PrimitiveInstance[Char](t, val)
                init_instance_primitive(instance)
                return instance
        end
@@ -219,8 +219,8 @@ class NaiveInterpreter
        # Return the float instance associated with `val`.
        fun float_instance(val: Float): Instance
        do
-               var ic = get_primitive_class("Float")
-               var instance = new PrimitiveInstance[Float](ic.mclass_type, val)
+               var t = mainmodule.float_type
+               var instance = new PrimitiveInstance[Float](t, val)
                init_instance_primitive(instance)
                return instance
        end
@@ -239,9 +239,9 @@ class NaiveInterpreter
        fun array_instance(values: Array[Instance], elttype: MType): Instance
        do
                assert not elttype.need_anchor
-               var nat = new PrimitiveInstance[Array[Instance]](get_primitive_class("NativeArray").get_mtype([elttype]), values)
+               var nat = new PrimitiveInstance[Array[Instance]](mainmodule.native_array_type(elttype), values)
                init_instance_primitive(nat)
-               var mtype = get_primitive_class("Array").get_mtype([elttype])
+               var mtype = mainmodule.array_type(elttype)
                var res = new MutableInstance(mtype)
                self.init_instance(res)
                self.send(self.force_get_primitive_method("with_native", mtype), [res, nat, self.int_instance(values.length)])
@@ -268,8 +268,8 @@ class NaiveInterpreter
        do
                var val = new FlatBuffer.from(txt)
                val.add('\0')
-               var ic = get_primitive_class("NativeString")
-               var instance = new PrimitiveInstance[Buffer](ic.mclass_type, val)
+               var t = mainmodule.native_string_type
+               var instance = new PrimitiveInstance[Buffer](t, val)
                init_instance_primitive(instance)
                return instance
        end
@@ -588,13 +588,6 @@ class NaiveInterpreter
        # A hook to initialize a `PrimitiveInstance`
        fun init_instance_primitive(recv: Instance) do end
 
-       # Return the primitive `MClass` corresponding to the `name` given in parameter
-       # `name` : name of the primitive class
-       fun get_primitive_class(name: String): MClass
-       do
-               return mainmodule.get_primitive_class(name)
-       end
-
        # This function determines the correct type according to the receiver of the current propdef (self).
        fun unanchor_type(mtype: MType): MType
        do
@@ -1030,7 +1023,7 @@ redef class AMethPropdef
                        if pname == "files" then
                                var res = new Array[Instance]
                                for f in str.files do res.add v.string_instance(f)
-                               return v.array_instance(res, v.get_primitive_class("String").mclass_type)
+                               return v.array_instance(res, v.mainmodule.string_type)
                        end
                else if pname == "calloc_string" then
                        return v.native_string_instance("!" * args[1].to_i)
@@ -1104,15 +1097,6 @@ redef class AMethPropdef
                        else if pname == "set_buffering_type" then
                                return v.int_instance(recvval.as(PrimitiveNativeFile).set_buffering_type(args[1].to_i, args[2].to_i))
                        end
-               else if pname == "calloc_array" then
-                       var recvtype = args.first.mtype.as(MClassType)
-                       var mtype: MType
-                       mtype = recvtype.supertype_to(v.mainmodule, recvtype, v.get_primitive_class("ArrayCapable"))
-                       mtype = mtype.arguments.first
-                       var val = new Array[Instance].filled_with(v.null_instance, args[1].to_i)
-                       var instance = new PrimitiveInstance[Array[Instance]](v.get_primitive_class("NativeArray").get_mtype([mtype]), val)
-                       v.init_instance_primitive(instance)
-                       return instance
                else if pname == "native_argc" then
                        return v.int_instance(v.arguments.length)
                else if pname == "native_argv" then
@@ -1606,7 +1590,7 @@ redef class ASuperstringExpr
                        if i == null then return null
                        array.add(i)
                end
-               var i = v.array_instance(array, v.get_primitive_class("Object").mclass_type)
+               var i = v.array_instance(array, v.mainmodule.object_type)
                var res = v.send(v.force_get_primitive_method("to_s", i.mtype), [i])
                assert res != null
                return res
index b9b484a..76a3839 100644 (file)
@@ -195,31 +195,40 @@ redef class MModule
        private var flatten_mclass_hierarchy_cache: nullable POSet[MClass] = null
 
        # The primitive type `Object`, the root of the class hierarchy
-       fun object_type: MClassType
-       do
-               var res = self.object_type_cache
-               if res != null then return res
-               res = self.get_primitive_class("Object").mclass_type
-               self.object_type_cache = res
-               return res
-       end
-
-       private var object_type_cache: nullable MClassType
+       var object_type: MClassType = self.get_primitive_class("Object").mclass_type is lazy
 
        # The type `Pointer`, super class to all extern classes
        var pointer_type: MClassType = self.get_primitive_class("Pointer").mclass_type is lazy
 
        # The primitive type `Bool`
-       fun bool_type: MClassType
-       do
-               var res = self.bool_type_cache
-               if res != null then return res
-               res = self.get_primitive_class("Bool").mclass_type
-               self.bool_type_cache = res
-               return res
-       end
+       var bool_type: MClassType = self.get_primitive_class("Bool").mclass_type is lazy
+
+       # The primitive type `Int`
+       var int_type: MClassType = self.get_primitive_class("Int").mclass_type is lazy
+
+       # The primitive type `Char`
+       var char_type: MClassType = self.get_primitive_class("Char").mclass_type is lazy
+
+       # The primitive type `Float`
+       var float_type: MClassType = self.get_primitive_class("Float").mclass_type is lazy
+
+       # The primitive type `String`
+       var string_type: MClassType = self.get_primitive_class("String").mclass_type is lazy
+
+       # The primitive type `NativeString`
+       var native_string_type: MClassType = self.get_primitive_class("NativeString").mclass_type is lazy
+
+       # A primitive type of `Array`
+       fun array_type(elt_type: MType): MClassType do return array_class.get_mtype([elt_type])
+
+       # The primitive class `Array`
+       var array_class: MClass = self.get_primitive_class("Array") is lazy
+
+       # A primitive type of `NativeArray`
+       fun native_array_type(elt_type: MType): MClassType do return native_array_class.get_mtype([elt_type])
 
-       private var bool_type_cache: nullable MClassType
+       # The primitive class `NativeArray`
+       var native_array_class: MClass = self.get_primitive_class("NativeArray") is lazy
 
        # The primitive type `Sys`, the main type of the program, if any
        fun sys_type: nullable MClassType
index 13f8c59..5a74d48 100644 (file)
@@ -21,8 +21,6 @@ import interpreter
 import frontend
 import parser_util
 import vm
-import vm_optimizations
-import variables_numbering
 
 # Create a tool context to handle options and paths
 var toolcontext = new ToolContext
index 1fef7a4..088cd92 100644 (file)
@@ -18,8 +18,6 @@
 module nitvm
 
 import vm
-import vm_optimizations
-import variables_numbering
 import frontend
 
 # Create a tool context to handle options and paths
index b411225..2c9e706 100644 (file)
@@ -42,7 +42,7 @@ class AndroidPlatform
 
        redef fun supports_linker_script do return false
 
-       redef fun toolchain(toolcontext) do return new AndroidToolchain(toolcontext)
+       redef fun toolchain(toolcontext, compiler) do return new AndroidToolchain(toolcontext, compiler)
 end
 
 class AndroidToolchain
@@ -57,9 +57,9 @@ class AndroidToolchain
                return "{android_project_root}/jni/nit_compile/"
        end
 
-       redef fun default_outname(mainmodule) do return "{mainmodule.name}.apk"
+       redef fun default_outname do return "{super}.apk"
 
-       redef fun write_files(compiler, compile_dir, cfiles)
+       redef fun write_files(compile_dir, cfiles)
        do
                var android_project_root = android_project_root.as(not null)
                var project = toolcontext.modelbuilder.android_project_for(compiler.mainmodule)
@@ -107,7 +107,7 @@ class AndroidToolchain
                if not dir.file_exists then dir.mkdir
 
                # compile normal C files
-               super(compiler, compile_dir, cfiles)
+               super
 
                # Gather extra C files generated elsewhere than in super
                for f in compiler.extern_bodies do
@@ -227,6 +227,7 @@ $(call import-module,android/native_app_glue)
 </manifest>
 <!-- END_INCLUDE(manifest) -->
 """
+               manifest_file.close
 
                ### Link to png sources
                # libpng is not available on Android NDK
@@ -310,12 +311,12 @@ $(call import-module,android/native_app_glue)
                end
        end
 
-       redef fun write_makefile(compiler, compile_dir, cfiles)
+       redef fun write_makefile(compile_dir, cfiles)
        do
                # Do nothing, already done in `write_files`
        end
 
-       redef fun compile_c_code(compiler, compile_dir)
+       redef fun compile_c_code(compile_dir)
        do
                var android_project_root = android_project_root.as(not null)
                var short_project_name = compiler.mainmodule.name.replace("-", "_")
index 3e9199a..90e361e 100644 (file)
@@ -35,17 +35,17 @@ class EmscriptenPlatform
        redef fun supports_libunwind do return false
        redef fun supports_libgc do return false
        redef fun supports_linker_script do return false
-       redef fun toolchain(toolcontext) do return new EnscriptenToolchain(toolcontext)
+       redef fun toolchain(toolcontext, compiler) do return new EnscriptenToolchain(toolcontext, compiler)
 end
 
 class EnscriptenToolchain
        super MakefileToolchain
 
-       redef fun makefile_name(mainmodule) do return "{mainmodule.name}.js.mk"
+       redef fun makefile_name do return "{super}.js.mk"
 
-       redef fun default_outname(mainmodule) do return "{super}.js"
+       redef fun default_outname do return "{super}.js"
 
-       redef fun write_makefile(compiler, compile_dir, cfiles)
+       redef fun write_makefile(compile_dir, cfiles)
        do
                super
 
diff --git a/src/platform/ios.nit b/src/platform/ios.nit
new file mode 100644 (file)
index 0000000..94d0c5f
--- /dev/null
@@ -0,0 +1,122 @@
+# This file is part of NIT ( http://www.nitlanguage.org )
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Compile programs for the iOS platform
+module ios
+
+import platform
+import compiler::abstract_compiler
+import xcode_templates
+private import annotation
+
+redef class ToolContext
+       redef fun platform_from_name(name)
+       do
+               if name == "ios" then return new IOSPlatform
+               return super
+       end
+end
+
+private class IOSPlatform
+       super Platform
+
+       redef fun supports_libunwind do return false
+       redef fun supports_libgc do return false
+       redef fun toolchain(toolcontext, compiler) do return new IOSToolchain(toolcontext, compiler)
+end
+
+private class IOSToolchain
+       super MakefileToolchain
+
+       # Root of the iOS project, usually `.nit_compile/ios/`
+       var ios_project_root: String is noinit
+
+       redef fun default_outname do return "{super}.app"
+
+       # Name of the current project of `compiler`
+       fun project_name: String
+       do
+               var project_name = null
+               # TODO unite the app_name annotation from Android with iOS
+               var annot = compiler.modelbuilder.lookup_annotation_on_modules("app_name", compiler.mainmodule)
+               if annot != null then project_name = annot.arg_as_string(compiler.modelbuilder)
+               if project_name == null then project_name = compiler.mainmodule.name
+               return project_name
+       end
+
+       # Compile C files in `ios_project_root/project_name`
+       redef fun compile_dir
+       do
+               ios_project_root = super/"ios"
+               return ios_project_root/project_name
+       end
+
+       redef fun write_files(compile_dir, cfiles)
+       do
+               # Clear the project directory before writing anything
+               if ios_project_root.file_exists then ios_project_root.rmdir
+               compile_dir.mkdir
+
+               super
+       end
+
+       redef fun write_makefile(compile_dir, cfiles)
+       do
+               var project_name = project_name
+
+               # Create an XCode project directory
+               var dir = ios_project_root/project_name+".xcodeproj"
+               if not dir.file_exists then dir.mkdir
+
+               # Create a PBX project file
+               var pbx = new PbxprojectTemplate(project_name)
+
+               ## Register all source files
+               for file in cfiles do pbx.add_file new PbxFile(file)
+               for file in compiler.extern_bodies do
+                       pbx.add_file new PbxFile(file.filename.basename(""))
+               end
+
+               ## TODO Register asset files
+
+               pbx.write_to_file dir/"project.pbxproj"
+
+               # Create the plist in the same directory as the generated C code
+               if not compile_dir.file_exists then compile_dir.mkdir
+               var plist = new PlistTemplate("org.nitlanguage") # TODO customize using an annotation
+               plist.write_to_file compile_dir/"Info.plist"
+       end
+
+       redef fun compile_c_code(compile_dir)
+       do
+               var project_name = project_name
+               var release = toolcontext.opt_release.value
+               var outfile = outfile(compiler.mainmodule)
+
+               # Compile with `xcodebuild`
+               #
+               # TODO support more than the iPhone and the simulator.
+               var args = ["sh", "-c", "cd {ios_project_root}; " +
+                       "xcodebuild -target '{project_name}' " +
+                       "-destination 'platform=iOS Simulator,name=iPhone' " +
+                       "-configuration {if release then "Release" else "Debug"} " +
+                       "-sdk iphonesimulator build"]
+               toolcontext.exec_and_check(args, "iOS project error")
+
+               # Move compiled app to destination
+               if outfile.file_exists then outfile.rmdir
+               args = ["mv", "{ios_project_root}/build/Debug-iphonesimulator/{project_name}.app", outfile]
+               toolcontext.exec_and_check(args, "iOS project error")
+       end
+end
index d7103db..8136187 100644 (file)
@@ -37,13 +37,13 @@ class PnaclPlatform
 
        redef fun no_main do return true
 
-       redef fun toolchain(toolcontext) do return new PnaclToolchain(toolcontext)
+       redef fun toolchain(toolcontext, compiler) do return new PnaclToolchain(toolcontext, compiler)
 end
 
 class PnaclToolchain
        super MakefileToolchain
 
-       redef fun write_files(compiler, compile_dir, cfiles)
+       redef fun write_files(compile_dir, cfiles)
        do
                var app_name = compiler.mainmodule.name
 
@@ -52,7 +52,7 @@ class PnaclToolchain
                if not dir.file_exists then dir.mkdir
 
                # compile normal C files
-               super(compiler, compile_dir, cfiles)
+               super
 
                # Gather extra C files generated elsewhere than in super
                for f in compiler.extern_bodies do
@@ -238,12 +238,12 @@ function updateStatus(opt_message) {
                """.write_to_file(file)
        end
 
-       redef fun write_makefile(compiler, compile_dir, cfiles)
+       redef fun write_makefile(compile_dir, cfiles)
        do
                # Do nothing, already done in `write_files`
        end
 
-       redef fun compile_c_code(compiler, compile_dir)
+       redef fun compile_c_code(compile_dir)
        do
                # Generate the pexe
                toolcontext.exec_and_check(["make", "-C", compile_dir, "-j", "4"], "PNaCl project error")
diff --git a/src/platform/xcode_templates.nit b/src/platform/xcode_templates.nit
new file mode 100644 (file)
index 0000000..de9d500
--- /dev/null
@@ -0,0 +1,681 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Templates and other services to create XCode projects
+module xcode_templates
+
+import template
+
+import platform
+import compiler::abstract_compiler
+
+redef class Sys
+       # Map to identify the PBX file type for a given file extension
+       private var pbx_file_types: Map[String, String] is lazy do
+               var map = new HashMap[String, String]
+
+               # Source code
+               map["m"] =                      "sourcecode.c.objc"
+               map["c"] =                      "sourcecode.c.c"
+               map["h"] =                      "sourcecode.c.h"
+               map["cpp"] =            "sourcecode.cpp.cpp"
+               map["hpp"] =            "sourcecode.cpp.h"
+               map["vsh"] =            "sourcecode.glsl"
+               map["fsh"] =            "sourcecode.glsl"
+
+               # Images
+               map["png"] =            "image.png"
+               map["gif"] =            "image.gif"
+               map["jpg"] =            "image.jpeg"
+               map["jpeg"] =           "image.jpeg"
+               map["pdf"] =            "image.pdf"
+               map["ico"] =            "image.ico"
+
+               # Others
+               map["app"] =            "wrapper.application"
+               map["plist"] =          "text.plist.xml"
+               map["storyboard"] =     "file.storyboard"
+               map["xib"] =            "file.xib"
+               map["xcassets"] =       "folder.assetcatalog"
+               map["xctest"] =         "wrapper.cfbundle"
+
+               return map
+       end
+
+       # Generator of PBX UUIDs quique to an execution of the compiler
+       private var pbx_uuid_generator = new PbxUUIDGenerator is lazy
+end
+
+# Generator of PBX UUIDs
+#
+# PBX UUID are composed of 24 hex characters.
+# They only need to be unique within the same project.
+#
+# This implementation simply counts upward from 0.
+class PbxUUIDGenerator
+       private var seed = 0
+
+       # Generate a new UUID
+       fun next_uuid: String
+       do
+               seed += 1
+
+               var hex_val = seed.to_hex.to_upper
+               return "0"*(24-hex_val.length) + hex_val
+       end
+end
+
+# Reference to a file for the PBX format of a project file
+#
+# TODO create subclasses for different file types, this is currently for
+# compilable source files only.
+class PbxFile
+
+       # Path to `self`
+       var path: String
+
+       # UUID for build elements
+       private var build_uuid: String = sys.pbx_uuid_generator.next_uuid is lazy
+
+       # File reference UUID
+       private var ref_uuid: String = sys.pbx_uuid_generator.next_uuid is lazy
+
+       # Documentation to add besides this file in the template
+       private fun doc: String do return path
+
+       # PBX file type for `self`
+       fun file_type: String
+       do
+               var map = sys.pbx_file_types
+               var ext = path.file_extension
+               if ext != null and map.keys.has(ext) then return map[ext]
+               return "unknown"
+       end
+
+       # PBX description of this file
+       private fun description: Writable do return """
+               {{{ref_uuid}}} /* {{{doc}}} */ = {
+                       isa = PBXFileReference;
+                       fileEncoding = 4;
+                       lastKnownFileType = {{{file_type}}};
+                       path = {{{path}}};
+                       sourceTree = "<group>";
+                       };
+"""
+
+       private fun add_to_project(project: PbxprojectTemplate)
+       do
+               project.source_files.add self
+               project.files.add self
+       end
+end
+
+# Template for a PBX project file, usually a `project.pbcproj`
+#
+# This file list all information required to build an XCode project.
+# It would usually be written and read by XCode.
+# From the command line, xcodebuild can read this file but not write it.
+#
+# Information in the file (simplified list):
+#
+# * Compilable source files
+# * Asset files
+# * Build configurations (Release and debug modes, cflags, etc.)
+# * List of files composing the project
+class PbxprojectTemplate
+       super Template
+
+       # Name of the project
+       var name: String
+
+       # All body/implementation source files to be compiled
+       private var source_files = new Array[PbxFile]
+
+       # All asset files added to the app package
+       private var asset_files = new Array[PbxFile]
+
+       # All files used by this project
+       private var files = new Array[PbxFile]
+
+       # Add `file` to this project
+       fun add_file(file: PbxFile) do file.add_to_project(self)
+
+       redef fun rendering
+       do
+               add """
+// !$*UTF8*$!
+{
+       archiveVersion = 1;
+       classes = {
+       };
+       objectVersion = 46;
+       objects = {
+
+/* Begin PBXBuildFile section */
+"""
+
+               # List build files (compilable sources and assets) with their reference UUID
+               for array in [source_files, asset_files] do for file in array do add """
+               {{{file.build_uuid}}} /* {{{file.doc}}} */ = {
+                       isa = PBXBuildFile;
+                       fileRef = {{{file.ref_uuid}}} /* {{{file.doc}}} */;
+                       };
+"""
+
+               add """
+/* End PBXBuildFile section */
+
+/* Begin PBXContainerItemProxy section */
+               AF9F83EA1A5F0D21004B62C0 /* PBXContainerItemProxy */ = {
+                       isa = PBXContainerItemProxy;
+                       containerPortal = AF9F83C41A5F0D21004B62C0 /* Project object */;
+                       proxyType = 1;
+                       remoteGlobalIDString = AF9F83CB1A5F0D21004B62C0;
+                       remoteInfo = {{{name}}};
+               };
+/* End PBXContainerItemProxy section */
+
+/* Begin PBXFileReference section */
+       /* Static generated files */
+               AF9F83CC1A5F0D21004B62C0 /* {{{name}}}.app */ = {
+                       isa = PBXFileReference;
+                       explicitFileType = wrapper.application;
+                       includeInIndex = 0;
+                       path = {{{name}}}.app;
+                       sourceTree = BUILT_PRODUCTS_DIR;
+                       };
+               AF9F83D01A5F0D21004B62C0 /* Info.plist */ = {
+                       isa = PBXFileReference;
+                       lastKnownFileType = text.plist.xml;
+                       path = Info.plist;
+                       sourceTree = "<group>";
+                       };
+               AF9F83DE1A5F0D21004B62C0 /* Base */ = {
+                       isa = PBXFileReference;
+                       lastKnownFileType = file.storyboard;
+                       name = Base;
+                       path = Base.lproj/Main.storyboard;
+                       sourceTree = "<group>";
+                       };
+               AF9F83E01A5F0D21004B62C0 /* Images.xcassets */ = {
+                       isa = PBXFileReference;
+                       lastKnownFileType = folder.assetcatalog;
+                       path = Images.xcassets;
+                       sourceTree = "<group>";
+                       };
+               AF9F83E31A5F0D21004B62C0 /* Base */ = {
+                       isa = PBXFileReference;
+                       lastKnownFileType = file.xib;
+                       name = Base;
+                       path = Base.lproj/LaunchScreen.xib;
+                       sourceTree = "<group>";
+                       };
+               AF9F83E91A5F0D21004B62C0 /* {{{name}}}Tests.xctest */ = {
+                       isa = PBXFileReference;
+                       explicitFileType = wrapper.cfbundle;
+                       includeInIndex = 0;
+                       path = {{{name}}}Tests.xctest;
+                       sourceTree = BUILT_PRODUCTS_DIR;
+                       };
+               AF9F83EE1A5F0D21004B62C0 /* Info.plist */ = {
+                       isa = PBXFileReference;
+                       lastKnownFileType = text.plist.xml;
+                       path = Info.plist;
+                       sourceTree = "<group>";
+                       };
+               AF9F83EF1A5F0D21004B62C0 /* {{{name}}}Tests.m */ = {
+                       isa = PBXFileReference;
+                       lastKnownFileType = sourcecode.c.objc;
+                       path = {{{name}}}Tests.m;
+                       sourceTree = "<group>";
+                       };
+
+       /* Changing generated files */
+"""
+               # Describe all known files
+               for file in files do add file.description
+
+               add """
+/* End PBXFileReference section */
+
+/* Begin PBXFrameworksBuildPhase section */
+               AF9F83C91A5F0D21004B62C0 /* Frameworks */ = {
+                       isa = PBXFrameworksBuildPhase;
+                       buildActionMask = 2147483647;
+                       files = (
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
+               AF9F83E61A5F0D21004B62C0 /* Frameworks */ = {
+                       isa = PBXFrameworksBuildPhase;
+                       buildActionMask = 2147483647;
+                       files = (
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
+/* End PBXFrameworksBuildPhase section */
+
+/* Begin PBXGroup section */
+               AF9F83C31A5F0D21004B62C0 = {
+                       isa = PBXGroup;
+                       children = (
+                               AF9F83CE1A5F0D21004B62C0 /* {{{name}}} */,
+                               AF9F83EC1A5F0D21004B62C0 /* {{{name}}}Tests */,
+                               AF9F83CD1A5F0D21004B62C0 /* Products */,
+                       );
+                       sourceTree = "<group>";
+               };
+               AF9F83CD1A5F0D21004B62C0 /* Products */ = {
+                       isa = PBXGroup;
+                       children = (
+                               AF9F83CC1A5F0D21004B62C0 /* {{{name}}}.app */,
+                               AF9F83E91A5F0D21004B62C0 /* {{{name}}}Tests.xctest */,
+                       );
+                       name = Products;
+                       sourceTree = "<group>";
+               };
+               AF9F83CE1A5F0D21004B62C0 /* {{{name}}} */ = {
+                       isa = PBXGroup;
+                       children = (
+"""
+                       # Reference all known files
+                       for file in files do add """
+                               {{{file.ref_uuid}}} /* {{{file.doc}}} */,
+"""
+
+               add """
+                       );
+                       path = {{{name}}};
+                       sourceTree = "<group>";
+               };
+               AF9F83CF1A5F0D21004B62C0 /* Supporting Files */ = {
+                       isa = PBXGroup;
+                       children = (
+                               AF9F83D01A5F0D21004B62C0 /* Info.plist */,
+                               AF9F83D11A5F0D21004B62C0 /* main.m */,
+                       );
+                       name = "Supporting Files";
+                       sourceTree = "<group>";
+               };
+               AF9F83EC1A5F0D21004B62C0 /* {{{name}}}Tests */ = {
+                       isa = PBXGroup;
+                       children = (
+                               AF9F83EF1A5F0D21004B62C0 /* {{{name}}}Tests.m */,
+                               AF9F83ED1A5F0D21004B62C0 /* Supporting Files */,
+                       );
+                       path = {{{name}}}Tests;
+                       sourceTree = "<group>";
+               };
+               AF9F83ED1A5F0D21004B62C0 /* Supporting Files */ = {
+                       isa = PBXGroup;
+                       children = (
+                               AF9F83EE1A5F0D21004B62C0 /* Info.plist */,
+                       );
+                       name = "Supporting Files";
+                       sourceTree = "<group>";
+               };
+/* End PBXGroup section */
+
+/* Begin PBXNativeTarget section */
+               AF9F83CB1A5F0D21004B62C0 /* {{{name}}} */ = {
+                       isa = PBXNativeTarget;
+                       buildConfigurationList = AF9F83F31A5F0D21004B62C0 /* Build configuration list for PBXNativeTarget "{{{name}}}" */;
+                       buildPhases = (
+                               AF9F83C81A5F0D21004B62C0 /* Sources */,
+                               AF9F83C91A5F0D21004B62C0 /* Frameworks */,
+                               AF9F83CA1A5F0D21004B62C0 /* Resources */,
+                       );
+                       buildRules = (
+                       );
+                       dependencies = (
+                       );
+                       name = {{{name}}};
+                       productName = {{{name}}};
+                       productReference = AF9F83CC1A5F0D21004B62C0 /* {{{name}}}.app */;
+                       productType = "com.apple.product-type.application";
+               };
+               AF9F83E81A5F0D21004B62C0 /* {{{name}}}Tests */ = {
+                       isa = PBXNativeTarget;
+                       buildConfigurationList = AF9F83F61A5F0D21004B62C0 /* Build configuration list for PBXNativeTarget "{{{name}}}Tests" */;
+                       buildPhases = (
+                               AF9F83E51A5F0D21004B62C0 /* Sources */,
+                               AF9F83E61A5F0D21004B62C0 /* Frameworks */,
+                               AF9F83E71A5F0D21004B62C0 /* Resources */,
+                       );
+                       buildRules = (
+                       );
+                       dependencies = (
+                               AF9F83EB1A5F0D21004B62C0 /* PBXTargetDependency */,
+                       );
+                       name = {{{name}}}Tests;
+                       productName = {{{name}}}Tests;
+                       productReference = AF9F83E91A5F0D21004B62C0 /* {{{name}}}Tests.xctest */;
+                       productType = "com.apple.product-type.bundle.unit-test";
+               };
+/* End PBXNativeTarget section */
+
+/* Begin PBXProject section */
+               AF9F83C41A5F0D21004B62C0 /* Project object */ = {
+                       isa = PBXProject;
+                       attributes = {
+                               LastUpgradeCheck = 0610;
+                               TargetAttributes = {
+                                       AF9F83CB1A5F0D21004B62C0 = {
+                                               CreatedOnToolsVersion = 6.1.1;
+                                       };
+                                       AF9F83E81A5F0D21004B62C0 = {
+                                               CreatedOnToolsVersion = 6.1.1;
+                                               TestTargetID = AF9F83CB1A5F0D21004B62C0;
+                                       };
+                               };
+                       };
+                       buildConfigurationList = AF9F83C71A5F0D21004B62C0 /* Build configuration list for PBXProject "{{{name}}}" */;
+                       compatibilityVersion = "Xcode 3.2";
+                       developmentRegion = English;
+                       hasScannedForEncodings = 0;
+                       knownRegions = (
+                               en,
+                               Base,
+                       );
+                       mainGroup = AF9F83C31A5F0D21004B62C0;
+                       productRefGroup = AF9F83CD1A5F0D21004B62C0 /* Products */;
+                       projectDirPath = "";
+                       projectRoot = "";
+                       targets = (
+                               AF9F83CB1A5F0D21004B62C0 /* {{{name}}} */,
+                               AF9F83E81A5F0D21004B62C0 /* {{{name}}}Tests */,
+                       );
+               };
+/* End PBXProject section */
+
+/* Begin PBXResourcesBuildPhase section */
+               AF9F83CA1A5F0D21004B62C0 /* Resources */ = {
+                       isa = PBXResourcesBuildPhase;
+                       buildActionMask = 2147483647;
+                       files = (
+"""
+               # Reference all asset files by their build UUID
+               for file in asset_files do add """
+                               {{{file.build_uuid}}} /* {{{file.doc}}} */,
+"""
+
+               add """
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
+               AF9F83E71A5F0D21004B62C0 /* Resources */ = {
+                       isa = PBXResourcesBuildPhase;
+                       buildActionMask = 2147483647;
+                       files = (
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
+/* End PBXResourcesBuildPhase section */
+
+/* Begin PBXSourcesBuildPhase section */
+               AF9F83C81A5F0D21004B62C0 /* Sources */ = {
+                       isa = PBXSourcesBuildPhase;
+                       buildActionMask = 2147483647;
+                       files = (
+"""
+               # Reference all compilable source files by their build UUID
+               for file in source_files do add """
+                               {{{file.build_uuid}}} /* {{{file.doc}}} */,
+"""
+               add """
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
+               AF9F83E51A5F0D21004B62C0 /* Sources */ = {
+                       isa = PBXSourcesBuildPhase;
+                       buildActionMask = 2147483647;
+                       files = (
+                               AF9F83F01A5F0D21004B62C0 /* {{{name}}}Tests.m in Sources */,
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
+/* End PBXSourcesBuildPhase section */
+
+/* Begin PBXTargetDependency section */
+               AF9F83EB1A5F0D21004B62C0 /* PBXTargetDependency */ = {
+                       isa = PBXTargetDependency;
+                       target = AF9F83CB1A5F0D21004B62C0 /* {{{name}}} */;
+                       targetProxy = AF9F83EA1A5F0D21004B62C0 /* PBXContainerItemProxy */;
+               };
+/* End PBXTargetDependency section */
+
+/* Begin PBXVariantGroup section */
+               AF9F83DD1A5F0D21004B62C0 /* Main.storyboard */ = {
+                       isa = PBXVariantGroup;
+                       children = (
+                               AF9F83DE1A5F0D21004B62C0 /* Base */,
+                       );
+                       name = Main.storyboard;
+                       sourceTree = "<group>";
+               };
+               AF9F83E21A5F0D21004B62C0 /* LaunchScreen.xib */ = {
+                       isa = PBXVariantGroup;
+                       children = (
+                               AF9F83E31A5F0D21004B62C0 /* Base */,
+                       );
+                       name = LaunchScreen.xib;
+                       sourceTree = "<group>";
+               };
+/* End PBXVariantGroup section */
+
+/* Begin XCBuildConfiguration section */
+               AF9F83F11A5F0D21004B62C0 /* Debug */ = {
+                       isa = XCBuildConfiguration;
+                       buildSettings = {
+                               ALWAYS_SEARCH_USER_PATHS = NO;
+                               CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
+                               CLANG_CXX_LIBRARY = "libc++";
+                               CLANG_ENABLE_MODULES = YES;
+                               CLANG_ENABLE_OBJC_ARC = YES;
+                               CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+                               CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+                               "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
+                               COPY_PHASE_STRIP = NO;
+                               ENABLE_STRICT_OBJC_MSGSEND = YES;
+                               GCC_C_LANGUAGE_STANDARD = gnu99;
+                               GCC_DYNAMIC_NO_PIC = NO;
+                               GCC_OPTIMIZATION_LEVEL = 0;
+                               GCC_PREPROCESSOR_DEFINITIONS = (
+                                       "DEBUG=1",
+                                       "$(inherited)",
+                               );
+                               GCC_SYMBOLS_PRIVATE_EXTERN = NO;
+                               GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+                               GCC_WARN_UNDECLARED_SELECTOR = YES;
+                               GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+                               IPHONEOS_DEPLOYMENT_TARGET = 8.1;
+                               MTL_ENABLE_DEBUG_INFO = YES;
+                               ONLY_ACTIVE_ARCH = YES;
+                               SDKROOT = iphoneos;
+                               TARGETED_DEVICE_FAMILY = "1,2";
+                       };
+                       name = Debug;
+               };
+               AF9F83F21A5F0D21004B62C0 /* Release */ = {
+                       isa = XCBuildConfiguration;
+                       buildSettings = {
+                               ALWAYS_SEARCH_USER_PATHS = NO;
+                               CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
+                               CLANG_CXX_LIBRARY = "libc++";
+                               CLANG_ENABLE_MODULES = YES;
+                               CLANG_ENABLE_OBJC_ARC = YES;
+                               CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+                               CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+                               "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
+                               COPY_PHASE_STRIP = YES;
+                               ENABLE_NS_ASSERTIONS = NO;
+                               ENABLE_STRICT_OBJC_MSGSEND = YES;
+                               GCC_C_LANGUAGE_STANDARD = gnu99;
+                               GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+                               GCC_WARN_UNDECLARED_SELECTOR = YES;
+                               GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+                               IPHONEOS_DEPLOYMENT_TARGET = 8.1;
+                               MTL_ENABLE_DEBUG_INFO = NO;
+                               SDKROOT = iphoneos;
+                               TARGETED_DEVICE_FAMILY = "1,2";
+                               VALIDATE_PRODUCT = YES;
+                       };
+                       name = Release;
+               };
+               AF9F83F41A5F0D21004B62C0 /* Debug */ = {
+                       isa = XCBuildConfiguration;
+                       buildSettings = {
+                               ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+                               INFOPLIST_FILE = {{{name}}}/Info.plist;
+                               LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
+                               PRODUCT_NAME = "$(TARGET_NAME)";
+                       };
+                       name = Debug;
+               };
+               AF9F83F51A5F0D21004B62C0 /* Release */ = {
+                       isa = XCBuildConfiguration;
+                       buildSettings = {
+                               ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+                               INFOPLIST_FILE = {{{name}}}/Info.plist;
+                               LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
+                               PRODUCT_NAME = "$(TARGET_NAME)";
+                       };
+                       name = Release;
+               };
+               AF9F83F71A5F0D21004B62C0 /* Debug */ = {
+                       isa = XCBuildConfiguration;
+                       buildSettings = {
+                               BUNDLE_LOADER = "$(TEST_HOST)";
+                               FRAMEWORK_SEARCH_PATHS = (
+                                       "$(SDKROOT)/Developer/Library/Frameworks",
+                                       "$(inherited)",
+                               );
+                               GCC_PREPROCESSOR_DEFINITIONS = (
+                                       "DEBUG=1",
+                                       "$(inherited)",
+                               );
+                               INFOPLIST_FILE = {{{name}}}Tests/Info.plist;
+                               LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
+                               PRODUCT_NAME = "$(TARGET_NAME)";
+                               TEST_HOST = "$(BUILT_PRODUCTS_DIR)/{{{name}}}.app/{{{name}}}";
+                       };
+                       name = Debug;
+               };
+               AF9F83F81A5F0D21004B62C0 /* Release */ = {
+                       isa = XCBuildConfiguration;
+                       buildSettings = {
+                               BUNDLE_LOADER = "$(TEST_HOST)";
+                               FRAMEWORK_SEARCH_PATHS = (
+                                       "$(SDKROOT)/Developer/Library/Frameworks",
+                                       "$(inherited)",
+                               );
+                               INFOPLIST_FILE = {{{name}}}Tests/Info.plist;
+                               LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
+                               PRODUCT_NAME = "$(TARGET_NAME)";
+                               TEST_HOST = "$(BUILT_PRODUCTS_DIR)/{{{name}}}.app/{{{name}}}";
+                       };
+                       name = Release;
+               };
+/* End XCBuildConfiguration section */
+
+/* Begin XCConfigurationList section */
+               AF9F83C71A5F0D21004B62C0 /* Build configuration list for PBXProject "{{{name}}}" */ = {
+                       isa = XCConfigurationList;
+                       buildConfigurations = (
+                               AF9F83F11A5F0D21004B62C0 /* Debug */,
+                               AF9F83F21A5F0D21004B62C0 /* Release */,
+                       );
+                       defaultConfigurationIsVisible = 0;
+                       defaultConfigurationName = Release;
+               };
+               AF9F83F31A5F0D21004B62C0 /* Build configuration list for PBXNativeTarget "{{{name}}}" */ = {
+                       isa = XCConfigurationList;
+                       buildConfigurations = (
+                               AF9F83F41A5F0D21004B62C0 /* Debug */,
+                               AF9F83F51A5F0D21004B62C0 /* Release */,
+                       );
+                       defaultConfigurationIsVisible = 0;
+               };
+               AF9F83F61A5F0D21004B62C0 /* Build configuration list for PBXNativeTarget "{{{name}}}Tests" */ = {
+                       isa = XCConfigurationList;
+                       buildConfigurations = (
+                               AF9F83F71A5F0D21004B62C0 /* Debug */,
+                               AF9F83F81A5F0D21004B62C0 /* Release */,
+                       );
+                       defaultConfigurationIsVisible = 0;
+               };
+/* End XCConfigurationList section */
+       };
+       rootObject = AF9F83C41A5F0D21004B62C0 /* Project object */;
+}
+"""
+       end
+end
+
+# Template for a property list used by XCode for iOS projects
+class PlistTemplate
+       super Template
+
+       # Package of the app
+       var package_name: String
+
+       redef fun rendering
+       do
+               add """
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+       <key>CFBundleDevelopmentRegion</key>
+       <string>en</string>
+       <key>CFBundleExecutable</key>
+       <string>$(EXECUTABLE_NAME)</string>
+       <key>CFBundleIdentifier</key>
+       <string>{{{package_name}}}.$(PRODUCT_NAME:rfc1034identifier)</string>
+       <key>CFBundleInfoDictionaryVersion</key>
+       <string>6.0</string>
+       <key>CFBundleName</key>
+       <string>$(PRODUCT_NAME)</string>
+       <key>CFBundlePackageType</key>
+       <string>APPL</string>
+       <key>CFBundleShortVersionString</key>
+       <string>1.0</string>
+       <key>CFBundleSignature</key>
+       <string>\\?\\?\\?\\?</string>
+       <key>CFBundleVersion</key>
+       <string>1</string>
+       <key>LSRequiresIPhoneOS</key>
+       <true/>
+       <key>UIRequiredDeviceCapabilities</key>
+       <array>
+               <string>armv7</string>
+       </array>
+       <key>UISupportedInterfaceOrientations</key>
+       <array>
+               <string>UIInterfaceOrientationPortrait</string>
+               <string>UIInterfaceOrientationLandscapeLeft</string>
+               <string>UIInterfaceOrientationLandscapeRight</string>
+       </array>
+       <key>UISupportedInterfaceOrientations~ipad</key>
+       <array>
+               <string>UIInterfaceOrientationPortrait</string>
+               <string>UIInterfaceOrientationPortraitUpsideDown</string>
+               <string>UIInterfaceOrientationLandscapeLeft</string>
+               <string>UIInterfaceOrientationLandscapeRight</string>
+       </array>
+</dict>
+</plist>
+"""
+       end
+end
index a6cd32e..69e9023 100644 (file)
@@ -225,9 +225,9 @@ class RapidTypeAnalysis
                                var node = self.modelbuilder.mpropdef2node(mmethoddef)
                                var elttype = mmethoddef.msignature.mparameters[vararg_rank].mtype
                                #elttype = elttype.anchor_to(self.mainmodule, v.receiver)
-                               var vararg = self.mainmodule.get_primitive_class("Array").get_mtype([elttype])
+                               var vararg = self.mainmodule.array_type(elttype)
                                v.add_type(vararg)
-                               var native = self.mainmodule.get_primitive_class("NativeArray").get_mtype([elttype])
+                               var native = self.mainmodule.native_array_type(elttype)
                                v.add_type(native)
                                v.add_monomorphic_send(vararg, self.modelbuilder.force_get_primitive_method(node, "with_native", vararg.mclass, self.mainmodule))
                        end
@@ -471,11 +471,6 @@ class RapidTypeVisitor
                return mtype
        end
 
-       fun get_class(name: String): MClass
-       do
-               return analysis.mainmodule.get_primitive_class(name)
-       end
-
        fun get_method(recv: MType, name: String): MMethod
        do
                var mtype = cleanup_type(recv)
@@ -540,7 +535,7 @@ redef class AArrayExpr
        do
                var mtype = self.mtype.as(MClassType)
                v.add_type(mtype)
-               var native = v.analysis.mainmodule.get_primitive_class("NativeArray").get_mtype([mtype.arguments.first])
+               var native = v.analysis.mainmodule.native_array_type(mtype.arguments.first)
                v.add_type(native)
                mtype = v.cleanup_type(mtype).as(not null)
                var prop = v.get_method(mtype, "with_native")
@@ -551,7 +546,7 @@ end
 redef class AStringFormExpr
        redef fun accept_rapid_type_visitor(v)
        do
-               var native = v.get_class("NativeString").mclass_type
+               var native = v.analysis.mainmodule.native_string_type
                v.add_type(native)
                var prop = v.get_method(native, "to_s_with_length")
                v.add_monomorphic_send(native, prop)
@@ -561,13 +556,17 @@ end
 redef class ASuperstringExpr
        redef fun accept_rapid_type_visitor(v)
        do
-               var arraytype = v.get_class("Array").get_mtype([v.get_class("Object").mclass_type])
+               var mmodule = v.analysis.mainmodule
+               var object_type = mmodule.object_type
+               var arraytype = mmodule.array_type(object_type)
                v.add_type(arraytype)
-               v.add_type(v.get_class("NativeArray").get_mtype([v.get_class("Object").mclass_type]))
+               var nattype = mmodule.native_array_type(object_type)
+               v.add_type(nattype)
                var prop = v.get_method(arraytype, "join")
                v.add_monomorphic_send(arraytype, prop)
                var prop2 = v.get_method(arraytype, "with_native")
                v.add_monomorphic_send(arraytype, prop2)
+               v.add_monomorphic_send(nattype, v.get_method(nattype, "native_to_s"))
        end
 end
 
index 8ae2c28..dac3c4f 100644 (file)
@@ -73,12 +73,6 @@ private class TransformVisitor
                node.full_transform_visitor(self)
        end
 
-       # Get a primitive class or display a fatal error on `location`.
-       fun get_class(location: AExpr, name: String): MClass
-       do
-               return mmodule.get_primitive_class(name)
-       end
-
        # Get a primitive method or display a fatal error on `location`.
        fun get_method(location: AExpr, name: String, recv: MClass): MMethod
        do
similarity index 99%
rename from src/variables_numbering.nit
rename to src/vm/variables_numbering.nit
index 1190fab..4de0eb4 100644 (file)
@@ -17,7 +17,7 @@
 # Handle all numbering operations related to local variables in the Nit virtual machine
 module variables_numbering
 
-import vm
+import virtual_machine
 
 redef class VirtualMachine
 
similarity index 99%
rename from src/vm.nit
rename to src/vm/virtual_machine.nit
index b253533..b33e7f8 100644 (file)
@@ -15,7 +15,7 @@
 # limitations under the License.
 
 # Implementation of the Nit virtual machine
-module vm
+module virtual_machine
 
 import interpreter::naive_interpreter
 import perfect_hashing
@@ -177,16 +177,6 @@ class VirtualMachine super NaiveInterpreter
                recv.vtable = recv.mtype.as(MClassType).mclass.vtable
        end
 
-       # Create a virtual table for this `MClass` if not already done
-       redef fun get_primitive_class(name: String): MClass
-       do
-               var mclass = super
-
-               if not mclass.loaded then create_class(mclass)
-
-               return mclass
-       end
-
        # Initialize the internal representation of an object (its attribute values)
        # `init_instance` is the initial value of attributes
        private fun init_internal_attributes(init_instance: Instance, size: Int): Pointer
diff --git a/src/vm/vm.nit b/src/vm/vm.nit
new file mode 100644 (file)
index 0000000..4027a15
--- /dev/null
@@ -0,0 +1,22 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Copyright 2015 Julien Pagès <julien.pages@lirmm.fr>
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Entry point of all vm components
+module vm
+
+import virtual_machine
+import vm_optimizations
+import variables_numbering
similarity index 99%
rename from src/vm_optimizations.nit
rename to src/vm/vm_optimizations.nit
index f657786..6f3c201 100644 (file)
@@ -17,7 +17,7 @@
 # Optimization of the nitvm
 module vm_optimizations
 
-import vm
+import virtual_machine
 
 redef class VirtualMachine
 
index 609105e..8a90f1d 100644 (file)
@@ -1,3 +1,5 @@
 cocoa_extern_types
 cocoa_message_box
 hello_cocoa
+hello_ios
+test_platform_ios
diff --git a/tests/sav/nitg-e/error_needed_method_alt4.res b/tests/sav/nitg-e/error_needed_method_alt4.res
deleted file mode 100644 (file)
index d1e2d76..0000000
+++ /dev/null
@@ -1 +0,0 @@
-alt/error_needed_method_alt4.nit:49,10--14: Fatal Error: NativeString must have a property named to_s_with_length.
index 36503ed..3e3247e 100644 (file)
@@ -475,7 +475,7 @@ Start ../src/test_parser.nit:17,1--147,1
                 AParExprs ../src/test_parser.nit:71,7--36
                   TOpar "(" ../src/test_parser.nit:71,7
                   AStringExpr ../src/test_parser.nit:71,8--35
-                    TString "\"  -n\11do not print anything\"" ../src/test_parser.nit:71,8--35
+                    TString "\"  -n\tdo not print anything\"" ../src/test_parser.nit:71,8--35
                   TCpar ")" ../src/test_parser.nit:71,36
               ACallExpr ../src/test_parser.nit:72,2--25
                 AImplicitSelfExpr ../src/test_parser.nit:72,2
@@ -483,7 +483,7 @@ Start ../src/test_parser.nit:17,1--147,1
                 AParExprs ../src/test_parser.nit:72,7--25
                   TOpar "(" ../src/test_parser.nit:72,7
                   AStringExpr ../src/test_parser.nit:72,8--24
-                    TString "\"  -l\11only lexer\"" ../src/test_parser.nit:72,8--24
+                    TString "\"  -l\tonly lexer\"" ../src/test_parser.nit:72,8--24
                   TCpar ")" ../src/test_parser.nit:72,25
               ACallExpr ../src/test_parser.nit:73,2--41
                 AImplicitSelfExpr ../src/test_parser.nit:73,2
@@ -491,7 +491,7 @@ Start ../src/test_parser.nit:17,1--147,1
                 AParExprs ../src/test_parser.nit:73,7--41
                   TOpar "(" ../src/test_parser.nit:73,7
                   AStringExpr ../src/test_parser.nit:73,8--40
-                    TString "\"  -p\11lexer and parser (default)\"" ../src/test_parser.nit:73,8--40
+                    TString "\"  -p\tlexer and parser (default)\"" ../src/test_parser.nit:73,8--40
                   TCpar ")" ../src/test_parser.nit:73,41
               ACallExpr ../src/test_parser.nit:74,2--68
                 AImplicitSelfExpr ../src/test_parser.nit:74,2
@@ -499,7 +499,7 @@ Start ../src/test_parser.nit:17,1--147,1
                 AParExprs ../src/test_parser.nit:74,7--68
                   TOpar "(" ../src/test_parser.nit:74,7
                   AStringExpr ../src/test_parser.nit:74,8--67
-                    TString "\"  -e\11instead on files, each argument is a content to parse\"" ../src/test_parser.nit:74,8--67
+                    TString "\"  -e\tinstead on files, each argument is a content to parse\"" ../src/test_parser.nit:74,8--67
                   TCpar ")" ../src/test_parser.nit:74,68
               ACallExpr ../src/test_parser.nit:75,2--51
                 AImplicitSelfExpr ../src/test_parser.nit:75,2
@@ -507,7 +507,7 @@ Start ../src/test_parser.nit:17,1--147,1
                 AParExprs ../src/test_parser.nit:75,7--51
                   TOpar "(" ../src/test_parser.nit:75,7
                   AStringExpr ../src/test_parser.nit:75,8--50
-                    TString "\"  -i\11tree to parse are read interactively\"" ../src/test_parser.nit:75,8--50
+                    TString "\"  -i\ttree to parse are read interactively\"" ../src/test_parser.nit:75,8--50
                   TCpar ")" ../src/test_parser.nit:75,51
               ACallExpr ../src/test_parser.nit:76,2--30
                 AImplicitSelfExpr ../src/test_parser.nit:76,2
@@ -515,7 +515,7 @@ Start ../src/test_parser.nit:17,1--147,1
                 AParExprs ../src/test_parser.nit:76,7--30
                   TOpar "(" ../src/test_parser.nit:76,7
                   AStringExpr ../src/test_parser.nit:76,8--29
-                    TString "\"  -h\11print this help\"" ../src/test_parser.nit:76,8--29
+                    TString "\"  -h\tprint this help\"" ../src/test_parser.nit:76,8--29
                   TCpar ")" ../src/test_parser.nit:76,30
             AIfExpr ../src/test_parser.nit:77,6--146,3
               TKwif "if" ../src/test_parser.nit:77,6--7
diff --git a/tests/test_platform_ios.nit b/tests/test_platform_ios.nit
new file mode 100644 (file)
index 0000000..b49f379
--- /dev/null
@@ -0,0 +1,15 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import ios