5151import com .oracle .graal .python .builtins .objects .function .PArguments ;
5252import com .oracle .graal .python .nodes .ModuleRootNode ;
5353import com .oracle .graal .python .nodes .SpecialMethodNames ;
54+ import com .oracle .graal .python .nodes .attributes .LookupInheritedAttributeNode ;
55+ import com .oracle .graal .python .nodes .call .CallNode ;
56+ import com .oracle .graal .python .nodes .frame .MaterializeFrameNodeGen .SyncFrameValuesNodeGen ;
5457import com .oracle .graal .python .nodes .function .ClassBodyRootNode ;
5558import com .oracle .graal .python .runtime .object .PythonObjectFactory ;
5659import com .oracle .truffle .api .dsl .Cached ;
7376 **/
7477public abstract class MaterializeFrameNode extends Node {
7578
79+ private final boolean adoptable ;
80+ private static final MaterializeFrameNode INSTANCE = MaterializeFrameNodeGen .create (false );
81+
82+ public MaterializeFrameNode () {
83+ this .adoptable = true ;
84+ }
85+
86+ protected MaterializeFrameNode (boolean adoptable ) {
87+ this .adoptable = adoptable ;
88+ }
89+
7690 public final PFrame execute (VirtualFrame frame , boolean markAsEscaped , Frame frameToMaterialize ) {
7791 return execute (frame , markAsEscaped , false , frameToMaterialize );
7892 }
@@ -96,7 +110,7 @@ public final PFrame execute(VirtualFrame frame, Node location, boolean markAsEsc
96110
97111 @ Specialization (guards = {"getPFrame(frameToMaterialize) == null" , "isGeneratorFrame(frameToMaterialize)" })
98112 static PFrame freshPFrameForGenerator (Node location , @ SuppressWarnings ("unused" ) boolean markAsEscaped , @ SuppressWarnings ("unused" ) boolean forceSync , Frame frameToMaterialize ,
99- @ Shared ("factory" ) @ Cached PythonObjectFactory factory ) {
113+ @ Shared ("factory" ) @ Cached ( "createFactory()" ) PythonObjectFactory factory ) {
100114 PFrame escapedFrame = factory .createPFrame (PArguments .getCurrentFrameInfo (frameToMaterialize ), location , PArguments .getGeneratorFrameLocals (frameToMaterialize ), false );
101115 syncArgs (frameToMaterialize , escapedFrame );
102116 PFrame .Reference topFrameRef = PArguments .getCurrentFrameInfo (frameToMaterialize );
@@ -106,17 +120,17 @@ static PFrame freshPFrameForGenerator(Node location, @SuppressWarnings("unused")
106120
107121 @ Specialization (guards = {"getPFrame(frameToMaterialize) == null" , "!inClassBody(frameToMaterialize)" , "!isGeneratorFrame(frameToMaterialize)" })
108122 static PFrame freshPFrame (VirtualFrame frame , Node location , boolean markAsEscaped , @ SuppressWarnings ("unused" ) boolean forceSync , Frame frameToMaterialize ,
109- @ Shared ("factory" ) @ Cached PythonObjectFactory factory ,
110- @ Shared ("syncValuesNode" ) @ Cached SyncFrameValuesNode syncValuesNode ) {
123+ @ Shared ("factory" ) @ Cached ( "createFactory()" ) PythonObjectFactory factory ,
124+ @ Shared ("syncValuesNode" ) @ Cached ( "createSyncNode()" ) SyncFrameValuesNode syncValuesNode ) {
111125 PDict locals = factory .createDictLocals (frameToMaterialize .getFrameDescriptor ());
112126 PFrame escapedFrame = factory .createPFrame (PArguments .getCurrentFrameInfo (frameToMaterialize ), location , locals , false );
113127 return doEscapeFrame (frame , frameToMaterialize , escapedFrame , markAsEscaped , forceSync && !inModuleRoot (location ), syncValuesNode );
114128 }
115129
116130 @ Specialization (guards = {"getPFrame(frameToMaterialize) == null" , "inClassBody(frameToMaterialize)" })
117131 static PFrame freshPFrameInClassBody (VirtualFrame frame , Node location , boolean markAsEscaped , @ SuppressWarnings ("unused" ) boolean forceSync , Frame frameToMaterialize ,
118- @ Shared ("factory" ) @ Cached PythonObjectFactory factory ,
119- @ Shared ("syncValuesNode" ) @ Cached SyncFrameValuesNode syncValuesNode ) {
132+ @ Shared ("factory" ) @ Cached ( "createFactory()" ) PythonObjectFactory factory ,
133+ @ Shared ("syncValuesNode" ) @ Cached ( "createSyncNode()" ) SyncFrameValuesNode syncValuesNode ) {
120134 // the namespace argument stores the locals
121135 PFrame escapedFrame = factory .createPFrame (PArguments .getCurrentFrameInfo (frameToMaterialize ), location , PArguments .getArgument (frameToMaterialize , 0 ), true );
122136 // The locals dict in a class body is always custom; we must not write the values from the
@@ -133,16 +147,16 @@ static PFrame freshPFrameInClassBody(VirtualFrame frame, Node location, boolean
133147 **/
134148 @ Specialization (guards = {"getPFrame(frameToMaterialize) != null" , "!getPFrame(frameToMaterialize).hasFrame()" })
135149 static PFrame incompleteFrame (VirtualFrame frame , Node location , boolean markAsEscaped , boolean forceSync , Frame frameToMaterialize ,
136- @ Shared ("factory" ) @ Cached PythonObjectFactory factory ,
137- @ Shared ("syncValuesNode" ) @ Cached SyncFrameValuesNode syncValuesNode ) {
150+ @ Shared ("factory" ) @ Cached ( "createFactory()" ) PythonObjectFactory factory ,
151+ @ Shared ("syncValuesNode" ) @ Cached ( "createSyncNode()" ) SyncFrameValuesNode syncValuesNode ) {
138152 Object locals = getPFrame (frameToMaterialize ).getLocalsDict ();
139153 PFrame escapedFrame = factory .createPFrame (PArguments .getCurrentFrameInfo (frameToMaterialize ), location , locals , inClassBody (frameToMaterialize ));
140154 return doEscapeFrame (frame , frameToMaterialize , escapedFrame , markAsEscaped , forceSync && !inModuleRoot (location ), syncValuesNode );
141155 }
142156
143157 @ Specialization (guards = {"getPFrame(frameToMaterialize) != null" , "getPFrame(frameToMaterialize).hasFrame()" }, replaces = "freshPFrame" )
144158 static PFrame alreadyEscapedFrame (VirtualFrame frame , Node location , boolean markAsEscaped , boolean forceSync , Frame frameToMaterialize ,
145- @ Shared ("syncValuesNode" ) @ Cached SyncFrameValuesNode syncValuesNode ,
159+ @ Shared ("syncValuesNode" ) @ Cached ( "createSyncNode()" ) SyncFrameValuesNode syncValuesNode ,
146160 @ Cached ("createBinaryProfile()" ) ConditionProfile syncProfile ) {
147161 PFrame pyFrame = getPFrame (frameToMaterialize );
148162 if (syncProfile .profile (forceSync && !inClassBody (frameToMaterialize ) && !inModuleRoot (location ))) {
@@ -158,8 +172,8 @@ static PFrame alreadyEscapedFrame(VirtualFrame frame, Node location, boolean mar
158172
159173 @ Specialization (replaces = {"freshPFrame" , "alreadyEscapedFrame" })
160174 static PFrame notInClassBody (VirtualFrame frame , Node location , boolean markAsEscaped , boolean forceSync , Frame frameToMaterialize ,
161- @ Shared ("factory" ) @ Cached PythonObjectFactory factory ,
162- @ Shared ("syncValuesNode" ) @ Cached SyncFrameValuesNode syncValuesNode ,
175+ @ Shared ("factory" ) @ Cached ( "createFactory()" ) PythonObjectFactory factory ,
176+ @ Shared ("syncValuesNode" ) @ Cached ( "createSyncNode()" ) SyncFrameValuesNode syncValuesNode ,
163177 @ Cached ("createBinaryProfile()" ) ConditionProfile syncProfile ) {
164178 if (getPFrame (frameToMaterialize ) != null ) {
165179 return alreadyEscapedFrame (frame , location , markAsEscaped , forceSync , frameToMaterialize , syncValuesNode , syncProfile );
@@ -220,6 +234,26 @@ protected static boolean inModuleRoot(Node location) {
220234 return location .getRootNode () instanceof ModuleRootNode ;
221235 }
222236
237+ protected final SyncFrameValuesNode createSyncNode () {
238+ return SyncFrameValuesNodeGen .create (isAdoptable ());
239+ }
240+
241+ protected final PythonObjectFactory createFactory () {
242+ if (isAdoptable ()) {
243+ return PythonObjectFactory .create ();
244+ }
245+ return PythonObjectFactory .getUncached ();
246+ }
247+
248+ @ Override
249+ public boolean isAdoptable () {
250+ return adoptable ;
251+ }
252+
253+ public static MaterializeFrameNode getUnadoptable () {
254+ return INSTANCE ;
255+ }
256+
223257 /**
224258 * When refreshing the frame values in the locals dict, there are 4 cases:
225259 * <ol>
@@ -241,6 +275,12 @@ protected static boolean inModuleRoot(Node location) {
241275 @ ImportStatic (SpecialMethodNames .class )
242276 public abstract static class SyncFrameValuesNode extends Node {
243277
278+ private final boolean adoptable ;
279+
280+ public SyncFrameValuesNode (boolean adoptable ) {
281+ this .adoptable = adoptable ;
282+ }
283+
244284 public abstract void execute (VirtualFrame frame , PFrame pyframe , Frame frameToSync );
245285
246286 @ Specialization (guards = {"hasLocalsStorage(pyFrame, frameToSync)" , "frameToSync.getFrameDescriptor() == cachedFd" }, //
@@ -315,11 +355,11 @@ static void doLocalsStorageUncached(PFrame pyFrame, Frame frameToSync) {
315355 }
316356 }
317357
318- @ Specialization (guards = {"isDictWithCustomStorage(pyFrame)" , "frameToSync.getFrameDescriptor() == cachedFd" }, //
358+ @ Specialization (guards = {"isDictWithCustomStorage(pyFrame)" , "frameToSync.getFrameDescriptor() == cachedFd" , "isAdoptable()" }, //
319359 assumptions = "cachedFd.getVersion()" , //
320360 limit = "1" )
321361 @ ExplodeLoop
322- static void doGenericDictCached (VirtualFrame frame , PFrame pyFrame , Frame frameToSync ,
362+ static void doGenericDictAdoptableCached (VirtualFrame frame , PFrame pyFrame , Frame frameToSync ,
323363 @ Cached ("frameToSync.getFrameDescriptor()" ) @ SuppressWarnings ("unused" ) FrameDescriptor cachedFd ,
324364 @ Cached (value = "getSlots(cachedFd)" , dimensions = 1 ) FrameSlot [] cachedSlots ,
325365 @ Cached (value = "getProfiles(cachedSlots.length)" , dimensions = 1 ) ConditionProfile [] profiles ,
@@ -346,8 +386,8 @@ static void doGenericDictCached(VirtualFrame frame, PFrame pyFrame, Frame frameT
346386 }
347387 }
348388
349- @ Specialization (guards = "isDictWithCustomStorage(pyFrame)" , replaces = "doGenericDictCached " )
350- static void doGenericDict (VirtualFrame frame , PFrame pyFrame , Frame frameToSync ,
389+ @ Specialization (guards = { "isDictWithCustomStorage(pyFrame)" , "isAdoptable()" }, replaces = "doGenericDictAdoptableCached " )
390+ static void doGenericDictAdoptable (VirtualFrame frame , PFrame pyFrame , Frame frameToSync ,
351391 @ Cached HashingCollectionNodes .SetItemNode setItemNode ,
352392 @ Cached HashingStorageNodes .DelItemNode deleteItemNode ) {
353393 // This can happen if someone received the locals dict using 'locals()' or similar and
@@ -373,6 +413,34 @@ static void doGenericDict(VirtualFrame frame, PFrame pyFrame, Frame frameToSync,
373413 }
374414 }
375415
416+ @ Specialization (guards = {"isDictWithCustomStorage(pyFrame)" , "!isAdoptable()" })
417+ static void doGenericDict (VirtualFrame frame , PFrame pyFrame , Frame frameToSync ) {
418+ // Same as 'doGenericDictAdoptable' but uses a full call node to call '__setitem__' and
419+ // '__delitem__' since this node is not adoptable.
420+
421+ FrameDescriptor fd = frameToSync .getFrameDescriptor ();
422+ FrameSlot [] slots = getSlots (fd );
423+ // The cast is guaranteed by the guard.
424+ PDict localsDict = (PDict ) pyFrame .getLocalsDict ();
425+
426+ // we need to use nodes where we are sure that they may not be adopted
427+ Object setItemMethod = LookupInheritedAttributeNode .Dynamic .getUncached ().execute (localsDict , SpecialMethodNames .__SETITEM__ );
428+ Object deleteItemMethod = LookupInheritedAttributeNode .Dynamic .getUncached ().execute (localsDict , SpecialMethodNames .__DELITEM__ );
429+
430+ for (int i = 0 ; i < slots .length ; i ++) {
431+ FrameSlot slot = slots [i ];
432+ if (FrameSlotIDs .isUserFrameSlot (slot .getIdentifier ())) {
433+ Object value = frameToSync .getValue (slot );
434+ if (value != null ) {
435+ CallNode .getUncached ().execute (frame , setItemMethod , localsDict , slot .getIdentifier (), resolveCellValue (ConditionProfile .getUncached (), value ));
436+ } else {
437+ // delete variable
438+ CallNode .getUncached ().execute (frame , deleteItemMethod , localsDict , slot .getIdentifier ());
439+ }
440+ }
441+ }
442+ }
443+
376444 @ Specialization (guards = "isCustomLocalsObject(pyFrame, frameToSync)" )
377445 @ SuppressWarnings ("unused" )
378446 static void doCustomLocalsObject (PFrame pyFrame , Frame frameToSync ) {
@@ -428,5 +496,11 @@ private static Object resolveCellValue(ConditionProfile profile, Object value) {
428496 }
429497 return value ;
430498 }
499+
500+ @ Override
501+ public boolean isAdoptable () {
502+ return adoptable ;
503+ }
431504 }
505+
432506}
0 commit comments