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,15 +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
102- private bool needClassCell { get ; set ; }
103106
104107 internal override bool TryBindOuter ( ScopeStatement from , PythonReference reference , out PythonVariable variable ) {
105108 if ( reference . Name == "__class__" ) {
106- needClassCell = true ;
109+ ClassVariable = variable = EnsureClassVariable ( ) ;
107110 ClassCellVariable = EnsureVariable ( "__classcell__" ) ;
108- ClassVariable = variable = EnsureVariable ( reference . Name ) ;
109111 variable . AccessedInNestedScope = true ;
110112 from . AddFreeVariable ( variable , true ) ;
111113 for ( ScopeStatement scope = from . Parent ; scope != this ; scope = scope . Parent ) {
@@ -124,18 +126,19 @@ internal override bool TryBindOuter(ScopeStatement from, PythonReference referen
124126 // Python semantics: The variables bound local in the class
125127 // scope are accessed by name - the dictionary behavior of classes
126128 if ( TryGetVariable ( reference . Name , out variable ) ) {
127- // TODO: This results in doing a dictionary lookup to get/set the local,
128- // when it should probably be an uninitialized check / global lookup for gets
129- // and a direct set
130- if ( variable . Kind == VariableKind . Global ) {
129+ if ( variable . Kind is VariableKind . Global ) {
131130 AddReferencedGlobal ( reference . Name ) ;
132- } 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
133135 return null ;
134- }
135-
136- 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 ) {
137139 return variable ;
138140 }
141+ // else NonLocal: continue binding
139142 }
140143
141144 // Try to bind in outer scopes, if we have an unqualified exec we need to leave the
@@ -150,6 +153,20 @@ internal override bool TryBindOuter(ScopeStatement from, PythonReference referen
150153 return null ;
151154 }
152155
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+
153170 internal override Ast LookupVariableExpression ( PythonVariable variable ) {
154171 // Emulates opcode LOAD_CLASSDEREF
155172 return Ast . Call (
@@ -242,6 +259,9 @@ static MSAst.Expression UnpackKeywordsHelper(MSAst.Expression context, ReadOnlyS
242259 }
243260 }
244261
262+ private MSAst . Expression SetLocalName ( string name , MSAst . Expression expression )
263+ => Ast . Call ( AstMethods . SetName , LocalContext , Ast . Constant ( name ) , expression ) ;
264+
245265 private Microsoft . Scripting . Ast . LightExpression < Func < CodeContext , CodeContext > > MakeClassBody ( ) {
246266 // we always need to create a nested context for class defs
247267
@@ -260,44 +280,41 @@ private Microsoft.Scripting.Ast.LightExpression<Func<CodeContext, CodeContext>>
260280 var createLocal = CreateLocalContext ( _parentContextParam ) ;
261281
262282 init . Add ( Ast . Assign ( LocalCodeContextVariable , createLocal ) ) ;
263- // __classcell__ == ClosureCell(__class__)
264- if ( needClassCell ) {
265- var exp = ( ClosureExpression ) GetVariableExpression ( ClassVariable ! ) ;
266- MSAst . Expression assignClassCell = AssignValue ( GetVariableExpression ( ClassCellVariable ! ) , exp . ClosureCell ) ;
267- init . Add ( assignClassCell ) ;
268- }
269-
270- List < MSAst . Expression > statements = new List < MSAst . Expression > ( ) ;
271- // Create the body
272- MSAst . Expression bodyStmt = Body ;
273-
274283
275284 // __module__ = __name__
276- MSAst . Expression modStmt = AssignValue ( GetVariableExpression ( ModVariable ! ) , GetVariableExpression ( ModuleNameVariable ! ) ) ;
285+ MSAst . Expression modStmt = SetLocalName ( "__module__" , AstUtils . Convert ( GetVariableExpression ( ModuleNameVariable ! ) , typeof ( object ) ) ) ;
286+
287+ // TODO: set __qualname__
277288
289+ // __doc__ = """..."""
290+ MSAst . Expression ? docStmt = null ;
278291 string doc = GetDocumentation ( Body ) ;
279- if ( doc != null ) {
280- statements . Add (
281- AssignValue (
282- GetVariableExpression ( DocVariable ! ) ,
283- AstUtils . Constant ( doc )
284- )
285- ) ;
292+ if ( doc is not null ) {
293+ docStmt = SetLocalName ( "__doc__" , Ast . Constant ( doc , typeof ( object ) ) ) ;
286294 }
287295
296+ // Create the body
297+ MSAst . Expression bodyStmt = Body ;
288298 if ( Body . CanThrow && GlobalParent . PyContext . PythonOptions . Frames ) {
289299 bodyStmt = AddFrame ( LocalContext , FuncCodeExpr , bodyStmt ) ;
290300 locals . Add ( FunctionStackVariable ) ;
291301 }
292302
303+ // __classcell__ == ClosureCell(__class__)
304+ MSAst . Expression ? assignClassCellStmt = null ;
305+ if ( ClassVariable is not null ) {
306+ var exp = ( ClosureExpression ) GetVariableExpression ( ClassVariable ) ;
307+ assignClassCellStmt = AssignValue ( GetVariableExpression ( ClassCellVariable ! ) , exp . ClosureCell ) ;
308+ }
309+
293310 bodyStmt = WrapScopeStatements (
294311 Ast . Block (
295312 Ast . Block ( init ) ,
296- statements . Count == 0 ?
297- EmptyBlock :
298- Ast . Block ( new ReadOnlyCollection < MSAst . Expression > ( statements ) ) ,
299313 modStmt ,
314+ // __qualname__
315+ docStmt is not null ? docStmt : AstUtils . Empty ( ) ,
300316 bodyStmt ,
317+ assignClassCellStmt is not null ? assignClassCellStmt : AstUtils . Empty ( ) ,
301318 LocalContext
302319 ) ,
303320 Body . CanThrow
0 commit comments