/src/hermes/lib/IRGen/ESTreeIRGen-expr.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * Copyright (c) Meta Platforms, Inc. and affiliates. |
3 | | * |
4 | | * This source code is licensed under the MIT license found in the |
5 | | * LICENSE file in the root directory of this source tree. |
6 | | */ |
7 | | |
8 | | #include "ESTreeIRGen.h" |
9 | | #include "hermes/Regex/RegexSerialization.h" |
10 | | #include "hermes/Support/UTF8.h" |
11 | | |
12 | | #include "llvh/ADT/ScopeExit.h" |
13 | | |
14 | | namespace hermes { |
15 | | namespace irgen { |
16 | | |
17 | 1.10M | Value *ESTreeIRGen::genExpression(ESTree::Node *expr, Identifier nameHint) { |
18 | 1.10M | LLVM_DEBUG( |
19 | 1.10M | llvh::dbgs() << "IRGen expression of type " << expr->getNodeName() |
20 | 1.10M | << "\n"); |
21 | 1.10M | IRBuilder::ScopedLocationChange slc(Builder, expr->getDebugLoc()); |
22 | | |
23 | | // Handle identifiers. |
24 | 1.10M | if (auto *Iden = llvh::dyn_cast<ESTree::IdentifierNode>(expr)) { |
25 | 439k | return genIdentifierExpression(Iden, false); |
26 | 439k | } |
27 | | |
28 | | // Handle Null Literals. |
29 | | // http://www.ecma-international.org/ecma-262/6.0/#sec-null-literals |
30 | 666k | if (llvh::isa<ESTree::NullLiteralNode>(expr)) { |
31 | 0 | return Builder.getLiteralNull(); |
32 | 0 | } |
33 | | |
34 | | // Handle String Literals. |
35 | | // http://www.ecma-international.org/ecma-262/6.0/#sec-literals-string-literals |
36 | 666k | if (auto *Lit = llvh::dyn_cast<ESTree::StringLiteralNode>(expr)) { |
37 | 10 | LLVM_DEBUG( |
38 | 10 | llvh::dbgs() << "Loading String Literal \"" << Lit->_value << "\"\n"); |
39 | 10 | return Builder.getLiteralString(Lit->_value->str()); |
40 | 10 | } |
41 | | |
42 | | // Handle Regexp Literals. |
43 | | // http://www.ecma-international.org/ecma-262/6.0/#sec-literals-regular-expression-literals |
44 | 666k | if (auto *Lit = llvh::dyn_cast<ESTree::RegExpLiteralNode>(expr)) { |
45 | 388 | return genRegExpLiteral(Lit); |
46 | 388 | } |
47 | | |
48 | | // Handle Boolean Literals. |
49 | | // http://www.ecma-international.org/ecma-262/6.0/#sec-boolean-literals |
50 | 666k | if (auto *Lit = llvh::dyn_cast<ESTree::BooleanLiteralNode>(expr)) { |
51 | 0 | LLVM_DEBUG( |
52 | 0 | llvh::dbgs() << "Loading String Literal \"" << Lit->_value << "\"\n"); |
53 | 0 | return Builder.getLiteralBool(Lit->_value); |
54 | 0 | } |
55 | | |
56 | | // Handle Number Literals. |
57 | | // http://www.ecma-international.org/ecma-262/6.0/#sec-literals-numeric-literals |
58 | 666k | if (auto *Lit = llvh::dyn_cast<ESTree::NumericLiteralNode>(expr)) { |
59 | 432k | LLVM_DEBUG( |
60 | 432k | llvh::dbgs() << "Loading Numeric Literal \"" << Lit->_value << "\"\n"); |
61 | 432k | return Builder.getLiteralNumber(Lit->_value); |
62 | 432k | } |
63 | | |
64 | | // Handle BigInt Literals. |
65 | | // https://262.ecma-international.org/#sec-ecmascript-language-types-bigint-type |
66 | 233k | if (auto *Lit = llvh::dyn_cast<ESTree::BigIntLiteralNode>(expr)) { |
67 | 25 | LLVM_DEBUG( |
68 | 25 | llvh::dbgs() << "Loading BitInt Literal \"" << Lit->_bigint->str() |
69 | 25 | << "\"\n"); |
70 | 25 | return Builder.getLiteralBigInt(Lit->_bigint); |
71 | 25 | } |
72 | | |
73 | | // Handle the assignment expression. |
74 | 233k | if (auto Assign = llvh::dyn_cast<ESTree::AssignmentExpressionNode>(expr)) { |
75 | 1.98k | return genAssignmentExpr(Assign); |
76 | 1.98k | } |
77 | | |
78 | | // Handle Call expressions. |
79 | 231k | if (auto *call = llvh::dyn_cast<ESTree::CallExpressionNode>(expr)) { |
80 | 5 | return genCallExpr(call); |
81 | 5 | } |
82 | | |
83 | | // Handle Call expressions. |
84 | 231k | if (auto *call = llvh::dyn_cast<ESTree::OptionalCallExpressionNode>(expr)) { |
85 | 0 | return genOptionalCallExpr(call, nullptr); |
86 | 0 | } |
87 | | |
88 | | // Handle the 'new' expressions. |
89 | 231k | if (auto *newExp = llvh::dyn_cast<ESTree::NewExpressionNode>(expr)) { |
90 | 50 | return genNewExpr(newExp); |
91 | 50 | } |
92 | | |
93 | | // Handle MemberExpression expressions for access property. |
94 | 231k | if (auto *Mem = llvh::dyn_cast<ESTree::MemberExpressionNode>(expr)) { |
95 | 7.32k | return genMemberExpression(Mem, MemberExpressionOperation::Load).result; |
96 | 7.32k | } |
97 | | |
98 | | // Handle MemberExpression expressions for access property. |
99 | 224k | if (auto *mem = llvh::dyn_cast<ESTree::OptionalMemberExpressionNode>(expr)) { |
100 | 206 | return genOptionalMemberExpression( |
101 | 206 | mem, nullptr, MemberExpressionOperation::Load) |
102 | 206 | .result; |
103 | 206 | } |
104 | | |
105 | | // Handle Array expressions (syntax: [1,2,3]). |
106 | 224k | if (auto *Arr = llvh::dyn_cast<ESTree::ArrayExpressionNode>(expr)) { |
107 | 113 | return genArrayExpr(Arr); |
108 | 113 | } |
109 | | |
110 | | // Handle object expressions (syntax: {"1" : "2"}). |
111 | 224k | if (auto *Obj = llvh::dyn_cast<ESTree::ObjectExpressionNode>(expr)) { |
112 | 0 | return genObjectExpr(Obj); |
113 | 0 | } |
114 | | |
115 | | // Handle logical expressions (short circuiting). |
116 | 224k | if (auto *L = llvh::dyn_cast<ESTree::LogicalExpressionNode>(expr)) { |
117 | 2.00k | return genLogicalExpression(L); |
118 | 2.00k | } |
119 | | |
120 | | // Handle Binary Expressions. |
121 | 222k | if (auto *Bin = llvh::dyn_cast<ESTree::BinaryExpressionNode>(expr)) { |
122 | 105k | return genBinaryExpression(Bin); |
123 | 105k | } |
124 | | |
125 | | // Handle Unary operator Expressions. |
126 | 116k | if (auto *U = llvh::dyn_cast<ESTree::UnaryExpressionNode>(expr)) { |
127 | 64.2k | return genUnaryExpression(U); |
128 | 64.2k | } |
129 | | |
130 | | // Handle the 'this' keyword. |
131 | 52.5k | if (llvh::isa<ESTree::ThisExpressionNode>(expr)) { |
132 | 53 | if (curFunction()->function->getDefinitionKind() == |
133 | 53 | Function::DefinitionKind::ES6Arrow) { |
134 | 15 | assert( |
135 | 15 | curFunction()->capturedThis && |
136 | 15 | "arrow function must have a captured this"); |
137 | 15 | return Builder.createLoadFrameInst( |
138 | 15 | curFunction()->capturedThis, currentIRScope_); |
139 | 15 | } |
140 | 38 | return curFunction()->function->getThisParameter(); |
141 | 53 | } |
142 | | |
143 | 52.4k | if (auto *MP = llvh::dyn_cast<ESTree::MetaPropertyNode>(expr)) { |
144 | 0 | return genMetaProperty(MP); |
145 | 0 | } |
146 | | |
147 | | // Handle function expressions. |
148 | 52.4k | if (auto *FE = llvh::dyn_cast<ESTree::FunctionExpressionNode>(expr)) { |
149 | 7 | return genFunctionExpression(FE, nameHint); |
150 | 7 | } |
151 | | |
152 | 52.4k | if (auto *AF = llvh::dyn_cast<ESTree::ArrowFunctionExpressionNode>(expr)) { |
153 | 1.76k | return genArrowFunctionExpression(AF, nameHint); |
154 | 1.76k | } |
155 | | |
156 | 50.6k | if (auto *U = llvh::dyn_cast<ESTree::UpdateExpressionNode>(expr)) { |
157 | 0 | return genUpdateExpr(U); |
158 | 0 | } |
159 | | |
160 | 50.6k | if (auto *C = llvh::dyn_cast<ESTree::ConditionalExpressionNode>(expr)) { |
161 | 5.06k | return genConditionalExpr(C); |
162 | 5.06k | } |
163 | | |
164 | 45.6k | if (auto *Sq = llvh::dyn_cast<ESTree::SequenceExpressionNode>(expr)) { |
165 | 251 | return genSequenceExpr(Sq); |
166 | 251 | } |
167 | | |
168 | 45.3k | if (auto *Tl = llvh::dyn_cast<ESTree::TemplateLiteralNode>(expr)) { |
169 | 40.3k | return genTemplateLiteralExpr(Tl); |
170 | 40.3k | } |
171 | | |
172 | 5.00k | if (auto *Tt = llvh::dyn_cast<ESTree::TaggedTemplateExpressionNode>(expr)) { |
173 | 5.00k | return genTaggedTemplateExpr(Tt); |
174 | 5.00k | } |
175 | | |
176 | 0 | if (auto *Y = llvh::dyn_cast<ESTree::YieldExpressionNode>(expr)) { |
177 | 0 | return Y->_delegate ? genYieldStarExpr(Y) : genYieldExpr(Y); |
178 | 0 | } |
179 | | |
180 | 0 | if (auto *A = llvh::dyn_cast<ESTree::AwaitExpressionNode>(expr)) { |
181 | 0 | return genAwaitExpr(A); |
182 | 0 | } |
183 | | |
184 | 0 | Builder.getModule()->getContext().getSourceErrorManager().error( |
185 | 0 | expr->getSourceRange(), Twine("Invalid expression encountered")); |
186 | 0 | return Builder.getLiteralUndefined(); |
187 | 0 | } |
188 | | |
189 | | void ESTreeIRGen::genExpressionBranch( |
190 | | ESTree::Node *expr, |
191 | | BasicBlock *onTrue, |
192 | | BasicBlock *onFalse, |
193 | 5.06k | BasicBlock *onNullish) { |
194 | 5.06k | switch (expr->getKind()) { |
195 | 0 | case ESTree::NodeKind::LogicalExpression: |
196 | 0 | return genLogicalExpressionBranch( |
197 | 0 | cast<ESTree::LogicalExpressionNode>(expr), |
198 | 0 | onTrue, |
199 | 0 | onFalse, |
200 | 0 | onNullish); |
201 | | |
202 | 2.76k | case ESTree::NodeKind::UnaryExpression: { |
203 | 2.76k | auto *e = cast<ESTree::UnaryExpressionNode>(expr); |
204 | 2.76k | switch (UnaryOperatorInst::parseOperator(e->_operator->str())) { |
205 | 0 | case UnaryOperatorInst::OpKind::BangKind: |
206 | | // Do not propagate onNullish here because !expr cannot be nullish. |
207 | 0 | return genExpressionBranch(e->_argument, onFalse, onTrue, nullptr); |
208 | 2.76k | default: |
209 | 2.76k | break; |
210 | 2.76k | } |
211 | | |
212 | 2.76k | break; |
213 | 2.76k | } |
214 | | |
215 | 2.76k | case ESTree::NodeKind::SequenceExpression: { |
216 | 0 | auto *e = cast<ESTree::SequenceExpressionNode>(expr); |
217 | |
|
218 | 0 | ESTree::NodePtr last = nullptr; |
219 | 0 | for (auto &ex : e->_expressions) { |
220 | 0 | if (last) |
221 | 0 | genExpression(last); |
222 | 0 | last = &ex; |
223 | 0 | } |
224 | 0 | if (last) |
225 | 0 | genExpressionBranch(last, onTrue, onFalse, onNullish); |
226 | 0 | return; |
227 | 2.76k | } |
228 | | |
229 | 2.30k | default: |
230 | 2.30k | break; |
231 | 5.06k | } |
232 | | |
233 | 5.06k | Value *condVal = genExpression(expr); |
234 | 5.06k | if (onNullish) { |
235 | 0 | Value *isNullish = Builder.createBinaryOperatorInst( |
236 | 0 | condVal, |
237 | 0 | Builder.getLiteralNull(), |
238 | 0 | BinaryOperatorInst::OpKind::EqualKind); |
239 | 0 | BasicBlock *notNullishBB = Builder.createBasicBlock(Builder.getFunction()); |
240 | 0 | Builder.createCondBranchInst(isNullish, onNullish, notNullishBB); |
241 | 0 | Builder.setInsertionBlock(notNullishBB); |
242 | 0 | } |
243 | 5.06k | Builder.createCondBranchInst(condVal, onTrue, onFalse); |
244 | 5.06k | } |
245 | | |
246 | 114 | Value *ESTreeIRGen::genArrayFromElements(ESTree::NodeList &list) { |
247 | 114 | LLVM_DEBUG(llvh::dbgs() << "Initializing a new array\n"); |
248 | 114 | AllocArrayInst::ArrayValueList elements; |
249 | | |
250 | | // Precalculate the minimum number of elements in case we need to call |
251 | | // AllocArrayInst at some point, as well as find out whether we have a spread |
252 | | // element (which will result in the final array having variable length). |
253 | 114 | unsigned minElements = 0; |
254 | 114 | bool variableLength = false; |
255 | 318k | for (auto &E : list) { |
256 | 318k | if (llvh::isa<ESTree::SpreadElementNode>(&E)) { |
257 | 7 | variableLength = true; |
258 | 7 | continue; |
259 | 7 | } |
260 | 318k | ++minElements; |
261 | 318k | } |
262 | | |
263 | | // We store consecutive elements until we encounter elision, |
264 | | // or we enounter a non-literal in limited-register mode. |
265 | | // The rest of them has to be initialized through property sets. |
266 | | |
267 | | // If we have a variable length array, then we store the next index in |
268 | | // a stack location `nextIndex`, to be updated when we encounter spread |
269 | | // elements. Otherwise, we simply count them in `count`. |
270 | 114 | unsigned count = 0; |
271 | 114 | AllocStackInst *nextIndex = nullptr; |
272 | 114 | if (variableLength) { |
273 | | // Avoid emitting the extra instructions unless we actually need to, |
274 | | // to simplify tests and because it's easy. |
275 | 7 | nextIndex = Builder.createAllocStackInst("nextIndex"); |
276 | 7 | Builder.createStoreStackInst(Builder.getLiteralPositiveZero(), nextIndex); |
277 | 7 | } |
278 | | |
279 | 114 | bool consecutive = true; |
280 | 114 | auto codeGenOpts = Mod->getContext().getCodeGenerationSettings(); |
281 | 114 | AllocArrayInst *allocArrayInst = nullptr; |
282 | 318k | for (auto &E : list) { |
283 | 318k | Value *value{nullptr}; |
284 | 318k | bool isSpread = false; |
285 | 318k | if (!llvh::isa<ESTree::EmptyNode>(&E)) { |
286 | 318k | if (auto *spread = llvh::dyn_cast<ESTree::SpreadElementNode>(&E)) { |
287 | 7 | isSpread = true; |
288 | 7 | value = genExpression(spread->_argument); |
289 | 318k | } else { |
290 | 318k | value = genExpression(&E); |
291 | 318k | } |
292 | 318k | } |
293 | 318k | if (!value || |
294 | 318k | (!llvh::isa<Literal>(value) && !codeGenOpts.unlimitedRegisters) || |
295 | 318k | isSpread) { |
296 | | // This is either an elision, |
297 | | // or a non-literal in limited-register mode, |
298 | | // or a spread element. |
299 | 97 | if (consecutive) { |
300 | | // So far we have been storing elements consecutively, |
301 | | // but not anymore, time to create the array. |
302 | 53 | allocArrayInst = Builder.createAllocArrayInst(elements, minElements); |
303 | 53 | consecutive = false; |
304 | 53 | } |
305 | 97 | } |
306 | 318k | if (isSpread) { |
307 | | // Spread the SpreadElement argument into the array. |
308 | | // HermesInternal.arraySpread returns the new value of nextIndex, |
309 | | // so update nextIndex accordingly and finish this iteration of the loop. |
310 | 7 | auto *newNextIndex = genBuiltinCall( |
311 | 7 | BuiltinMethod::HermesBuiltin_arraySpread, |
312 | 7 | {allocArrayInst, value, Builder.createLoadStackInst(nextIndex)}); |
313 | 7 | Builder.createStoreStackInst(newNextIndex, nextIndex); |
314 | 7 | continue; |
315 | 7 | } |
316 | | // The element is not a spread element, so perform the store here. |
317 | 318k | if (value) { |
318 | 318k | if (consecutive) { |
319 | 123k | elements.push_back(value); |
320 | 195k | } else { |
321 | 195k | Builder.createStoreOwnPropertyInst( |
322 | 195k | value, |
323 | 195k | allocArrayInst, |
324 | 195k | variableLength ? cast<Value>(Builder.createLoadStackInst(nextIndex)) |
325 | 195k | : cast<Value>(Builder.getLiteralNumber(count)), |
326 | 195k | IRBuilder::PropEnumerable::Yes); |
327 | 195k | } |
328 | 318k | } |
329 | | // Update the next index or the count depending on if it's a variable length |
330 | | // array. |
331 | 318k | if (variableLength) { |
332 | | // We perform this update on any leading elements before the first spread |
333 | | // element as well, but the optimizer will eliminate the extra adds |
334 | | // because we know the initial value (0) and how incrementing works. |
335 | 35 | Builder.createStoreStackInst( |
336 | 35 | Builder.createBinaryOperatorInst( |
337 | 35 | Builder.createLoadStackInst(nextIndex), |
338 | 35 | Builder.getLiteralNumber(1), |
339 | 35 | BinaryOperatorInst::OpKind::AddKind), |
340 | 35 | nextIndex); |
341 | 318k | } else { |
342 | 318k | count++; |
343 | 318k | } |
344 | 318k | } |
345 | | |
346 | 114 | if (!allocArrayInst) { |
347 | 61 | assert( |
348 | 61 | !variableLength && |
349 | 61 | "variable length arrays must allocate their own arrays"); |
350 | 61 | allocArrayInst = Builder.createAllocArrayInst(elements, list.size()); |
351 | 61 | } |
352 | 114 | if (!list.empty() && llvh::isa<ESTree::EmptyNode>(&list.back())) { |
353 | | // Last element is an elision, VM cannot derive the length properly. |
354 | | // We have to explicitly set it. |
355 | 2 | Value *newLength; |
356 | 2 | if (variableLength) |
357 | 1 | newLength = Builder.createLoadStackInst(nextIndex); |
358 | 1 | else |
359 | 1 | newLength = Builder.getLiteralNumber(count); |
360 | 2 | Builder.createStorePropertyInst( |
361 | 2 | newLength, allocArrayInst, llvh::StringRef("length")); |
362 | 2 | } |
363 | 114 | return allocArrayInst; |
364 | 114 | } |
365 | | |
366 | 113 | Value *ESTreeIRGen::genArrayExpr(ESTree::ArrayExpressionNode *Expr) { |
367 | 113 | return genArrayFromElements(Expr->_elements); |
368 | 113 | } |
369 | | |
370 | 5 | Value *ESTreeIRGen::genCallExpr(ESTree::CallExpressionNode *call) { |
371 | 5 | LLVM_DEBUG(llvh::dbgs() << "IRGen 'call' statement/expression.\n"); |
372 | | |
373 | | // Check for a direct call to eval(). |
374 | 5 | if (auto *identNode = llvh::dyn_cast<ESTree::IdentifierNode>(call->_callee)) { |
375 | 4 | if (Identifier::getFromPointer(identNode->_name) == identEval_) { |
376 | 0 | auto *evalVar = nameTable_.lookup(identEval_); |
377 | 0 | if (!evalVar || llvh::isa<GlobalObjectProperty>(evalVar)) |
378 | 0 | return genCallEvalExpr(call); |
379 | 0 | } |
380 | 4 | } |
381 | | |
382 | 5 | Value *thisVal; |
383 | 5 | Value *callee; |
384 | | |
385 | | // Handle MemberExpression expression calls that sets the 'this' property. |
386 | 5 | if (auto *Mem = llvh::dyn_cast<ESTree::MemberExpressionNode>(call->_callee)) { |
387 | 0 | MemberExpressionResult memResult = |
388 | 0 | genMemberExpression(Mem, MemberExpressionOperation::Load); |
389 | | |
390 | | // Call the callee with obj as the 'this' pointer. |
391 | 0 | thisVal = memResult.base; |
392 | 0 | callee = memResult.result; |
393 | 5 | } else if ( |
394 | 5 | auto *Mem = |
395 | 5 | llvh::dyn_cast<ESTree::OptionalMemberExpressionNode>(call->_callee)) { |
396 | 0 | MemberExpressionResult memResult = genOptionalMemberExpression( |
397 | 0 | Mem, nullptr, MemberExpressionOperation::Load); |
398 | | |
399 | | // Call the callee with obj as the 'this' pointer. |
400 | 0 | thisVal = memResult.base; |
401 | 0 | callee = memResult.result; |
402 | 5 | } else { |
403 | 5 | thisVal = Builder.getLiteralUndefined(); |
404 | 5 | callee = genExpression(call->_callee); |
405 | 5 | } |
406 | | |
407 | 5 | return emitCall(call, callee, thisVal); |
408 | 5 | } |
409 | | |
410 | | Value *ESTreeIRGen::genOptionalCallExpr( |
411 | | ESTree::OptionalCallExpressionNode *call, |
412 | 32 | BasicBlock *shortCircuitBB) { |
413 | 32 | PhiInst::ValueListType values; |
414 | 32 | PhiInst::BasicBlockListType blocks; |
415 | | |
416 | | // true when this is the genOptionalCallExpr call containing |
417 | | // the logic for shortCircuitBB. |
418 | 32 | bool isFirstOptional = shortCircuitBB == nullptr; |
419 | | |
420 | | // If isFirstOptional, the final result will be computed in continueBB and |
421 | | // returned. |
422 | 32 | BasicBlock *continueBB = nullptr; |
423 | | |
424 | 32 | if (!shortCircuitBB) { |
425 | | // If shortCircuitBB is null, then this is the outermost in the optional |
426 | | // chain, so we must create it here and pass it through to every other |
427 | | // OptionalCallExpression and OptionalMemberExpression in the chain. |
428 | 0 | continueBB = Builder.createBasicBlock(Builder.getFunction()); |
429 | 0 | auto *insertionBB = Builder.getInsertionBlock(); |
430 | 0 | shortCircuitBB = Builder.createBasicBlock(Builder.getFunction()); |
431 | 0 | Builder.setInsertionBlock(shortCircuitBB); |
432 | 0 | values.push_back(Builder.getLiteralUndefined()); |
433 | 0 | blocks.push_back(shortCircuitBB); |
434 | 0 | Builder.createBranchInst(continueBB); |
435 | 0 | Builder.setInsertionBlock(insertionBB); |
436 | 0 | } |
437 | | |
438 | 32 | Value *thisVal; |
439 | 32 | Value *callee; |
440 | | |
441 | | // Handle MemberExpression expression calls that sets the 'this' property. |
442 | 32 | if (auto *me = llvh::dyn_cast<ESTree::MemberExpressionNode>(call->_callee)) { |
443 | 0 | MemberExpressionResult memResult = |
444 | 0 | genMemberExpression(me, MemberExpressionOperation::Load); |
445 | | |
446 | | // Call the callee with obj as the 'this' pointer. |
447 | 0 | thisVal = memResult.base; |
448 | 0 | callee = memResult.result; |
449 | 32 | } else if ( |
450 | 32 | auto *ome = |
451 | 32 | llvh::dyn_cast<ESTree::OptionalMemberExpressionNode>(call->_callee)) { |
452 | 32 | MemberExpressionResult memResult = genOptionalMemberExpression( |
453 | 32 | ome, shortCircuitBB, MemberExpressionOperation::Load); |
454 | | |
455 | | // Call the callee with obj as the 'this' pointer. |
456 | 32 | thisVal = memResult.base; |
457 | 32 | callee = memResult.result; |
458 | 32 | } else if ( |
459 | 0 | auto *oce = |
460 | 0 | llvh::dyn_cast<ESTree::OptionalCallExpressionNode>(call->_callee)) { |
461 | 0 | thisVal = Builder.getLiteralUndefined(); |
462 | 0 | callee = genOptionalCallExpr(oce, shortCircuitBB); |
463 | 0 | } else { |
464 | 0 | thisVal = Builder.getLiteralUndefined(); |
465 | 0 | callee = genExpression(getCallee(call)); |
466 | 0 | } |
467 | | |
468 | 32 | if (call->_optional) { |
469 | 0 | BasicBlock *evalRHSBB = Builder.createBasicBlock(Builder.getFunction()); |
470 | | |
471 | | // If callee is undefined or null, then return undefined. |
472 | | // NOTE: We use `obj == null` to account for both null and undefined. |
473 | 0 | Builder.createCondBranchInst( |
474 | 0 | Builder.createBinaryOperatorInst( |
475 | 0 | callee, |
476 | 0 | Builder.getLiteralNull(), |
477 | 0 | BinaryOperatorInst::OpKind::EqualKind), |
478 | 0 | shortCircuitBB, |
479 | 0 | evalRHSBB); |
480 | | |
481 | | // baseValue is not undefined or null. |
482 | 0 | Builder.setInsertionBlock(evalRHSBB); |
483 | 0 | } |
484 | | |
485 | 32 | Value *callResult = emitCall(call, callee, thisVal); |
486 | | |
487 | 32 | if (isFirstOptional) { |
488 | 0 | values.push_back(callResult); |
489 | 0 | blocks.push_back(Builder.getInsertionBlock()); |
490 | 0 | Builder.createBranchInst(continueBB); |
491 | |
|
492 | 0 | Builder.setInsertionBlock(continueBB); |
493 | 0 | return Builder.createPhiInst(values, blocks); |
494 | 0 | } |
495 | | |
496 | | // If this isn't the first optional, no Phi needed, just return the |
497 | | // callResult. |
498 | 32 | return callResult; |
499 | 32 | } |
500 | | |
501 | | /// \return a LiteralString with a textual representation of given \p call |
502 | | /// expression. This representation is built from the original source code, with |
503 | | /// some changes: |
504 | | /// |
505 | | /// 1. unprintable character, new line, and spaces following new lines, are not |
506 | | /// emitted. |
507 | | /// 2. output is limited to kMaxTextifiedCalleeSizeUTF8Chars. If the source code |
508 | | /// is longer than the maximum number of characters, then the returned callee |
509 | | /// will look like |
510 | | /// |
511 | | /// source code : aaaaaaaaaa[.]+bbbbbbbbbb |
512 | | /// textified callee: "aaaaaaaaaa(...)bbbbbbbbbb" |
513 | | static LiteralString *getTextifiedCallExpr( |
514 | | IRBuilder &builder, |
515 | 37 | ESTree::Node *callee) { |
516 | 37 | constexpr uint32_t kMaxTextifiedCalleeSizeUTF8Chars = 64; |
517 | | // Pessimizing the maximum buffer size for the textified callee as if all |
518 | | // characters were 4 bytes. |
519 | 37 | llvh::SmallVector<char, kMaxTextifiedCalleeSizeUTF8Chars * 4> textifiedCallee; |
520 | 37 | llvh::raw_svector_ostream OS(textifiedCallee); |
521 | | |
522 | | // Count of how many UTF8 character are on the textified callee string. |
523 | 37 | uint32_t numUTF8Chars = 0; |
524 | 37 | const char *begin = callee->getSourceRange().Start.getPointer(); |
525 | 37 | const char *end = callee->getSourceRange().End.getPointer(); |
526 | | |
527 | 37 | const char *pos = begin; |
528 | | |
529 | | /// Helper function that scans the input source code searching for the next |
530 | | /// UTF8 char starting at \p iter. \return a StringRef with the chars that |
531 | | /// form the next UTF8 char on the input, or None if the input has been |
532 | | /// exhausted. Upon return, \p iter will be modified 1-past the last char |
533 | | /// consumed from the input. |
534 | 86.8k | auto nextUTF8Char = [end](const char *&iter) -> OptValue<llvh::StringRef> { |
535 | 86.8k | assert(iter < end && "iter is past end"); |
536 | 86.8k | const char *start; |
537 | 86.8k | bool skipSpace = false; |
538 | 86.8k | bool newLine; |
539 | 86.8k | bool unprintableChar; |
540 | | // The function may need to decode several UTF8 charaters to skip new lines, |
541 | | // spaces following new lines, and unprintable characters. |
542 | 86.9k | do { |
543 | 86.9k | if (iter == end) { |
544 | 0 | return llvh::None; |
545 | 0 | } |
546 | | |
547 | | // From start, where does the current UTF8 character ends? |
548 | 86.9k | start = iter; |
549 | 86.9k | for (++iter; iter < end && isUTF8ContinuationByte(*iter); ++iter) { |
550 | | // nothing |
551 | 43 | } |
552 | | |
553 | 86.9k | newLine = *start == '\n'; |
554 | 86.9k | skipSpace |= newLine; |
555 | 86.9k | unprintableChar = static_cast<uint8_t>(*start) < 32; |
556 | 86.9k | } while (unprintableChar || newLine || (skipSpace && *start == ' ')); |
557 | | |
558 | 86.8k | const size_t utf8CharLength = static_cast<size_t>(iter - start); |
559 | 86.8k | return llvh::StringRef{start, utf8CharLength}; |
560 | 86.8k | }; |
561 | | |
562 | | // The algorithm is split in three separate steps: |
563 | | // 1. Find the range in the input source that contains the first half of the |
564 | | // UTF8 characters that should be present on the output. |
565 | | // 2. Find the window into the input source starting at the end of the range |
566 | | // computed in 1. and containing another half of the maximum output length. |
567 | | // This window is [mark, pos). |
568 | | // 3. Slide the [mark, pos) window found in 2 until pos equals end. |
569 | | // |
570 | | // If the input is exhausted in 1. or 2., then the output string will contain |
571 | | // a "copy" of the input source (minus the characters listed above); it will |
572 | | // otherwise be prefix"(...)"suffix, with prefix being the first |
573 | | // kMaxTextifiedCalleeSizeUTF8Chars / 2 UTF8 chars in the input source, and |
574 | | // suffix, the last kMaxTextifiedCalleeSizeUTF8Chars / 2 UTF8 chars. |
575 | | |
576 | | // Scan the input string starting from the first position until half of the |
577 | | // maximum of allowed character have been scanned. |
578 | 1.00k | while (pos < end && numUTF8Chars < kMaxTextifiedCalleeSizeUTF8Chars / 2) { |
579 | 969 | if (auto ch = nextUTF8Char(pos)) { |
580 | 969 | ++numUTF8Chars; |
581 | 969 | OS << *ch; |
582 | 969 | } |
583 | 969 | } |
584 | | |
585 | 37 | assert( |
586 | 37 | (pos == end || numUTF8Chars == kMaxTextifiedCalleeSizeUTF8Chars / 2) && |
587 | 37 | "Invalid source range"); |
588 | | |
589 | | // Now save the current position, and advance it until the end of the buffer, |
590 | | // or until enough UTF8 characters are found. |
591 | 37 | const char *mark = pos; |
592 | 451 | while (pos < end && numUTF8Chars < kMaxTextifiedCalleeSizeUTF8Chars) { |
593 | 414 | if (nextUTF8Char(pos)) { |
594 | 414 | ++numUTF8Chars; |
595 | 414 | } |
596 | 414 | } |
597 | | |
598 | 37 | assert( |
599 | 37 | (pos == end || numUTF8Chars == kMaxTextifiedCalleeSizeUTF8Chars) && |
600 | 37 | "Invalid source range"); |
601 | | |
602 | 37 | if (pos < end) { |
603 | | // Slide the suffix window if pos is not at the end of the input. |
604 | 9 | OS << "(...)"; |
605 | | |
606 | 42.5k | while (pos < end) { |
607 | 42.5k | auto ch = nextUTF8Char(mark); |
608 | 42.5k | assert(ch && "should have a character"); |
609 | 42.5k | (void)ch; |
610 | 42.5k | nextUTF8Char(pos); |
611 | 42.5k | } |
612 | 9 | } |
613 | | |
614 | | // The input should be exhausted by now, and pos should be equal to end -- |
615 | | // meaning all characters have been decoded. |
616 | 37 | assert(pos == end && "Invalid source range"); |
617 | | |
618 | | // Now add all characters between mark and end to the textified callable. |
619 | 451 | while (mark < end) { |
620 | 414 | auto ch = nextUTF8Char(mark); |
621 | 414 | assert(ch && "should have a character"); |
622 | 414 | OS << *ch; |
623 | 414 | } |
624 | | |
625 | 37 | return builder.getLiteralString(OS.str()); |
626 | 37 | } |
627 | | |
628 | | Value *ESTreeIRGen::emitCall( |
629 | | ESTree::CallExpressionLikeNode *call, |
630 | | Value *callee, |
631 | 37 | Value *thisVal) { |
632 | 37 | bool hasSpread = false; |
633 | 37 | for (auto &arg : getArguments(call)) { |
634 | 4 | if (llvh::isa<ESTree::SpreadElementNode>(&arg)) { |
635 | 0 | hasSpread = true; |
636 | 0 | } |
637 | 4 | } |
638 | | |
639 | 37 | if (!hasSpread) { |
640 | 37 | CallInst::ArgumentList args; |
641 | 37 | for (auto &arg : getArguments(call)) { |
642 | 4 | args.push_back(genExpression(&arg)); |
643 | 4 | } |
644 | | |
645 | 37 | return Builder.createCallInst( |
646 | 37 | getTextifiedCallExpr(Builder, getCallee(call)), callee, thisVal, args); |
647 | 37 | } |
648 | | |
649 | | // Otherwise, there exists a spread argument, so the number of arguments |
650 | | // is variable. |
651 | | // Generate IR for this by creating an array and populating it with the |
652 | | // arguments, then calling HermesInternal.apply. |
653 | 0 | auto *args = genArrayFromElements(getArguments(call)); |
654 | 0 | return genBuiltinCall( |
655 | 0 | BuiltinMethod::HermesBuiltin_apply, {callee, args, thisVal}); |
656 | 37 | } |
657 | | |
658 | | ESTreeIRGen::MemberExpressionResult ESTreeIRGen::genMemberExpression( |
659 | | ESTree::MemberExpressionNode *mem, |
660 | 7.33k | MemberExpressionOperation op) { |
661 | 7.33k | Value *baseValue = genExpression(mem->_object); |
662 | 7.33k | Value *prop = genMemberExpressionProperty(mem); |
663 | 7.33k | switch (op) { |
664 | 7.32k | case MemberExpressionOperation::Load: |
665 | 7.32k | return MemberExpressionResult{ |
666 | 7.32k | Builder.createLoadPropertyInst(baseValue, prop), baseValue}; |
667 | 2 | case MemberExpressionOperation::Delete: |
668 | 2 | return MemberExpressionResult{ |
669 | 2 | Builder.createDeletePropertyInst(baseValue, prop), baseValue}; |
670 | 7.33k | } |
671 | 7.33k | llvm_unreachable("No other kind of member expression"); |
672 | 7.33k | } |
673 | | |
674 | | ESTreeIRGen::MemberExpressionResult ESTreeIRGen::genOptionalMemberExpression( |
675 | | ESTree::OptionalMemberExpressionNode *mem, |
676 | | BasicBlock *shortCircuitBB, |
677 | 6.76k | MemberExpressionOperation op) { |
678 | 6.76k | PhiInst::ValueListType values; |
679 | 6.76k | PhiInst::BasicBlockListType blocks; |
680 | | |
681 | | // true when this is the genOptionalMemberExpression call containing |
682 | | // the logic for shortCircuitBB. |
683 | 6.76k | bool isFirstOptional = shortCircuitBB == nullptr; |
684 | | |
685 | | // If isFirstOptional, the final result will be computed in continueBB and |
686 | | // returned. |
687 | 6.76k | BasicBlock *continueBB = nullptr; |
688 | | |
689 | 6.76k | if (isFirstOptional) { |
690 | | // If shortCircuitBB is null, then this is the outermost in the optional |
691 | | // chain, so we must create it here and pass it through to every other |
692 | | // OptionalCallExpression and OptionalMemberExpression in the chain. |
693 | 206 | continueBB = Builder.createBasicBlock(Builder.getFunction()); |
694 | 206 | auto *insertionBB = Builder.getInsertionBlock(); |
695 | 206 | shortCircuitBB = Builder.createBasicBlock(Builder.getFunction()); |
696 | 206 | Builder.setInsertionBlock(shortCircuitBB); |
697 | 206 | values.push_back(Builder.getLiteralUndefined()); |
698 | 206 | blocks.push_back(shortCircuitBB); |
699 | 206 | Builder.createBranchInst(continueBB); |
700 | 206 | Builder.setInsertionBlock(insertionBB); |
701 | 206 | } |
702 | | |
703 | 6.76k | Value *baseValue = nullptr; |
704 | 6.76k | if (ESTree::OptionalMemberExpressionNode *ome = |
705 | 6.76k | llvh::dyn_cast<ESTree::OptionalMemberExpressionNode>(mem->_object)) { |
706 | 6.52k | baseValue = genOptionalMemberExpression( |
707 | 6.52k | ome, shortCircuitBB, MemberExpressionOperation::Load) |
708 | 6.52k | .result; |
709 | 6.52k | } else if ( |
710 | 238 | ESTree::OptionalCallExpressionNode *oce = |
711 | 238 | llvh::dyn_cast<ESTree::OptionalCallExpressionNode>(mem->_object)) { |
712 | 32 | baseValue = genOptionalCallExpr(oce, shortCircuitBB); |
713 | 206 | } else { |
714 | 206 | baseValue = genExpression(mem->_object); |
715 | 206 | } |
716 | | |
717 | 6.76k | if (mem->_optional) { |
718 | 404 | BasicBlock *evalRHSBB = Builder.createBasicBlock(Builder.getFunction()); |
719 | | |
720 | | // If baseValue is undefined or null, then return undefined. |
721 | | // NOTE: We use `obj == null` to account for both null and undefined. |
722 | 404 | Builder.createCondBranchInst( |
723 | 404 | Builder.createBinaryOperatorInst( |
724 | 404 | baseValue, |
725 | 404 | Builder.getLiteralNull(), |
726 | 404 | BinaryOperatorInst::OpKind::EqualKind), |
727 | 404 | shortCircuitBB, |
728 | 404 | evalRHSBB); |
729 | | |
730 | | // baseValue is not undefined, look up the property properly. |
731 | 404 | Builder.setInsertionBlock(evalRHSBB); |
732 | 404 | } |
733 | | |
734 | 6.76k | Value *prop = genMemberExpressionProperty(mem); |
735 | 6.76k | Value *result = nullptr; |
736 | 6.76k | switch (op) { |
737 | 6.76k | case MemberExpressionOperation::Load: |
738 | 6.76k | result = Builder.createLoadPropertyInst(baseValue, prop); |
739 | 6.76k | break; |
740 | 0 | case MemberExpressionOperation::Delete: |
741 | 0 | result = Builder.createDeletePropertyInst(baseValue, prop); |
742 | 0 | break; |
743 | 6.76k | } |
744 | 6.76k | assert(result && "result must be set"); |
745 | | |
746 | 6.76k | if (isFirstOptional) { |
747 | 206 | values.push_back(result); |
748 | 206 | blocks.push_back(Builder.getInsertionBlock()); |
749 | 206 | Builder.createBranchInst(continueBB); |
750 | | |
751 | 206 | Builder.setInsertionBlock(continueBB); |
752 | 206 | return {Builder.createPhiInst(values, blocks), baseValue}; |
753 | 206 | } |
754 | | |
755 | | // If this isn't the first optional, no Phi needed, just return the result. |
756 | 6.55k | return {result, baseValue}; |
757 | 6.76k | } |
758 | | |
759 | 0 | Value *ESTreeIRGen::genCallEvalExpr(ESTree::CallExpressionNode *call) { |
760 | 0 | if (call->_arguments.empty()) { |
761 | 0 | Mod->getContext().getSourceErrorManager().warning( |
762 | 0 | call->getSourceRange(), "eval() without arguments returns undefined"); |
763 | 0 | return Builder.getLiteralUndefined(); |
764 | 0 | } |
765 | | |
766 | 0 | Mod->getContext().getSourceErrorManager().warning( |
767 | 0 | Warning::DirectEval, |
768 | 0 | call->getSourceRange(), |
769 | 0 | "Direct call to eval(), but lexical scope is not supported."); |
770 | |
|
771 | 0 | llvh::SmallVector<Value *, 1> args; |
772 | 0 | for (auto &arg : call->_arguments) { |
773 | 0 | args.push_back(genExpression(&arg)); |
774 | 0 | } |
775 | |
|
776 | 0 | if (args.size() > 1) { |
777 | 0 | Mod->getContext().getSourceErrorManager().warning( |
778 | 0 | call->getSourceRange(), "Extra eval() arguments are ignored"); |
779 | 0 | } |
780 | |
|
781 | 0 | bool isStrict = Builder.getInsertionBlock()->getParent()->isStrictMode(); |
782 | 0 | return Builder.createDirectEvalInst( |
783 | 0 | args[0], Builder.getLiteralBool(isStrict)); |
784 | 0 | } |
785 | | |
786 | | /// Convert a property key node to its JavaScript string representation. |
787 | | static llvh::StringRef propertyKeyAsString( |
788 | | llvh::SmallVectorImpl<char> &storage, |
789 | 0 | ESTree::Node *Key) { |
790 | | // Handle String Literals. |
791 | | // http://www.ecma-international.org/ecma-262/6.0/#sec-literals-string-literals |
792 | 0 | if (auto *Lit = llvh::dyn_cast<ESTree::StringLiteralNode>(Key)) { |
793 | 0 | LLVM_DEBUG( |
794 | 0 | llvh::dbgs() << "Loading String Literal \"" << Lit->_value << "\"\n"); |
795 | 0 | return Lit->_value->str(); |
796 | 0 | } |
797 | | |
798 | | // Handle identifiers as if they are String Literals. |
799 | 0 | if (auto *Iden = llvh::dyn_cast<ESTree::IdentifierNode>(Key)) { |
800 | 0 | LLVM_DEBUG( |
801 | 0 | llvh::dbgs() << "Loading String Literal \"" << Iden->_name << "\"\n"); |
802 | 0 | return Iden->_name->str(); |
803 | 0 | } |
804 | | |
805 | | // Handle Number Literals. |
806 | | // http://www.ecma-international.org/ecma-262/6.0/#sec-literals-numeric-literals |
807 | 0 | if (auto *Lit = llvh::dyn_cast<ESTree::NumericLiteralNode>(Key)) { |
808 | 0 | LLVM_DEBUG( |
809 | 0 | llvh::dbgs() << "Loading Numeric Literal \"" << Lit->_value << "\"\n"); |
810 | 0 | storage.resize(NUMBER_TO_STRING_BUF_SIZE); |
811 | 0 | auto len = numberToString(Lit->_value, storage.data(), storage.size()); |
812 | 0 | return llvh::StringRef(storage.begin(), len); |
813 | 0 | } |
814 | | |
815 | 0 | llvm_unreachable("Don't know this kind of property key"); |
816 | 0 | return llvh::StringRef(); |
817 | 0 | } |
818 | | |
819 | 0 | Value *ESTreeIRGen::genObjectExpr(ESTree::ObjectExpressionNode *Expr) { |
820 | 0 | LLVM_DEBUG(llvh::dbgs() << "Initializing a new object\n"); |
821 | | |
822 | | /// Store information about a property. Is it an accessor (getter/setter) or |
823 | | /// a value, and the actual value. |
824 | 0 | class PropertyValue { |
825 | 0 | public: |
826 | | /// Is this a getter/setter value. |
827 | 0 | bool isAccessor = false; |
828 | | /// Tracks the state of generating IR for this value. |
829 | 0 | enum { None, Placeholder, IRGenerated } state{None}; |
830 | | /// The value, if this is a regular property |
831 | 0 | ESTree::Node *valueNode{}; |
832 | | /// Getter accessor, if this is an accessor property. |
833 | 0 | ESTree::FunctionExpressionNode *getterNode{}; |
834 | | /// Setter accessor, if this is an accessor property. |
835 | 0 | ESTree::FunctionExpressionNode *setterNode{}; |
836 | |
|
837 | 0 | SMRange getSourceRange() { |
838 | 0 | if (valueNode) { |
839 | 0 | return valueNode->getSourceRange(); |
840 | 0 | } |
841 | |
|
842 | 0 | if (getterNode) { |
843 | 0 | return getterNode->getSourceRange(); |
844 | 0 | } |
845 | |
|
846 | 0 | if (setterNode) { |
847 | 0 | return setterNode->getSourceRange(); |
848 | 0 | } |
849 | |
|
850 | 0 | llvm_unreachable("Unset node has no location info"); |
851 | 0 | } |
852 | |
|
853 | 0 | void setValue(ESTree::Node *val) { |
854 | 0 | isAccessor = false; |
855 | 0 | valueNode = val; |
856 | 0 | getterNode = setterNode = nullptr; |
857 | 0 | } |
858 | 0 | void setGetter(ESTree::FunctionExpressionNode *get) { |
859 | 0 | if (!isAccessor) { |
860 | 0 | valueNode = nullptr; |
861 | 0 | setterNode = nullptr; |
862 | 0 | isAccessor = true; |
863 | 0 | } |
864 | 0 | getterNode = get; |
865 | 0 | } |
866 | 0 | void setSetter(ESTree::FunctionExpressionNode *set) { |
867 | 0 | if (!isAccessor) { |
868 | 0 | valueNode = nullptr; |
869 | 0 | getterNode = nullptr; |
870 | 0 | isAccessor = true; |
871 | 0 | } |
872 | 0 | setterNode = set; |
873 | 0 | } |
874 | 0 | }; |
875 | | |
876 | | // First accumulate all getters and setters. We walk all properties, convert |
877 | | // them to string and store the value into propMap, possibly overwriting the |
878 | | // previous value. |
879 | | // Note that computed properties are not stored in the propMap as we do not |
880 | | // know the keys at compilation time. |
881 | 0 | llvh::StringMap<PropertyValue> propMap; |
882 | | // The first location where a given property name is encountered. |
883 | 0 | llvh::StringMap<SMRange> firstLocMap; |
884 | 0 | llvh::SmallVector<char, 32> stringStorage; |
885 | | /// The optional __proto__ property. |
886 | 0 | ESTree::PropertyNode *protoProperty = nullptr; |
887 | |
|
888 | 0 | uint32_t numComputed = 0; |
889 | |
|
890 | 0 | for (auto &P : Expr->_properties) { |
891 | 0 | if (llvh::isa<ESTree::SpreadElementNode>(&P)) { |
892 | 0 | continue; |
893 | 0 | } |
894 | | |
895 | | // We are reusing the storage, so make sure it is cleared at every |
896 | | // iteration. |
897 | 0 | stringStorage.clear(); |
898 | |
|
899 | 0 | auto *prop = cast<ESTree::PropertyNode>(&P); |
900 | 0 | if (prop->_computed) { |
901 | | // Can't store any useful information if the name is computed. |
902 | | // Just generate the code in the next loop. |
903 | 0 | ++numComputed; |
904 | 0 | continue; |
905 | 0 | } |
906 | | |
907 | 0 | auto propName = propertyKeyAsString(stringStorage, prop->_key); |
908 | | |
909 | | // protoProperty should only be recorded if the property is not a method |
910 | | // nor a shorthand value. |
911 | 0 | if (prop->_kind->str() == "init" && propName == "__proto__" && |
912 | 0 | !prop->_method && !prop->_shorthand) { |
913 | 0 | if (!protoProperty) { |
914 | 0 | protoProperty = prop; |
915 | 0 | } else { |
916 | 0 | Builder.getModule()->getContext().getSourceErrorManager().error( |
917 | 0 | prop->getSourceRange(), |
918 | 0 | "__proto__ was set multiple times in the object definition."); |
919 | 0 | Builder.getModule()->getContext().getSourceErrorManager().note( |
920 | 0 | protoProperty->getSourceRange(), "The first definition was here."); |
921 | 0 | } |
922 | 0 | continue; |
923 | 0 | } |
924 | | |
925 | 0 | PropertyValue *propValue = &propMap[propName]; |
926 | 0 | if (prop->_kind->str() == "get") { |
927 | 0 | propValue->setGetter(cast<ESTree::FunctionExpressionNode>(prop->_value)); |
928 | 0 | } else if (prop->_kind->str() == "set") { |
929 | 0 | propValue->setSetter(cast<ESTree::FunctionExpressionNode>(prop->_value)); |
930 | 0 | } else { |
931 | 0 | assert(prop->_kind->str() == "init" && "invalid PropertyNode kind"); |
932 | | // We record the propValue if this is a regular property |
933 | 0 | propValue->setValue(prop->_value); |
934 | 0 | } |
935 | | |
936 | 0 | std::string key = (prop->_kind->str() + propName).str(); |
937 | 0 | auto iterAndSuccess = firstLocMap.try_emplace(key, prop->getSourceRange()); |
938 | 0 | if (!iterAndSuccess.second) { |
939 | 0 | Builder.getModule()->getContext().getSourceErrorManager().warning( |
940 | 0 | prop->getSourceRange(), |
941 | 0 | Twine("the property \"") + propName + |
942 | 0 | "\" was set multiple times in the object definition."); |
943 | |
|
944 | 0 | Builder.getModule()->getContext().getSourceErrorManager().note( |
945 | 0 | iterAndSuccess.first->second, "The first definition was here."); |
946 | 0 | } |
947 | 0 | } |
948 | | |
949 | | /// Attempt to determine whether we can directly use the value of the |
950 | | /// __proto__ property as a parent when creating the object for an object |
951 | | /// initializer, instead of setting it later with |
952 | | /// HermesInternal.silentSetPrototypeOf(). That is not possible to determine |
953 | | /// statically in the general case, but we can check for the simple cases: |
954 | | /// - __proto__ property is first. |
955 | | /// - the value of __proto__ is constant. |
956 | 0 | Value *objectParent = nullptr; |
957 | 0 | if (protoProperty && |
958 | 0 | (&Expr->_properties.front() == protoProperty || |
959 | 0 | isConstantExpr(protoProperty->_value))) { |
960 | 0 | objectParent = genExpression(protoProperty->_value); |
961 | 0 | } |
962 | | |
963 | | // Allocate a new javascript object on the heap. |
964 | 0 | auto Obj = |
965 | 0 | Builder.createAllocObjectInst(propMap.size() + numComputed, objectParent); |
966 | | |
967 | | // haveSeenComputedProp tracks whether we have processed a computed property. |
968 | | // Once we do, for all future properties, we can no longer generate |
969 | | // StoreNewOwnPropertyInst because the computed property could have already |
970 | | // defined any property. |
971 | 0 | bool haveSeenComputedProp = false; |
972 | | |
973 | | // Initialize all properties. We check whether the value of each property |
974 | | // will be overwritten (by comparing against what we have saved in propMap). |
975 | | // In that case we still compute the value (it could have side effects), but |
976 | | // we don't store it. The exception to this are accessor functions - there |
977 | | // is no need to create them if we don't use them because creating a function |
978 | | // has no side effects. |
979 | 0 | for (auto &P : Expr->_properties) { |
980 | 0 | if (auto *spread = llvh::dyn_cast<ESTree::SpreadElementNode>(&P)) { |
981 | 0 | genBuiltinCall( |
982 | 0 | BuiltinMethod::HermesBuiltin_copyDataProperties, |
983 | 0 | {Obj, genExpression(spread->_argument)}); |
984 | 0 | haveSeenComputedProp = true; |
985 | 0 | continue; |
986 | 0 | } |
987 | | |
988 | | // We are reusing the storage, so make sure it is cleared at every |
989 | | // iteration. |
990 | 0 | stringStorage.clear(); |
991 | |
|
992 | 0 | auto *prop = cast<ESTree::PropertyNode>(&P); |
993 | |
|
994 | 0 | if (prop->_computed) { |
995 | | // TODO (T46136220): Set the .name property for anonymous functions that |
996 | | // are values for computed property keys. |
997 | 0 | auto *key = genExpression(prop->_key); |
998 | 0 | auto *value = genExpression(prop->_value); |
999 | 0 | if (prop->_kind->str() == "get") { |
1000 | 0 | Builder.createStoreGetterSetterInst( |
1001 | 0 | value, |
1002 | 0 | Builder.getLiteralUndefined(), |
1003 | 0 | Obj, |
1004 | 0 | key, |
1005 | 0 | IRBuilder::PropEnumerable::Yes); |
1006 | 0 | } else if (prop->_kind->str() == "set") { |
1007 | 0 | Builder.createStoreGetterSetterInst( |
1008 | 0 | Builder.getLiteralUndefined(), |
1009 | 0 | value, |
1010 | 0 | Obj, |
1011 | 0 | key, |
1012 | 0 | IRBuilder::PropEnumerable::Yes); |
1013 | 0 | } else { |
1014 | 0 | Builder.createStoreOwnPropertyInst( |
1015 | 0 | value, Obj, key, IRBuilder::PropEnumerable::Yes); |
1016 | 0 | } |
1017 | 0 | haveSeenComputedProp = true; |
1018 | 0 | continue; |
1019 | 0 | } |
1020 | | |
1021 | 0 | llvh::StringRef keyStr = propertyKeyAsString(stringStorage, prop->_key); |
1022 | |
|
1023 | 0 | if (prop == protoProperty) { |
1024 | | // This is the first definition of __proto__. If we already used it |
1025 | | // as an object parent we just skip it, but otherwise we must |
1026 | | // explicitly set the parent now by calling \c |
1027 | | // HermesInternal.silentSetPrototypeOf(). |
1028 | 0 | if (!objectParent) { |
1029 | 0 | auto *parent = genExpression(prop->_value); |
1030 | |
|
1031 | 0 | IRBuilder::SaveRestore saveState{Builder}; |
1032 | 0 | Builder.setLocation(prop->_key->getDebugLoc()); |
1033 | |
|
1034 | 0 | genBuiltinCall( |
1035 | 0 | BuiltinMethod::HermesBuiltin_silentSetPrototypeOf, {Obj, parent}); |
1036 | 0 | } |
1037 | |
|
1038 | 0 | continue; |
1039 | 0 | } |
1040 | | |
1041 | 0 | PropertyValue *propValue = &propMap[keyStr]; |
1042 | | |
1043 | | // For any node that has a corresponding propValue, we need to ensure that |
1044 | | // the we insert either a placeholder or the final IR before the end of this |
1045 | | // iteration. |
1046 | 0 | auto checkState = llvh::make_scope_exit( |
1047 | 0 | [&] { assert(propValue->state != PropertyValue::None); }); |
1048 | |
|
1049 | 0 | auto *Key = Builder.getLiteralString(keyStr); |
1050 | |
|
1051 | 0 | auto maybeInsertPlaceholder = [&] { |
1052 | 0 | if (propValue->state == PropertyValue::None) { |
1053 | | // This value is going to be overwritten, but insert a placeholder in |
1054 | | // order to maintain insertion order. |
1055 | 0 | if (haveSeenComputedProp) { |
1056 | 0 | Builder.createStoreOwnPropertyInst( |
1057 | 0 | Builder.getLiteralNull(), |
1058 | 0 | Obj, |
1059 | 0 | Key, |
1060 | 0 | IRBuilder::PropEnumerable::Yes); |
1061 | 0 | } else { |
1062 | 0 | Builder.createStoreNewOwnPropertyInst( |
1063 | 0 | Builder.getLiteralNull(), |
1064 | 0 | Obj, |
1065 | 0 | Key, |
1066 | 0 | IRBuilder::PropEnumerable::Yes); |
1067 | 0 | } |
1068 | 0 | propValue->state = PropertyValue::Placeholder; |
1069 | 0 | } |
1070 | 0 | }; |
1071 | |
|
1072 | 0 | if (prop->_kind->str() == "get" || prop->_kind->str() == "set") { |
1073 | | // If we already generated it, skip. |
1074 | 0 | if (propValue->state == PropertyValue::IRGenerated) |
1075 | 0 | continue; |
1076 | | |
1077 | 0 | if (!propValue->isAccessor) { |
1078 | 0 | maybeInsertPlaceholder(); |
1079 | 0 | continue; |
1080 | 0 | } |
1081 | | |
1082 | 0 | Value *getter = Builder.getLiteralUndefined(); |
1083 | 0 | Value *setter = Builder.getLiteralUndefined(); |
1084 | |
|
1085 | 0 | if (propValue->getterNode) { |
1086 | 0 | getter = genExpression( |
1087 | 0 | propValue->getterNode, |
1088 | 0 | Builder.createIdentifier("get " + keyStr.str())); |
1089 | 0 | } |
1090 | |
|
1091 | 0 | if (propValue->setterNode) { |
1092 | 0 | setter = genExpression( |
1093 | 0 | propValue->setterNode, |
1094 | 0 | Builder.createIdentifier("set " + keyStr.str())); |
1095 | 0 | } |
1096 | |
|
1097 | 0 | Builder.createStoreGetterSetterInst( |
1098 | 0 | getter, setter, Obj, Key, IRBuilder::PropEnumerable::Yes); |
1099 | |
|
1100 | 0 | propValue->state = PropertyValue::IRGenerated; |
1101 | |
|
1102 | 0 | continue; |
1103 | 0 | } |
1104 | | |
1105 | | // Always generate the values, even if we don't need it, for the side |
1106 | | // effects. |
1107 | 0 | auto value = genExpression(prop->_value, Builder.createIdentifier(keyStr)); |
1108 | | |
1109 | | // Only store the value if it won't be overwritten. |
1110 | 0 | if (propMap[keyStr].valueNode == prop->_value) { |
1111 | 0 | assert( |
1112 | 0 | propValue->state != PropertyValue::IRGenerated && |
1113 | 0 | "IR can only be generated once"); |
1114 | 0 | if (haveSeenComputedProp || |
1115 | 0 | propValue->state == PropertyValue::Placeholder) { |
1116 | 0 | Builder.createStoreOwnPropertyInst( |
1117 | 0 | value, Obj, Key, IRBuilder::PropEnumerable::Yes); |
1118 | 0 | } else { |
1119 | 0 | Builder.createStoreNewOwnPropertyInst( |
1120 | 0 | value, Obj, Key, IRBuilder::PropEnumerable::Yes); |
1121 | 0 | } |
1122 | 0 | propValue->state = PropertyValue::IRGenerated; |
1123 | 0 | } else { |
1124 | 0 | maybeInsertPlaceholder(); |
1125 | 0 | } |
1126 | 0 | } |
1127 | | |
1128 | | // Return the newly allocated object (because this is an expression, not a |
1129 | | // statement). |
1130 | 0 | return Obj; |
1131 | 0 | } |
1132 | | |
1133 | 251 | Value *ESTreeIRGen::genSequenceExpr(ESTree::SequenceExpressionNode *Sq) { |
1134 | 251 | Value *result = Builder.getLiteralUndefined(); |
1135 | | |
1136 | | // Generate all expressions in the sequence, but take only the last one. |
1137 | 544 | for (auto &Ex : Sq->_expressions) { |
1138 | 544 | result = genExpression(&Ex); |
1139 | 544 | } |
1140 | | |
1141 | 251 | return result; |
1142 | 251 | } |
1143 | | |
1144 | 0 | Value *ESTreeIRGen::genYieldExpr(ESTree::YieldExpressionNode *Y) { |
1145 | 0 | assert(!Y->_delegate && "must use genYieldStarExpr for yield*"); |
1146 | | |
1147 | 0 | Value *value = Y->_argument ? genExpression(Y->_argument) |
1148 | 0 | : Builder.getLiteralUndefined(); |
1149 | 0 | return genYieldOrAwaitExpr(value); |
1150 | 0 | } |
1151 | | |
1152 | 0 | Value *ESTreeIRGen::genAwaitExpr(ESTree::AwaitExpressionNode *A) { |
1153 | 0 | Value *value = genExpression(A->_argument); |
1154 | 0 | return genYieldOrAwaitExpr(value); |
1155 | 0 | } |
1156 | | |
1157 | 0 | Value *ESTreeIRGen::genYieldOrAwaitExpr(Value *value) { |
1158 | 0 | auto *bb = Builder.getInsertionBlock(); |
1159 | 0 | auto *next = Builder.createBasicBlock(bb->getParent()); |
1160 | |
|
1161 | 0 | auto *resumeIsReturn = |
1162 | 0 | Builder.createAllocStackInst(genAnonymousLabelName("isReturn")); |
1163 | |
|
1164 | 0 | Builder.createSaveAndYieldInst(value, next); |
1165 | 0 | Builder.setInsertionBlock(next); |
1166 | 0 | return genResumeGenerator( |
1167 | 0 | GenFinally::Yes, |
1168 | 0 | resumeIsReturn, |
1169 | 0 | Builder.createBasicBlock(bb->getParent())); |
1170 | 0 | } |
1171 | | |
1172 | | /// Generate the code for `yield* value`. |
1173 | | /// We use some stack locations to store state while iterating: |
1174 | | /// - received (the value passed by the user to .next(), etc) |
1175 | | /// - result (the final result of the yield* expression) |
1176 | | /// |
1177 | | /// iteratorRecord stores the iterator which we are iterating over. |
1178 | | /// |
1179 | | /// Final IR has the following basic blocks for normal control flow: |
1180 | | /// |
1181 | | /// getNext: Get the next value from the iterator. |
1182 | | /// - Call next() on the iteratorRecord and stores to `result` |
1183 | | /// - If done, go to exit |
1184 | | /// - Otherwise, go to body |
1185 | | /// |
1186 | | /// resume: Runs the ResumeGenerator instruction. |
1187 | | /// - Code for `finally` is also emitted here. |
1188 | | /// |
1189 | | /// body: Yield the result of the next() call |
1190 | | /// - Calls HermesInternal.generatorSetDelegated so that the result is not |
1191 | | /// wrapped by the VM in an IterResult object. |
1192 | | /// |
1193 | | /// exit: Returns `result` which should have the final results stored in it. |
1194 | | /// |
1195 | | /// When the user calls `.return`, the finalizer is executed to call |
1196 | | /// `iteratorRecord.return` if it exists. The code for that is contained within |
1197 | | /// the SurroundingTry. If the .return function is defined, it is called and the |
1198 | | /// 'done' property of the result of the call is used to either branch back to |
1199 | | /// the 'resume' block or to propagate the return. |
1200 | | /// |
1201 | | /// When the user calls '.throw', the code in emitHandler is executed. All |
1202 | | /// generators used as delegates must have a .throw() method, so that is checked |
1203 | | /// for and called. The result is then used to either resume if not done, or to |
1204 | | /// return immediately by branching to the 'exit' block. |
1205 | 0 | Value *ESTreeIRGen::genYieldStarExpr(ESTree::YieldExpressionNode *Y) { |
1206 | 0 | assert(Y->_delegate && "must use genYieldExpr for yield"); |
1207 | 0 | auto *function = Builder.getInsertionBlock()->getParent(); |
1208 | 0 | auto *getNextBlock = Builder.createBasicBlock(function); |
1209 | 0 | auto *bodyBlock = Builder.createBasicBlock(function); |
1210 | 0 | auto *exitBlock = Builder.createBasicBlock(function); |
1211 | | |
1212 | | // Calls ResumeGenerator and returns or throws if requested. |
1213 | 0 | auto *resumeBB = Builder.createBasicBlock(function); |
1214 | |
|
1215 | 0 | auto *exprValue = genExpression(Y->_argument); |
1216 | 0 | IteratorRecordSlow iteratorRecord = emitGetIteratorSlow(exprValue); |
1217 | | |
1218 | | // The "received" value when the user resumes the generator. |
1219 | | // Initialized to undefined on the first run, then stored to immediately |
1220 | | // following any genResumeGenerator. |
1221 | 0 | auto *received = |
1222 | 0 | Builder.createAllocStackInst(genAnonymousLabelName("received")); |
1223 | 0 | Builder.createStoreStackInst(Builder.getLiteralUndefined(), received); |
1224 | | |
1225 | | // The "isReturn" value when the user resumes the generator. |
1226 | | // Stored to immediately following any genResumeGenerator. |
1227 | 0 | auto *resumeIsReturn = |
1228 | 0 | Builder.createAllocStackInst(genAnonymousLabelName("isReturn")); |
1229 | | |
1230 | | // The final result of the `yield*` expression. |
1231 | | // This can be set from either the body or the handler, so it is placed |
1232 | | // in the stack to allow populating it from anywhere. |
1233 | 0 | auto *result = Builder.createAllocStackInst(genAnonymousLabelName("result")); |
1234 | |
|
1235 | 0 | Builder.createBranchInst(getNextBlock); |
1236 | | |
1237 | | // 7.a.i. Let innerResult be ? Call( |
1238 | | // iteratorRecord.[[NextMethod]], |
1239 | | // iteratorRecord.[[Iterator]], |
1240 | | // <received.[[Value]]> |
1241 | | // ) |
1242 | | // Avoid using emitIteratorNext here because the spec does not. |
1243 | 0 | Builder.setInsertionBlock(getNextBlock); |
1244 | 0 | auto *nextResult = Builder.createCallInst( |
1245 | 0 | CallInst::kNoTextifiedCallee, |
1246 | 0 | iteratorRecord.nextMethod, |
1247 | 0 | iteratorRecord.iterator, |
1248 | 0 | {Builder.createLoadStackInst(received)}); |
1249 | 0 | emitEnsureObject(nextResult, "iterator.next() did not return an object"); |
1250 | |
|
1251 | 0 | Builder.createStoreStackInst(nextResult, result); |
1252 | 0 | auto *done = emitIteratorCompleteSlow(nextResult); |
1253 | 0 | Builder.createCondBranchInst(done, exitBlock, bodyBlock); |
1254 | |
|
1255 | 0 | Builder.setInsertionBlock(bodyBlock); |
1256 | |
|
1257 | 0 | emitTryCatchScaffolding( |
1258 | 0 | getNextBlock, |
1259 | | // emitBody. |
1260 | 0 | [this, |
1261 | 0 | Y, |
1262 | 0 | resumeIsReturn, |
1263 | 0 | getNextBlock, |
1264 | 0 | resumeBB, |
1265 | 0 | nextResult, |
1266 | 0 | received, |
1267 | 0 | &iteratorRecord]() { |
1268 | | // Generate IR for the body of Try |
1269 | 0 | SurroundingTry thisTry{ |
1270 | 0 | curFunction(), |
1271 | 0 | Y, |
1272 | 0 | {}, |
1273 | 0 | [this, resumeBB, received, &iteratorRecord]( |
1274 | 0 | ESTree::Node *, ControlFlowChange cfc, BasicBlock *) { |
1275 | 0 | if (cfc == ControlFlowChange::Break) { |
1276 | | // This finalizer block is executed upon early return during |
1277 | | // the yield*, which happens when the user requests a .return(). |
1278 | 0 | auto *function = Builder.getFunction(); |
1279 | 0 | auto *haveReturnBB = Builder.createBasicBlock(function); |
1280 | 0 | auto *noReturnBB = Builder.createBasicBlock(function); |
1281 | 0 | auto *isDoneBB = Builder.createBasicBlock(function); |
1282 | 0 | auto *isNotDoneBB = Builder.createBasicBlock(function); |
1283 | | |
1284 | | // Check if "returnMethod" is undefined. |
1285 | 0 | auto *returnMethod = genBuiltinCall( |
1286 | 0 | BuiltinMethod::HermesBuiltin_getMethod, |
1287 | 0 | {iteratorRecord.iterator, |
1288 | 0 | Builder.getLiteralString("return")}); |
1289 | 0 | Builder.createCompareBranchInst( |
1290 | 0 | returnMethod, |
1291 | 0 | Builder.getLiteralUndefined(), |
1292 | 0 | BinaryOperatorInst::OpKind::StrictlyEqualKind, |
1293 | 0 | noReturnBB, |
1294 | 0 | haveReturnBB); |
1295 | |
|
1296 | 0 | Builder.setInsertionBlock(haveReturnBB); |
1297 | | // iv. Let innerReturnResult be |
1298 | | // ? Call(return, iterator, received.[[Value]]). |
1299 | 0 | auto *innerReturnResult = Builder.createCallInst( |
1300 | 0 | CallInst::kNoTextifiedCallee, |
1301 | 0 | returnMethod, |
1302 | 0 | iteratorRecord.iterator, |
1303 | 0 | {Builder.createLoadStackInst(received)}); |
1304 | | // vi. If Type(innerReturnResult) is not Object, |
1305 | | // throw a TypeError exception. |
1306 | 0 | emitEnsureObject( |
1307 | 0 | innerReturnResult, |
1308 | 0 | "iterator.return() did not return an object"); |
1309 | | // vii. Let done be ? IteratorComplete(innerReturnResult). |
1310 | 0 | auto *done = emitIteratorCompleteSlow(innerReturnResult); |
1311 | 0 | Builder.createCondBranchInst(done, isDoneBB, isNotDoneBB); |
1312 | |
|
1313 | 0 | Builder.setInsertionBlock(isDoneBB); |
1314 | | // viii. 1. Let value be ? IteratorValue(innerReturnResult). |
1315 | 0 | auto *value = emitIteratorValueSlow(innerReturnResult); |
1316 | 0 | genFinallyBeforeControlChange( |
1317 | 0 | curFunction()->surroundingTry, |
1318 | 0 | nullptr, |
1319 | 0 | ControlFlowChange::Break); |
1320 | | // viii. 2. Return Completion |
1321 | | // { [[Type]]: return, [[Value]]: value, [[Target]]: empty }. |
1322 | 0 | Builder.createReturnInst(value); |
1323 | | |
1324 | | // x. Else, set received to GeneratorYield(innerReturnResult). |
1325 | 0 | Builder.setInsertionBlock(isNotDoneBB); |
1326 | 0 | genBuiltinCall( |
1327 | 0 | BuiltinMethod::HermesBuiltin_generatorSetDelegated, {}); |
1328 | 0 | Builder.createSaveAndYieldInst(innerReturnResult, resumeBB); |
1329 | | |
1330 | | // If return is undefined, return Completion(received). |
1331 | 0 | Builder.setInsertionBlock(noReturnBB); |
1332 | 0 | } |
1333 | 0 | }}; |
1334 | | |
1335 | | // The primary call path for yielding the next result. |
1336 | 0 | genBuiltinCall(BuiltinMethod::HermesBuiltin_generatorSetDelegated, {}); |
1337 | 0 | Builder.createSaveAndYieldInst(nextResult, resumeBB); |
1338 | | |
1339 | | // Note that resumeBB was created above to allow all SaveAndYield insts |
1340 | | // to have the same resume point (including SaveAndYield in the catch |
1341 | | // handler), but we must populate it inside the scaffolding so that the |
1342 | | // SurroundingTry is correct for the genFinallyBeforeControlChange |
1343 | | // call emitted by genResumeGenerator. |
1344 | 0 | Builder.setInsertionBlock(resumeBB); |
1345 | 0 | genResumeGenerator( |
1346 | 0 | GenFinally::Yes, resumeIsReturn, getNextBlock, received); |
1347 | | |
1348 | | // SaveAndYieldInst is a Terminator, but emitTryCatchScaffolding |
1349 | | // needs a block from which to Branch to the TryEnd instruction. |
1350 | | // Make a dummy block which can do that. |
1351 | 0 | Builder.setInsertionBlock( |
1352 | 0 | Builder.createBasicBlock(Builder.getFunction())); |
1353 | 0 | }, |
1354 | | // emitNormalCleanup. |
1355 | 0 | []() {}, |
1356 | | // emitHandler. |
1357 | 0 | [this, resumeBB, exitBlock, result, &iteratorRecord]( |
1358 | 0 | BasicBlock *getNextBlock) { |
1359 | 0 | auto *catchReg = Builder.createCatchInst(); |
1360 | |
|
1361 | 0 | auto *function = Builder.getFunction(); |
1362 | 0 | auto *hasThrowMethodBB = Builder.createBasicBlock(function); |
1363 | 0 | auto *noThrowMethodBB = Builder.createBasicBlock(function); |
1364 | 0 | auto *isDoneBB = Builder.createBasicBlock(function); |
1365 | 0 | auto *isNotDoneBB = Builder.createBasicBlock(function); |
1366 | | |
1367 | | // b.i. Let throw be ? GetMethod(iterator, "throw"). |
1368 | 0 | auto *throwMethod = genBuiltinCall( |
1369 | 0 | BuiltinMethod::HermesBuiltin_getMethod, |
1370 | 0 | {iteratorRecord.iterator, Builder.getLiteralString("throw")}); |
1371 | 0 | Builder.createCompareBranchInst( |
1372 | 0 | throwMethod, |
1373 | 0 | Builder.getLiteralUndefined(), |
1374 | 0 | BinaryOperatorInst::OpKind::StrictlyEqualKind, |
1375 | 0 | noThrowMethodBB, |
1376 | 0 | hasThrowMethodBB); |
1377 | | |
1378 | | // ii. If throw is not undefined, then |
1379 | 0 | Builder.setInsertionBlock(hasThrowMethodBB); |
1380 | | // ii. 1. Let innerResult be |
1381 | | // ? Call(throw, iterator, « received.[[Value]] »). |
1382 | | // ii. 3. NOTE: Exceptions from the inner iterator throw method are |
1383 | | // propagated. Normal completions from an inner throw method are |
1384 | | // processed similarly to an inner next. |
1385 | 0 | auto *innerResult = Builder.createCallInst( |
1386 | 0 | CallInst::kNoTextifiedCallee, |
1387 | 0 | throwMethod, |
1388 | 0 | iteratorRecord.iterator, |
1389 | 0 | {catchReg}); |
1390 | | // ii. 4. If Type(innerResult) is not Object, |
1391 | | // throw a TypeError exception. |
1392 | 0 | emitEnsureObject( |
1393 | 0 | innerResult, "iterator.throw() did not return an object"); |
1394 | | // ii. 5. Let done be ? IteratorComplete(innerResult). |
1395 | 0 | auto *throwDone = emitIteratorCompleteSlow(innerResult); |
1396 | 0 | Builder.createCondBranchInst(throwDone, isDoneBB, isNotDoneBB); |
1397 | | |
1398 | | // ii. 6. If done is true, then return ? IteratorValue(innerResult). |
1399 | 0 | Builder.setInsertionBlock(isDoneBB); |
1400 | 0 | Builder.createStoreStackInst(innerResult, result); |
1401 | 0 | Builder.createBranchInst(exitBlock); |
1402 | | |
1403 | | // ii. 8. Else, set received to GeneratorYield(innerResult). |
1404 | 0 | Builder.setInsertionBlock(isNotDoneBB); |
1405 | 0 | genBuiltinCall(BuiltinMethod::HermesBuiltin_generatorSetDelegated, {}); |
1406 | 0 | Builder.createSaveAndYieldInst(innerResult, resumeBB); |
1407 | | |
1408 | | // NOTE: If iterator does not have a throw method, this throw is |
1409 | | // going to terminate the yield* loop. But first we need to give |
1410 | | // iterator a chance to clean up. |
1411 | 0 | Builder.setInsertionBlock(noThrowMethodBB); |
1412 | 0 | emitIteratorCloseSlow(iteratorRecord, false); |
1413 | 0 | genBuiltinCall( |
1414 | 0 | BuiltinMethod::HermesBuiltin_throwTypeError, |
1415 | 0 | {Builder.getLiteralString( |
1416 | 0 | "yield* delegate must have a .throw() method")}); |
1417 | | // HermesInternal.throwTypeError will necessarily throw, but we need to |
1418 | | // have a terminator on this BB to allow proper optimization. |
1419 | 0 | Builder.createThrowInst(Builder.getLiteralUndefined()); |
1420 | 0 | }); |
1421 | |
|
1422 | 0 | Builder.setInsertionBlock(exitBlock); |
1423 | |
|
1424 | 0 | return emitIteratorValueSlow(Builder.createLoadStackInst(result)); |
1425 | 0 | } |
1426 | | |
1427 | | Value *ESTreeIRGen::genResumeGenerator( |
1428 | | GenFinally genFinally, |
1429 | | AllocStackInst *isReturn, |
1430 | | BasicBlock *nextBB, |
1431 | 10 | AllocStackInst *received) { |
1432 | 10 | auto *resume = Builder.createResumeGeneratorInst(isReturn); |
1433 | 10 | if (received) { |
1434 | 0 | Builder.createStoreStackInst(resume, received); |
1435 | 0 | } |
1436 | 10 | auto *retBB = |
1437 | 10 | Builder.createBasicBlock(Builder.getInsertionBlock()->getParent()); |
1438 | | |
1439 | 10 | Builder.createCondBranchInst( |
1440 | 10 | Builder.createLoadStackInst(isReturn), retBB, nextBB); |
1441 | | |
1442 | 10 | Builder.setInsertionBlock(retBB); |
1443 | 10 | if (received) { |
1444 | 0 | Builder.createStoreStackInst(resume, received); |
1445 | 0 | } |
1446 | 10 | if (genFinally == GenFinally::Yes) { |
1447 | 0 | genFinallyBeforeControlChange( |
1448 | 0 | curFunction()->surroundingTry, nullptr, ControlFlowChange::Break); |
1449 | 0 | } |
1450 | 10 | Builder.createReturnInst(resume); |
1451 | | |
1452 | 10 | Builder.setInsertionBlock(nextBB); |
1453 | 10 | return resume; |
1454 | 10 | } |
1455 | | |
1456 | 105k | Value *ESTreeIRGen::genBinaryExpression(ESTree::BinaryExpressionNode *bin) { |
1457 | | // Handle long chains of +/- non-recursively. |
1458 | 105k | if (bin->_operator->str() == "+" || bin->_operator->str() == "-") { |
1459 | 24.8k | auto list = linearizeLeft(bin, {"+", "-"}); |
1460 | | |
1461 | 24.8k | Value *LHS = genExpression(list[0]->_left); |
1462 | 172k | for (auto *e : list) { |
1463 | 172k | Value *RHS = genExpression(e->_right); |
1464 | 172k | Builder.setLocation(e->getDebugLoc()); |
1465 | 172k | auto cookie = instrumentIR_.preBinaryExpression(e, LHS, RHS); |
1466 | 172k | auto Kind = BinaryOperatorInst::parseOperator(e->_operator->str()); |
1467 | 172k | BinaryOperatorInst *result = |
1468 | 172k | Builder.createBinaryOperatorInst(LHS, RHS, Kind); |
1469 | 172k | LHS = instrumentIR_.postBinaryExpression(e, cookie, result, LHS, RHS); |
1470 | 172k | } |
1471 | | |
1472 | 24.8k | return LHS; |
1473 | 24.8k | } |
1474 | | |
1475 | 80.5k | Value *LHS = genExpression(bin->_left); |
1476 | 80.5k | Value *RHS = genExpression(bin->_right); |
1477 | 80.5k | auto cookie = instrumentIR_.preBinaryExpression(bin, LHS, RHS); |
1478 | | |
1479 | 80.5k | auto Kind = BinaryOperatorInst::parseOperator(bin->_operator->str()); |
1480 | | |
1481 | 80.5k | BinaryOperatorInst *result = Builder.createBinaryOperatorInst(LHS, RHS, Kind); |
1482 | 80.5k | return instrumentIR_.postBinaryExpression(bin, cookie, result, LHS, RHS); |
1483 | 105k | } |
1484 | | |
1485 | 64.2k | Value *ESTreeIRGen::genUnaryExpression(ESTree::UnaryExpressionNode *U) { |
1486 | 64.2k | auto kind = UnaryOperatorInst::parseOperator(U->_operator->str()); |
1487 | | |
1488 | | // Handle the delete unary expression. https://es5.github.io/#x11.4.1 |
1489 | 64.2k | if (kind == UnaryOperatorInst::OpKind::DeleteKind) { |
1490 | 50.4k | if (auto *memberExpr = |
1491 | 50.4k | llvh::dyn_cast<ESTree::MemberExpressionNode>(U->_argument)) { |
1492 | 2 | LLVM_DEBUG(llvh::dbgs() << "IRGen delete member expression.\n"); |
1493 | | |
1494 | 2 | return genMemberExpression(memberExpr, MemberExpressionOperation::Delete) |
1495 | 2 | .result; |
1496 | 2 | } |
1497 | | |
1498 | 50.4k | if (auto *memberExpr = llvh::dyn_cast<ESTree::OptionalMemberExpressionNode>( |
1499 | 50.4k | U->_argument)) { |
1500 | 0 | LLVM_DEBUG(llvh::dbgs() << "IRGen delete optional member expression.\n"); |
1501 | |
|
1502 | 0 | return genOptionalMemberExpression( |
1503 | 0 | memberExpr, nullptr, MemberExpressionOperation::Delete) |
1504 | 0 | .result; |
1505 | 0 | } |
1506 | | |
1507 | | // Check for "delete identifier". Note that deleting unqualified identifiers |
1508 | | // is prohibited in strict mode, so that case is handled earlier in the |
1509 | | // semantic validator. Here we are left to handle the non-strict mode case. |
1510 | 50.4k | if (auto *iden = llvh::dyn_cast<ESTree::IdentifierNode>(U->_argument)) { |
1511 | 50.0k | assert( |
1512 | 50.0k | !curFunction()->function->isStrictMode() && |
1513 | 50.0k | "delete identifier encountered in strict mode"); |
1514 | | // Check if this is a known variable. |
1515 | 50.0k | Identifier name = getNameFieldFromID(iden); |
1516 | 50.0k | auto *var = nameTable_.lookup(name); |
1517 | | |
1518 | 50.0k | if (!var || llvh::isa<GlobalObjectProperty>(var)) { |
1519 | | // If the variable doesn't exist or if it is global, we must generate |
1520 | | // a delete global property instruction. |
1521 | 22.3k | return Builder.createDeletePropertyInst( |
1522 | 22.3k | Builder.getGlobalObject(), Builder.getLiteralString(name)); |
1523 | 27.7k | } else { |
1524 | | // Otherwise it is a local variable which can't be deleted and we just |
1525 | | // return false. |
1526 | 27.7k | return Builder.getLiteralBool(false); |
1527 | 27.7k | } |
1528 | 50.0k | } |
1529 | | |
1530 | | // Generate the code for the delete operand. |
1531 | 395 | genExpression(U->_argument); |
1532 | | |
1533 | | // Deleting any value or a result of an expression returns True. |
1534 | 395 | return Builder.getLiteralBool(true); |
1535 | 50.4k | } |
1536 | | |
1537 | | // Need to handle the special case of "typeof <undefined variable>". |
1538 | 13.8k | if (kind == UnaryOperatorInst::OpKind::TypeofKind) { |
1539 | 3 | if (auto *id = llvh::dyn_cast<ESTree::IdentifierNode>(U->_argument)) { |
1540 | 0 | Value *argument = genIdentifierExpression(id, true); |
1541 | 0 | return Builder.createUnaryOperatorInst(argument, kind); |
1542 | 0 | } |
1543 | 3 | } |
1544 | | |
1545 | | // Generate the unary operand: |
1546 | 13.8k | Value *argument = genExpression(U->_argument); |
1547 | 13.8k | auto *cookie = instrumentIR_.preUnaryExpression(U, argument); |
1548 | | |
1549 | 13.8k | Value *result; |
1550 | 13.8k | if (kind == UnaryOperatorInst::OpKind::PlusKind) |
1551 | 298 | result = Builder.createAsNumberInst(argument); |
1552 | 13.5k | else |
1553 | 13.5k | result = Builder.createUnaryOperatorInst(argument, kind); |
1554 | 13.8k | return instrumentIR_.postUnaryExpression(U, cookie, result, argument); |
1555 | 13.8k | } |
1556 | | |
1557 | 0 | Value *ESTreeIRGen::genUpdateExpr(ESTree::UpdateExpressionNode *updateExpr) { |
1558 | 0 | LLVM_DEBUG(llvh::dbgs() << "IRGen update expression.\n"); |
1559 | 0 | bool isPrefix = updateExpr->_prefix; |
1560 | |
|
1561 | 0 | UnaryOperatorInst::OpKind opKind; |
1562 | 0 | if (updateExpr->_operator->str() == "++") { |
1563 | 0 | opKind = UnaryOperatorInst::OpKind::IncKind; |
1564 | 0 | } else if (updateExpr->_operator->str() == "--") { |
1565 | 0 | opKind = UnaryOperatorInst::OpKind::DecKind; |
1566 | 0 | } else { |
1567 | 0 | llvm_unreachable("Invalid update operator"); |
1568 | 0 | } |
1569 | | |
1570 | 0 | LReference lref = createLRef(updateExpr->_argument, false); |
1571 | | |
1572 | | // Load the original value. Postfix updates need to convert it toNumeric |
1573 | | // before Inc/Dec to ensure the updateExpr has the proper result value. |
1574 | 0 | Value *original = |
1575 | 0 | isPrefix ? lref.emitLoad() : Builder.createAsNumericInst(lref.emitLoad()); |
1576 | | |
1577 | | // Create the inc or dec. |
1578 | 0 | Value *result = Builder.createUnaryOperatorInst(original, opKind); |
1579 | | |
1580 | | // Store the result. |
1581 | 0 | lref.emitStore(result); |
1582 | | |
1583 | | // Depending on the prefixness return the previous value or the modified |
1584 | | // value. |
1585 | 0 | return (isPrefix ? result : original); |
1586 | 0 | } |
1587 | | |
1588 | | /// Extract a name hint from a LReference. |
1589 | 1.98k | static Identifier extractNameHint(const LReference &lref) { |
1590 | 1.98k | Identifier nameHint{}; |
1591 | 1.98k | if (auto *var = lref.castAsVariable()) { |
1592 | 3 | nameHint = var->getName(); |
1593 | 1.98k | } else if (auto *globProp = lref.castAsGlobalObjectProperty()) { |
1594 | 1.52k | nameHint = globProp->getName()->getValue(); |
1595 | 1.52k | } |
1596 | 1.98k | return nameHint; |
1597 | 1.98k | } |
1598 | | |
1599 | 1.98k | Value *ESTreeIRGen::genAssignmentExpr(ESTree::AssignmentExpressionNode *AE) { |
1600 | 1.98k | LLVM_DEBUG(llvh::dbgs() << "IRGen assignment operator.\n"); |
1601 | | |
1602 | 1.98k | auto opStr = AE->_operator->str(); |
1603 | | |
1604 | | // Handle nested normal assignments non-recursively. |
1605 | 1.98k | if (opStr == "=") { |
1606 | 1.97k | auto list = ESTree::linearizeRight(AE, {"="}); |
1607 | | |
1608 | | // Create an LReference for every assignment left side. |
1609 | 1.97k | llvh::SmallVector<LReference, 1> lrefs; |
1610 | 1.97k | lrefs.reserve(list.size()); |
1611 | 2.30k | for (auto *e : list) { |
1612 | 2.30k | lrefs.push_back(createLRef(e->_left, false)); |
1613 | 2.30k | } |
1614 | | |
1615 | 1.97k | Value *RHS = nullptr; |
1616 | 1.97k | auto lrefIterator = lrefs.end(); |
1617 | 2.30k | for (auto *e : llvh::make_range(list.rbegin(), list.rend())) { |
1618 | 2.30k | --lrefIterator; |
1619 | 2.30k | if (!RHS) |
1620 | 1.97k | RHS = genExpression(e->_right, extractNameHint(*lrefIterator)); |
1621 | 2.30k | Builder.setLocation(e->getDebugLoc()); |
1622 | 2.30k | auto *cookie = instrumentIR_.preAssignment(e, nullptr, RHS); |
1623 | 2.30k | RHS = instrumentIR_.postAssignment(e, cookie, RHS, nullptr, RHS); |
1624 | 2.30k | lrefIterator->emitStore(RHS); |
1625 | 2.30k | } |
1626 | | |
1627 | 1.97k | return RHS; |
1628 | 1.97k | } |
1629 | | |
1630 | 12 | auto AssignmentKind = BinaryOperatorInst::parseAssignmentOperator(opStr); |
1631 | | |
1632 | 12 | LReference lref = createLRef(AE->_left, false); |
1633 | 12 | Identifier nameHint = extractNameHint(lref); |
1634 | | |
1635 | 12 | Value *result; |
1636 | 12 | if (AssignmentKind == BinaryOperatorInst::OpKind::AssignShortCircuitOrKind || |
1637 | 12 | AssignmentKind == BinaryOperatorInst::OpKind::AssignShortCircuitAndKind || |
1638 | 12 | AssignmentKind == BinaryOperatorInst::OpKind::AssignNullishCoalesceKind) { |
1639 | 8 | return genLogicalAssignmentExpr(AE, AssignmentKind, lref, nameHint); |
1640 | 8 | } |
1641 | | |
1642 | 4 | assert(AssignmentKind != BinaryOperatorInst::OpKind::IdentityKind); |
1643 | | // Section 11.13.1 specifies that we should first load the |
1644 | | // LHS before materializing the RHS. Unlike in C, this |
1645 | | // code is well defined: "x+= x++". |
1646 | | // https://es5.github.io/#x11.13.1 |
1647 | 4 | auto V = lref.emitLoad(); |
1648 | 4 | auto *RHS = genExpression(AE->_right, nameHint); |
1649 | 4 | auto *cookie = instrumentIR_.preAssignment(AE, V, RHS); |
1650 | 4 | result = Builder.createBinaryOperatorInst(V, RHS, AssignmentKind); |
1651 | 4 | result = instrumentIR_.postAssignment(AE, cookie, result, V, RHS); |
1652 | | |
1653 | 4 | lref.emitStore(result); |
1654 | | |
1655 | | // Return the value that we stored as the result of the expression. |
1656 | 4 | return result; |
1657 | 4 | } |
1658 | | |
1659 | 388 | Value *ESTreeIRGen::genRegExpLiteral(ESTree::RegExpLiteralNode *RE) { |
1660 | 388 | LLVM_DEBUG(llvh::dbgs() << "IRGen reg exp literal.\n"); |
1661 | 388 | LLVM_DEBUG( |
1662 | 388 | llvh::dbgs() << "Loading regexp Literal \"" << RE->_pattern->str() |
1663 | 388 | << " / " << RE->_flags->str() << "\"\n"); |
1664 | 388 | auto exp = Builder.createRegExpInst( |
1665 | 388 | Identifier::getFromPointer(RE->_pattern), |
1666 | 388 | Identifier::getFromPointer(RE->_flags)); |
1667 | | |
1668 | 388 | auto ®exp = Builder.getModule()->getContext().getCompiledRegExp( |
1669 | 388 | RE->_pattern, RE->_flags); |
1670 | | |
1671 | 388 | if (regexp.getMapping().size()) { |
1672 | 0 | auto &mapping = regexp.getMapping(); |
1673 | 0 | HBCAllocObjectFromBufferInst::ObjectPropertyMap propMap; |
1674 | 0 | for (auto &identifier : regexp.getOrderedGroupNames()) { |
1675 | 0 | std::string converted; |
1676 | 0 | convertUTF16ToUTF8WithSingleSurrogates(converted, identifier); |
1677 | 0 | auto *key = Builder.getLiteralString(converted); |
1678 | 0 | auto groupIdxRes = mapping.find(identifier); |
1679 | 0 | assert( |
1680 | 0 | groupIdxRes != mapping.end() && |
1681 | 0 | "identifier not found in named groups"); |
1682 | 0 | auto groupIdx = groupIdxRes->second; |
1683 | 0 | auto *val = Builder.getLiteralNumber(groupIdx); |
1684 | 0 | propMap.emplace_back(key, val); |
1685 | 0 | } |
1686 | 0 | auto sz = mapping.size(); |
1687 | |
|
1688 | 0 | auto literalObj = Builder.createHBCAllocObjectFromBufferInst(propMap, sz); |
1689 | |
|
1690 | 0 | Value *params[] = {exp, literalObj}; |
1691 | 0 | Builder.createCallBuiltinInst( |
1692 | 0 | BuiltinMethod::HermesBuiltin_initRegexNamedGroups, params); |
1693 | 0 | } |
1694 | | |
1695 | 388 | return exp; |
1696 | 388 | } |
1697 | | |
1698 | | Value *ESTreeIRGen::genLogicalAssignmentExpr( |
1699 | | ESTree::AssignmentExpressionNode *AE, |
1700 | | BinaryOperatorInst::OpKind AssignmentKind, |
1701 | | LReference lref, |
1702 | 8 | Identifier nameHint) { |
1703 | | // Logical assignment expressions must use short-circuiting logic. |
1704 | | // BB which actually performs the assignment. |
1705 | 8 | BasicBlock *assignBB = Builder.createBasicBlock(Builder.getFunction()); |
1706 | | // BB which simply continues without performing the assignment. |
1707 | 8 | BasicBlock *continueBB = Builder.createBasicBlock(Builder.getFunction()); |
1708 | 8 | auto *lhs = lref.emitLoad(); |
1709 | | |
1710 | 8 | PhiInst::ValueListType values; |
1711 | 8 | PhiInst::BasicBlockListType blocks; |
1712 | | |
1713 | 8 | values.push_back(lhs); |
1714 | 8 | blocks.push_back(Builder.getInsertionBlock()); |
1715 | | |
1716 | 8 | switch (AssignmentKind) { |
1717 | 8 | case BinaryOperatorInst::OpKind::AssignShortCircuitOrKind: |
1718 | 8 | Builder.createCondBranchInst(lhs, continueBB, assignBB); |
1719 | 8 | break; |
1720 | 0 | case BinaryOperatorInst::OpKind::AssignShortCircuitAndKind: |
1721 | 0 | Builder.createCondBranchInst(lhs, assignBB, continueBB); |
1722 | 0 | break; |
1723 | 0 | case BinaryOperatorInst::OpKind::AssignNullishCoalesceKind: |
1724 | 0 | Builder.createCondBranchInst( |
1725 | 0 | Builder.createBinaryOperatorInst( |
1726 | 0 | lhs, |
1727 | 0 | Builder.getLiteralNull(), |
1728 | 0 | BinaryOperatorInst::OpKind::EqualKind), |
1729 | 0 | assignBB, |
1730 | 0 | continueBB); |
1731 | 0 | break; |
1732 | 0 | default: |
1733 | 0 | llvm_unreachable("invalid AssignmentKind in this branch"); |
1734 | 8 | } |
1735 | | |
1736 | 8 | Builder.setInsertionBlock(assignBB); |
1737 | 8 | auto *rhs = genExpression(AE->_right, nameHint); |
1738 | 8 | auto *cookie = instrumentIR_.preAssignment(AE, lhs, rhs); |
1739 | 8 | auto *result = instrumentIR_.postAssignment(AE, cookie, rhs, lhs, rhs); |
1740 | 8 | lref.emitStore(result); |
1741 | 8 | values.push_back(result); |
1742 | 8 | blocks.push_back(Builder.getInsertionBlock()); |
1743 | 8 | Builder.createBranchInst(continueBB); |
1744 | | |
1745 | 8 | Builder.setInsertionBlock(continueBB); |
1746 | | // Final result is either the original value or the value assigned, |
1747 | | // depending on which branch was taken. |
1748 | 8 | return Builder.createPhiInst(std::move(values), std::move(blocks)); |
1749 | 8 | } |
1750 | | |
1751 | 5.06k | Value *ESTreeIRGen::genConditionalExpr(ESTree::ConditionalExpressionNode *C) { |
1752 | 5.06k | auto parentFunc = Builder.getInsertionBlock()->getParent(); |
1753 | | |
1754 | 5.06k | PhiInst::ValueListType values; |
1755 | 5.06k | PhiInst::BasicBlockListType blocks; |
1756 | | |
1757 | 5.06k | auto alternateBlock = Builder.createBasicBlock(parentFunc); |
1758 | 5.06k | auto consequentBlock = Builder.createBasicBlock(parentFunc); |
1759 | 5.06k | auto continueBlock = Builder.createBasicBlock(parentFunc); |
1760 | | |
1761 | | // Implement the ternary operator using control flow. We must use control |
1762 | | // flow because the expressions may have side effects. |
1763 | 5.06k | genExpressionBranch(C->_test, consequentBlock, alternateBlock, nullptr); |
1764 | | |
1765 | | // The 'then' side: |
1766 | 5.06k | Builder.setInsertionBlock(consequentBlock); |
1767 | | |
1768 | 5.06k | values.push_back(genExpression(C->_consequent)); |
1769 | 5.06k | blocks.push_back(Builder.getInsertionBlock()); |
1770 | 5.06k | Builder.createBranchInst(continueBlock); |
1771 | | |
1772 | | // The 'else' side: |
1773 | 5.06k | Builder.setInsertionBlock(alternateBlock); |
1774 | 5.06k | values.push_back(genExpression(C->_alternate)); |
1775 | 5.06k | blocks.push_back(Builder.getInsertionBlock()); |
1776 | 5.06k | Builder.createBranchInst(continueBlock); |
1777 | | |
1778 | | // Continue: |
1779 | 5.06k | Builder.setInsertionBlock(continueBlock); |
1780 | 5.06k | return Builder.createPhiInst(values, blocks); |
1781 | 5.06k | } |
1782 | | |
1783 | | Value *ESTreeIRGen::genIdentifierExpression( |
1784 | | ESTree::IdentifierNode *Iden, |
1785 | 439k | bool afterTypeOf) { |
1786 | 439k | LLVM_DEBUG( |
1787 | 439k | llvh::dbgs() << "Looking for identifier \"" << Iden->_name << "\"\n"); |
1788 | | |
1789 | | // 'arguments' is an array-like object holding all function arguments. |
1790 | | // If one of the parameters is called "arguments" then it shadows the |
1791 | | // arguments keyword. |
1792 | 439k | if (Iden->_name->str() == "arguments" && |
1793 | 439k | !nameTable_.count(getNameFieldFromID(Iden))) { |
1794 | | // If it is captured, we must use the captured value. |
1795 | 52 | if (curFunction()->capturedArguments) { |
1796 | 0 | return Builder.createLoadFrameInst( |
1797 | 0 | curFunction()->capturedArguments, currentIRScope_); |
1798 | 0 | } |
1799 | | |
1800 | 52 | return curFunction()->createArgumentsInst; |
1801 | 52 | } |
1802 | | |
1803 | | // Lookup variable name. |
1804 | 439k | auto StrName = getNameFieldFromID(Iden); |
1805 | | |
1806 | 439k | auto *Var = ensureVariableExists(Iden); |
1807 | | |
1808 | | // For uses of undefined as the global property, we make an optimization |
1809 | | // to always return undefined constant. |
1810 | 439k | if (llvh::isa<GlobalObjectProperty>(Var) && StrName.str() == "undefined") { |
1811 | 0 | return Builder.getLiteralUndefined(); |
1812 | 0 | } |
1813 | | |
1814 | 439k | LLVM_DEBUG( |
1815 | 439k | llvh::dbgs() << "Found variable " << StrName << " in function \"" |
1816 | 439k | << (llvh::isa<GlobalObjectProperty>(Var) |
1817 | 439k | ? llvh::StringRef("global") |
1818 | 439k | : cast<Variable>(Var) |
1819 | 439k | ->getParent() |
1820 | 439k | ->getFunction() |
1821 | 439k | ->getInternalNameStr()) |
1822 | 439k | << "\"\n"); |
1823 | | |
1824 | | // Typeof <variable> does not throw. |
1825 | 439k | return emitLoad(Var, afterTypeOf); |
1826 | 439k | } |
1827 | | |
1828 | 0 | Value *ESTreeIRGen::genMetaProperty(ESTree::MetaPropertyNode *MP) { |
1829 | | // Recognize "new.target" |
1830 | 0 | if (cast<ESTree::IdentifierNode>(MP->_meta)->_name->str() == "new") { |
1831 | 0 | if (cast<ESTree::IdentifierNode>(MP->_property)->_name->str() == "target") { |
1832 | 0 | Value *value; |
1833 | |
|
1834 | 0 | if (curFunction()->function->getDefinitionKind() == |
1835 | 0 | Function::DefinitionKind::ES6Arrow || |
1836 | 0 | curFunction()->function->getDefinitionKind() == |
1837 | 0 | Function::DefinitionKind::ES6Method) { |
1838 | 0 | value = curFunction()->capturedNewTarget; |
1839 | 0 | } else { |
1840 | 0 | value = Builder.createGetNewTargetInst(); |
1841 | 0 | } |
1842 | | |
1843 | | // If it is a variable, we must issue a load. |
1844 | 0 | if (auto *V = llvh::dyn_cast<Variable>(value)) |
1845 | 0 | return Builder.createLoadFrameInst(V, currentIRScope_); |
1846 | | |
1847 | 0 | return value; |
1848 | 0 | } |
1849 | 0 | } |
1850 | | |
1851 | 0 | llvm_unreachable("invalid MetaProperty"); |
1852 | 0 | } |
1853 | | |
1854 | 50 | Value *ESTreeIRGen::genNewExpr(ESTree::NewExpressionNode *N) { |
1855 | 50 | LLVM_DEBUG(llvh::dbgs() << "IRGen 'new' statement/expression.\n"); |
1856 | | |
1857 | 50 | Value *callee = genExpression(N->_callee); |
1858 | | |
1859 | 50 | bool hasSpread = false; |
1860 | 50 | for (auto &arg : N->_arguments) { |
1861 | 1 | if (llvh::isa<ESTree::SpreadElementNode>(&arg)) { |
1862 | 1 | hasSpread = true; |
1863 | 1 | } |
1864 | 1 | } |
1865 | | |
1866 | 50 | if (!hasSpread) { |
1867 | 49 | ConstructInst::ArgumentList args; |
1868 | 49 | for (auto &arg : N->_arguments) { |
1869 | 0 | args.push_back(genExpression(&arg)); |
1870 | 0 | } |
1871 | | |
1872 | 49 | return Builder.createConstructInst(callee, callee, args); |
1873 | 49 | } |
1874 | | |
1875 | | // Otherwise, there exists a spread argument, so the number of arguments |
1876 | | // is variable. |
1877 | | // Generate IR for this by creating an array and populating it with the |
1878 | | // arguments, then calling HermesInternal.apply. |
1879 | 1 | auto *args = genArrayFromElements(N->_arguments); |
1880 | | |
1881 | 1 | return genBuiltinCall(BuiltinMethod::HermesBuiltin_apply, {callee, args}); |
1882 | 50 | } |
1883 | | |
1884 | | Value *ESTreeIRGen::genLogicalExpression( |
1885 | 2.00k | ESTree::LogicalExpressionNode *logical) { |
1886 | 2.00k | auto opStr = logical->_operator->str(); |
1887 | 2.00k | LLVM_DEBUG(llvh::dbgs() << "IRGen of short circuiting: " << opStr << ".\n"); |
1888 | | |
1889 | 2.00k | enum class Kind { |
1890 | 2.00k | And, // && |
1891 | 2.00k | Or, // || |
1892 | 2.00k | Coalesce, // ?? |
1893 | 2.00k | }; |
1894 | | |
1895 | 2.00k | Kind kind; |
1896 | | |
1897 | 2.00k | if (opStr == "&&") { |
1898 | 10 | kind = Kind::And; |
1899 | 1.99k | } else if (opStr == "||") { |
1900 | 1.85k | kind = Kind::Or; |
1901 | 1.85k | } else if (opStr == "??") { |
1902 | 142 | kind = Kind::Coalesce; |
1903 | 142 | } else { |
1904 | 0 | llvm_unreachable("Invalid update operator"); |
1905 | 0 | } |
1906 | | |
1907 | | // Generate a new temporary stack allocation. |
1908 | 2.00k | auto tempVarName = genAnonymousLabelName("logical"); |
1909 | 2.00k | auto parentFunc = Builder.getInsertionBlock()->getParent(); |
1910 | 2.00k | auto tempVar = Builder.createAllocStackInst(tempVarName); |
1911 | | |
1912 | 2.00k | auto evalRHSBlock = Builder.createBasicBlock(parentFunc); |
1913 | 2.00k | auto continueBlock = Builder.createBasicBlock(parentFunc); |
1914 | | |
1915 | 2.00k | auto LHS = genExpression(logical->_left); |
1916 | | |
1917 | | // Store the LHS value of the expression in preparation for the case where we |
1918 | | // won't need to evaluate the RHS side of the expression. In that case, we |
1919 | | // jump to continueBlock, which returns tempVar. |
1920 | 2.00k | Builder.createStoreStackInst(LHS, tempVar); |
1921 | | |
1922 | | // Notice that instead of negating the condition we swap the operands of the |
1923 | | // branch. |
1924 | 2.00k | switch (kind) { |
1925 | 10 | case Kind::And: |
1926 | | // Evaluate RHS only when the LHS is true. |
1927 | 10 | Builder.createCondBranchInst(LHS, evalRHSBlock, continueBlock); |
1928 | 10 | break; |
1929 | 1.85k | case Kind::Or: |
1930 | | // Evaluate RHS only when the LHS is false. |
1931 | 1.85k | Builder.createCondBranchInst(LHS, continueBlock, evalRHSBlock); |
1932 | 1.85k | break; |
1933 | 142 | case Kind::Coalesce: |
1934 | | // Evaluate RHS only if the value is undefined or null. |
1935 | | // Use == instead of === to account for both values at once. |
1936 | 142 | Builder.createCondBranchInst( |
1937 | 142 | Builder.createBinaryOperatorInst( |
1938 | 142 | LHS, |
1939 | 142 | Builder.getLiteralNull(), |
1940 | 142 | BinaryOperatorInst::OpKind::EqualKind), |
1941 | 142 | evalRHSBlock, |
1942 | 142 | continueBlock); |
1943 | | |
1944 | 142 | break; |
1945 | 2.00k | } |
1946 | | |
1947 | | // Continue the evaluation of the right-hand-side of the expression. |
1948 | 2.00k | Builder.setInsertionBlock(evalRHSBlock); |
1949 | 2.00k | auto RHS = genExpression(logical->_right); |
1950 | | |
1951 | | // Evaluate the RHS and store the result into the temporary variable. |
1952 | 2.00k | Builder.createStoreStackInst(RHS, tempVar); |
1953 | | |
1954 | | // Finally, jump to the continuation block. |
1955 | 2.00k | Builder.createBranchInst(continueBlock); |
1956 | | |
1957 | | // Load the content of the temp variable that was set in one of the branches. |
1958 | 2.00k | Builder.setInsertionBlock(continueBlock); |
1959 | 2.00k | return Builder.createLoadStackInst(tempVar); |
1960 | 2.00k | } |
1961 | | |
1962 | | void ESTreeIRGen::genLogicalExpressionBranch( |
1963 | | ESTree::LogicalExpressionNode *logical, |
1964 | | BasicBlock *onTrue, |
1965 | | BasicBlock *onFalse, |
1966 | 0 | BasicBlock *onNullish) { |
1967 | 0 | auto opStr = logical->_operator->str(); |
1968 | 0 | LLVM_DEBUG( |
1969 | 0 | llvh::dbgs() << "IRGen of short circuiting: " << opStr << " branch.\n"); |
1970 | |
|
1971 | 0 | auto parentFunc = Builder.getInsertionBlock()->getParent(); |
1972 | 0 | auto *block = Builder.createBasicBlock(parentFunc); |
1973 | |
|
1974 | 0 | if (opStr == "&&") { |
1975 | 0 | genExpressionBranch(logical->_left, block, onFalse, onNullish); |
1976 | 0 | } else if (opStr == "||") { |
1977 | 0 | genExpressionBranch(logical->_left, onTrue, block, onNullish); |
1978 | 0 | } else { |
1979 | 0 | assert(opStr == "??" && "invalid logical operator"); |
1980 | 0 | genExpressionBranch(logical->_left, onTrue, onFalse, block); |
1981 | 0 | } |
1982 | | |
1983 | 0 | Builder.setInsertionBlock(block); |
1984 | 0 | genExpressionBranch(logical->_right, onTrue, onFalse, onNullish); |
1985 | 0 | } |
1986 | | |
1987 | 40.3k | Value *ESTreeIRGen::genTemplateLiteralExpr(ESTree::TemplateLiteralNode *Expr) { |
1988 | 40.3k | LLVM_DEBUG(llvh::dbgs() << "IRGen 'TemplateLiteral' expression.\n"); |
1989 | | |
1990 | 40.3k | assert( |
1991 | 40.3k | Expr->_quasis.size() == Expr->_expressions.size() + 1 && |
1992 | 40.3k | "The string count should always be one more than substitution count."); |
1993 | | |
1994 | | // Construct an argument list for calling HermesInternal.concat(): |
1995 | | // cookedStr0, substitution0, cookedStr1, ..., substitutionN, cookedStrN + 1, |
1996 | | // skipping any empty string, except for the first cooked string, which is |
1997 | | // going to be the `this` to the concat call. |
1998 | | |
1999 | | // Get the first cooked string. |
2000 | 40.3k | auto strItr = Expr->_quasis.begin(); |
2001 | 40.3k | auto *tempEltNode = cast<ESTree::TemplateElementNode>(&*strItr); |
2002 | 40.3k | auto *firstCookedStr = Builder.getLiteralString(tempEltNode->_cooked->str()); |
2003 | 40.3k | ++strItr; |
2004 | | // If the template literal is effectively only one string, directly return it. |
2005 | 40.3k | if (strItr == Expr->_quasis.end()) { |
2006 | 40.3k | return firstCookedStr; |
2007 | 40.3k | } |
2008 | 0 | CallInst::ArgumentList argList; |
2009 | 0 | auto exprItr = Expr->_expressions.begin(); |
2010 | 0 | while (strItr != Expr->_quasis.end()) { |
2011 | 0 | auto *sub = genExpression(&*exprItr); |
2012 | 0 | argList.push_back(sub); |
2013 | 0 | tempEltNode = cast<ESTree::TemplateElementNode>(&*strItr); |
2014 | 0 | auto cookedStr = tempEltNode->_cooked->str(); |
2015 | 0 | if (!cookedStr.empty()) { |
2016 | 0 | argList.push_back(Builder.getLiteralString(cookedStr)); |
2017 | 0 | } |
2018 | 0 | ++strItr; |
2019 | 0 | ++exprItr; |
2020 | 0 | } |
2021 | 0 | assert( |
2022 | 0 | exprItr == Expr->_expressions.end() && |
2023 | 0 | "All the substitutions must have been collected."); |
2024 | | |
2025 | | // Generate a function call to HermesInternal.concat() with these arguments. |
2026 | 0 | return genHermesInternalCall("concat", firstCookedStr, argList); |
2027 | 0 | } |
2028 | | |
2029 | | Value *ESTreeIRGen::genTaggedTemplateExpr( |
2030 | 5.00k | ESTree::TaggedTemplateExpressionNode *Expr) { |
2031 | 5.00k | LLVM_DEBUG(llvh::dbgs() << "IRGen 'TaggedTemplateExpression' expression.\n"); |
2032 | | // Step 1: get the template object. |
2033 | 5.00k | auto *templateLit = cast<ESTree::TemplateLiteralNode>(Expr->_quasi); |
2034 | | |
2035 | | // Construct an argument list for calling HermesInternal.getTemplateObject(): |
2036 | | // [template object ID, dup, raw strings, (optional) cooked strings]. |
2037 | 5.00k | CallInst::ArgumentList argList; |
2038 | | // Retrieve template object ID. |
2039 | 5.00k | Module::RawStringList rawStrings; |
2040 | 116k | for (auto &n : templateLit->_quasis) { |
2041 | 116k | auto element = cast<ESTree::TemplateElementNode>(&n); |
2042 | 116k | rawStrings.push_back(Builder.getLiteralString(element->_raw->str())); |
2043 | 116k | } |
2044 | 5.00k | uint32_t templateObjID = Mod->getTemplateObjectID(std::move(rawStrings)); |
2045 | 5.00k | argList.push_back(Builder.getLiteralNumber(templateObjID)); |
2046 | | |
2047 | | // dup is true if the cooked strings and raw strings are duplicated. |
2048 | 5.00k | bool dup = true; |
2049 | | // Add the argument dup first as a placeholder which we overwrite with |
2050 | | // the correct value later. |
2051 | 5.00k | argList.push_back(Builder.getLiteralBool(dup)); |
2052 | 116k | for (auto &node : templateLit->_quasis) { |
2053 | 116k | auto *templateElt = cast<ESTree::TemplateElementNode>(&node); |
2054 | 116k | if (templateElt->_cooked != templateElt->_raw) { |
2055 | 1.10k | dup = false; |
2056 | 1.10k | } |
2057 | 116k | argList.push_back(Builder.getLiteralString(templateElt->_raw->str())); |
2058 | 116k | } |
2059 | 5.00k | argList[1] = Builder.getLiteralBool(dup); |
2060 | | // If the cooked strings are not the same as raw strings, append them to |
2061 | | // argument list. |
2062 | 5.00k | if (!dup) { |
2063 | 1.11k | for (auto &node : templateLit->_quasis) { |
2064 | 1.11k | auto *templateElt = cast<ESTree::TemplateElementNode>(&node); |
2065 | 1.11k | if (templateElt->_cooked) { |
2066 | 851 | argList.push_back( |
2067 | 851 | Builder.getLiteralString(templateElt->_cooked->str())); |
2068 | 851 | } else { |
2069 | 259 | argList.push_back(Builder.getLiteralUndefined()); |
2070 | 259 | } |
2071 | 1.11k | } |
2072 | 1.10k | } |
2073 | | |
2074 | | // Generate a function call to HermesInternal.getTemplateObject() with these |
2075 | | // arguments. |
2076 | 5.00k | auto *templateObj = |
2077 | 5.00k | genBuiltinCall(BuiltinMethod::HermesBuiltin_getTemplateObject, argList); |
2078 | | |
2079 | | // Step 2: call the tag function, passing the template object followed by a |
2080 | | // list of substitutions as arguments. |
2081 | 5.00k | CallInst::ArgumentList tagFuncArgList; |
2082 | 5.00k | tagFuncArgList.push_back(templateObj); |
2083 | 111k | for (auto &sub : templateLit->_expressions) { |
2084 | 111k | tagFuncArgList.push_back(genExpression(&sub)); |
2085 | 111k | } |
2086 | | |
2087 | 5.00k | Value *callee; |
2088 | 5.00k | Value *thisVal; |
2089 | | // Tag function is a member expression. |
2090 | 5.00k | if (auto *Mem = llvh::dyn_cast<ESTree::MemberExpressionNode>(Expr->_tag)) { |
2091 | 31 | Value *obj = genExpression(Mem->_object); |
2092 | 31 | Value *prop = genMemberExpressionProperty(Mem); |
2093 | | // Call the callee with obj as the 'this'. |
2094 | 31 | thisVal = obj; |
2095 | 31 | callee = Builder.createLoadPropertyInst(obj, prop); |
2096 | 4.97k | } else { |
2097 | 4.97k | thisVal = Builder.getLiteralUndefined(); |
2098 | 4.97k | callee = genExpression(Expr->_tag); |
2099 | 4.97k | } |
2100 | | |
2101 | 5.00k | return Builder.createCallInst( |
2102 | 5.00k | CallInst::kNoTextifiedCallee, callee, thisVal, tagFuncArgList); |
2103 | 5.00k | } |
2104 | | |
2105 | | } // namespace irgen |
2106 | | } // namespace hermes |