OLD | NEW |
---|---|
1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file |
2 // for details. All rights reserved. Use of this source code is governed by a | 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. | 3 // BSD-style license that can be found in the LICENSE file. |
4 | 4 |
5 library dart2js.ir_builder; | 5 library dart2js.ir_builder; |
6 | 6 |
7 import '../compile_time_constants.dart' show BackendConstantEnvironment; | 7 import '../compile_time_constants.dart' show BackendConstantEnvironment; |
8 import '../constants/constant_system.dart'; | 8 import '../constants/constant_system.dart'; |
9 import '../constants/expressions.dart'; | 9 import '../constants/expressions.dart'; |
10 import '../constants/values.dart' show ConstantValue, PrimitiveConstantValue; | 10 import '../constants/values.dart' show ConstantValue, PrimitiveConstantValue; |
(...skipping 113 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
124 /// give a handle to the continuation and its environment. | 124 /// give a handle to the continuation and its environment. |
125 abstract class JumpCollector { | 125 abstract class JumpCollector { |
126 final JumpTarget target; | 126 final JumpTarget target; |
127 | 127 |
128 ir.Continuation _continuation = null; | 128 ir.Continuation _continuation = null; |
129 final Environment _continuationEnvironment; | 129 final Environment _continuationEnvironment; |
130 | 130 |
131 final List<Iterable<LocalVariableElement>> _boxedTryVariables = | 131 final List<Iterable<LocalVariableElement>> _boxedTryVariables = |
132 <Iterable<LocalVariableElement>>[]; | 132 <Iterable<LocalVariableElement>>[]; |
133 | 133 |
134 /// A stack of all the enclosing finally blocks up to the target of the jump. | |
135 /// | |
136 /// There are null entries which correspond to try/catch (no finally). | |
137 final List<SubbuildFunction> _finallyBlocks = <SubbuildFunction>[]; | |
138 | |
134 JumpCollector(this._continuationEnvironment, this.target); | 139 JumpCollector(this._continuationEnvironment, this.target); |
135 | 140 |
136 /// True if the collector has not recorded any jumps to its continuation. | 141 /// True if the collector has not recorded any jumps to its continuation. |
137 bool get isEmpty; | 142 bool get isEmpty; |
138 | 143 |
139 /// The continuation encapsulated by this collector. | 144 /// The continuation encapsulated by this collector. |
140 ir.Continuation get continuation; | 145 ir.Continuation get continuation; |
141 | 146 |
142 /// The compile-time environment to be used for translating code in the body | 147 /// The compile-time environment to be used for translating code in the body |
143 /// of the continuation. | 148 /// of the continuation. |
144 Environment get environment; | 149 Environment get environment; |
145 | 150 |
146 /// Emit a jump to the continuation for a given [IrBuilder]. | 151 /// Emit a jump to the continuation for a given [IrBuilder]. |
147 void addJump(IrBuilder builder); | 152 void addJump(IrBuilder builder); |
148 | 153 |
149 /// Add a set of variables that were boxed on entry to a try block. | 154 /// Add a set of variables that were boxed on entry to a try block. |
150 /// | 155 /// |
151 /// All jumps from a try block to targets outside have to unbox the | 156 /// All jumps from a try block to targets outside have to unbox the |
152 /// variables that were boxed on entry before invoking the target | 157 /// variables that were boxed on entry before invoking the target |
153 /// continuation. Call this function before translating a try block and | 158 /// continuation. Call this function before translating a try block and |
154 /// call [leaveTry] after translating it. | 159 /// call [leaveTry] after translating it. |
155 void enterTry(Iterable<LocalVariableElement> boxedOnEntry) { | 160 void enterTry(Iterable<LocalVariableElement> boxedOnEntry, |
161 [SubbuildFunction buildFinallyBlock]) { | |
156 // The boxed variables are maintained as a stack to make leaving easy. | 162 // The boxed variables are maintained as a stack to make leaving easy. |
157 _boxedTryVariables.add(boxedOnEntry); | 163 _boxedTryVariables.add(boxedOnEntry); |
164 _finallyBlocks.add(buildFinallyBlock); | |
158 } | 165 } |
159 | 166 |
160 /// Remove the most recently added set of variables boxed on entry to a try | 167 /// Remove the most recently added set of variables boxed on entry to a try |
161 /// block. | 168 /// block. |
162 /// | 169 /// |
163 /// Call [enterTry] before translating a try block and call this function | 170 /// Call [enterTry] before translating a try block and call this function |
164 /// after translating it. | 171 /// after translating it. |
165 void leaveTry() { | 172 void leaveTry() { |
166 _boxedTryVariables.removeLast(); | 173 _boxedTryVariables.removeLast(); |
174 _finallyBlocks.removeLast(); | |
167 } | 175 } |
168 | 176 |
169 void _buildTryExit(IrBuilder builder) { | 177 void _buildTryExit(IrBuilder builder) { |
170 for (Iterable<LocalVariableElement> boxedOnEntry in _boxedTryVariables) { | 178 for (Iterable<LocalVariableElement> boxedOnEntry in _boxedTryVariables) { |
171 for (LocalVariableElement variable in boxedOnEntry) { | 179 for (LocalVariableElement variable in boxedOnEntry) { |
172 assert(builder.isInMutableVariable(variable)); | 180 assert(builder.isInMutableVariable(variable)); |
173 ir.Primitive value = builder.buildLocalVariableGet(variable); | 181 ir.Primitive value = builder.buildLocalVariableGet(variable); |
174 builder.environment.update(variable, value); | 182 builder.environment.update(variable, value); |
175 } | 183 } |
176 } | 184 } |
185 for (SubbuildFunction buildFinallyBlock in _finallyBlocks.reversed) { | |
186 if (buildFinallyBlock != null) buildFinallyBlock(builder); | |
187 if (!builder.isOpen) return; | |
asgerf
2015/06/23 08:43:40
The inlined finally block seems to be evaluated in
Kevin Millikin (Google)
2015/06/23 09:26:59
That's pretty serious, thanks for spotting it.
| |
188 } | |
177 } | 189 } |
178 } | 190 } |
179 | 191 |
180 /// A class to collect 'forward' jumps. | 192 /// A class to collect 'forward' jumps. |
181 /// | 193 /// |
182 /// A forward jump to a continuation in the sense of the CPS translation is | 194 /// A forward jump to a continuation in the sense of the CPS translation is |
183 /// a jump where the jump is emitted before any code in the body of the | 195 /// a jump where the jump is emitted before any code in the body of the |
184 /// continuation is translated. They have the property that continuation | 196 /// continuation is translated. They have the property that continuation |
185 /// parameters and the environment for the translation of the body can be | 197 /// parameters and the environment for the translation of the body can be |
186 /// determined based on the invocations, before translating the body. A | 198 /// determined based on the invocations, before translating the body. A |
(...skipping 30 matching lines...) Expand all Loading... | |
217 } | 229 } |
218 | 230 |
219 Environment get environment { | 231 Environment get environment { |
220 if (_continuation == null) _setContinuation(); | 232 if (_continuation == null) _setContinuation(); |
221 return _continuationEnvironment; | 233 return _continuationEnvironment; |
222 } | 234 } |
223 | 235 |
224 void addJump(IrBuilder builder) { | 236 void addJump(IrBuilder builder) { |
225 assert(_continuation == null); | 237 assert(_continuation == null); |
226 _buildTryExit(builder); | 238 _buildTryExit(builder); |
239 if (!builder.isOpen) return; | |
240 | |
227 ir.InvokeContinuation invoke = new ir.InvokeContinuation.uninitialized(); | 241 ir.InvokeContinuation invoke = new ir.InvokeContinuation.uninitialized(); |
228 builder.add(invoke); | 242 builder.add(invoke); |
229 _invocations.add(invoke); | 243 _invocations.add(invoke); |
230 _invocationEnvironments.add(builder.environment); | 244 _invocationEnvironments.add(builder.environment); |
231 builder._current = null; | 245 builder._current = null; |
232 // TODO(kmillikin): Can we set builder.environment to null to make it | 246 // TODO(kmillikin): Can we set builder.environment to null to make it |
233 // less likely to mutate it? | 247 // less likely to mutate it? |
234 } | 248 } |
235 | 249 |
236 void _setContinuation() { | 250 void _setContinuation() { |
(...skipping 78 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
315 _continuation = new ir.Continuation(parameters, isRecursive: true); | 329 _continuation = new ir.Continuation(parameters, isRecursive: true); |
316 } | 330 } |
317 | 331 |
318 bool isEmpty = true; | 332 bool isEmpty = true; |
319 | 333 |
320 ir.Continuation get continuation => _continuation; | 334 ir.Continuation get continuation => _continuation; |
321 Environment get environment => _continuationEnvironment; | 335 Environment get environment => _continuationEnvironment; |
322 | 336 |
323 void addJump(IrBuilder builder) { | 337 void addJump(IrBuilder builder) { |
324 assert(_continuation.parameters.length <= builder.environment.length); | 338 assert(_continuation.parameters.length <= builder.environment.length); |
339 _buildTryExit(builder); | |
340 if (!builder.isOpen) return; | |
341 | |
325 isEmpty = false; | 342 isEmpty = false; |
326 _buildTryExit(builder); | |
327 builder.add(new ir.InvokeContinuation(_continuation, | 343 builder.add(new ir.InvokeContinuation(_continuation, |
328 builder.environment.index2value.take(_continuation.parameters.length) | 344 builder.environment.index2value.take(_continuation.parameters.length) |
329 .toList(), | 345 .toList(), |
330 isRecursive: true)); | 346 isRecursive: true)); |
331 builder._current = null; | 347 builder._current = null; |
332 } | 348 } |
333 } | 349 } |
334 | 350 |
335 /// Function for building a node in the context of the current builder. | 351 /// Function for building a node in the context of the current builder. |
336 typedef ir.Node BuildFunction(node); | 352 typedef ir.Node BuildFunction(node); |
(...skipping 55 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
392 final BackendConstantEnvironment constants; | 408 final BackendConstantEnvironment constants; |
393 | 409 |
394 ConstantSystem get constantSystem => constants.constantSystem; | 410 ConstantSystem get constantSystem => constants.constantSystem; |
395 | 411 |
396 /// A stack of collectors for breaks. | 412 /// A stack of collectors for breaks. |
397 final List<JumpCollector> breakCollectors = <JumpCollector>[]; | 413 final List<JumpCollector> breakCollectors = <JumpCollector>[]; |
398 | 414 |
399 /// A stack of collectors for continues. | 415 /// A stack of collectors for continues. |
400 final List<JumpCollector> continueCollectors = <JumpCollector>[]; | 416 final List<JumpCollector> continueCollectors = <JumpCollector>[]; |
401 | 417 |
418 /// A stack of enclosing finally blocks, used when translating return. | |
419 final List<SubbuildFunction> finallyBlocks = <SubbuildFunction>[]; | |
420 | |
402 final List<ConstDeclaration> localConstants = <ConstDeclaration>[]; | 421 final List<ConstDeclaration> localConstants = <ConstDeclaration>[]; |
403 | 422 |
404 final ExecutableElement currentElement; | 423 final ExecutableElement currentElement; |
405 | 424 |
406 final ir.Continuation returnContinuation = new ir.Continuation.retrn(); | 425 final ir.Continuation returnContinuation = new ir.Continuation.retrn(); |
407 ir.Parameter _thisParameter; | 426 ir.Parameter _thisParameter; |
408 ir.Parameter enclosingMethodThisParameter; | 427 ir.Parameter enclosingMethodThisParameter; |
409 | 428 |
410 final List<ir.Parameter> functionParameters = <ir.Parameter>[]; | 429 final List<ir.Parameter> functionParameters = <ir.Parameter>[]; |
411 | 430 |
(...skipping 1312 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1724 /// | 1743 /// |
1725 /// [tryInfo] provides information on local variables declared and boxed | 1744 /// [tryInfo] provides information on local variables declared and boxed |
1726 /// within this try statement. | 1745 /// within this try statement. |
1727 /// [buildTryBlock] builds the try block. | 1746 /// [buildTryBlock] builds the try block. |
1728 /// [catchClauseInfos] provides access to the catch type, exception variable, | 1747 /// [catchClauseInfos] provides access to the catch type, exception variable, |
1729 /// and stack trace variable, and a function for building the catch block. | 1748 /// and stack trace variable, and a function for building the catch block. |
1730 void buildTry( | 1749 void buildTry( |
1731 {TryStatementInfo tryStatementInfo, | 1750 {TryStatementInfo tryStatementInfo, |
1732 SubbuildFunction buildTryBlock, | 1751 SubbuildFunction buildTryBlock, |
1733 List<CatchClauseInfo> catchClauseInfos: const <CatchClauseInfo>[], | 1752 List<CatchClauseInfo> catchClauseInfos: const <CatchClauseInfo>[], |
1753 SubbuildFunction buildFinallyBlock, | |
1734 ClosureClassMap closureClassMap}) { | 1754 ClosureClassMap closureClassMap}) { |
1735 assert(isOpen); | 1755 assert(isOpen); |
1736 | 1756 |
1737 // Catch handlers are in scope for their body. The CPS translation of | 1757 // Catch handlers are in scope for their body. The CPS translation of |
1738 // [[try tryBlock catch (e) catchBlock; successor]] is: | 1758 // [[try tryBlock catch (e) catchBlock; successor]] is: |
1739 // | 1759 // |
1740 // let cont join(v0, v1, ...) = [[successor]] in | 1760 // let cont join(v0, v1, ...) = [[successor]] in |
1741 // let mutable m0 = x0 in | 1761 // let mutable m0 = x0 in |
1742 // let mutable m1 = x1 in | 1762 // let mutable m1 = x1 in |
1743 // ... | 1763 // ... |
(...skipping 12 matching lines...) Expand all Loading... | |
1756 // | 1776 // |
1757 // In other words, both the try and catch block are in the scope of the | 1777 // In other words, both the try and catch block are in the scope of the |
1758 // join-point continuation, and they are both in the scope of a sequence | 1778 // join-point continuation, and they are both in the scope of a sequence |
1759 // of mutable bindings for the variables assigned in the try. The join- | 1779 // of mutable bindings for the variables assigned in the try. The join- |
1760 // point continuation is not in the scope of these mutable bindings. | 1780 // point continuation is not in the scope of these mutable bindings. |
1761 // The tryBlock is in the scope of a binding for the catch handler. Each | 1781 // The tryBlock is in the scope of a binding for the catch handler. Each |
1762 // instruction (specifically, each call) in the tryBlock is in the dynamic | 1782 // instruction (specifically, each call) in the tryBlock is in the dynamic |
1763 // scope of the handler. The mutable bindings are dereferenced at the end | 1783 // scope of the handler. The mutable bindings are dereferenced at the end |
1764 // of the try block and at the beginning of the catch block, so the | 1784 // of the try block and at the beginning of the catch block, so the |
1765 // variables are unboxed in the catch block and at the join point. | 1785 // variables are unboxed in the catch block and at the join point. |
1766 JumpCollector join = new ForwardJumpCollector(environment); | 1786 if (catchClauseInfos.isNotEmpty) { |
1767 IrBuilder tryCatchBuilder = makeDelimitedBuilder(); | 1787 JumpCollector join = new ForwardJumpCollector(environment); |
1788 IrBuilder tryCatchBuilder = makeDelimitedBuilder(); | |
1768 | 1789 |
1769 // Variables treated as mutable in a try are not mutable outside of it. | 1790 // Variables treated as mutable in a try are not mutable outside of it. |
1770 // Work with a copy of the outer builder's mutable variables. | 1791 // Work with a copy of the outer builder's mutable variables. |
1771 tryCatchBuilder.mutableVariables = | 1792 tryCatchBuilder.mutableVariables = |
1772 new Map<Local, ir.MutableVariable>.from(mutableVariables); | 1793 new Map<Local, ir.MutableVariable>.from(mutableVariables); |
1773 for (LocalVariableElement variable in tryStatementInfo.boxedOnEntry) { | 1794 for (LocalVariableElement variable in tryStatementInfo.boxedOnEntry) { |
1774 assert(!tryCatchBuilder.isInMutableVariable(variable)); | 1795 assert(!tryCatchBuilder.isInMutableVariable(variable)); |
1775 ir.Primitive value = tryCatchBuilder.buildLocalVariableGet(variable); | 1796 ir.Primitive value = tryCatchBuilder.buildLocalVariableGet(variable); |
1776 tryCatchBuilder.makeMutableVariable(variable); | 1797 tryCatchBuilder.makeMutableVariable(variable); |
1777 tryCatchBuilder.declareLocalVariable(variable, initialValue: value); | 1798 tryCatchBuilder.declareLocalVariable(variable, initialValue: value); |
1799 } | |
1800 | |
1801 IrBuilder tryBuilder = tryCatchBuilder.makeDelimitedBuilder(); | |
1802 | |
1803 void interceptJump(JumpCollector collector) { | |
1804 collector.enterTry(tryStatementInfo.boxedOnEntry); | |
1805 } | |
1806 void restoreJump(JumpCollector collector) { | |
1807 collector.leaveTry(); | |
1808 } | |
1809 tryBuilder.state.breakCollectors.forEach(interceptJump); | |
1810 tryBuilder.state.continueCollectors.forEach(interceptJump); | |
1811 buildTryBlock(tryBuilder); | |
1812 if (tryBuilder.isOpen) { | |
1813 interceptJump(join); | |
1814 tryBuilder.jumpTo(join); | |
1815 restoreJump(join); | |
1816 } | |
1817 tryBuilder.state.breakCollectors.forEach(restoreJump); | |
1818 tryBuilder.state.continueCollectors.forEach(restoreJump); | |
1819 | |
1820 IrBuilder catchBuilder = tryCatchBuilder.makeDelimitedBuilder(); | |
1821 for (LocalVariableElement variable in tryStatementInfo.boxedOnEntry) { | |
1822 assert(catchBuilder.isInMutableVariable(variable)); | |
1823 ir.Primitive value = catchBuilder.buildLocalVariableGet(variable); | |
1824 // After this point, the variables that were boxed on entry to the try | |
1825 // are no longer treated as mutable. | |
1826 catchBuilder.removeMutableVariable(variable); | |
1827 catchBuilder.environment.update(variable, value); | |
1828 } | |
1829 | |
1830 // Handlers are always translated as having both exception and stack trace | |
1831 // parameters. Multiple clauses do not have to use the same names for | |
1832 // them. Choose the first of each as the name hint for the respective | |
1833 // handler parameter. | |
1834 ir.Parameter exceptionParameter = | |
1835 new ir.Parameter(catchClauseInfos.first.exceptionVariable); | |
1836 LocalVariableElement traceVariable; | |
1837 CatchClauseInfo catchAll; | |
1838 for (int i = 0; i < catchClauseInfos.length; ++i) { | |
1839 CatchClauseInfo info = catchClauseInfos[i]; | |
1840 if (info.type == null) { | |
1841 catchAll = info; | |
1842 catchClauseInfos.length = i; | |
1843 break; | |
1844 } | |
1845 if (traceVariable == null) { | |
1846 traceVariable = info.stackTraceVariable; | |
1847 } | |
1848 } | |
1849 ir.Parameter traceParameter = new ir.Parameter(traceVariable); | |
1850 // Expand multiple catch clauses into an explicit if/then/else. Iterate | |
1851 // them in reverse so the current block becomes the next else block. | |
1852 ir.Expression catchBody; | |
1853 if (catchAll == null) { | |
1854 catchBody = new ir.Rethrow(); | |
1855 } else { | |
1856 IrBuilder clauseBuilder = catchBuilder.makeDelimitedBuilder(); | |
1857 clauseBuilder.declareLocalVariable(catchAll.exceptionVariable, | |
1858 initialValue: exceptionParameter); | |
1859 if (catchAll.stackTraceVariable != null) { | |
1860 clauseBuilder.declareLocalVariable(catchAll.stackTraceVariable, | |
1861 initialValue: traceParameter); | |
1862 } | |
1863 catchAll.buildCatchBlock(clauseBuilder); | |
1864 if (clauseBuilder.isOpen) clauseBuilder.jumpTo(join); | |
1865 catchBody = clauseBuilder._root; | |
1866 } | |
1867 for (CatchClauseInfo clause in catchClauseInfos.reversed) { | |
1868 IrBuilder clauseBuilder = catchBuilder.makeDelimitedBuilder(); | |
1869 clauseBuilder.declareLocalVariable(clause.exceptionVariable, | |
1870 initialValue: exceptionParameter); | |
1871 if (clause.stackTraceVariable != null) { | |
1872 clauseBuilder.declareLocalVariable(clause.stackTraceVariable, | |
1873 initialValue: traceParameter); | |
1874 } | |
1875 clause.buildCatchBlock(clauseBuilder); | |
1876 if (clauseBuilder.isOpen) clauseBuilder.jumpTo(join); | |
1877 ir.Continuation thenContinuation = new ir.Continuation([]); | |
1878 thenContinuation.body = clauseBuilder._root; | |
1879 ir.Continuation elseContinuation = new ir.Continuation([]); | |
1880 elseContinuation.body = catchBody; | |
1881 | |
1882 // Build the type test guarding this clause. We can share the | |
1883 // environment with the nested builder because this part cannot mutate | |
1884 // it. | |
1885 IrBuilder checkBuilder = catchBuilder.makeDelimitedBuilder(environment); | |
1886 ir.Primitive typeMatches = | |
1887 checkBuilder.buildTypeOperator(exceptionParameter, | |
1888 clause.type, | |
1889 isTypeTest: true); | |
1890 checkBuilder.add(new ir.LetCont.many([thenContinuation, | |
1891 elseContinuation], | |
1892 new ir.Branch(new ir.IsTrue(typeMatches), | |
1893 thenContinuation, | |
1894 elseContinuation))); | |
1895 catchBody = checkBuilder._root; | |
1896 } | |
1897 | |
1898 List<ir.Parameter> catchParameters = | |
1899 <ir.Parameter>[exceptionParameter, traceParameter]; | |
1900 ir.Continuation catchContinuation = new ir.Continuation(catchParameters); | |
1901 catchBuilder.add(catchBody); | |
1902 catchContinuation.body = catchBuilder._root; | |
1903 | |
1904 tryCatchBuilder.add( | |
1905 new ir.LetHandler(catchContinuation, tryBuilder._root)); | |
1906 add(new ir.LetCont(join.continuation, tryCatchBuilder._root)); | |
1907 environment = join.environment; | |
1908 } else { | |
1909 // Try/finally. | |
1910 JumpCollector join = new ForwardJumpCollector(environment); | |
1911 IrBuilder tryFinallyBuilder = makeDelimitedBuilder(); | |
1912 | |
1913 tryFinallyBuilder.mutableVariables = | |
1914 new Map<Local, ir.MutableVariable>.from(mutableVariables); | |
1915 for (LocalVariableElement variable in tryStatementInfo.boxedOnEntry) { | |
1916 assert(!tryFinallyBuilder.isInMutableVariable(variable)); | |
1917 ir.Primitive value = tryFinallyBuilder.buildLocalVariableGet(variable); | |
1918 tryFinallyBuilder.makeMutableVariable(variable); | |
1919 tryFinallyBuilder.declareLocalVariable(variable, initialValue: value); | |
1920 } | |
1921 | |
1922 IrBuilder tryBuilder = tryFinallyBuilder.makeDelimitedBuilder(); | |
1923 | |
1924 void interceptJump(JumpCollector collector) { | |
1925 collector.enterTry(tryStatementInfo.boxedOnEntry, buildFinallyBlock); | |
1926 } | |
1927 void restoreJump(JumpCollector collector) { | |
1928 collector.leaveTry(); | |
1929 } | |
1930 tryBuilder.state.breakCollectors.forEach(interceptJump); | |
1931 tryBuilder.state.continueCollectors.forEach(interceptJump); | |
1932 tryBuilder.state.finallyBlocks.add(buildFinallyBlock); | |
1933 buildTryBlock(tryBuilder); | |
1934 if (tryBuilder.isOpen) { | |
1935 // To cover control falling off the end of the try block, the finally | |
1936 // code is translated at the join point. This ensures that it is | |
1937 // correctly outside the scope of the catch handler. | |
1938 join.enterTry(tryStatementInfo.boxedOnEntry); | |
1939 tryBuilder.jumpTo(join); | |
1940 join.leaveTry(); | |
1941 } | |
1942 tryBuilder.state.breakCollectors.forEach(restoreJump); | |
1943 tryBuilder.state.continueCollectors.forEach(restoreJump); | |
1944 tryBuilder.state.finallyBlocks.removeLast(); | |
1945 | |
1946 IrBuilder catchBuilder = tryFinallyBuilder.makeDelimitedBuilder(); | |
1947 for (LocalVariableElement variable in tryStatementInfo.boxedOnEntry) { | |
1948 assert(catchBuilder.isInMutableVariable(variable)); | |
1949 ir.Primitive value = catchBuilder.buildLocalVariableGet(variable); | |
1950 catchBuilder.removeMutableVariable(variable); | |
1951 catchBuilder.environment.update(variable, value); | |
1952 } | |
1953 | |
1954 buildFinallyBlock(catchBuilder); | |
1955 if (catchBuilder.isOpen) { | |
1956 catchBuilder.add(new ir.Rethrow()); | |
1957 catchBuilder._current = null; | |
1958 } | |
1959 List<ir.Parameter> catchParameters = | |
1960 <ir.Parameter>[new ir.Parameter(null), new ir.Parameter(null)]; | |
1961 ir.Continuation catchContinuation = new ir.Continuation(catchParameters); | |
1962 catchContinuation.body = catchBuilder._root; | |
1963 tryFinallyBuilder.add( | |
1964 new ir.LetHandler(catchContinuation, tryBuilder._root)); | |
1965 add(new ir.LetCont(join.continuation, tryFinallyBuilder._root)); | |
1966 environment = join.environment; | |
1967 buildFinallyBlock(this); | |
1778 } | 1968 } |
1779 | |
1780 IrBuilder tryBuilder = tryCatchBuilder.makeDelimitedBuilder(); | |
1781 | |
1782 void interceptJumps(JumpCollector collector) { | |
1783 collector.enterTry(tryStatementInfo.boxedOnEntry); | |
1784 } | |
1785 void restoreJumps(JumpCollector collector) { | |
1786 collector.leaveTry(); | |
1787 } | |
1788 tryBuilder.state.breakCollectors.forEach(interceptJumps); | |
1789 tryBuilder.state.continueCollectors.forEach(interceptJumps); | |
1790 buildTryBlock(tryBuilder); | |
1791 if (tryBuilder.isOpen) { | |
1792 interceptJumps(join); | |
1793 tryBuilder.jumpTo(join); | |
1794 restoreJumps(join); | |
1795 } | |
1796 tryBuilder.state.breakCollectors.forEach(restoreJumps); | |
1797 tryBuilder.state.continueCollectors.forEach(restoreJumps); | |
1798 | |
1799 IrBuilder catchBuilder = tryCatchBuilder.makeDelimitedBuilder(); | |
1800 for (LocalVariableElement variable in tryStatementInfo.boxedOnEntry) { | |
1801 assert(catchBuilder.isInMutableVariable(variable)); | |
1802 ir.Primitive value = catchBuilder.buildLocalVariableGet(variable); | |
1803 // After this point, the variables that were boxed on entry to the try | |
1804 // are no longer treated as mutable. | |
1805 catchBuilder.removeMutableVariable(variable); | |
1806 catchBuilder.environment.update(variable, value); | |
1807 } | |
1808 | |
1809 // Handlers are always translated as having both exception and stack trace | |
1810 // parameters. Multiple clauses do not have to use the same names for | |
1811 // them. Choose the first of each as the name hint for the respective | |
1812 // handler parameter. | |
1813 ir.Parameter exceptionParameter = | |
1814 new ir.Parameter(catchClauseInfos.first.exceptionVariable); | |
1815 LocalVariableElement traceVariable; | |
1816 CatchClauseInfo catchAll; | |
1817 for (int i = 0; i < catchClauseInfos.length; ++i) { | |
1818 CatchClauseInfo info = catchClauseInfos[i]; | |
1819 if (info.type == null) { | |
1820 catchAll = info; | |
1821 catchClauseInfos.length = i; | |
1822 break; | |
1823 } | |
1824 if (traceVariable == null) { | |
1825 traceVariable = info.stackTraceVariable; | |
1826 } | |
1827 } | |
1828 ir.Parameter traceParameter = new ir.Parameter(traceVariable); | |
1829 // Expand multiple catch clauses into an explicit if/then/else. Iterate | |
1830 // them in reverse so the current block becomes the next else block. | |
1831 ir.Expression catchBody; | |
1832 if (catchAll == null) { | |
1833 catchBody = new ir.Rethrow(); | |
1834 } else { | |
1835 IrBuilder clauseBuilder = catchBuilder.makeDelimitedBuilder(); | |
1836 clauseBuilder.declareLocalVariable(catchAll.exceptionVariable, | |
1837 initialValue: exceptionParameter); | |
1838 if (catchAll.stackTraceVariable != null) { | |
1839 clauseBuilder.declareLocalVariable(catchAll.stackTraceVariable, | |
1840 initialValue: traceParameter); | |
1841 } | |
1842 catchAll.buildCatchBlock(clauseBuilder); | |
1843 if (clauseBuilder.isOpen) clauseBuilder.jumpTo(join); | |
1844 catchBody = clauseBuilder._root; | |
1845 } | |
1846 for (CatchClauseInfo clause in catchClauseInfos.reversed) { | |
1847 IrBuilder clauseBuilder = catchBuilder.makeDelimitedBuilder(); | |
1848 clauseBuilder.declareLocalVariable(clause.exceptionVariable, | |
1849 initialValue: exceptionParameter); | |
1850 if (clause.stackTraceVariable != null) { | |
1851 clauseBuilder.declareLocalVariable(clause.stackTraceVariable, | |
1852 initialValue: traceParameter); | |
1853 } | |
1854 clause.buildCatchBlock(clauseBuilder); | |
1855 if (clauseBuilder.isOpen) clauseBuilder.jumpTo(join); | |
1856 ir.Continuation thenContinuation = new ir.Continuation([]); | |
1857 thenContinuation.body = clauseBuilder._root; | |
1858 ir.Continuation elseContinuation = new ir.Continuation([]); | |
1859 elseContinuation.body = catchBody; | |
1860 | |
1861 // Build the type test guarding this clause. We can share the environment | |
1862 // with the nested builder because this part cannot mutate it. | |
1863 IrBuilder checkBuilder = catchBuilder.makeDelimitedBuilder(environment); | |
1864 ir.Primitive typeMatches = | |
1865 checkBuilder.buildTypeOperator(exceptionParameter, | |
1866 clause.type, | |
1867 isTypeTest: true); | |
1868 checkBuilder.add(new ir.LetCont.many([thenContinuation, elseContinuation], | |
1869 new ir.Branch(new ir.IsTrue(typeMatches), | |
1870 thenContinuation, | |
1871 elseContinuation))); | |
1872 catchBody = checkBuilder._root; | |
1873 } | |
1874 | |
1875 List<ir.Parameter> catchParameters = | |
1876 <ir.Parameter>[exceptionParameter, traceParameter]; | |
1877 ir.Continuation catchContinuation = new ir.Continuation(catchParameters); | |
1878 catchBuilder.add(catchBody); | |
1879 catchContinuation.body = catchBuilder._root; | |
1880 | |
1881 tryCatchBuilder.add( | |
1882 new ir.LetHandler(catchContinuation, tryBuilder._root)); | |
1883 add(new ir.LetCont(join.continuation, tryCatchBuilder._root)); | |
1884 environment = join.environment; | |
1885 } | 1969 } |
1886 | 1970 |
1887 /// Create a return statement `return value;` or `return;` if [value] is | 1971 /// Create a return statement `return value;` or `return;` if [value] is |
1888 /// null. | 1972 /// null. |
1889 void buildReturn([ir.Primitive value]) { | 1973 void buildReturn([ir.Primitive value]) { |
1890 // Build(Return(e), C) = C'[InvokeContinuation(return, x)] | 1974 // Build(Return(e), C) = C'[InvokeContinuation(return, x)] |
1891 // where (C', x) = Build(e, C) | 1975 // where (C', x) = Build(e, C) |
1892 // | 1976 // |
1893 // Return without a subexpression is translated as if it were return null. | 1977 // Return without a subexpression is translated as if it were return null. |
1894 assert(isOpen); | 1978 assert(isOpen); |
1895 if (value == null) { | 1979 if (value == null) { |
1896 value = buildNullConstant(); | 1980 value = buildNullConstant(); |
1897 } | 1981 } |
1898 add(new ir.InvokeContinuation(state.returnContinuation, [value])); | 1982 for (SubbuildFunction buildFinallyBlock in state.finallyBlocks.reversed) { |
1899 _current = null; | 1983 buildFinallyBlock(this); |
1984 if (!isOpen) break; | |
1985 } | |
1986 if (isOpen) { | |
1987 add(new ir.InvokeContinuation(state.returnContinuation, [value])); | |
1988 _current = null; | |
1989 } | |
1900 } | 1990 } |
1901 | 1991 |
1902 /// Create a blocks of [statements] by applying [build] to all reachable | 1992 /// Create a blocks of [statements] by applying [build] to all reachable |
1903 /// statements. The first statement is assumed to be reachable. | 1993 /// statements. The first statement is assumed to be reachable. |
1904 // TODO(johnniwinther): Type [statements] as `Iterable` when `NodeList` uses | 1994 // TODO(johnniwinther): Type [statements] as `Iterable` when `NodeList` uses |
1905 // `List` instead of `Link`. | 1995 // `List` instead of `Link`. |
1906 void buildBlock(var statements, BuildFunction build) { | 1996 void buildBlock(var statements, BuildFunction build) { |
1907 // Build(Block(stamements), C) = C' | 1997 // Build(Block(stamements), C) = C' |
1908 // where C' = statements.fold(Build, C) | 1998 // where C' = statements.fold(Build, C) |
1909 assert(isOpen); | 1999 assert(isOpen); |
(...skipping 719 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
2629 this.buildCatchBlock}); | 2719 this.buildCatchBlock}); |
2630 } | 2720 } |
2631 | 2721 |
2632 class SwitchCaseInfo { | 2722 class SwitchCaseInfo { |
2633 final List<ir.Primitive> constants = <ir.Primitive>[]; | 2723 final List<ir.Primitive> constants = <ir.Primitive>[]; |
2634 final SubbuildFunction buildBody; | 2724 final SubbuildFunction buildBody; |
2635 | 2725 |
2636 SwitchCaseInfo(this.buildBody); | 2726 SwitchCaseInfo(this.buildBody); |
2637 | 2727 |
2638 void addConstant(ir.Primitive constant) => constants.add(constant); | 2728 void addConstant(ir.Primitive constant) => constants.add(constant); |
2639 } | 2729 } |
OLD | NEW |