OLD | NEW |
(Empty) | |
| 1 // Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a |
| 3 // BSD-style license that can be found in the LICENSE file. |
| 4 |
| 5 library kernel.transformations.closure.context; |
| 6 |
| 7 import '../../ast.dart' |
| 8 show |
| 9 Arguments, |
| 10 Class, |
| 11 ConstructorInvocation, |
| 12 Expression, |
| 13 ExpressionStatement, |
| 14 IntLiteral, |
| 15 InterfaceType, |
| 16 MethodInvocation, |
| 17 Name, |
| 18 NullLiteral, |
| 19 PropertyGet, |
| 20 PropertySet, |
| 21 StringLiteral, |
| 22 Throw, |
| 23 VariableDeclaration, |
| 24 VariableGet, |
| 25 VariableSet; |
| 26 |
| 27 import '../../frontend/accessors.dart' |
| 28 show Accessor, IndexAccessor, VariableAccessor; |
| 29 |
| 30 import 'converter.dart' show ClosureConverter; |
| 31 |
| 32 abstract class Context { |
| 33 /// Returns a new expression for accessing this context. |
| 34 Expression get expression; |
| 35 |
| 36 /// Returns an accessor (or null) for accessing this context. |
| 37 Accessor get accessor; |
| 38 |
| 39 /// Extend the context to include [variable] initialized to [value]. For |
| 40 /// example, this replaces the [VariableDeclaration] node of a captured local |
| 41 /// variable. |
| 42 /// |
| 43 /// This may create a new context and update the `context` field of the |
| 44 /// current [ClosureConverter]. |
| 45 // TODO(ahe): Return context instead? |
| 46 void extend(VariableDeclaration variable, Expression value); |
| 47 |
| 48 /// Update the initializer [value] of [variable] which was previously added |
| 49 /// with [extend]. This is used when [value] isn't available when the context |
| 50 /// was extended. |
| 51 void update(VariableDeclaration variable, Expression value) { |
| 52 throw "not supported $runtimeType"; |
| 53 } |
| 54 |
| 55 /// Returns a new expression for reading the value of [variable] from this |
| 56 /// context. For example, for replacing a [VariableGet] of a captured local |
| 57 /// variable. |
| 58 Expression lookup(VariableDeclaration variable); |
| 59 |
| 60 /// Returns a new expression which stores [value] in [variable] in this |
| 61 /// context. For example, for replacing a [VariableSet] of a captured local |
| 62 /// variable. |
| 63 Expression assign(VariableDeclaration variable, Expression value, |
| 64 {bool voidContext: false}); |
| 65 |
| 66 /// Returns a new context whose parent is this context. The optional argument |
| 67 /// [accessor] controls how the nested context access this context. This is |
| 68 /// used, for example, when hoisting a local function. In this case, access |
| 69 /// to this context can't be accessed directly via [expression]. In other |
| 70 /// cases, for example, a for-loop, this context is still in scope and can be |
| 71 /// accessed directly (with [accessor]). |
| 72 Context toNestedContext([Accessor accessor]); |
| 73 |
| 74 /// Returns a new expression which will copy this context and store the copy |
| 75 /// in the local variable currently holding this context. |
| 76 Expression clone() { |
| 77 return new Throw( |
| 78 new StringLiteral("Context clone not implemented for ${runtimeType}")); |
| 79 } |
| 80 } |
| 81 |
| 82 class NoContext extends Context { |
| 83 final ClosureConverter converter; |
| 84 |
| 85 NoContext(this.converter); |
| 86 |
| 87 Expression get expression => new NullLiteral(); |
| 88 |
| 89 Accessor get accessor => null; |
| 90 |
| 91 void extend(VariableDeclaration variable, Expression value) { |
| 92 converter.context = new LocalContext(converter, this) |
| 93 ..extend(variable, value); |
| 94 } |
| 95 |
| 96 Expression lookup(VariableDeclaration variable) { |
| 97 throw 'Unbound NoContext.lookup($variable)'; |
| 98 } |
| 99 |
| 100 Expression assign(VariableDeclaration variable, Expression value, |
| 101 {bool voidContext: false}) { |
| 102 throw 'Unbound NoContext.assign($variable, ...)'; |
| 103 } |
| 104 |
| 105 Context toNestedContext([Accessor accessor]) { |
| 106 return new NestedContext( |
| 107 converter, accessor, <List<VariableDeclaration>>[]); |
| 108 } |
| 109 } |
| 110 |
| 111 class LocalContext extends Context { |
| 112 final ClosureConverter converter; |
| 113 final Context parent; |
| 114 final VariableDeclaration self; |
| 115 final IntLiteral size; |
| 116 final List<VariableDeclaration> variables = <VariableDeclaration>[]; |
| 117 final Map<VariableDeclaration, Arguments> initializers = |
| 118 <VariableDeclaration, Arguments>{}; |
| 119 |
| 120 LocalContext._internal(this.converter, this.parent, this.self, this.size); |
| 121 |
| 122 factory LocalContext(ClosureConverter converter, Context parent) { |
| 123 Class contextClass = converter.contextClass; |
| 124 assert(contextClass.constructors.length == 1); |
| 125 IntLiteral zero = new IntLiteral(0); |
| 126 VariableDeclaration declaration = new VariableDeclaration.forValue( |
| 127 new ConstructorInvocation( |
| 128 contextClass.constructors.first, new Arguments(<Expression>[zero])), |
| 129 type: new InterfaceType(contextClass)); |
| 130 declaration.name = "#context"; |
| 131 converter.insert(declaration); |
| 132 converter.insert(new ExpressionStatement(new PropertySet( |
| 133 new VariableGet(declaration), new Name('parent'), parent.expression))); |
| 134 |
| 135 return new LocalContext._internal(converter, parent, declaration, zero); |
| 136 } |
| 137 |
| 138 Expression get expression => accessor.buildSimpleRead(); |
| 139 |
| 140 Accessor get accessor => new VariableAccessor(self); |
| 141 |
| 142 void extend(VariableDeclaration variable, Expression value) { |
| 143 Arguments arguments = |
| 144 new Arguments(<Expression>[new IntLiteral(variables.length), value]); |
| 145 converter.insert(new ExpressionStatement( |
| 146 new MethodInvocation(expression, new Name('[]='), arguments))); |
| 147 ++size.value; |
| 148 variables.add(variable); |
| 149 initializers[variable] = arguments; |
| 150 } |
| 151 |
| 152 void update(VariableDeclaration variable, Expression value) { |
| 153 Arguments arguments = initializers[variable]; |
| 154 arguments.positional[1] = value; |
| 155 value.parent = arguments; |
| 156 } |
| 157 |
| 158 Expression lookup(VariableDeclaration variable) { |
| 159 var index = variables.indexOf(variable); |
| 160 return index == -1 |
| 161 ? parent.lookup(variable) |
| 162 : new MethodInvocation(expression, new Name('[]'), |
| 163 new Arguments(<Expression>[new IntLiteral(index)])); |
| 164 } |
| 165 |
| 166 Expression assign(VariableDeclaration variable, Expression value, |
| 167 {bool voidContext: false}) { |
| 168 var index = variables.indexOf(variable); |
| 169 return index == -1 |
| 170 ? parent.assign(variable, value, voidContext: voidContext) |
| 171 : IndexAccessor |
| 172 .make(expression, new IntLiteral(index), null, null) |
| 173 .buildAssignment(value, voidContext: voidContext); |
| 174 } |
| 175 |
| 176 Context toNestedContext([Accessor accessor]) { |
| 177 accessor ??= this.accessor; |
| 178 List<List<VariableDeclaration>> variabless = <List<VariableDeclaration>>[]; |
| 179 var current = this; |
| 180 while (current != null && current is! NoContext) { |
| 181 if (current is LocalContext) { |
| 182 variabless.add(current.variables); |
| 183 current = current.parent; |
| 184 } else if (current is NestedContext) { |
| 185 variabless.addAll((current as NestedContext).variabless); |
| 186 current = null; |
| 187 } |
| 188 } |
| 189 return new NestedContext(converter, accessor, variabless); |
| 190 } |
| 191 |
| 192 Expression clone() { |
| 193 self.isFinal = false; |
| 194 return new VariableSet( |
| 195 self, |
| 196 new MethodInvocation( |
| 197 new VariableGet(self), new Name("copy"), new Arguments.empty())); |
| 198 } |
| 199 } |
| 200 |
| 201 class NestedContext extends Context { |
| 202 final ClosureConverter converter; |
| 203 final Accessor accessor; |
| 204 final List<List<VariableDeclaration>> variabless; |
| 205 |
| 206 NestedContext(this.converter, this.accessor, this.variabless); |
| 207 |
| 208 Expression get expression { |
| 209 return accessor?.buildSimpleRead() ?? new NullLiteral(); |
| 210 } |
| 211 |
| 212 void extend(VariableDeclaration variable, Expression value) { |
| 213 converter.context = new LocalContext(converter, this) |
| 214 ..extend(variable, value); |
| 215 } |
| 216 |
| 217 Expression lookup(VariableDeclaration variable) { |
| 218 var context = expression; |
| 219 for (var variables in variabless) { |
| 220 var index = variables.indexOf(variable); |
| 221 if (index != -1) { |
| 222 return new MethodInvocation(context, new Name('[]'), |
| 223 new Arguments(<Expression>[new IntLiteral(index)])); |
| 224 } |
| 225 context = new PropertyGet(context, new Name('parent')); |
| 226 } |
| 227 throw 'Unbound NestedContext.lookup($variable)'; |
| 228 } |
| 229 |
| 230 Expression assign(VariableDeclaration variable, Expression value, |
| 231 {bool voidContext: false}) { |
| 232 var context = expression; |
| 233 for (var variables in variabless) { |
| 234 var index = variables.indexOf(variable); |
| 235 if (index != -1) { |
| 236 return IndexAccessor |
| 237 .make(context, new IntLiteral(index), null, null) |
| 238 .buildAssignment(value, voidContext: voidContext); |
| 239 } |
| 240 context = new PropertyGet(context, new Name('parent')); |
| 241 } |
| 242 throw 'Unbound NestedContext.lookup($variable)'; |
| 243 } |
| 244 |
| 245 Context toNestedContext([Accessor accessor]) { |
| 246 return new NestedContext(converter, accessor ?? this.accessor, variabless); |
| 247 } |
| 248 } |
OLD | NEW |