2003-01-20 Gonzalo Paniagua Javier <gonzalo@ximian.com>
[mono.git] / mcs / class / System.Web / System.Web.Compilation / AspGenerator.cs
index 3c464a6ce8497d39bfe568b05e3abd8b44e91065..8aebca2408759595ea186742458bd5c6922a9802 100644 (file)
@@ -42,6 +42,7 @@ class ControlStack
                public StringBuilder dataBindFunction;
                public StringBuilder codeRenderFunction;
                public bool useCodeRender;
+               public int codeRenderIndex;
 
                public ControlStackData (Type controlType,
                                         string controlID,
@@ -226,6 +227,12 @@ class ControlStack
                }
        }
 
+       public int CodeRenderIndex {
+               get {
+                       return top.codeRenderIndex++;
+               }
+       }
+       
        public StringBuilder CodeRenderFunction
        {
                get {
@@ -313,8 +320,8 @@ class AspGenerator
        private string interfaces;
        private string basetype;
        private string parent;
+       private Type parentType;
        private string fullPath;
-       private static string enableSessionStateLiteral =  ", System.Web.SessionState.IRequiresSessionState";
 
        Hashtable options;
        string privateBinPath;
@@ -325,6 +332,13 @@ class AspGenerator
        bool isUserControl;
        bool isApplication;
 
+       HttpContext context;
+
+       SessionState sessionState = SessionState.Enabled;
+
+       static Type styleType = typeof (System.Web.UI.WebControls.Style);
+       static Type fontinfoType = typeof (System.Web.UI.WebControls.FontInfo);
+
        enum UserControlResult
        {
                OK = 0,
@@ -332,6 +346,13 @@ class AspGenerator
                CompilationFailed = 2
        }
 
+       enum SessionState
+       {
+               Enabled,
+               ReadOnly,
+               Disabled
+       }
+       
        public AspGenerator (string pathToFile, ArrayList elements)
        {
                if (elements == null)
@@ -397,10 +418,31 @@ class AspGenerator
                }
        }
        
+       internal HttpContext Context {
+               get { return context; }
+               set { context = value; }
+       }
+       
+       bool AddUsing (string nspace)
+       {
+               string _using = "using " + nspace + ";";
+               if (prolog.ToString ().IndexOf (_using) == -1) {
+                       prolog.AppendFormat ("\t{0}\n", _using);
+                       return true;
+               }
+
+               return false;
+       }
+
+       void AddInterface (Type type)
+       {
+               AddInterface (type.ToString ());
+       }
+       
        public void AddInterface (string iface)
        {
-               if (interfaces == "") {
-                       interfaces = iface;
+               if (interfaces == null) {
+                       interfaces = ", " + iface;
                } else {
                        string s = ", " + iface;
                        if (interfaces.IndexOf (s) == -1)
@@ -503,6 +545,72 @@ class AspGenerator
                return output;
        }
        
+       bool AddProtectedField (Type type, string fieldName)
+       {
+               if (parentType == null) {
+                       declarations.AppendFormat ("\t\tprotected {0} {1};\n", type.ToString (), fieldName);
+                       return true;
+               }
+
+               FieldInfo field = parentType.GetField (fieldName, BindingFlags.Public |
+                                                                 BindingFlags.NonPublic |
+                                                                 BindingFlags.Instance |
+                                                                 BindingFlags.Static);
+
+               if (field == null || (!field.IsPublic && !field.IsFamily)) {
+                       declarations.AppendFormat ("\t\tprotected {0} {1};\n", type.ToString (), fieldName);
+                       return true;
+               }
+
+               if (!field.FieldType.IsAssignableFrom (type)) {
+                       string message = String.Format ("The base class includes the field '{0}', but its " +
+                                                       "type '{1}' is not compatible with {2}",
+                                                       fieldName, field.FieldType, type);
+
+                       throw new ApplicationException (message);
+               }
+
+               return false;
+       }
+       
+       private Type LoadParentType (string typeName)
+       {
+               // First try loaded assemblies, then try assemblies in Bin directory.
+               // By now i do this 'by hand' but may be this is a runtime/gac task.
+               Type type = null;
+               Assembly [] assemblies = AppDomain.CurrentDomain.GetAssemblies ();
+               foreach (Assembly ass in assemblies) {
+                       type = ass.GetType (typeName);
+                       if (type != null)
+                               return type;
+               }
+
+               Assembly assembly;
+               string [] binDlls = Directory.GetFiles (privateBinPath, "*.dll");
+               foreach (string dll in binDlls) {
+                       string dllPath = Path.Combine (privateBinPath, dll);
+                       assembly = null;
+                       try {
+                               assembly = Assembly.LoadFrom (dllPath);
+                               type = assembly.GetType (typeName);
+                       } catch (Exception e) {
+                               if (assembly != null) {
+                                       Console.WriteLine ("ASP.NET Warning: assembly {0} loaded", dllPath);
+                                       Console.WriteLine ("ASP.NET Warning: but type {0} not found", typeName);
+                               } else {
+                                       Console.WriteLine ("ASP.NET Warning: unable to load type {0} from {1}",
+                                                          typeName, dllPath);
+                               }
+                               Console.WriteLine ("ASP.NET Warning: error was: {0}", e.Message);
+                       }
+
+                       if (type != null)
+                               return type;
+               }
+
+               return null;
+       }
+
        private void PageDirective (TagAttributes att)
        {
                if (att ["ClassName"] != null){
@@ -511,16 +619,27 @@ class AspGenerator
                }
 
                if (att ["EnableSessionState"] != null){
+                       if (!IsPage)
+                               throw new ApplicationException ("EnableSessionState not allowed here.");
+                       
                        string est = (string) att ["EnableSessionState"];
                        if (0 == String.Compare (est, "false", true))
-                               interfaces = interfaces.Replace (enableSessionStateLiteral, "");
-                       else if (0 != String.Compare (est, "true", true))
+                               sessionState = SessionState.Disabled;
+                       else if (0 == String.Compare (est, "true", true))
+                               sessionState = SessionState.Enabled;
+                       else if (0 == String.Compare (est, "readonly", true))
+                               sessionState = SessionState.ReadOnly;
+                       else
                                throw new ApplicationException ("EnableSessionState in Page directive not set to " +
                                                                "a correct value: " + est);
                }
 
-               if (att ["Inherits"] != null)
+               if (att ["Inherits"] != null) {
                        parent = (string) att ["Inherits"];
+                       parentType = LoadParentType (parent);
+                       if (parentType == null)
+                               throw new ApplicationException ("The class " + parent + " cannot be found.");
+               }
 
                if (att ["CompilerOptions"] != null)
                        Options ["CompilerOptions"] = (string) att ["CompilerOptions"];
@@ -565,8 +684,14 @@ class AspGenerator
                        if (tag_name != "" || src != "")
                                throw new ApplicationException ("Invalid attributes for @ Register: " +
                                                                att.ToString ());
-                       prolog.AppendFormat ("\tusing {0};\n", name_space);
+
+                       AddUsing (name_space);
                        string dll = privateBinPath + Path.DirectorySeparatorChar + assembly_name + ".dll";
+                       // Hack: it should use assembly.load semantics...
+                       // may be when we don't run mcs as a external program...
+                       if (!File.Exists (dll))
+                               dll = assembly_name;
+
                        Foundry.RegisterFoundry (tag_prefix, dll, name_space);
                        AddReference (dll);
                        return;
@@ -581,13 +706,11 @@ class AspGenerator
                                throw new ApplicationException ("Source file extension for controls " + 
                                                                "must be .ascx");
 
-                       string srcLocation = PathUtil.Combine (null, src);
-                       UserControlData data = GenerateUserControl (srcLocation);
+                       UserControlData data = GenerateUserControl (src, Context);
                        switch (data.result) {
                        case UserControlResult.OK:
-                               prolog.AppendFormat ("\tusing {0};\n", "ASP");
-                               string dll = "output" + Path.DirectorySeparatorChar + data.assemblyName + ".dll";
-                               Foundry.RegisterFoundry (tag_prefix, data.assemblyName, "ASP", data.className);
+                               AddUsing ("ASP");
+                               Foundry.RegisterFoundry (tag_prefix, tag_name, data.assemblyName, "ASP", data.className);
                                AddReference (data.assemblyName);
                                break;
                        case UserControlResult.FileNotFound:
@@ -646,8 +769,7 @@ class AspGenerator
                                throw new ApplicationException ("Wrong syntax in Import directive.");
 
                        string _using = "using " + value + ";";
-                       if (prolog.ToString ().IndexOf (_using) == -1) {
-                               prolog.AppendFormat ("\t{0}\n", _using);
+                       if (AddUsing (value) == true) {
                                string imports = Options ["Import"] as string;
                                if (imports == null) {
                                        imports = value;
@@ -663,7 +785,7 @@ class AspGenerator
                                throw new ApplicationException ("@ Implements not allowed in an application file.");
 
                        string iface = (string) att ["interface"];
-                       interfaces += ", " + iface;
+                       AddInterface (iface);
                        break;
                case "REGISTER":
                        if (IsApplication)
@@ -792,17 +914,19 @@ class AspGenerator
                controls.Push (control_type, control_id, tag_id, children_kind, defaultPropertyName);
                bool is_generic = control_type ==  typeof (System.Web.UI.HtmlControls.HtmlGenericControl);
                functions.Push (current_function);
-               if (control_type != typeof (System.Web.UI.WebControls.ListItem))
+               if (control_type != typeof (System.Web.UI.WebControls.ListItem) &&
+                   prev_children_kind != ChildrenKind.DBCOLUMNS) {
                        current_function.AppendFormat ("\t\tprivate System.Web.UI.Control __BuildControl_" +
                                                        "{0} ()\n\t\t{{\n\t\t\t{1} __ctrl;\n\n\t\t\t__ctrl" +
                                                        " = new {1} ({2});\n\t\t\tthis.{0} = __ctrl;\n",
                                                        control_id, control_type,
                                                        (is_generic? "\"" + tag_id + "\"" : ""));
-               else
+               } else {
                        current_function.AppendFormat ("\t\tprivate void __BuildControl_{0} ()\n\t\t{{" +
                                                        "\n\t\t\t{1} __ctrl;\n\t\t\t__ctrl = new {1} ();" +
                                                        "\n\t\t\tthis.{0} = __ctrl;\n",
                                                        control_id, control_type);
+               }
 
                if (children_kind == ChildrenKind.CONTROLS || children_kind == ChildrenKind.OPTION)
                        current_function.Append ("\t\t\tSystem.Web.UI.IParserAccessor __parser = " + 
@@ -818,10 +942,11 @@ class AspGenerator
                string control_type_string = controls.PeekType ().ToString ();
                StringBuilder db_function = controls.DataBindFunction;
                string container;
-               if (controls.Container == null)
+               if (controls.Container == null || !typeof (INamingContainer).IsAssignableFrom (controls.Container))
                        container = "System.Web.UI.Control";
-               else
+               else {
                        container = controls.Container.ToString ();
+               }
 
                if (db_function.Length == 0)
                        db_function.AppendFormat ("\t\tpublic void __DataBind_{0} (object sender, " + 
@@ -848,31 +973,31 @@ class AspGenerator
        /*
         * Returns true if it generates some code for the specified property
         */
-       private void AddPropertyCode (Type prop_type, string var_name, string att, bool isDataBound)
+       private void AddCodeForPropertyOrField (Type type, string var_name, string att, bool isDataBound)
        {
                /* FIXME: should i check for this or let the compiler fail?
                 * if (!prop.CanWrite)
                 *    ....
                 */
                if (isDataBound) {
-                       DataBoundProperty (prop_type, var_name, att);
+                       DataBoundProperty (type, var_name, att);
                }
-               else if (prop_type == typeof (string)){
+               else if (type == typeof (string)){
                        if (att == null)
                                throw new ApplicationException ("null value for attribute " + var_name );
 
                        current_function.AppendFormat ("\t\t\t__ctrl.{0} = \"{1}\";\n", var_name,
                                                        Escape (att)); // FIXME: really Escape this?
                } 
-               else if (prop_type.IsEnum){
+               else if (type.IsEnum){
                        if (att == null)
                                throw new ApplicationException ("null value for attribute " + var_name );
 
-                       string enum_value = EnumValueNameToString (prop_type, att);
+                       string enum_value = EnumValueNameToString (type, att);
 
                        current_function.AppendFormat ("\t\t\t__ctrl.{0} = {1};\n", var_name, enum_value);
                } 
-               else if (prop_type == typeof (bool)){
+               else if (type == typeof (bool)){
                        string value;
                        if (att == null)
                                value = "true"; //FIXME: is this ok for non Style properties?
@@ -885,7 +1010,7 @@ class AspGenerator
 
                        current_function.AppendFormat ("\t\t\t__ctrl.{0} = {1};\n", var_name, value);
                }
-               else if (prop_type == typeof (System.Web.UI.WebControls.Unit)){
+               else if (type == typeof (System.Web.UI.WebControls.Unit)){
                         //FIXME: should use the culture specified in Page
                        try {
                                Unit value = Unit.Parse (att, System.Globalization.CultureInfo.InvariantCulture);
@@ -897,7 +1022,7 @@ class AspGenerator
                                                        "System.Globalization.CultureInfo.InvariantCulture);\n", 
                                                        var_name, att);
                }
-               else if (prop_type == typeof (System.Web.UI.WebControls.FontUnit)){
+               else if (type == typeof (System.Web.UI.WebControls.FontUnit)){
                         //FIXME: should use the culture specified in Page
                        try {
                                FontUnit value = FontUnit.Parse (att, System.Globalization.CultureInfo.InvariantCulture);
@@ -909,9 +1034,7 @@ class AspGenerator
                                                        "System.Globalization.CultureInfo.InvariantCulture);\n", 
                                                        var_name, att);
                }
-               else if (prop_type == typeof (Int16) ||
-                        prop_type == typeof (Int32) ||
-                        prop_type == typeof (Int64)){
+               else if (type == typeof (Int16) || type == typeof (Int32) || type == typeof (Int64)) {
                        long value;
                        try {
                                value = Int64.Parse (att); //FIXME: should use the culture specified in Page
@@ -922,9 +1045,7 @@ class AspGenerator
 
                        current_function.AppendFormat ("\t\t\t__ctrl.{0} = {1};\n", var_name, value);
                }
-               else if (prop_type == typeof (UInt16) ||
-                        prop_type == typeof (UInt32) ||
-                        prop_type == typeof (UInt64)){
+               else if (type == typeof (UInt16) || type == typeof (UInt32) || type == typeof (UInt64)) {
                        ulong value;
                        try {
                                value = UInt64.Parse (att); //FIXME: should use the culture specified in Page
@@ -935,7 +1056,7 @@ class AspGenerator
 
                        current_function.AppendFormat ("\t\t\t__ctrl.{0} = {1};\n", var_name, value);
                }
-               else if (prop_type == typeof (float)){
+               else if (type == typeof (float)) {
                        float value;
                        try {
                                value = Single.Parse (att);
@@ -946,7 +1067,7 @@ class AspGenerator
 
                        current_function.AppendFormat ("\t\t\t__ctrl.{0} = {1};\n", var_name, value);
                }
-               else if (prop_type == typeof (double)){
+               else if (type == typeof (double)){
                        double value;
                        try {
                                value = Double.Parse (att);
@@ -957,7 +1078,7 @@ class AspGenerator
 
                        current_function.AppendFormat ("\t\t\t__ctrl.{0} = {1};\n", var_name, value);
                }
-               else if (prop_type == typeof (System.Drawing.Color)){
+               else if (type == typeof (System.Drawing.Color)){
                        Color c;
                        try {
                                c = (Color) TypeDescriptor.GetConverter (typeof (Color)).ConvertFromString (att);
@@ -976,33 +1097,49 @@ class AspGenerator
                                                               "FromArgb ({1}, {2}, {3}, {4});\n",
                                                               var_name, c.A, c.R, c.G, c.B);
                        }
-               }       
-               else {
+               }
+               else if (type == typeof (string [])) {
+                       string [] subStrings = att.Split (',');
+                       current_function.AppendFormat ("\t\t\t__ctrl.{0} = new String [] {{\n", var_name);
+                       int end = subStrings.Length;
+                       for (int i = 0; i < end; i++) {
+                               string s = subStrings [i].Trim ();
+                               current_function.AppendFormat ("\t\t\t\t\"{0}\"", s);
+                               if (i == end - 1)
+                                       current_function.Append ("\t\t\t\t};\n");
+                               else
+                                       current_function.Append (",\n");
+                       }
+               } else {
                        throw new ApplicationException ("Unsupported type in property: " + 
-                                                       prop_type.ToString ());
+                                                       type.ToString ());
                }
        }
 
-       private bool ProcessProperties (PropertyInfo prop, string id, TagAttributes att)
+       private bool ProcessPropertiesAndFields (MemberInfo member, string id, TagAttributes att)
        {
                int hyphen = id.IndexOf ('-');
 
-               if (hyphen == -1 && prop.CanWrite == false)
-                       return false;
+               bool isPropertyInfo = (member is PropertyInfo);
 
                bool is_processed = false;
                bool isDataBound = att.IsDataBound ((string) att [id]);
-               Type type = prop.PropertyType;
-               Type style = typeof (System.Web.UI.WebControls.Style);
-               Type fontinfo = typeof (System.Web.UI.WebControls.FontInfo);
+               Type type;
+               if (isPropertyInfo) {
+                       type = ((PropertyInfo) member).PropertyType;
+                       if (hyphen == -1 && ((PropertyInfo) member).CanWrite == false)
+                               return false;
+               } else {
+                       type = ((FieldInfo) member).FieldType;
+               }
 
-               if (0 == String.Compare (prop.Name, id, true)){
-                       AddPropertyCode (type, prop.Name, (string) att [id], isDataBound);
+               if (0 == String.Compare (member.Name, id, true)){
+                       AddCodeForPropertyOrField (type, member.Name, (string) att [id], isDataBound);
                        is_processed = true;
-               } else if ((type == fontinfo || type == style || type.IsSubclassOf (style)) && hyphen != -1){
+               } else if (hyphen != -1 && (type == fontinfoType || type == styleType || type.IsSubclassOf (styleType))){
                        string prop_field = id.Replace ("-", ".");
                        string [] parts = prop_field.Split (new char [] {'.'});
-                       if (parts.Length != 2 || 0 != String.Compare (prop.Name, parts [0], true))
+                       if (parts.Length != 2 || 0 != String.Compare (member.Name, parts [0], true))
                                return false;
 
                        PropertyInfo [] subprops = type.GetProperties ();
@@ -1014,19 +1151,19 @@ class AspGenerator
                                        return false;
 
                                bool is_bool = subprop.PropertyType == typeof (bool);
-                               if (!is_bool && att == null){
+                               if (!is_bool && att [id] == null){
                                        att [id] = ""; // Font-Size -> Font-Size="" as html
                                        return false;
                                }
 
                                string value;
-                               if (att == null && is_bool)
+                               if (att [id] == null && is_bool)
                                        value = "true"; // Font-Bold <=> Font-Bold="true"
                                else
                                        value = (string) att [id];
 
-                               AddPropertyCode (subprop.PropertyType,
-                                                prop.Name + "." + subprop.Name,
+                               AddCodeForPropertyOrField (subprop.PropertyType,
+                                                member.Name + "." + subprop.Name,
                                                 value, isDataBound);
                                is_processed = true;
                        }
@@ -1039,6 +1176,7 @@ class AspGenerator
        {
                EventInfo [] ev_info = type.GetEvents ();
                PropertyInfo [] prop_info = type.GetProperties ();
+               FieldInfo [] field_info = type.GetFields ();
                bool is_processed = false;
                ArrayList processed = new ArrayList ();
 
@@ -1065,11 +1203,19 @@ class AspGenerator
                        } 
 
                        foreach (PropertyInfo prop in prop_info){
-                               is_processed = ProcessProperties (prop, id, att);
+                               is_processed = ProcessPropertiesAndFields (prop, id, att);
                                if (is_processed)
                                        break;
                        }
 
+                       if (!is_processed) {
+                               foreach (FieldInfo field in field_info){
+                                       is_processed = ProcessPropertiesAndFields (field, id, att);
+                                       if (is_processed)
+                                               break;
+                               }
+                       }
+
                        if (is_processed){
                                is_processed = false;
                                continue;
@@ -1081,6 +1227,11 @@ class AspGenerator
                }
        }
        
+       private void AddCodeRenderControl (StringBuilder function)
+       {
+               AddCodeRenderControl (function, controls.CodeRenderIndex);
+       }
+
        private void AddCodeRenderControl (StringBuilder function, int index)
        {
                function.AppendFormat ("\t\t\tparameterContainer.Controls [{0}]." + 
@@ -1297,7 +1448,7 @@ class AspGenerator
                }
                
                Type controlType = html_ctrl.ControlType;
-               declarations.AppendFormat ("\t\tprotected {0} {1};\n", controlType, html_ctrl.ControlID);
+               AddProtectedField (controlType, html_ctrl.ControlID);
 
                ChildrenKind children_kind;
                if (0 == String.Compare (html_ctrl.TagID, "table", true))
@@ -1346,15 +1497,17 @@ class AspGenerator
        {
                AspComponent component = (AspComponent) elements.Current;
                Type component_type = component.ComponentType;
-               declarations.AppendFormat ("\t\tprotected {0} {1};\n", component_type, component.ControlID);
+               AddProtectedField (component_type, component.ControlID);
 
                NewControlFunction (component.TagID, component.ControlID, component_type,
                                    component.ChildrenKind, component.DefaultPropertyName); 
 
-               if (component_type.IsSubclassOf (typeof (System.Web.UI.UserControl)))
+               if (component_type == typeof (UserControl) ||
+                   component_type.IsSubclassOf (typeof (System.Web.UI.UserControl)))
                        current_function.Append ("\t\t\t__ctrl.InitializeAsUserControl (Page);\n");
 
-               if (component_type.IsSubclassOf (typeof (System.Web.UI.Control)))
+               if (component_type == typeof (Control) ||
+                   component_type.IsSubclassOf (typeof (System.Web.UI.Control)))
                        current_function.AppendFormat ("\t\t\t__ctrl.ID = \"{0}\";\n", component.ControlID);
 
                AddCodeForAttributes (component.ComponentType, component.Attributes);
@@ -1399,7 +1552,7 @@ class AspGenerator
 
                        bool is_processed = false;
                        foreach (PropertyInfo subprop in subprop_info){
-                               is_processed = ProcessProperties (subprop, id, att);
+                               is_processed = ProcessPropertiesAndFields (subprop, id, att);
                                if (is_processed){
                                        subprop_name = subprop.Name;
                                        break;
@@ -1479,7 +1632,7 @@ class AspGenerator
                current_function = new StringBuilder ();
                functions.Push (current_function);
                current_function.AppendFormat ("\t\tprivate void __BuildControl_{0} " +
-                                               "(System.Web.UI.WebControl.DataGridColumnCollection __ctrl)\n" +
+                                               "(System.Web.UI.WebControls.DataGridColumnCollection __ctrl)\n" +
                                                "\t\t{{\n", prop_id);
        }
 
@@ -1512,7 +1665,7 @@ class AspGenerator
 
                        string default_id = Tag.GetDefaultID ();
                        Type type = typeof (System.Web.UI.WebControls.ListItem);
-                       declarations.AppendFormat ("\t\tprotected {0} {1};\n", type, default_id);
+                       AddProtectedField (type, default_id);
                        NewControlFunction (tag.TagID, default_id, type, ChildrenKind.CONTROLS, null); 
                        return;
                }
@@ -1635,7 +1788,7 @@ class AspGenerator
                StringBuilder db_function = new StringBuilder ();
                string control_id = Tag.GetDefaultID ();
                string control_type_string = "System.Web.UI.DataBoundLiteralControl";
-               declarations.AppendFormat ("\t\tprotected {0} {1};\n", control_type_string, control_id);
+               AddProtectedField (typeof (System.Web.UI.DataBoundLiteralControl), control_id);
                // Build the control
                db_function.AppendFormat ("\t\tprivate System.Web.UI.Control __BuildControl_{0} ()\n" +
                                          "\t\t{{\n\t\t\t{1} __ctrl;\n\n" +
@@ -1662,6 +1815,8 @@ class AspGenerator
                init_funcs.Append (db_function);
                current_function.AppendFormat ("\t\t\tthis.__BuildControl_{0} ();\n\t\t\t__parser." +
                                               "AddParsedSubObject (this.{0});\n\n", control_id);
+
+               AddCodeRenderControl (controls.CodeRenderFunction);
        }
 
        private void ProcessCodeRenderTag ()
@@ -1728,8 +1883,31 @@ class AspGenerator
                }
        }
 
+       private string GetTemplateDirectory ()
+       {
+               string templatePath = Path.GetDirectoryName (fullPath);
+               string appPath = Path.GetDirectoryName (HttpRuntime.AppDomainAppPath);
+
+               if (templatePath == appPath)
+                       return "/";
+
+               templatePath = templatePath.Substring (appPath.Length);
+               if (Path.DirectorySeparatorChar != '/')
+                       templatePath = templatePath.Replace (Path.DirectorySeparatorChar, '/');
+                       
+               return templatePath;
+       }
+
        private void End ()
        {
+               if (isPage) {
+                       if (sessionState == SessionState.Enabled || sessionState == SessionState.ReadOnly)
+                               AddInterface (typeof (System.Web.SessionState.IRequiresSessionState));
+
+                       if (sessionState == SessionState.ReadOnly)
+                               AddInterface (typeof (System.Web.SessionState.IReadOnlySessionState));
+               }
+               
                classDecl = "\tpublic class " + className + " : " + parent + interfaces + " {\n"; 
                prolog.Append ("\n" + classDecl);
                declarations.Append ("\t\tprivate static bool __intialized = false;\n\n");
@@ -1765,12 +1943,11 @@ class AspGenerator
                                "\t\tprotected System.Web.HttpApplication ApplicationInstance\n\t\t{\n" +
                                "\t\t\tget { return (System.Web.HttpApplication) this.Context.ApplicationInstance; }\n" +
                                "\t\t}\n\n");
-                       //FIXME: add TemplateSourceDirectory: don't know what for...yet!
-                       //FIXME: it should be the path from the root where the file resides
+
                        constructor.AppendFormat (
                                "\t\tpublic override string TemplateSourceDirectory\n\t\t{{\n" +
                                "\t\t\tget {{ return \"{0}\"; }}\n" +
-                               "\t\t}}\n\n", Path.GetDirectoryName (fullPath)); // FIXME: should be rooted on .appVPath
+                               "\t\t}}\n\n", GetTemplateDirectory ());
 
                        epilog.Append ("\n\t\tprotected override void FrameworkInitialize ()\n\t\t{\n" +
                                        "\t\t\tthis.__BuildControlTree (this);\n");
@@ -1832,17 +2009,12 @@ class AspGenerator
                public string assemblyName;
        }
 
-       private static UserControlData GenerateUserControl (string src)
+       private static UserControlData GenerateUserControl (string src, HttpContext context)
        {
                UserControlData data = new UserControlData ();
                data.result = UserControlResult.OK;
 
-               if (!File.Exists (src)) {
-                       data.result = UserControlResult.FileNotFound;
-                       return data;
-               }
-
-               UserControlCompiler compiler = new UserControlCompiler (new UserControlParser (src));
+               UserControlCompiler compiler = new UserControlCompiler (new UserControlParser (src, context));
                Type t = compiler.GetCompiledType ();
                if (t == null) {
                        data.result = UserControlResult.CompilationFailed;