From 1f930f79cc4b2390a797cfb503de2e070ea8f6ac Mon Sep 17 00:00:00 2001 From: Samah Date: Thu, 12 Mar 2015 08:42:24 +1030 Subject: [PATCH 1/4] Added support for "Internal" visibility modifier keyword that restricts member access to source files within the same directory. --- modules/trans/decl.monkey | 16 +++++++++++++--- modules/trans/parser.monkey | 12 ++++++++++++ modules/trans/preprocessor.monkey | 2 ++ modules/trans/toker.monkey | 2 +- 4 files changed, 28 insertions(+), 4 deletions(-) diff --git a/modules/trans/decl.monkey b/modules/trans/decl.monkey index 5f0f9c72..ce9f4afb 100644 --- a/modules/trans/decl.monkey +++ b/modules/trans/decl.monkey @@ -10,6 +10,7 @@ Const DECL_EXTERN= $000100 Const DECL_PRIVATE= $000200 Const DECL_ABSTRACT= $000400 Const DECL_FINAL= $000800 +Const DECL_INTERNAL= $004000 Const CLASS_INTERFACE= $001000 Const CLASS_THROWABLE= $002000 @@ -75,6 +76,10 @@ Class Decl Return (attrs & DECL_ABSTRACT)<>0 End + Method IsInternal() + Return (attrs & DECL_INTERNAL)<>0 + End + Method IsSemanted() Return (attrs & DECL_SEMANTED)<>0 End @@ -104,7 +109,7 @@ Class Decl End Method CheckAccess() - If IsPrivate() And ModuleScope()<>_env.ModuleScope() + If IsPrivate() And ModuleScope()<>_env.ModuleScope() Or IsInternal() And ModuleScope().filedir<>_env.ModuleScope().filedir Local fdecl:=_env.FuncScope() If fdecl And fdecl.attrs & DECL_REFLECTOR Return True Return False @@ -114,7 +119,11 @@ Class Decl Method AssertAccess() If Not CheckAccess() - Err ToString() +" is private." + If IsInternal() + Err ToString() +" is internal." + Else + Err ToString() +" is private." + End Endif End @@ -1221,7 +1230,7 @@ Const MODULE_SEMANTALL=2 Class ModuleDecl Extends ScopeDecl - Field modpath$,rmodpath$,filepath$ + Field modpath$,rmodpath$,filepath$,filedir$ Field imported:=New StringMap 'Maps filepath to modules Field pubImported:=New StringMap 'Ditto for publicly imported modules @@ -1237,6 +1246,7 @@ Class ModuleDecl Extends ScopeDecl Self.modpath=modpath Self.rmodpath=modpath Self.filepath=filepath + Self.filedir=ExtractDir(filepath) If modpath.Contains( "." ) Local bits:=modpath.Split( "." ),n:=bits.Length diff --git a/modules/trans/parser.monkey b/modules/trans/parser.monkey index 69cdf094..8ac9f666 100644 --- a/modules/trans/parser.monkey +++ b/modules/trans/parser.monkey @@ -1522,9 +1522,15 @@ Class Parser Case "private" NextToke decl_attrs|=DECL_PRIVATE + decl_attrs&=~DECL_INTERNAL Case "public" NextToke decl_attrs&=~DECL_PRIVATE + decl_attrs&=~DECL_INTERNAL + Case "internal" + NextToke + decl_attrs|=DECL_INTERNAL + decl_attrs&=~DECL_PRIVATE Case "const","global","field" If (attrs & CLASS_INTERFACE) And _toke<>"const" Err "Interfaces can only contain constants and methods." classDecl.InsertDecls ParseDecls( _toke,decl_attrs ) @@ -1599,6 +1605,9 @@ Class Parser Case "private" NextToke attrs=DECL_PRIVATE + Case "internal" + NextToke + attrs=DECL_INTERNAL Case "import" NextToke If _tokeType=TOKE_STRINGLIT @@ -1664,6 +1673,9 @@ Class Parser Case "private" NextToke attrs=DECL_PRIVATE + Case "internal" + NextToke + attrs=DECL_INTERNAL Case "extern" If ENV_SAFEMODE If _app.mainModule=_module diff --git a/modules/trans/preprocessor.monkey b/modules/trans/preprocessor.monkey index 9490f848..17ce4d95 100644 --- a/modules/trans/preprocessor.monkey +++ b/modules/trans/preprocessor.monkey @@ -86,6 +86,8 @@ Function PreProcess$( path$,mdecl:ModuleDecl=Null ) attrs=0 Case "private" attrs=DECL_PRIVATE + Case "internal" + attrs=DECL_INTERNAL Case "import" While toker.TokeType=TOKE_SPACE toke+=toker.Toke diff --git a/modules/trans/toker.monkey b/modules/trans/toker.monkey index 760dcadc..de24e2b4 100644 --- a/modules/trans/toker.monkey +++ b/modules/trans/toker.monkey @@ -36,7 +36,7 @@ Class Toker If _keywords Return Const keywords:="void strict "+ - "public private property "+ + "public private property internal "+ "bool int float string array object mod continue exit "+ "include import module extern "+ "new self super eachin true false null not "+ From d6fdc8f6aed1ab3ab39f41018f27bb375df96986 Mon Sep 17 00:00:00 2001 From: Samah Date: Fri, 13 Mar 2015 11:31:47 +1030 Subject: [PATCH 2/4] Added support for protected keyword which restricts visibility to subclasses. It can be used alongside or instead of internal. --- modules/trans/decl.monkey | 42 ++++++++++++++++++++++++++++--- modules/trans/parser.monkey | 8 ++++++ modules/trans/preprocessor.monkey | 12 ++++++++- modules/trans/toker.monkey | 2 +- 4 files changed, 59 insertions(+), 5 deletions(-) diff --git a/modules/trans/decl.monkey b/modules/trans/decl.monkey index ce9f4afb..14c65ff3 100644 --- a/modules/trans/decl.monkey +++ b/modules/trans/decl.monkey @@ -11,6 +11,7 @@ Const DECL_PRIVATE= $000200 Const DECL_ABSTRACT= $000400 Const DECL_FINAL= $000800 Const DECL_INTERNAL= $004000 +Const DECL_PROTECTED= $008000 Const CLASS_INTERFACE= $001000 Const CLASS_THROWABLE= $002000 @@ -80,6 +81,10 @@ Class Decl Return (attrs & DECL_INTERNAL)<>0 End + Method IsProtected() + Return (attrs & DECL_PROTECTED)<>0 + End + Method IsSemanted() Return (attrs & DECL_SEMANTED)<>0 End @@ -109,7 +114,34 @@ Class Decl End Method CheckAccess() - If IsPrivate() And ModuleScope()<>_env.ModuleScope() Or IsInternal() And ModuleScope().filedir<>_env.ModuleScope().filedir + ' if no environment, just let us through + If Not _env Return True + Local accessible:=False + If ModuleScope()=_env.ModuleScope() + ' if same module, always accessible + accessible=True + Elseif Not IsInternal() And Not IsProtected() + ' if not internal and not protected, accessible if not private + ' this caters for public + accessible=Not IsPrivate() + Else + ' if internal and same directory, accessible + If IsInternal() And ModuleScope().filedir=_env.ModuleScope().filedir accessible = True + ' if protected and subclassed, accessible + If Not accessible And IsProtected() + Local thisClass:=ClassScope() + Local currentClass:=_env.ClassScope() + While Not accessible And currentClass + If currentClass=thisClass + accessible=True + Else + currentClass=currentClass.superClass + End + End + End + End + ' if inaccessible, throw an error + If Not accessible Local fdecl:=_env.FuncScope() If fdecl And fdecl.attrs & DECL_REFLECTOR Return True Return False @@ -119,10 +151,14 @@ Class Decl Method AssertAccess() If Not CheckAccess() - If IsInternal() + If IsProtected() + Err ToString() +" is protected." + Elseif IsInternal() Err ToString() +" is internal." - Else + Elseif IsPrivate() Err ToString() +" is private." + Else + Err ToString() +" is inaccessible." End Endif End diff --git a/modules/trans/parser.monkey b/modules/trans/parser.monkey index 8ac9f666..662eb8b6 100644 --- a/modules/trans/parser.monkey +++ b/modules/trans/parser.monkey @@ -1523,14 +1523,22 @@ Class Parser NextToke decl_attrs|=DECL_PRIVATE decl_attrs&=~DECL_INTERNAL + decl_attrs&=~DECL_PROTECTED Case "public" NextToke decl_attrs&=~DECL_PRIVATE decl_attrs&=~DECL_INTERNAL + decl_attrs&=~DECL_PROTECTED Case "internal" NextToke decl_attrs|=DECL_INTERNAL decl_attrs&=~DECL_PRIVATE + ' we can be both internal and protected, so don't reset DECL_PROTECTED + Case "protected" + NextToke + decl_attrs|=DECL_PROTECTED + decl_attrs&=~DECL_PRIVATE + ' we can be both internal and protected, so don't reset DECL_INTERNAL Case "const","global","field" If (attrs & CLASS_INTERFACE) And _toke<>"const" Err "Interfaces can only contain constants and methods." classDecl.InsertDecls ParseDecls( _toke,decl_attrs ) diff --git a/modules/trans/preprocessor.monkey b/modules/trans/preprocessor.monkey index 17ce4d95..87e8c3ae 100644 --- a/modules/trans/preprocessor.monkey +++ b/modules/trans/preprocessor.monkey @@ -87,7 +87,17 @@ Function PreProcess$( path$,mdecl:ModuleDecl=Null ) Case "private" attrs=DECL_PRIVATE Case "internal" - attrs=DECL_INTERNAL + If attrs<>DECL_PROTECTED + attrs=DECL_INTERNAL + Else + attrs|=DECL_INTERNAL + End + Case "protected" + If attrs<>DECL_INTERNAL + attrs=DECL_PROTECTED + Else + attrs|=DECL_PROTECTED + End Case "import" While toker.TokeType=TOKE_SPACE toke+=toker.Toke diff --git a/modules/trans/toker.monkey b/modules/trans/toker.monkey index de24e2b4..78962a98 100644 --- a/modules/trans/toker.monkey +++ b/modules/trans/toker.monkey @@ -36,7 +36,7 @@ Class Toker If _keywords Return Const keywords:="void strict "+ - "public private property internal "+ + "public private property internal protected "+ "bool int float string array object mod continue exit "+ "include import module extern "+ "new self super eachin true false null not "+ From 6590f95405d971f17fd7f086c68a5f4cb700a4fc Mon Sep 17 00:00:00 2001 From: Samah Date: Fri, 13 Mar 2015 15:45:27 +1030 Subject: [PATCH 3/4] Changed Protected and Internal to be mutually exclusive and brought in a third keyword ProtectedInternal to apply both. --- modules/trans/decl.monkey | 56 +++++++++++++++++-------------- modules/trans/parser.monkey | 9 +++-- modules/trans/preprocessor.monkey | 14 +++----- modules/trans/toker.monkey | 2 +- 4 files changed, 43 insertions(+), 38 deletions(-) diff --git a/modules/trans/decl.monkey b/modules/trans/decl.monkey index 14c65ff3..b6547ceb 100644 --- a/modules/trans/decl.monkey +++ b/modules/trans/decl.monkey @@ -85,6 +85,14 @@ Class Decl Return (attrs & DECL_PROTECTED)<>0 End + Method IsProtectedInternal() + Return IsInternal() And IsProtected() + End + + Method IsPublic() + Return Not IsPrivate() And Not IsInternal() And Not IsProtected() + End + Method IsSemanted() Return (attrs & DECL_SEMANTED)<>0 End @@ -116,42 +124,40 @@ Class Decl Method CheckAccess() ' if no environment, just let us through If Not _env Return True - Local accessible:=False - If ModuleScope()=_env.ModuleScope() - ' if same module, always accessible - accessible=True - Elseif Not IsInternal() And Not IsProtected() - ' if not internal and not protected, accessible if not private - ' this caters for public - accessible=Not IsPrivate() - Else + + ' if public, always accessible + If IsPublic() Return True + + ' if same module, always accessible + If ModuleScope()=_env.ModuleScope() Return True + + ' if not private, check internal/protected + If Not IsPrivate() ' if internal and same directory, accessible - If IsInternal() And ModuleScope().filedir=_env.ModuleScope().filedir accessible = True + If IsInternal() And ModuleScope().filedir=_env.ModuleScope().filedir Return True + ' if protected and subclassed, accessible - If Not accessible And IsProtected() + If IsProtected() Local thisClass:=ClassScope() Local currentClass:=_env.ClassScope() - While Not accessible And currentClass - If currentClass=thisClass - accessible=True - Else - currentClass=currentClass.superClass - End + While currentClass + If currentClass=thisClass Return True + currentClass=currentClass.superClass End End End - ' if inaccessible, throw an error - If Not accessible - Local fdecl:=_env.FuncScope() - If fdecl And fdecl.attrs & DECL_REFLECTOR Return True - Return False - Endif - Return True + + ' inaccessible, throw an error + Local fdecl:=_env.FuncScope() + If fdecl And fdecl.attrs & DECL_REFLECTOR Return True + Return False End Method AssertAccess() If Not CheckAccess() - If IsProtected() + If IsProtectedInternal() + Err ToString() +" is protected and internal." + Elseif IsProtected() Err ToString() +" is protected." Elseif IsInternal() Err ToString() +" is internal." diff --git a/modules/trans/parser.monkey b/modules/trans/parser.monkey index 662eb8b6..412781df 100644 --- a/modules/trans/parser.monkey +++ b/modules/trans/parser.monkey @@ -1533,12 +1533,17 @@ Class Parser NextToke decl_attrs|=DECL_INTERNAL decl_attrs&=~DECL_PRIVATE - ' we can be both internal and protected, so don't reset DECL_PROTECTED + decl_attrs&=~DECL_PROTECTED Case "protected" NextToke decl_attrs|=DECL_PROTECTED decl_attrs&=~DECL_PRIVATE - ' we can be both internal and protected, so don't reset DECL_INTERNAL + decl_attrs&=~DECL_INTERNAL + Case "protectedinternal" + NextToke + decl_attrs|=DECL_PROTECTED + decl_attrs|=DECL_INTERNAL + decl_attrs&=~DECL_PRIVATE Case "const","global","field" If (attrs & CLASS_INTERFACE) And _toke<>"const" Err "Interfaces can only contain constants and methods." classDecl.InsertDecls ParseDecls( _toke,decl_attrs ) diff --git a/modules/trans/preprocessor.monkey b/modules/trans/preprocessor.monkey index 87e8c3ae..ae65b199 100644 --- a/modules/trans/preprocessor.monkey +++ b/modules/trans/preprocessor.monkey @@ -87,17 +87,11 @@ Function PreProcess$( path$,mdecl:ModuleDecl=Null ) Case "private" attrs=DECL_PRIVATE Case "internal" - If attrs<>DECL_PROTECTED - attrs=DECL_INTERNAL - Else - attrs|=DECL_INTERNAL - End + attrs=DECL_INTERNAL Case "protected" - If attrs<>DECL_INTERNAL - attrs=DECL_PROTECTED - Else - attrs|=DECL_PROTECTED - End + attrs=DECL_PROTECTED + Case "protectedinternal" + attrs=DECL_PROTECTED|DECL_INTERNAL Case "import" While toker.TokeType=TOKE_SPACE toke+=toker.Toke diff --git a/modules/trans/toker.monkey b/modules/trans/toker.monkey index 78962a98..b5c42338 100644 --- a/modules/trans/toker.monkey +++ b/modules/trans/toker.monkey @@ -36,7 +36,7 @@ Class Toker If _keywords Return Const keywords:="void strict "+ - "public private property internal protected "+ + "public private property internal protected protectedinternal "+ "bool int float string array object mod continue exit "+ "include import module extern "+ "new self super eachin true false null not "+ From b54f59227f6a465252a43fb5551d109aef680ff6 Mon Sep 17 00:00:00 2001 From: Samah Date: Sat, 14 Mar 2015 11:31:36 +1030 Subject: [PATCH 4/4] Removed ProtectedInternal keyword and left Protected and Internal as mutually exclusive. --- modules/trans/decl.monkey | 8 +------- modules/trans/parser.monkey | 5 ----- modules/trans/preprocessor.monkey | 2 -- modules/trans/toker.monkey | 2 +- 4 files changed, 2 insertions(+), 15 deletions(-) diff --git a/modules/trans/decl.monkey b/modules/trans/decl.monkey index b6547ceb..7a1a99d5 100644 --- a/modules/trans/decl.monkey +++ b/modules/trans/decl.monkey @@ -85,10 +85,6 @@ Class Decl Return (attrs & DECL_PROTECTED)<>0 End - Method IsProtectedInternal() - Return IsInternal() And IsProtected() - End - Method IsPublic() Return Not IsPrivate() And Not IsInternal() And Not IsProtected() End @@ -155,9 +151,7 @@ Class Decl Method AssertAccess() If Not CheckAccess() - If IsProtectedInternal() - Err ToString() +" is protected and internal." - Elseif IsProtected() + If IsProtected() Err ToString() +" is protected." Elseif IsInternal() Err ToString() +" is internal." diff --git a/modules/trans/parser.monkey b/modules/trans/parser.monkey index 412781df..66df934b 100644 --- a/modules/trans/parser.monkey +++ b/modules/trans/parser.monkey @@ -1539,11 +1539,6 @@ Class Parser decl_attrs|=DECL_PROTECTED decl_attrs&=~DECL_PRIVATE decl_attrs&=~DECL_INTERNAL - Case "protectedinternal" - NextToke - decl_attrs|=DECL_PROTECTED - decl_attrs|=DECL_INTERNAL - decl_attrs&=~DECL_PRIVATE Case "const","global","field" If (attrs & CLASS_INTERFACE) And _toke<>"const" Err "Interfaces can only contain constants and methods." classDecl.InsertDecls ParseDecls( _toke,decl_attrs ) diff --git a/modules/trans/preprocessor.monkey b/modules/trans/preprocessor.monkey index ae65b199..339b8c0a 100644 --- a/modules/trans/preprocessor.monkey +++ b/modules/trans/preprocessor.monkey @@ -90,8 +90,6 @@ Function PreProcess$( path$,mdecl:ModuleDecl=Null ) attrs=DECL_INTERNAL Case "protected" attrs=DECL_PROTECTED - Case "protectedinternal" - attrs=DECL_PROTECTED|DECL_INTERNAL Case "import" While toker.TokeType=TOKE_SPACE toke+=toker.Toke diff --git a/modules/trans/toker.monkey b/modules/trans/toker.monkey index b5c42338..78962a98 100644 --- a/modules/trans/toker.monkey +++ b/modules/trans/toker.monkey @@ -36,7 +36,7 @@ Class Toker If _keywords Return Const keywords:="void strict "+ - "public private property internal protected protectedinternal "+ + "public private property internal protected "+ "bool int float string array object mod continue exit "+ "include import module extern "+ "new self super eachin true false null not "+