UNPKG

4.35 kBJavaScriptView Raw
1var util = require('util');
2var traverse = require('../traverse');
3var helpers = require('../helpers');
4var BinaryExpression = require('./BinaryExpression');
5var BlockStatement = require('./BlockStatement');
6var CatchClause = require('./CatchClause');
7var CallExpression = require('./CallExpression');
8var ExpressionStatement = require('./ExpressionStatement');
9var FunctionExpression = require('./FunctionExpression');
10var Identifier = require('./Identifier');
11var IfStatement = require('./IfStatement');
12var ReturnStatement = require('./ReturnStatement');
13
14var TryStatement = module.exports = function(block, handlers, finalizer) {
15 this.type = 'TryStatement';
16 this.block = block;
17 this.handlers = handlers;
18 this.finalizer = finalizer;
19 if (!util.isArray(this.handlers)) {
20 this.handlers = [this.handlers];
21 }
22 if (!this.finalizer) {
23 this.finalizer = null;
24 }
25 this.async = false;
26};
27
28TryStatement.prototype.normalize = function (place) {
29 this.block.normalize();
30 for (var i = 0; i < this.handlers.length; i++) {
31 this.handlers[i].normalize();
32 }
33 if (this.finalizer) {
34 this.finalizer.normalize();
35 }
36 place.push(this);
37};
38
39TryStatement.prototype.transform = function (place) {
40 var innerPlace = this.block.transform(place);
41
42 //TODO multiple handlers
43 var handler = this.handlers[0];
44
45 if (handler) {
46 var handlerPlace = handler.transform(place);
47 }
48
49 //Not transform if not async calls inside
50 if (!this.block.async && ((handler && !handler.async) || !handler) ) {
51 place.push(this);
52 return innerPlace;
53 }
54
55 var contIdentifier = new Identifier(helpers.continuationIdentifier);
56 var errIdentifier = new Identifier(helpers.errName);
57 var cont = new ExpressionStatement(new CallExpression(contIdentifier));
58 var breakCont = new ExpressionStatement(new CallExpression(contIdentifier, errIdentifier));
59
60 if (this.block.async) {
61 //Push explicit continuation statement
62 innerPlace.push(cont);
63
64 //Make function for try block
65 var tryFunction = new FunctionExpression(null, [contIdentifier], this.block);
66
67 //Add try..catch block into every inner functions
68 traverse(tryFunction, function (node) {
69 if (node.type === 'FunctionExpression') {
70 var innerTry = new TryStatement(node.body, new CatchClause(
71 errIdentifier,
72 null,
73 new BlockStatement(breakCont)
74 ));
75 node.body = new BlockStatement(innerTry);
76 }
77 return node;
78 });
79
80 //Generate try handler function
81 var judgeIfException = new IfStatement(
82 new BinaryExpression('!==', handler.param, new Identifier('undefined')),
83 handler.body,
84 null
85 );
86 var handlerFunction = new FunctionExpression(
87 null,
88 [handler.param],
89 new BlockStatement(judgeIfException)
90 );
91 var tryCatch = new ExpressionStatement(new CallExpression(tryFunction, handlerFunction));
92 } else {
93 var tryCatch = this;
94 }
95
96 if (handler) {
97 if (handler.async) {
98 //If there's async call in catch clause, do CPS transformation on catch function
99 var continuationFunction = new FunctionExpression(null, [], new BlockStatement());
100 var catchFunction = new FunctionExpression(
101 null,
102 [contIdentifier],
103 new BlockStatement(tryCatch)
104 );
105
106 var tryCatchContinuation = new ExpressionStatement(new CallExpression(
107 catchFunction,
108 [continuationFunction]
109 ));
110 place.push(tryCatchContinuation);
111
112 handlerPlace.push(cont);
113 if (this.block.async) {
114 //Set continuation for non-exception condition
115 judgeIfException.alternate = cont;
116 } else {
117 //Terminate execution after async call
118 handler.body.body.push(new ReturnStatement());
119 //Add continuation call for non-exception condition
120 catchFunction.body.body.push(cont);
121 }
122 var continuationPlace = continuationFunction.body.body;
123 } else {
124 //Or else directly add catch function
125 place.push(tryCatch);
126 var continuationPlace = handlerFunction.body.body;
127 }
128 }
129
130 //If finally clause found, push it into continuation place
131 if (this.finalizer !== null) {
132 for (var i = 0; i < this.finalizer.body.length; i++) {
133 continuationPlace.push(this.finalizer.body[i]);
134 }
135 }
136
137 return continuationPlace;
138};