55using System ;
66using System . Collections . Generic ;
77using System . Collections . ObjectModel ;
8+ using System . Diagnostics ;
89using System . Diagnostics . CodeAnalysis ;
910using System . Linq ;
1011using System . Runtime . CompilerServices ;
@@ -97,13 +98,16 @@ internal override bool HasLateBoundVariableSets {
9798 }
9899 }
99100
100- internal override bool ExposesLocalVariable ( PythonVariable variable ) => true ;
101+ internal override bool ExposesLocalVariable ( PythonVariable variable ) {
102+ Debug . Assert ( variable . Name == "__class__" ) ;
103+ return true ;
104+ }
101105
102106
103107 internal override bool TryBindOuter ( ScopeStatement from , PythonReference reference , out PythonVariable variable ) {
104108 if ( reference . Name == "__class__" ) {
109+ ClassVariable = variable = EnsureClassVariable ( ) ;
105110 ClassCellVariable = EnsureVariable ( "__classcell__" ) ;
106- ClassVariable = variable = EnsureVariable ( reference . Name ) ;
107111 variable . AccessedInNestedScope = true ;
108112 from . AddFreeVariable ( variable , true ) ;
109113 for ( ScopeStatement scope = from . Parent ; scope != this ; scope = scope . Parent ) {
@@ -122,18 +126,19 @@ internal override bool TryBindOuter(ScopeStatement from, PythonReference referen
122126 // Python semantics: The variables bound local in the class
123127 // scope are accessed by name - the dictionary behavior of classes
124128 if ( TryGetVariable ( reference . Name , out variable ) ) {
125- // TODO: This results in doing a dictionary lookup to get/set the local,
126- // when it should probably be an uninitialized check / global lookup for gets
127- // and a direct set
128- if ( variable . Kind == VariableKind . Global ) {
129+ if ( variable . Kind is VariableKind . Global ) {
129130 AddReferencedGlobal ( reference . Name ) ;
130- } else if ( variable . Kind == VariableKind . Local ) {
131+ } else if ( variable . Kind is VariableKind . Local ) {
132+ // TODO: This results in doing a dictionary lookup to get/set the local,
133+ // when it should probably be an uninitialized check / global lookup for gets
134+ // and a direct set
131135 return null ;
132- }
133-
134- if ( variable . Kind != VariableKind . Nonlocal ) {
136+ } else if ( variable . Kind is VariableKind . Attribute ) {
137+ return null ; // fall back on LookupName/SetName in local context dict, which is faster than LookupGlobalVariable
138+ } else if ( variable . Kind is VariableKind . Parameter ) {
135139 return variable ;
136140 }
141+ // else NonLocal: continue binding
137142 }
138143
139144 // Try to bind in outer scopes, if we have an unqualified exec we need to leave the
@@ -148,6 +153,20 @@ internal override bool TryBindOuter(ScopeStatement from, PythonReference referen
148153 return null ;
149154 }
150155
156+ internal override PythonVariable EnsureVariable ( string name ) {
157+ if ( TryGetVariable ( name , out PythonVariable variable ) ) {
158+ return variable ;
159+ }
160+ return CreateVariable ( name , VariableKind . Attribute ) ;
161+ }
162+
163+ internal PythonVariable EnsureClassVariable ( ) {
164+ if ( TryGetVariable ( "$__class__" , out PythonVariable variable ) ) {
165+ return variable ;
166+ }
167+ return CreateVariable ( "__class__" , VariableKind . Local , "$__class__" ) ;
168+ }
169+
151170 internal override Ast LookupVariableExpression ( PythonVariable variable ) {
152171 // Emulates opcode LOAD_CLASSDEREF
153172 return Ast . Call (
@@ -240,6 +259,9 @@ static MSAst.Expression UnpackKeywordsHelper(MSAst.Expression context, ReadOnlyS
240259 }
241260 }
242261
262+ private MSAst . Expression SetLocalName ( string name , MSAst . Expression expression )
263+ => Ast . Call ( AstMethods . SetName , LocalContext , Ast . Constant ( name ) , expression ) ;
264+
243265 private Microsoft . Scripting . Ast . LightExpression < Func < CodeContext , CodeContext > > MakeClassBody ( ) {
244266 // we always need to create a nested context for class defs
245267
@@ -260,15 +282,15 @@ private Microsoft.Scripting.Ast.LightExpression<Func<CodeContext, CodeContext>>
260282 init . Add ( Ast . Assign ( LocalCodeContextVariable , createLocal ) ) ;
261283
262284 // __module__ = __name__
263- MSAst . Expression modStmt = AssignValue ( GetVariableExpression ( ModVariable ! ) , GetVariableExpression ( ModuleNameVariable ! ) ) ;
285+ MSAst . Expression modStmt = SetLocalName ( "__module__" , AstUtils . Convert ( GetVariableExpression ( ModuleNameVariable ! ) , typeof ( object ) ) ) ;
264286
265287 // TODO: set __qualname__
266288
267289 // __doc__ = """..."""
268290 MSAst . Expression ? docStmt = null ;
269291 string doc = GetDocumentation ( Body ) ;
270292 if ( doc is not null ) {
271- docStmt = AssignValue ( GetVariableExpression ( DocVariable ! ) , AstUtils . Constant ( doc ) ) ;
293+ docStmt = SetLocalName ( "__doc__" , Ast . Constant ( doc , typeof ( object ) ) ) ;
272294 }
273295
274296 // Create the body
@@ -280,9 +302,9 @@ private Microsoft.Scripting.Ast.LightExpression<Func<CodeContext, CodeContext>>
280302
281303 // __classcell__ == ClosureCell(__class__)
282304 MSAst . Expression ? assignClassCellStmt = null ;
283- if ( ClassCellVariable is not null ) {
284- var exp = ( ClosureExpression ) GetVariableExpression ( ClassVariable ! ) ;
285- assignClassCellStmt = AssignValue ( GetVariableExpression ( ClassCellVariable ) , exp . ClosureCell ) ;
305+ if ( ClassVariable is not null ) {
306+ var exp = ( ClosureExpression ) GetVariableExpression ( ClassVariable ) ;
307+ assignClassCellStmt = AssignValue ( GetVariableExpression ( ClassCellVariable ! ) , exp . ClosureCell ) ;
286308 }
287309
288310 bodyStmt = WrapScopeStatements (
0 commit comments