File: | out/../deps/v8/src/asmjs/asm-parser.cc |
Warning: | line 1374, column 5 Value stored to 'value' is never read |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
1 | // Copyright 2017 the V8 project authors. All rights reserved. |
2 | // Use of this source code is governed by a BSD-style license that can be |
3 | // found in the LICENSE file. |
4 | |
5 | #include "src/asmjs/asm-parser.h" |
6 | |
7 | #include <math.h> |
8 | #include <string.h> |
9 | |
10 | #include <algorithm> |
11 | |
12 | #include "src/asmjs/asm-js.h" |
13 | #include "src/asmjs/asm-types.h" |
14 | #include "src/base/optional.h" |
15 | #include "src/base/overflowing-math.h" |
16 | #include "src/flags/flags.h" |
17 | #include "src/numbers/conversions-inl.h" |
18 | #include "src/parsing/scanner.h" |
19 | #include "src/wasm/wasm-limits.h" |
20 | #include "src/wasm/wasm-opcodes.h" |
21 | |
22 | namespace v8 { |
23 | namespace internal { |
24 | namespace wasm { |
25 | |
26 | #ifdef DEBUG |
27 | #define FAIL_AND_RETURN(ret, msg)failed_ = true; failure_message_ = msg; failure_location_ = static_cast <int>(scanner_.Position()); return ret; \ |
28 | failed_ = true; \ |
29 | failure_message_ = msg; \ |
30 | failure_location_ = static_cast<int>(scanner_.Position()); \ |
31 | if (FLAG_trace_asm_parser) { \ |
32 | PrintF("[asm.js failure: %s, token: '%s', see: %s:%d]\n", msg, \ |
33 | scanner_.Name(scanner_.Token()).c_str(), __FILE__"../deps/v8/src/asmjs/asm-parser.cc", __LINE__33); \ |
34 | } \ |
35 | return ret; |
36 | #else |
37 | #define FAIL_AND_RETURN(ret, msg)failed_ = true; failure_message_ = msg; failure_location_ = static_cast <int>(scanner_.Position()); return ret; \ |
38 | failed_ = true; \ |
39 | failure_message_ = msg; \ |
40 | failure_location_ = static_cast<int>(scanner_.Position()); \ |
41 | return ret; |
42 | #endif |
43 | |
44 | #define FAIL(msg)failed_ = true; failure_message_ = msg; failure_location_ = static_cast <int>(scanner_.Position()); return ; FAIL_AND_RETURN(, msg)failed_ = true; failure_message_ = msg; failure_location_ = static_cast <int>(scanner_.Position()); return ; |
45 | #define FAILn(msg)failed_ = true; failure_message_ = msg; failure_location_ = static_cast <int>(scanner_.Position()); return nullptr; FAIL_AND_RETURN(nullptr, msg)failed_ = true; failure_message_ = msg; failure_location_ = static_cast <int>(scanner_.Position()); return nullptr; |
46 | |
47 | #define EXPECT_TOKEN_OR_RETURN(ret, token)do { if (scanner_.Token() != token) { failed_ = true; failure_message_ = "Unexpected token"; failure_location_ = static_cast<int >(scanner_.Position()); return ret;; } scanner_.Next(); } while (false) \ |
48 | do { \ |
49 | if (scanner_.Token() != token) { \ |
50 | FAIL_AND_RETURN(ret, "Unexpected token")failed_ = true; failure_message_ = "Unexpected token"; failure_location_ = static_cast<int>(scanner_.Position()); return ret;; \ |
51 | } \ |
52 | scanner_.Next(); \ |
53 | } while (false) |
54 | |
55 | #define EXPECT_TOKEN(token)do { if (scanner_.Token() != token) { failed_ = true; failure_message_ = "Unexpected token"; failure_location_ = static_cast<int >(scanner_.Position()); return ;; } scanner_.Next(); } while (false) EXPECT_TOKEN_OR_RETURN(, token)do { if (scanner_.Token() != token) { failed_ = true; failure_message_ = "Unexpected token"; failure_location_ = static_cast<int >(scanner_.Position()); return ;; } scanner_.Next(); } while (false) |
56 | #define EXPECT_TOKENn(token)do { if (scanner_.Token() != token) { failed_ = true; failure_message_ = "Unexpected token"; failure_location_ = static_cast<int >(scanner_.Position()); return nullptr;; } scanner_.Next() ; } while (false) EXPECT_TOKEN_OR_RETURN(nullptr, token)do { if (scanner_.Token() != token) { failed_ = true; failure_message_ = "Unexpected token"; failure_location_ = static_cast<int >(scanner_.Position()); return nullptr;; } scanner_.Next() ; } while (false) |
57 | |
58 | #define RECURSE_OR_RETURN(ret, call)do { ((void) 0); if (GetCurrentStackPosition() < stack_limit_ ) { failed_ = true; failure_message_ = "Stack overflow while parsing asm.js module." ; failure_location_ = static_cast<int>(scanner_.Position ()); return ret;; } call; if (failed_) return ret; } while (false ) \ |
59 | do { \ |
60 | DCHECK(!failed_)((void) 0); \ |
61 | if (GetCurrentStackPosition() < stack_limit_) { \ |
62 | FAIL_AND_RETURN(ret, "Stack overflow while parsing asm.js module.")failed_ = true; failure_message_ = "Stack overflow while parsing asm.js module." ; failure_location_ = static_cast<int>(scanner_.Position ()); return ret;; \ |
63 | } \ |
64 | call; \ |
65 | if (failed_) return ret; \ |
66 | } while (false) |
67 | |
68 | #define RECURSE(call) RECURSE_OR_RETURN(, call)do { ((void) 0); if (GetCurrentStackPosition() < stack_limit_ ) { failed_ = true; failure_message_ = "Stack overflow while parsing asm.js module." ; failure_location_ = static_cast<int>(scanner_.Position ()); return ;; } call; if (failed_) return ; } while (false) |
69 | #define RECURSEn(call)do { ((void) 0); if (GetCurrentStackPosition() < stack_limit_ ) { failed_ = true; failure_message_ = "Stack overflow while parsing asm.js module." ; failure_location_ = static_cast<int>(scanner_.Position ()); return nullptr;; } call; if (failed_) return nullptr; } while (false) RECURSE_OR_RETURN(nullptr, call)do { ((void) 0); if (GetCurrentStackPosition() < stack_limit_ ) { failed_ = true; failure_message_ = "Stack overflow while parsing asm.js module." ; failure_location_ = static_cast<int>(scanner_.Position ()); return nullptr;; } call; if (failed_) return nullptr; } while (false) |
70 | |
71 | #define TOK(name)AsmJsScanner::kToken_name AsmJsScanner::kToken_##name |
72 | |
73 | AsmJsParser::AsmJsParser(Zone* zone, uintptr_t stack_limit, |
74 | Utf16CharacterStream* stream) |
75 | : zone_(zone), |
76 | scanner_(stream), |
77 | module_builder_(zone->New<WasmModuleBuilder>(zone)), |
78 | stack_limit_(stack_limit), |
79 | block_stack_(zone), |
80 | global_imports_(zone) { |
81 | module_builder_->SetMinMemorySize(0); |
82 | InitializeStdlibTypes(); |
83 | } |
84 | |
85 | void AsmJsParser::InitializeStdlibTypes() { |
86 | auto* d = AsmType::Double(); |
87 | auto* dq = AsmType::DoubleQ(); |
88 | stdlib_dq2d_ = AsmType::Function(zone(), d); |
89 | stdlib_dq2d_->AsFunctionType()->AddArgument(dq); |
90 | |
91 | stdlib_dqdq2d_ = AsmType::Function(zone(), d); |
92 | stdlib_dqdq2d_->AsFunctionType()->AddArgument(dq); |
93 | stdlib_dqdq2d_->AsFunctionType()->AddArgument(dq); |
94 | |
95 | auto* f = AsmType::Float(); |
96 | auto* fh = AsmType::Floatish(); |
97 | auto* fq = AsmType::FloatQ(); |
98 | auto* fq2fh = AsmType::Function(zone(), fh); |
99 | fq2fh->AsFunctionType()->AddArgument(fq); |
100 | |
101 | auto* s = AsmType::Signed(); |
102 | auto* u = AsmType::Unsigned(); |
103 | auto* s2u = AsmType::Function(zone(), u); |
104 | s2u->AsFunctionType()->AddArgument(s); |
105 | |
106 | auto* i = AsmType::Int(); |
107 | stdlib_i2s_ = AsmType::Function(zone_, s); |
108 | stdlib_i2s_->AsFunctionType()->AddArgument(i); |
109 | |
110 | stdlib_ii2s_ = AsmType::Function(zone(), s); |
111 | stdlib_ii2s_->AsFunctionType()->AddArgument(i); |
112 | stdlib_ii2s_->AsFunctionType()->AddArgument(i); |
113 | |
114 | // The signatures in "9 Standard Library" of the spec draft are outdated and |
115 | // have been superseded with the following by an errata: |
116 | // - Math.min/max : (signed, signed...) -> signed |
117 | // (double, double...) -> double |
118 | // (float, float...) -> float |
119 | auto* minmax_d = AsmType::MinMaxType(zone(), d, d); |
120 | auto* minmax_f = AsmType::MinMaxType(zone(), f, f); |
121 | auto* minmax_s = AsmType::MinMaxType(zone(), s, s); |
122 | stdlib_minmax_ = AsmType::OverloadedFunction(zone()); |
123 | stdlib_minmax_->AsOverloadedFunctionType()->AddOverload(minmax_s); |
124 | stdlib_minmax_->AsOverloadedFunctionType()->AddOverload(minmax_f); |
125 | stdlib_minmax_->AsOverloadedFunctionType()->AddOverload(minmax_d); |
126 | |
127 | // The signatures in "9 Standard Library" of the spec draft are outdated and |
128 | // have been superseded with the following by an errata: |
129 | // - Math.abs : (signed) -> unsigned |
130 | // (double?) -> double |
131 | // (float?) -> floatish |
132 | stdlib_abs_ = AsmType::OverloadedFunction(zone()); |
133 | stdlib_abs_->AsOverloadedFunctionType()->AddOverload(s2u); |
134 | stdlib_abs_->AsOverloadedFunctionType()->AddOverload(stdlib_dq2d_); |
135 | stdlib_abs_->AsOverloadedFunctionType()->AddOverload(fq2fh); |
136 | |
137 | // The signatures in "9 Standard Library" of the spec draft are outdated and |
138 | // have been superseded with the following by an errata: |
139 | // - Math.ceil/floor/sqrt : (double?) -> double |
140 | // (float?) -> floatish |
141 | stdlib_ceil_like_ = AsmType::OverloadedFunction(zone()); |
142 | stdlib_ceil_like_->AsOverloadedFunctionType()->AddOverload(stdlib_dq2d_); |
143 | stdlib_ceil_like_->AsOverloadedFunctionType()->AddOverload(fq2fh); |
144 | |
145 | stdlib_fround_ = AsmType::FroundType(zone()); |
146 | } |
147 | |
148 | FunctionSig* AsmJsParser::ConvertSignature(AsmType* return_type, |
149 | const ZoneVector<AsmType*>& params) { |
150 | FunctionSig::Builder sig_builder( |
151 | zone(), !return_type->IsA(AsmType::Void()) ? 1 : 0, params.size()); |
152 | for (auto param : params) { |
153 | if (param->IsA(AsmType::Double())) { |
154 | sig_builder.AddParam(kWasmF64); |
155 | } else if (param->IsA(AsmType::Float())) { |
156 | sig_builder.AddParam(kWasmF32); |
157 | } else if (param->IsA(AsmType::Int())) { |
158 | sig_builder.AddParam(kWasmI32); |
159 | } else { |
160 | UNREACHABLE()V8_Fatal("unreachable code"); |
161 | } |
162 | } |
163 | if (!return_type->IsA(AsmType::Void())) { |
164 | if (return_type->IsA(AsmType::Double())) { |
165 | sig_builder.AddReturn(kWasmF64); |
166 | } else if (return_type->IsA(AsmType::Float())) { |
167 | sig_builder.AddReturn(kWasmF32); |
168 | } else if (return_type->IsA(AsmType::Signed())) { |
169 | sig_builder.AddReturn(kWasmI32); |
170 | } else { |
171 | UNREACHABLE()V8_Fatal("unreachable code"); |
172 | } |
173 | } |
174 | return sig_builder.Build(); |
175 | } |
176 | |
177 | bool AsmJsParser::Run() { |
178 | ValidateModule(); |
179 | return !failed_; |
180 | } |
181 | |
182 | class V8_NODISCARD[[nodiscard]] AsmJsParser::TemporaryVariableScope { |
183 | public: |
184 | explicit TemporaryVariableScope(AsmJsParser* parser) : parser_(parser) { |
185 | local_depth_ = parser_->function_temp_locals_depth_; |
186 | parser_->function_temp_locals_depth_++; |
187 | } |
188 | ~TemporaryVariableScope() { |
189 | DCHECK_EQ(local_depth_, parser_->function_temp_locals_depth_ - 1)((void) 0); |
190 | parser_->function_temp_locals_depth_--; |
191 | } |
192 | uint32_t get() const { return parser_->TempVariable(local_depth_); } |
193 | |
194 | private: |
195 | AsmJsParser* parser_; |
196 | int local_depth_; |
197 | }; |
198 | |
199 | wasm::AsmJsParser::VarInfo* AsmJsParser::GetVarInfo( |
200 | AsmJsScanner::token_t token) { |
201 | const bool is_global = AsmJsScanner::IsGlobal(token); |
202 | DCHECK(is_global || AsmJsScanner::IsLocal(token))((void) 0); |
203 | base::Vector<VarInfo>& var_info = |
204 | is_global ? global_var_info_ : local_var_info_; |
205 | size_t old_capacity = var_info.size(); |
206 | size_t index = is_global ? AsmJsScanner::GlobalIndex(token) |
207 | : AsmJsScanner::LocalIndex(token); |
208 | if (is_global && index + 1 > num_globals_) num_globals_ = index + 1; |
209 | if (index + 1 > old_capacity) { |
210 | size_t new_size = std::max(2 * old_capacity, index + 1); |
211 | base::Vector<VarInfo> new_info{zone_->NewArray<VarInfo>(new_size), |
212 | new_size}; |
213 | std::uninitialized_fill(new_info.begin(), new_info.end(), VarInfo{}); |
214 | std::copy(var_info.begin(), var_info.end(), new_info.begin()); |
215 | var_info = new_info; |
216 | } |
217 | return &var_info[index]; |
218 | } |
219 | |
220 | uint32_t AsmJsParser::VarIndex(VarInfo* info) { |
221 | DCHECK_EQ(info->kind, VarKind::kGlobal)((void) 0); |
222 | return info->index + static_cast<uint32_t>(global_imports_.size()); |
223 | } |
224 | |
225 | void AsmJsParser::AddGlobalImport(base::Vector<const char> name, AsmType* type, |
226 | ValueType vtype, bool mutable_variable, |
227 | VarInfo* info) { |
228 | // Allocate a separate variable for the import. |
229 | // TODO(asmjs): Consider using the imported global directly instead of |
230 | // allocating a separate global variable for immutable (i.e. const) imports. |
231 | DeclareGlobal(info, mutable_variable, type, vtype); |
232 | |
233 | // Record the need to initialize the global from the import. |
234 | global_imports_.push_back({name, vtype, info}); |
235 | } |
236 | |
237 | void AsmJsParser::DeclareGlobal(VarInfo* info, bool mutable_variable, |
238 | AsmType* type, ValueType vtype, |
239 | WasmInitExpr init) { |
240 | info->kind = VarKind::kGlobal; |
241 | info->type = type; |
242 | info->index = module_builder_->AddGlobal(vtype, true, init); |
243 | info->mutable_variable = mutable_variable; |
244 | } |
245 | |
246 | void AsmJsParser::DeclareStdlibFunc(VarInfo* info, VarKind kind, |
247 | AsmType* type) { |
248 | info->kind = kind; |
249 | info->type = type; |
250 | info->index = 0; // unused |
251 | info->mutable_variable = false; |
252 | } |
253 | |
254 | uint32_t AsmJsParser::TempVariable(int index) { |
255 | if (index + 1 > function_temp_locals_used_) { |
256 | function_temp_locals_used_ = index + 1; |
257 | } |
258 | return function_temp_locals_offset_ + index; |
259 | } |
260 | |
261 | base::Vector<const char> AsmJsParser::CopyCurrentIdentifierString() { |
262 | const std::string& str = scanner_.GetIdentifierString(); |
263 | char* buffer = zone()->NewArray<char>(str.size()); |
264 | str.copy(buffer, str.size()); |
265 | return base::Vector<const char>(buffer, static_cast<int>(str.size())); |
266 | } |
267 | |
268 | void AsmJsParser::SkipSemicolon() { |
269 | if (Check(';')) { |
270 | // Had a semicolon. |
271 | } else if (!Peek('}') && !scanner_.IsPrecededByNewline()) { |
272 | FAIL("Expected ;")failed_ = true; failure_message_ = "Expected ;"; failure_location_ = static_cast<int>(scanner_.Position()); return ;; |
273 | } |
274 | } |
275 | |
276 | void AsmJsParser::Begin(AsmJsScanner::token_t label) { |
277 | BareBegin(BlockKind::kRegular, label); |
278 | current_function_builder_->EmitWithU8(kExprBlock, kVoidCode); |
279 | } |
280 | |
281 | void AsmJsParser::Loop(AsmJsScanner::token_t label) { |
282 | BareBegin(BlockKind::kLoop, label); |
283 | size_t position = scanner_.Position(); |
284 | current_function_builder_->AddAsmWasmOffset(position, position); |
285 | current_function_builder_->EmitWithU8(kExprLoop, kVoidCode); |
286 | } |
287 | |
288 | void AsmJsParser::End() { |
289 | BareEnd(); |
290 | current_function_builder_->Emit(kExprEnd); |
291 | } |
292 | |
293 | void AsmJsParser::BareBegin(BlockKind kind, AsmJsScanner::token_t label) { |
294 | BlockInfo info; |
295 | info.kind = kind; |
296 | info.label = label; |
297 | block_stack_.push_back(info); |
298 | } |
299 | |
300 | void AsmJsParser::BareEnd() { |
301 | DCHECK_GT(block_stack_.size(), 0)((void) 0); |
302 | block_stack_.pop_back(); |
303 | } |
304 | |
305 | int AsmJsParser::FindContinueLabelDepth(AsmJsScanner::token_t label) { |
306 | int count = 0; |
307 | for (auto it = block_stack_.rbegin(); it != block_stack_.rend(); |
308 | ++it, ++count) { |
309 | // A 'continue' statement targets ... |
310 | // - The innermost {kLoop} block if no label is given. |
311 | // - The matching {kLoop} block (when a label is provided). |
312 | if (it->kind == BlockKind::kLoop && |
313 | (label == kTokenNone || it->label == label)) { |
314 | return count; |
315 | } |
316 | } |
317 | return -1; |
318 | } |
319 | |
320 | int AsmJsParser::FindBreakLabelDepth(AsmJsScanner::token_t label) { |
321 | int count = 0; |
322 | for (auto it = block_stack_.rbegin(); it != block_stack_.rend(); |
323 | ++it, ++count) { |
324 | // A 'break' statement targets ... |
325 | // - The innermost {kRegular} block if no label is given. |
326 | // - The matching {kRegular} or {kNamed} block (when a label is provided). |
327 | if ((it->kind == BlockKind::kRegular && |
328 | (label == kTokenNone || it->label == label)) || |
329 | (it->kind == BlockKind::kNamed && it->label == label)) { |
330 | return count; |
331 | } |
332 | } |
333 | return -1; |
334 | } |
335 | |
336 | // 6.1 ValidateModule |
337 | void AsmJsParser::ValidateModule() { |
338 | RECURSE(ValidateModuleParameters()); |
339 | EXPECT_TOKEN('{')do { if (scanner_.Token() != '{') { failed_ = true; failure_message_ = "Unexpected token"; failure_location_ = static_cast<int >(scanner_.Position()); return ;; } scanner_.Next(); } while (false); |
340 | EXPECT_TOKEN(TOK(UseAsm))do { if (scanner_.Token() != AsmJsScanner::kToken_UseAsm) { failed_ = true; failure_message_ = "Unexpected token"; failure_location_ = static_cast<int>(scanner_.Position()); return ;; } scanner_ .Next(); } while (false); |
341 | RECURSE(SkipSemicolon()); |
342 | RECURSE(ValidateModuleVars()); |
343 | while (Peek(TOK(function)AsmJsScanner::kToken_function)) { |
344 | RECURSE(ValidateFunction()); |
345 | } |
346 | while (Peek(TOK(var)AsmJsScanner::kToken_var)) { |
347 | RECURSE(ValidateFunctionTable()); |
348 | } |
349 | RECURSE(ValidateExport()); |
350 | RECURSE(SkipSemicolon()); |
351 | EXPECT_TOKEN('}')do { if (scanner_.Token() != '}') { failed_ = true; failure_message_ = "Unexpected token"; failure_location_ = static_cast<int >(scanner_.Position()); return ;; } scanner_.Next(); } while (false); |
352 | |
353 | // Check that all functions were eventually defined. |
354 | for (auto& info : global_var_info_.SubVector(0, num_globals_)) { |
355 | if (info.kind == VarKind::kFunction && !info.function_defined) { |
356 | FAIL("Undefined function")failed_ = true; failure_message_ = "Undefined function"; failure_location_ = static_cast<int>(scanner_.Position()); return ;; |
357 | } |
358 | if (info.kind == VarKind::kTable && !info.function_defined) { |
359 | FAIL("Undefined function table")failed_ = true; failure_message_ = "Undefined function table" ; failure_location_ = static_cast<int>(scanner_.Position ()); return ;; |
360 | } |
361 | if (info.kind == VarKind::kImportedFunction && !info.function_defined) { |
362 | // For imported functions without a single call site, we insert a dummy |
363 | // import here to preserve the fact that there actually was an import. |
364 | FunctionSig* void_void_sig = FunctionSig::Builder(zone(), 0, 0).Build(); |
365 | module_builder_->AddImport(info.import->function_name, void_void_sig); |
366 | } |
367 | } |
368 | |
369 | // Add start function to initialize things. |
370 | WasmFunctionBuilder* start = module_builder_->AddFunction(); |
371 | module_builder_->MarkStartFunction(start); |
372 | for (auto& global_import : global_imports_) { |
373 | uint32_t import_index = module_builder_->AddGlobalImport( |
374 | global_import.import_name, global_import.value_type, |
375 | false /* mutability */); |
376 | start->EmitWithI32V(kExprGlobalGet, import_index); |
377 | start->EmitWithI32V(kExprGlobalSet, VarIndex(global_import.var_info)); |
378 | } |
379 | start->Emit(kExprEnd); |
380 | FunctionSig::Builder b(zone(), 0, 0); |
381 | start->SetSignature(b.Build()); |
382 | } |
383 | |
384 | // 6.1 ValidateModule - parameters |
385 | void AsmJsParser::ValidateModuleParameters() { |
386 | EXPECT_TOKEN('(')do { if (scanner_.Token() != '(') { failed_ = true; failure_message_ = "Unexpected token"; failure_location_ = static_cast<int >(scanner_.Position()); return ;; } scanner_.Next(); } while (false); |
387 | stdlib_name_ = 0; |
388 | foreign_name_ = 0; |
389 | heap_name_ = 0; |
390 | if (!Peek(')')) { |
391 | if (!scanner_.IsGlobal()) { |
392 | FAIL("Expected stdlib parameter")failed_ = true; failure_message_ = "Expected stdlib parameter" ; failure_location_ = static_cast<int>(scanner_.Position ()); return ;; |
393 | } |
394 | stdlib_name_ = Consume(); |
395 | if (!Peek(')')) { |
396 | EXPECT_TOKEN(',')do { if (scanner_.Token() != ',') { failed_ = true; failure_message_ = "Unexpected token"; failure_location_ = static_cast<int >(scanner_.Position()); return ;; } scanner_.Next(); } while (false); |
397 | if (!scanner_.IsGlobal()) { |
398 | FAIL("Expected foreign parameter")failed_ = true; failure_message_ = "Expected foreign parameter" ; failure_location_ = static_cast<int>(scanner_.Position ()); return ;; |
399 | } |
400 | foreign_name_ = Consume(); |
401 | if (stdlib_name_ == foreign_name_) { |
402 | FAIL("Duplicate parameter name")failed_ = true; failure_message_ = "Duplicate parameter name" ; failure_location_ = static_cast<int>(scanner_.Position ()); return ;; |
403 | } |
404 | if (!Peek(')')) { |
405 | EXPECT_TOKEN(',')do { if (scanner_.Token() != ',') { failed_ = true; failure_message_ = "Unexpected token"; failure_location_ = static_cast<int >(scanner_.Position()); return ;; } scanner_.Next(); } while (false); |
406 | if (!scanner_.IsGlobal()) { |
407 | FAIL("Expected heap parameter")failed_ = true; failure_message_ = "Expected heap parameter"; failure_location_ = static_cast<int>(scanner_.Position ()); return ;; |
408 | } |
409 | heap_name_ = Consume(); |
410 | if (heap_name_ == stdlib_name_ || heap_name_ == foreign_name_) { |
411 | FAIL("Duplicate parameter name")failed_ = true; failure_message_ = "Duplicate parameter name" ; failure_location_ = static_cast<int>(scanner_.Position ()); return ;; |
412 | } |
413 | } |
414 | } |
415 | } |
416 | EXPECT_TOKEN(')')do { if (scanner_.Token() != ')') { failed_ = true; failure_message_ = "Unexpected token"; failure_location_ = static_cast<int >(scanner_.Position()); return ;; } scanner_.Next(); } while (false); |
417 | } |
418 | |
419 | // 6.1 ValidateModule - variables |
420 | void AsmJsParser::ValidateModuleVars() { |
421 | while (Peek(TOK(var)AsmJsScanner::kToken_var) || Peek(TOK(const)AsmJsScanner::kToken_const)) { |
422 | bool mutable_variable = true; |
423 | if (Check(TOK(var)AsmJsScanner::kToken_var)) { |
424 | // Had a var. |
425 | } else { |
426 | EXPECT_TOKEN(TOK(const))do { if (scanner_.Token() != AsmJsScanner::kToken_const) { failed_ = true; failure_message_ = "Unexpected token"; failure_location_ = static_cast<int>(scanner_.Position()); return ;; } scanner_ .Next(); } while (false); |
427 | mutable_variable = false; |
428 | } |
429 | for (;;) { |
430 | RECURSE(ValidateModuleVar(mutable_variable)); |
431 | if (Check(',')) { |
432 | continue; |
433 | } |
434 | break; |
435 | } |
436 | SkipSemicolon(); |
437 | } |
438 | } |
439 | |
440 | // 6.1 ValidateModule - one variable |
441 | void AsmJsParser::ValidateModuleVar(bool mutable_variable) { |
442 | if (!scanner_.IsGlobal()) { |
443 | FAIL("Expected identifier")failed_ = true; failure_message_ = "Expected identifier"; failure_location_ = static_cast<int>(scanner_.Position()); return ;; |
444 | } |
445 | VarInfo* info = GetVarInfo(Consume()); |
446 | if (info->kind != VarKind::kUnused) { |
447 | FAIL("Redefinition of variable")failed_ = true; failure_message_ = "Redefinition of variable" ; failure_location_ = static_cast<int>(scanner_.Position ()); return ;; |
448 | } |
449 | EXPECT_TOKEN('=')do { if (scanner_.Token() != '=') { failed_ = true; failure_message_ = "Unexpected token"; failure_location_ = static_cast<int >(scanner_.Position()); return ;; } scanner_.Next(); } while (false); |
450 | double dvalue = 0.0; |
451 | uint32_t uvalue = 0; |
452 | if (CheckForDouble(&dvalue)) { |
453 | DeclareGlobal(info, mutable_variable, AsmType::Double(), kWasmF64, |
454 | WasmInitExpr(dvalue)); |
455 | } else if (CheckForUnsigned(&uvalue)) { |
456 | if (uvalue > 0x7FFFFFFF) { |
457 | FAIL("Numeric literal out of range")failed_ = true; failure_message_ = "Numeric literal out of range" ; failure_location_ = static_cast<int>(scanner_.Position ()); return ;; |
458 | } |
459 | DeclareGlobal(info, mutable_variable, |
460 | mutable_variable ? AsmType::Int() : AsmType::Signed(), |
461 | kWasmI32, WasmInitExpr(static_cast<int32_t>(uvalue))); |
462 | } else if (Check('-')) { |
463 | if (CheckForDouble(&dvalue)) { |
464 | DeclareGlobal(info, mutable_variable, AsmType::Double(), kWasmF64, |
465 | WasmInitExpr(-dvalue)); |
466 | } else if (CheckForUnsigned(&uvalue)) { |
467 | if (uvalue > 0x7FFFFFFF) { |
468 | FAIL("Numeric literal out of range")failed_ = true; failure_message_ = "Numeric literal out of range" ; failure_location_ = static_cast<int>(scanner_.Position ()); return ;; |
469 | } |
470 | if (uvalue == 0) { |
471 | // '-0' is treated as float. |
472 | DeclareGlobal(info, mutable_variable, AsmType::Float(), kWasmF32, |
473 | WasmInitExpr(-0.f)); |
474 | } else { |
475 | DeclareGlobal(info, mutable_variable, |
476 | mutable_variable ? AsmType::Int() : AsmType::Signed(), |
477 | kWasmI32, WasmInitExpr(-static_cast<int32_t>(uvalue))); |
478 | } |
479 | } else { |
480 | FAIL("Expected numeric literal")failed_ = true; failure_message_ = "Expected numeric literal" ; failure_location_ = static_cast<int>(scanner_.Position ()); return ;; |
481 | } |
482 | } else if (Check(TOK(new)AsmJsScanner::kToken_new)) { |
483 | RECURSE(ValidateModuleVarNewStdlib(info)); |
484 | } else if (Check(stdlib_name_)) { |
485 | EXPECT_TOKEN('.')do { if (scanner_.Token() != '.') { failed_ = true; failure_message_ = "Unexpected token"; failure_location_ = static_cast<int >(scanner_.Position()); return ;; } scanner_.Next(); } while (false); |
486 | RECURSE(ValidateModuleVarStdlib(info)); |
487 | } else if (Peek(foreign_name_) || Peek('+')) { |
488 | RECURSE(ValidateModuleVarImport(info, mutable_variable)); |
489 | } else if (scanner_.IsGlobal()) { |
490 | RECURSE(ValidateModuleVarFromGlobal(info, mutable_variable)); |
491 | } else { |
492 | FAIL("Bad variable declaration")failed_ = true; failure_message_ = "Bad variable declaration" ; failure_location_ = static_cast<int>(scanner_.Position ()); return ;; |
493 | } |
494 | } |
495 | |
496 | // 6.1 ValidateModule - global float declaration |
497 | void AsmJsParser::ValidateModuleVarFromGlobal(VarInfo* info, |
498 | bool mutable_variable) { |
499 | VarInfo* src_info = GetVarInfo(Consume()); |
500 | if (!src_info->type->IsA(stdlib_fround_)) { |
501 | if (src_info->mutable_variable) { |
502 | FAIL("Can only use immutable variables in global definition")failed_ = true; failure_message_ = "Can only use immutable variables in global definition" ; failure_location_ = static_cast<int>(scanner_.Position ()); return ;; |
503 | } |
504 | if (mutable_variable) { |
505 | FAIL("Can only define immutable variables with other immutables")failed_ = true; failure_message_ = "Can only define immutable variables with other immutables" ; failure_location_ = static_cast<int>(scanner_.Position ()); return ;; |
506 | } |
507 | if (!src_info->type->IsA(AsmType::Int()) && |
508 | !src_info->type->IsA(AsmType::Float()) && |
509 | !src_info->type->IsA(AsmType::Double())) { |
510 | FAIL("Expected int, float, double, or fround for global definition")failed_ = true; failure_message_ = "Expected int, float, double, or fround for global definition" ; failure_location_ = static_cast<int>(scanner_.Position ()); return ;; |
511 | } |
512 | info->kind = VarKind::kGlobal; |
513 | info->type = src_info->type; |
514 | info->index = src_info->index; |
515 | info->mutable_variable = false; |
516 | return; |
517 | } |
518 | EXPECT_TOKEN('(')do { if (scanner_.Token() != '(') { failed_ = true; failure_message_ = "Unexpected token"; failure_location_ = static_cast<int >(scanner_.Position()); return ;; } scanner_.Next(); } while (false); |
519 | bool negate = false; |
520 | if (Check('-')) { |
521 | negate = true; |
522 | } |
523 | double dvalue = 0.0; |
524 | uint32_t uvalue = 0; |
525 | if (CheckForDouble(&dvalue)) { |
526 | if (negate) { |
527 | dvalue = -dvalue; |
528 | } |
529 | DeclareGlobal(info, mutable_variable, AsmType::Float(), kWasmF32, |
530 | WasmInitExpr(DoubleToFloat32(dvalue))); |
531 | } else if (CheckForUnsigned(&uvalue)) { |
532 | dvalue = uvalue; |
533 | if (negate) { |
534 | dvalue = -dvalue; |
535 | } |
536 | DeclareGlobal(info, mutable_variable, AsmType::Float(), kWasmF32, |
537 | WasmInitExpr(static_cast<float>(dvalue))); |
538 | } else { |
539 | FAIL("Expected numeric literal")failed_ = true; failure_message_ = "Expected numeric literal" ; failure_location_ = static_cast<int>(scanner_.Position ()); return ;; |
540 | } |
541 | EXPECT_TOKEN(')')do { if (scanner_.Token() != ')') { failed_ = true; failure_message_ = "Unexpected token"; failure_location_ = static_cast<int >(scanner_.Position()); return ;; } scanner_.Next(); } while (false); |
542 | } |
543 | |
544 | // 6.1 ValidateModule - foreign imports |
545 | void AsmJsParser::ValidateModuleVarImport(VarInfo* info, |
546 | bool mutable_variable) { |
547 | if (Check('+')) { |
548 | EXPECT_TOKEN(foreign_name_)do { if (scanner_.Token() != foreign_name_) { failed_ = true; failure_message_ = "Unexpected token"; failure_location_ = static_cast <int>(scanner_.Position()); return ;; } scanner_.Next() ; } while (false); |
549 | EXPECT_TOKEN('.')do { if (scanner_.Token() != '.') { failed_ = true; failure_message_ = "Unexpected token"; failure_location_ = static_cast<int >(scanner_.Position()); return ;; } scanner_.Next(); } while (false); |
550 | base::Vector<const char> name = CopyCurrentIdentifierString(); |
551 | AddGlobalImport(name, AsmType::Double(), kWasmF64, mutable_variable, info); |
552 | scanner_.Next(); |
553 | } else { |
554 | EXPECT_TOKEN(foreign_name_)do { if (scanner_.Token() != foreign_name_) { failed_ = true; failure_message_ = "Unexpected token"; failure_location_ = static_cast <int>(scanner_.Position()); return ;; } scanner_.Next() ; } while (false); |
555 | EXPECT_TOKEN('.')do { if (scanner_.Token() != '.') { failed_ = true; failure_message_ = "Unexpected token"; failure_location_ = static_cast<int >(scanner_.Position()); return ;; } scanner_.Next(); } while (false); |
556 | base::Vector<const char> name = CopyCurrentIdentifierString(); |
557 | scanner_.Next(); |
558 | if (Check('|')) { |
559 | if (!CheckForZero()) { |
560 | FAIL("Expected |0 type annotation for foreign integer import")failed_ = true; failure_message_ = "Expected |0 type annotation for foreign integer import" ; failure_location_ = static_cast<int>(scanner_.Position ()); return ;; |
561 | } |
562 | AddGlobalImport(name, AsmType::Int(), kWasmI32, mutable_variable, info); |
563 | } else { |
564 | info->kind = VarKind::kImportedFunction; |
565 | info->import = zone()->New<FunctionImportInfo>(name, zone()); |
566 | info->mutable_variable = false; |
567 | } |
568 | } |
569 | } |
570 | |
571 | // 6.1 ValidateModule - one variable |
572 | // 9 - Standard Library - heap types |
573 | void AsmJsParser::ValidateModuleVarNewStdlib(VarInfo* info) { |
574 | EXPECT_TOKEN(stdlib_name_)do { if (scanner_.Token() != stdlib_name_) { failed_ = true; failure_message_ = "Unexpected token"; failure_location_ = static_cast<int >(scanner_.Position()); return ;; } scanner_.Next(); } while (false); |
575 | EXPECT_TOKEN('.')do { if (scanner_.Token() != '.') { failed_ = true; failure_message_ = "Unexpected token"; failure_location_ = static_cast<int >(scanner_.Position()); return ;; } scanner_.Next(); } while (false); |
576 | switch (Consume()) { |
577 | #define V(name, _junk1, _junk2, _junk3) \ |
578 | case TOK(name)AsmJsScanner::kToken_name: \ |
579 | DeclareStdlibFunc(info, VarKind::kSpecial, AsmType::name()); \ |
580 | stdlib_uses_.Add(StandardMember::k##name); \ |
581 | break; |
582 | STDLIB_ARRAY_TYPE_LIST(V)V(Int8Array, Mem8S, Mem8, I32) V(Uint8Array, Mem8U, Mem8, I32 ) V(Int16Array, Mem16S, Mem16, I32) V(Uint16Array, Mem16U, Mem16 , I32) V(Int32Array, Mem, Mem, I32) V(Uint32Array, Mem, Mem, I32 ) V(Float32Array, Mem, Mem, F32) V(Float64Array, Mem, Mem, F64 ) |
583 | #undef V |
584 | default: |
585 | FAIL("Expected ArrayBuffer view")failed_ = true; failure_message_ = "Expected ArrayBuffer view" ; failure_location_ = static_cast<int>(scanner_.Position ()); return ;; |
586 | } |
587 | EXPECT_TOKEN('(')do { if (scanner_.Token() != '(') { failed_ = true; failure_message_ = "Unexpected token"; failure_location_ = static_cast<int >(scanner_.Position()); return ;; } scanner_.Next(); } while (false); |
588 | EXPECT_TOKEN(heap_name_)do { if (scanner_.Token() != heap_name_) { failed_ = true; failure_message_ = "Unexpected token"; failure_location_ = static_cast<int >(scanner_.Position()); return ;; } scanner_.Next(); } while (false); |
589 | EXPECT_TOKEN(')')do { if (scanner_.Token() != ')') { failed_ = true; failure_message_ = "Unexpected token"; failure_location_ = static_cast<int >(scanner_.Position()); return ;; } scanner_.Next(); } while (false); |
590 | } |
591 | |
592 | // 6.1 ValidateModule - one variable |
593 | // 9 - Standard Library |
594 | void AsmJsParser::ValidateModuleVarStdlib(VarInfo* info) { |
595 | if (Check(TOK(Math)AsmJsScanner::kToken_Math)) { |
596 | EXPECT_TOKEN('.')do { if (scanner_.Token() != '.') { failed_ = true; failure_message_ = "Unexpected token"; failure_location_ = static_cast<int >(scanner_.Position()); return ;; } scanner_.Next(); } while (false); |
597 | switch (Consume()) { |
598 | #define V(name, const_value) \ |
599 | case TOK(name)AsmJsScanner::kToken_name: \ |
600 | DeclareGlobal(info, false, AsmType::Double(), kWasmF64, \ |
601 | WasmInitExpr(const_value)); \ |
602 | stdlib_uses_.Add(StandardMember::kMath##name); \ |
603 | break; |
604 | STDLIB_MATH_VALUE_LIST(V)V(E, 2.718281828459045) V(LN10, 2.302585092994046) V(LN2, 0.6931471805599453 ) V(LOG2E, 1.4426950408889634) V(LOG10E, 0.4342944819032518) V (PI, 3.141592653589793) V(SQRT1_2, 0.7071067811865476) V(SQRT2 , 1.4142135623730951) |
605 | #undef V |
606 | #define V(name, Name, op, sig) \ |
607 | case TOK(name)AsmJsScanner::kToken_name: \ |
608 | DeclareStdlibFunc(info, VarKind::kMath##Name, stdlib_##sig##_); \ |
609 | stdlib_uses_.Add(StandardMember::kMath##Name); \ |
610 | break; |
611 | STDLIB_MATH_FUNCTION_LIST(V)V(min, Min, x, minmax) V(max, Max, x, minmax) V(abs, Abs, x, abs ) V(fround, Fround, x, fround) V(acos, Acos, kExprF64Acos, dq2d ) V(asin, Asin, kExprF64Asin, dq2d) V(atan, Atan, kExprF64Atan , dq2d) V(cos, Cos, kExprF64Cos, dq2d) V(sin, Sin, kExprF64Sin , dq2d) V(tan, Tan, kExprF64Tan, dq2d) V(exp, Exp, kExprF64Exp , dq2d) V(log, Log, kExprF64Log, dq2d) V(atan2, Atan2, kExprF64Atan2 , dqdq2d) V(pow, Pow, kExprF64Pow, dqdq2d) V(imul, Imul, kExprI32Mul , ii2s) V(clz32, Clz32, kExprI32Clz, i2s) V(ceil, Ceil, x, ceil_like ) V(floor, Floor, x, ceil_like) V(sqrt, Sqrt, x, ceil_like) |
612 | #undef V |
613 | default: |
614 | FAIL("Invalid member of stdlib.Math")failed_ = true; failure_message_ = "Invalid member of stdlib.Math" ; failure_location_ = static_cast<int>(scanner_.Position ()); return ;; |
615 | } |
616 | } else if (Check(TOK(Infinity)AsmJsScanner::kToken_Infinity)) { |
617 | DeclareGlobal(info, false, AsmType::Double(), kWasmF64, |
618 | WasmInitExpr(std::numeric_limits<double>::infinity())); |
619 | stdlib_uses_.Add(StandardMember::kInfinity); |
620 | } else if (Check(TOK(NaN)AsmJsScanner::kToken_NaN)) { |
621 | DeclareGlobal(info, false, AsmType::Double(), kWasmF64, |
622 | WasmInitExpr(std::numeric_limits<double>::quiet_NaN())); |
623 | stdlib_uses_.Add(StandardMember::kNaN); |
624 | } else { |
625 | FAIL("Invalid member of stdlib")failed_ = true; failure_message_ = "Invalid member of stdlib" ; failure_location_ = static_cast<int>(scanner_.Position ()); return ;; |
626 | } |
627 | } |
628 | |
629 | // 6.2 ValidateExport |
630 | void AsmJsParser::ValidateExport() { |
631 | // clang-format off |
632 | EXPECT_TOKEN(TOK(return))do { if (scanner_.Token() != AsmJsScanner::kToken_return) { failed_ = true; failure_message_ = "Unexpected token"; failure_location_ = static_cast<int>(scanner_.Position()); return ;; } scanner_ .Next(); } while (false); |
633 | // clang-format on |
634 | if (Check('{')) { |
635 | for (;;) { |
636 | base::Vector<const char> name = CopyCurrentIdentifierString(); |
637 | if (!scanner_.IsGlobal() && !scanner_.IsLocal()) { |
638 | FAIL("Illegal export name")failed_ = true; failure_message_ = "Illegal export name"; failure_location_ = static_cast<int>(scanner_.Position()); return ;; |
639 | } |
640 | Consume(); |
641 | EXPECT_TOKEN(':')do { if (scanner_.Token() != ':') { failed_ = true; failure_message_ = "Unexpected token"; failure_location_ = static_cast<int >(scanner_.Position()); return ;; } scanner_.Next(); } while (false); |
642 | if (!scanner_.IsGlobal()) { |
643 | FAIL("Expected function name")failed_ = true; failure_message_ = "Expected function name"; failure_location_ = static_cast<int>(scanner_.Position()); return ;; |
644 | } |
645 | VarInfo* info = GetVarInfo(Consume()); |
646 | if (info->kind != VarKind::kFunction) { |
647 | FAIL("Expected function")failed_ = true; failure_message_ = "Expected function"; failure_location_ = static_cast<int>(scanner_.Position()); return ;; |
648 | } |
649 | module_builder_->AddExport(name, info->function_builder); |
650 | if (Check(',')) { |
651 | if (!Peek('}')) { |
652 | continue; |
653 | } |
654 | } |
655 | break; |
656 | } |
657 | EXPECT_TOKEN('}')do { if (scanner_.Token() != '}') { failed_ = true; failure_message_ = "Unexpected token"; failure_location_ = static_cast<int >(scanner_.Position()); return ;; } scanner_.Next(); } while (false); |
658 | } else { |
659 | if (!scanner_.IsGlobal()) { |
660 | FAIL("Single function export must be a function name")failed_ = true; failure_message_ = "Single function export must be a function name" ; failure_location_ = static_cast<int>(scanner_.Position ()); return ;; |
661 | } |
662 | VarInfo* info = GetVarInfo(Consume()); |
663 | if (info->kind != VarKind::kFunction) { |
664 | FAIL("Single function export must be a function")failed_ = true; failure_message_ = "Single function export must be a function" ; failure_location_ = static_cast<int>(scanner_.Position ()); return ;; |
665 | } |
666 | module_builder_->AddExport(base::CStrVector(AsmJs::kSingleFunctionName), |
667 | info->function_builder); |
668 | } |
669 | } |
670 | |
671 | // 6.3 ValidateFunctionTable |
672 | void AsmJsParser::ValidateFunctionTable() { |
673 | EXPECT_TOKEN(TOK(var))do { if (scanner_.Token() != AsmJsScanner::kToken_var) { failed_ = true; failure_message_ = "Unexpected token"; failure_location_ = static_cast<int>(scanner_.Position()); return ;; } scanner_ .Next(); } while (false); |
674 | if (!scanner_.IsGlobal()) { |
675 | FAIL("Expected table name")failed_ = true; failure_message_ = "Expected table name"; failure_location_ = static_cast<int>(scanner_.Position()); return ;; |
676 | } |
677 | VarInfo* table_info = GetVarInfo(Consume()); |
678 | if (table_info->kind == VarKind::kTable) { |
679 | if (table_info->function_defined) { |
680 | FAIL("Function table redefined")failed_ = true; failure_message_ = "Function table redefined" ; failure_location_ = static_cast<int>(scanner_.Position ()); return ;; |
681 | } |
682 | table_info->function_defined = true; |
683 | } else if (table_info->kind != VarKind::kUnused) { |
684 | FAIL("Function table name collides")failed_ = true; failure_message_ = "Function table name collides" ; failure_location_ = static_cast<int>(scanner_.Position ()); return ;; |
685 | } |
686 | EXPECT_TOKEN('=')do { if (scanner_.Token() != '=') { failed_ = true; failure_message_ = "Unexpected token"; failure_location_ = static_cast<int >(scanner_.Position()); return ;; } scanner_.Next(); } while (false); |
687 | EXPECT_TOKEN('[')do { if (scanner_.Token() != '[') { failed_ = true; failure_message_ = "Unexpected token"; failure_location_ = static_cast<int >(scanner_.Position()); return ;; } scanner_.Next(); } while (false); |
688 | uint64_t count = 0; |
689 | for (;;) { |
690 | if (!scanner_.IsGlobal()) { |
691 | FAIL("Expected function name")failed_ = true; failure_message_ = "Expected function name"; failure_location_ = static_cast<int>(scanner_.Position()); return ;; |
692 | } |
693 | VarInfo* info = GetVarInfo(Consume()); |
694 | if (info->kind != VarKind::kFunction) { |
695 | FAIL("Expected function")failed_ = true; failure_message_ = "Expected function"; failure_location_ = static_cast<int>(scanner_.Position()); return ;; |
696 | } |
697 | // Only store the function into a table if we used the table somewhere |
698 | // (i.e. tables are first seen at their use sites and allocated there). |
699 | if (table_info->kind == VarKind::kTable) { |
700 | if (count >= static_cast<uint64_t>(table_info->mask) + 1) { |
701 | FAIL("Exceeded function table size")failed_ = true; failure_message_ = "Exceeded function table size" ; failure_location_ = static_cast<int>(scanner_.Position ()); return ;; |
702 | } |
703 | if (!info->type->IsA(table_info->type)) { |
704 | FAIL("Function table definition doesn't match use")failed_ = true; failure_message_ = "Function table definition doesn't match use" ; failure_location_ = static_cast<int>(scanner_.Position ()); return ;; |
705 | } |
706 | module_builder_->SetIndirectFunction( |
707 | 0, static_cast<uint32_t>(table_info->index + count), info->index, |
708 | WasmModuleBuilder::WasmElemSegment::kRelativeToDeclaredFunctions); |
709 | } |
710 | ++count; |
711 | if (Check(',')) { |
712 | if (!Peek(']')) { |
713 | continue; |
714 | } |
715 | } |
716 | break; |
717 | } |
718 | EXPECT_TOKEN(']')do { if (scanner_.Token() != ']') { failed_ = true; failure_message_ = "Unexpected token"; failure_location_ = static_cast<int >(scanner_.Position()); return ;; } scanner_.Next(); } while (false); |
719 | if (table_info->kind == VarKind::kTable && |
720 | count != static_cast<uint64_t>(table_info->mask) + 1) { |
721 | FAIL("Function table size does not match uses")failed_ = true; failure_message_ = "Function table size does not match uses" ; failure_location_ = static_cast<int>(scanner_.Position ()); return ;; |
722 | } |
723 | SkipSemicolon(); |
724 | } |
725 | |
726 | // 6.4 ValidateFunction |
727 | void AsmJsParser::ValidateFunction() { |
728 | // Remember position of the 'function' token as start position. |
729 | size_t function_start_position = scanner_.Position(); |
730 | |
731 | EXPECT_TOKEN(TOK(function))do { if (scanner_.Token() != AsmJsScanner::kToken_function) { failed_ = true; failure_message_ = "Unexpected token"; failure_location_ = static_cast<int>(scanner_.Position()); return ;; } scanner_ .Next(); } while (false); |
732 | if (!scanner_.IsGlobal()) { |
733 | FAIL("Expected function name")failed_ = true; failure_message_ = "Expected function name"; failure_location_ = static_cast<int>(scanner_.Position()); return ;; |
734 | } |
735 | |
736 | base::Vector<const char> function_name_str = CopyCurrentIdentifierString(); |
737 | AsmJsScanner::token_t function_name = Consume(); |
738 | VarInfo* function_info = GetVarInfo(function_name); |
739 | if (function_info->kind == VarKind::kUnused) { |
740 | function_info->kind = VarKind::kFunction; |
741 | function_info->function_builder = module_builder_->AddFunction(); |
742 | function_info->index = function_info->function_builder->func_index(); |
743 | function_info->mutable_variable = false; |
744 | } else if (function_info->kind != VarKind::kFunction) { |
745 | FAIL("Function name collides with variable")failed_ = true; failure_message_ = "Function name collides with variable" ; failure_location_ = static_cast<int>(scanner_.Position ()); return ;; |
746 | } else if (function_info->function_defined) { |
747 | FAIL("Function redefined")failed_ = true; failure_message_ = "Function redefined"; failure_location_ = static_cast<int>(scanner_.Position()); return ;; |
748 | } |
749 | |
750 | function_info->function_defined = true; |
751 | function_info->function_builder->SetName(function_name_str); |
752 | current_function_builder_ = function_info->function_builder; |
753 | return_type_ = nullptr; |
754 | |
755 | // Record start of the function, used as position for the stack check. |
756 | current_function_builder_->SetAsmFunctionStartPosition( |
757 | function_start_position); |
758 | |
759 | CachedVector<AsmType*> params(&cached_asm_type_p_vectors_); |
760 | ValidateFunctionParams(¶ms); |
761 | |
762 | // Check against limit on number of parameters. |
763 | if (params.size() > kV8MaxWasmFunctionParams) { |
764 | FAIL("Number of parameters exceeds internal limit")failed_ = true; failure_message_ = "Number of parameters exceeds internal limit" ; failure_location_ = static_cast<int>(scanner_.Position ()); return ;; |
765 | } |
766 | |
767 | CachedVector<ValueType> locals(&cached_valuetype_vectors_); |
768 | ValidateFunctionLocals(params.size(), &locals); |
769 | |
770 | function_temp_locals_offset_ = static_cast<uint32_t>( |
771 | params.size() + locals.size()); |
772 | function_temp_locals_used_ = 0; |
773 | function_temp_locals_depth_ = 0; |
774 | |
775 | bool last_statement_is_return = false; |
776 | while (!failed_ && !Peek('}')) { |
777 | // clang-format off |
778 | last_statement_is_return = Peek(TOK(return)AsmJsScanner::kToken_return); |
779 | // clang-format on |
780 | RECURSE(ValidateStatement()); |
781 | } |
782 | |
783 | size_t function_end_position = scanner_.Position() + 1; |
784 | |
785 | EXPECT_TOKEN('}')do { if (scanner_.Token() != '}') { failed_ = true; failure_message_ = "Unexpected token"; failure_location_ = static_cast<int >(scanner_.Position()); return ;; } scanner_.Next(); } while (false); |
786 | |
787 | if (!last_statement_is_return) { |
788 | if (return_type_ == nullptr) { |
789 | return_type_ = AsmType::Void(); |
790 | } else if (!return_type_->IsA(AsmType::Void())) { |
791 | FAIL("Expected return at end of non-void function")failed_ = true; failure_message_ = "Expected return at end of non-void function" ; failure_location_ = static_cast<int>(scanner_.Position ()); return ;; |
792 | } |
793 | } |
794 | DCHECK_NOT_NULL(return_type_)((void) 0); |
795 | |
796 | // TODO(bradnelson): WasmModuleBuilder can't take this in the right order. |
797 | // We should fix that so we can use it instead. |
798 | FunctionSig* sig = ConvertSignature(return_type_, params); |
799 | current_function_builder_->SetSignature(sig); |
800 | for (auto local : locals) { |
801 | current_function_builder_->AddLocal(local); |
802 | } |
803 | // Add bonus temps. |
804 | for (int i = 0; i < function_temp_locals_used_; ++i) { |
805 | current_function_builder_->AddLocal(kWasmI32); |
806 | } |
807 | |
808 | // Check against limit on number of local variables. |
809 | if (locals.size() + function_temp_locals_used_ > kV8MaxWasmFunctionLocals) { |
810 | FAIL("Number of local variables exceeds internal limit")failed_ = true; failure_message_ = "Number of local variables exceeds internal limit" ; failure_location_ = static_cast<int>(scanner_.Position ()); return ;; |
811 | } |
812 | |
813 | // End function |
814 | current_function_builder_->Emit(kExprEnd); |
815 | |
816 | // Emit function end position as the last position for this function. |
817 | current_function_builder_->AddAsmWasmOffset(function_end_position, |
818 | function_end_position); |
819 | |
820 | if (current_function_builder_->GetPosition() > kV8MaxWasmFunctionSize) { |
821 | FAIL("Size of function body exceeds internal limit")failed_ = true; failure_message_ = "Size of function body exceeds internal limit" ; failure_location_ = static_cast<int>(scanner_.Position ()); return ;; |
822 | } |
823 | // Record (or validate) function type. |
824 | AsmType* function_type = AsmType::Function(zone(), return_type_); |
825 | for (auto t : params) { |
826 | function_type->AsFunctionType()->AddArgument(t); |
827 | } |
828 | function_info = GetVarInfo(function_name); |
829 | if (function_info->type->IsA(AsmType::None())) { |
830 | DCHECK_EQ(function_info->kind, VarKind::kFunction)((void) 0); |
831 | function_info->type = function_type; |
832 | } else if (!function_type->IsA(function_info->type)) { |
833 | // TODO(bradnelson): Should IsExactly be used here? |
834 | FAIL("Function definition doesn't match use")failed_ = true; failure_message_ = "Function definition doesn't match use" ; failure_location_ = static_cast<int>(scanner_.Position ()); return ;; |
835 | } |
836 | |
837 | scanner_.ResetLocals(); |
838 | std::fill(local_var_info_.begin(), local_var_info_.end(), VarInfo{}); |
839 | } |
840 | |
841 | // 6.4 ValidateFunction |
842 | void AsmJsParser::ValidateFunctionParams(ZoneVector<AsmType*>* params) { |
843 | // TODO(bradnelson): Do this differently so that the scanner doesn't need to |
844 | // have a state transition that needs knowledge of how the scanner works |
845 | // inside. |
846 | scanner_.EnterLocalScope(); |
847 | EXPECT_TOKEN('(')do { if (scanner_.Token() != '(') { failed_ = true; failure_message_ = "Unexpected token"; failure_location_ = static_cast<int >(scanner_.Position()); return ;; } scanner_.Next(); } while (false); |
848 | CachedVector<AsmJsScanner::token_t> function_parameters( |
849 | &cached_token_t_vectors_); |
850 | while (!failed_ && !Peek(')')) { |
851 | if (!scanner_.IsLocal()) { |
852 | FAIL("Expected parameter name")failed_ = true; failure_message_ = "Expected parameter name"; failure_location_ = static_cast<int>(scanner_.Position ()); return ;; |
853 | } |
854 | function_parameters.push_back(Consume()); |
855 | if (!Peek(')')) { |
856 | EXPECT_TOKEN(',')do { if (scanner_.Token() != ',') { failed_ = true; failure_message_ = "Unexpected token"; failure_location_ = static_cast<int >(scanner_.Position()); return ;; } scanner_.Next(); } while (false); |
857 | } |
858 | } |
859 | EXPECT_TOKEN(')')do { if (scanner_.Token() != ')') { failed_ = true; failure_message_ = "Unexpected token"; failure_location_ = static_cast<int >(scanner_.Position()); return ;; } scanner_.Next(); } while (false); |
860 | scanner_.EnterGlobalScope(); |
861 | EXPECT_TOKEN('{')do { if (scanner_.Token() != '{') { failed_ = true; failure_message_ = "Unexpected token"; failure_location_ = static_cast<int >(scanner_.Position()); return ;; } scanner_.Next(); } while (false); |
862 | // 5.1 Parameter Type Annotations |
863 | for (auto p : function_parameters) { |
864 | EXPECT_TOKEN(p)do { if (scanner_.Token() != p) { failed_ = true; failure_message_ = "Unexpected token"; failure_location_ = static_cast<int >(scanner_.Position()); return ;; } scanner_.Next(); } while (false); |
865 | EXPECT_TOKEN('=')do { if (scanner_.Token() != '=') { failed_ = true; failure_message_ = "Unexpected token"; failure_location_ = static_cast<int >(scanner_.Position()); return ;; } scanner_.Next(); } while (false); |
866 | VarInfo* info = GetVarInfo(p); |
867 | if (info->kind != VarKind::kUnused) { |
868 | FAIL("Duplicate parameter name")failed_ = true; failure_message_ = "Duplicate parameter name" ; failure_location_ = static_cast<int>(scanner_.Position ()); return ;; |
869 | } |
870 | if (Check(p)) { |
871 | EXPECT_TOKEN('|')do { if (scanner_.Token() != '|') { failed_ = true; failure_message_ = "Unexpected token"; failure_location_ = static_cast<int >(scanner_.Position()); return ;; } scanner_.Next(); } while (false); |
872 | if (!CheckForZero()) { |
873 | FAIL("Bad integer parameter annotation.")failed_ = true; failure_message_ = "Bad integer parameter annotation." ; failure_location_ = static_cast<int>(scanner_.Position ()); return ;; |
874 | } |
875 | info->kind = VarKind::kLocal; |
876 | info->type = AsmType::Int(); |
877 | info->index = static_cast<uint32_t>(params->size()); |
878 | params->push_back(AsmType::Int()); |
879 | } else if (Check('+')) { |
880 | EXPECT_TOKEN(p)do { if (scanner_.Token() != p) { failed_ = true; failure_message_ = "Unexpected token"; failure_location_ = static_cast<int >(scanner_.Position()); return ;; } scanner_.Next(); } while (false); |
881 | info->kind = VarKind::kLocal; |
882 | info->type = AsmType::Double(); |
883 | info->index = static_cast<uint32_t>(params->size()); |
884 | params->push_back(AsmType::Double()); |
885 | } else { |
886 | if (!scanner_.IsGlobal() || |
887 | !GetVarInfo(Consume())->type->IsA(stdlib_fround_)) { |
888 | FAIL("Expected fround")failed_ = true; failure_message_ = "Expected fround"; failure_location_ = static_cast<int>(scanner_.Position()); return ;; |
889 | } |
890 | EXPECT_TOKEN('(')do { if (scanner_.Token() != '(') { failed_ = true; failure_message_ = "Unexpected token"; failure_location_ = static_cast<int >(scanner_.Position()); return ;; } scanner_.Next(); } while (false); |
891 | EXPECT_TOKEN(p)do { if (scanner_.Token() != p) { failed_ = true; failure_message_ = "Unexpected token"; failure_location_ = static_cast<int >(scanner_.Position()); return ;; } scanner_.Next(); } while (false); |
892 | EXPECT_TOKEN(')')do { if (scanner_.Token() != ')') { failed_ = true; failure_message_ = "Unexpected token"; failure_location_ = static_cast<int >(scanner_.Position()); return ;; } scanner_.Next(); } while (false); |
893 | info->kind = VarKind::kLocal; |
894 | info->type = AsmType::Float(); |
895 | info->index = static_cast<uint32_t>(params->size()); |
896 | params->push_back(AsmType::Float()); |
897 | } |
898 | SkipSemicolon(); |
899 | } |
900 | } |
901 | |
902 | // 6.4 ValidateFunction - locals |
903 | void AsmJsParser::ValidateFunctionLocals(size_t param_count, |
904 | ZoneVector<ValueType>* locals) { |
905 | DCHECK(locals->empty())((void) 0); |
906 | // Local Variables. |
907 | while (Peek(TOK(var)AsmJsScanner::kToken_var)) { |
908 | scanner_.EnterLocalScope(); |
909 | EXPECT_TOKEN(TOK(var))do { if (scanner_.Token() != AsmJsScanner::kToken_var) { failed_ = true; failure_message_ = "Unexpected token"; failure_location_ = static_cast<int>(scanner_.Position()); return ;; } scanner_ .Next(); } while (false); |
910 | scanner_.EnterGlobalScope(); |
911 | for (;;) { |
912 | if (!scanner_.IsLocal()) { |
913 | FAIL("Expected local variable identifier")failed_ = true; failure_message_ = "Expected local variable identifier" ; failure_location_ = static_cast<int>(scanner_.Position ()); return ;; |
914 | } |
915 | VarInfo* info = GetVarInfo(Consume()); |
916 | if (info->kind != VarKind::kUnused) { |
917 | FAIL("Duplicate local variable name")failed_ = true; failure_message_ = "Duplicate local variable name" ; failure_location_ = static_cast<int>(scanner_.Position ()); return ;; |
918 | } |
919 | // Store types. |
920 | EXPECT_TOKEN('=')do { if (scanner_.Token() != '=') { failed_ = true; failure_message_ = "Unexpected token"; failure_location_ = static_cast<int >(scanner_.Position()); return ;; } scanner_.Next(); } while (false); |
921 | double dvalue = 0.0; |
922 | uint32_t uvalue = 0; |
923 | if (Check('-')) { |
924 | if (CheckForDouble(&dvalue)) { |
925 | info->kind = VarKind::kLocal; |
926 | info->type = AsmType::Double(); |
927 | info->index = static_cast<uint32_t>(param_count + locals->size()); |
928 | locals->push_back(kWasmF64); |
929 | current_function_builder_->EmitF64Const(-dvalue); |
930 | current_function_builder_->EmitSetLocal(info->index); |
931 | } else if (CheckForUnsigned(&uvalue)) { |
932 | if (uvalue > 0x7FFFFFFF) { |
933 | FAIL("Numeric literal out of range")failed_ = true; failure_message_ = "Numeric literal out of range" ; failure_location_ = static_cast<int>(scanner_.Position ()); return ;; |
934 | } |
935 | info->kind = VarKind::kLocal; |
936 | info->type = AsmType::Int(); |
937 | info->index = static_cast<uint32_t>(param_count + locals->size()); |
938 | locals->push_back(kWasmI32); |
939 | int32_t value = -static_cast<int32_t>(uvalue); |
940 | current_function_builder_->EmitI32Const(value); |
941 | current_function_builder_->EmitSetLocal(info->index); |
942 | } else { |
943 | FAIL("Expected variable initial value")failed_ = true; failure_message_ = "Expected variable initial value" ; failure_location_ = static_cast<int>(scanner_.Position ()); return ;; |
944 | } |
945 | } else if (scanner_.IsGlobal()) { |
946 | VarInfo* sinfo = GetVarInfo(Consume()); |
947 | if (sinfo->kind == VarKind::kGlobal) { |
948 | if (sinfo->mutable_variable) { |
949 | FAIL("Initializing from global requires const variable")failed_ = true; failure_message_ = "Initializing from global requires const variable" ; failure_location_ = static_cast<int>(scanner_.Position ()); return ;; |
950 | } |
951 | info->kind = VarKind::kLocal; |
952 | info->type = sinfo->type; |
953 | info->index = static_cast<uint32_t>(param_count + locals->size()); |
954 | if (sinfo->type->IsA(AsmType::Int())) { |
955 | locals->push_back(kWasmI32); |
956 | } else if (sinfo->type->IsA(AsmType::Float())) { |
957 | locals->push_back(kWasmF32); |
958 | } else if (sinfo->type->IsA(AsmType::Double())) { |
959 | locals->push_back(kWasmF64); |
960 | } else { |
961 | FAIL("Bad local variable definition")failed_ = true; failure_message_ = "Bad local variable definition" ; failure_location_ = static_cast<int>(scanner_.Position ()); return ;; |
962 | } |
963 | current_function_builder_->EmitWithI32V(kExprGlobalGet, |
964 | VarIndex(sinfo)); |
965 | current_function_builder_->EmitSetLocal(info->index); |
966 | } else if (sinfo->type->IsA(stdlib_fround_)) { |
967 | EXPECT_TOKEN('(')do { if (scanner_.Token() != '(') { failed_ = true; failure_message_ = "Unexpected token"; failure_location_ = static_cast<int >(scanner_.Position()); return ;; } scanner_.Next(); } while (false); |
968 | bool negate = false; |
969 | if (Check('-')) { |
970 | negate = true; |
971 | } |
972 | if (CheckForDouble(&dvalue)) { |
973 | info->kind = VarKind::kLocal; |
974 | info->type = AsmType::Float(); |
975 | info->index = static_cast<uint32_t>(param_count + locals->size()); |
976 | locals->push_back(kWasmF32); |
977 | if (negate) { |
978 | dvalue = -dvalue; |
979 | } |
980 | float fvalue = DoubleToFloat32(dvalue); |
981 | current_function_builder_->EmitF32Const(fvalue); |
982 | current_function_builder_->EmitSetLocal(info->index); |
983 | } else if (CheckForUnsigned(&uvalue)) { |
984 | if (uvalue > 0x7FFFFFFF) { |
985 | FAIL("Numeric literal out of range")failed_ = true; failure_message_ = "Numeric literal out of range" ; failure_location_ = static_cast<int>(scanner_.Position ()); return ;; |
986 | } |
987 | info->kind = VarKind::kLocal; |
988 | info->type = AsmType::Float(); |
989 | info->index = static_cast<uint32_t>(param_count + locals->size()); |
990 | locals->push_back(kWasmF32); |
991 | int32_t value = static_cast<int32_t>(uvalue); |
992 | if (negate) { |
993 | value = -value; |
994 | } |
995 | float fvalue = static_cast<float>(value); |
996 | current_function_builder_->EmitF32Const(fvalue); |
997 | current_function_builder_->EmitSetLocal(info->index); |
998 | } else { |
999 | FAIL("Expected variable initial value")failed_ = true; failure_message_ = "Expected variable initial value" ; failure_location_ = static_cast<int>(scanner_.Position ()); return ;; |
1000 | } |
1001 | EXPECT_TOKEN(')')do { if (scanner_.Token() != ')') { failed_ = true; failure_message_ = "Unexpected token"; failure_location_ = static_cast<int >(scanner_.Position()); return ;; } scanner_.Next(); } while (false); |
1002 | } else { |
1003 | FAIL("expected fround or const global")failed_ = true; failure_message_ = "expected fround or const global" ; failure_location_ = static_cast<int>(scanner_.Position ()); return ;; |
1004 | } |
1005 | } else if (CheckForDouble(&dvalue)) { |
1006 | info->kind = VarKind::kLocal; |
1007 | info->type = AsmType::Double(); |
1008 | info->index = static_cast<uint32_t>(param_count + locals->size()); |
1009 | locals->push_back(kWasmF64); |
1010 | current_function_builder_->EmitF64Const(dvalue); |
1011 | current_function_builder_->EmitSetLocal(info->index); |
1012 | } else if (CheckForUnsigned(&uvalue)) { |
1013 | info->kind = VarKind::kLocal; |
1014 | info->type = AsmType::Int(); |
1015 | info->index = static_cast<uint32_t>(param_count + locals->size()); |
1016 | locals->push_back(kWasmI32); |
1017 | int32_t value = static_cast<int32_t>(uvalue); |
1018 | current_function_builder_->EmitI32Const(value); |
1019 | current_function_builder_->EmitSetLocal(info->index); |
1020 | } else { |
1021 | FAIL("Expected variable initial value")failed_ = true; failure_message_ = "Expected variable initial value" ; failure_location_ = static_cast<int>(scanner_.Position ()); return ;; |
1022 | } |
1023 | if (!Peek(',')) { |
1024 | break; |
1025 | } |
1026 | scanner_.EnterLocalScope(); |
1027 | EXPECT_TOKEN(',')do { if (scanner_.Token() != ',') { failed_ = true; failure_message_ = "Unexpected token"; failure_location_ = static_cast<int >(scanner_.Position()); return ;; } scanner_.Next(); } while (false); |
1028 | scanner_.EnterGlobalScope(); |
1029 | } |
1030 | SkipSemicolon(); |
1031 | } |
1032 | } |
1033 | |
1034 | // 6.5 ValidateStatement |
1035 | void AsmJsParser::ValidateStatement() { |
1036 | call_coercion_ = nullptr; |
1037 | if (Peek('{')) { |
1038 | RECURSE(Block()); |
1039 | } else if (Peek(';')) { |
1040 | RECURSE(EmptyStatement()); |
1041 | } else if (Peek(TOK(if)AsmJsScanner::kToken_if)) { |
1042 | RECURSE(IfStatement()); |
1043 | // clang-format off |
1044 | } else if (Peek(TOK(return)AsmJsScanner::kToken_return)) { |
1045 | // clang-format on |
1046 | RECURSE(ReturnStatement()); |
1047 | } else if (IterationStatement()) { |
1048 | // Handled in IterationStatement. |
1049 | } else if (Peek(TOK(break)AsmJsScanner::kToken_break)) { |
1050 | RECURSE(BreakStatement()); |
1051 | } else if (Peek(TOK(continue)AsmJsScanner::kToken_continue)) { |
1052 | RECURSE(ContinueStatement()); |
1053 | } else if (Peek(TOK(switch)AsmJsScanner::kToken_switch)) { |
1054 | RECURSE(SwitchStatement()); |
1055 | } else { |
1056 | RECURSE(ExpressionStatement()); |
1057 | } |
1058 | } |
1059 | |
1060 | // 6.5.1 Block |
1061 | void AsmJsParser::Block() { |
1062 | bool can_break_to_block = pending_label_ != 0; |
1063 | if (can_break_to_block) { |
1064 | BareBegin(BlockKind::kNamed, pending_label_); |
1065 | current_function_builder_->EmitWithU8(kExprBlock, kVoidCode); |
1066 | } |
1067 | pending_label_ = 0; |
1068 | EXPECT_TOKEN('{')do { if (scanner_.Token() != '{') { failed_ = true; failure_message_ = "Unexpected token"; failure_location_ = static_cast<int >(scanner_.Position()); return ;; } scanner_.Next(); } while (false); |
1069 | while (!failed_ && !Peek('}')) { |
1070 | RECURSE(ValidateStatement()); |
1071 | } |
1072 | EXPECT_TOKEN('}')do { if (scanner_.Token() != '}') { failed_ = true; failure_message_ = "Unexpected token"; failure_location_ = static_cast<int >(scanner_.Position()); return ;; } scanner_.Next(); } while (false); |
1073 | if (can_break_to_block) { |
1074 | End(); |
1075 | } |
1076 | } |
1077 | |
1078 | // 6.5.2 ExpressionStatement |
1079 | void AsmJsParser::ExpressionStatement() { |
1080 | if (scanner_.IsGlobal() || scanner_.IsLocal()) { |
1081 | // NOTE: Both global or local identifiers can also be used as labels. |
1082 | scanner_.Next(); |
1083 | if (Peek(':')) { |
1084 | scanner_.Rewind(); |
1085 | RECURSE(LabelledStatement()); |
1086 | return; |
1087 | } |
1088 | scanner_.Rewind(); |
1089 | } |
1090 | AsmType* ret; |
1091 | RECURSE(ret = ValidateExpression()); |
1092 | if (!ret->IsA(AsmType::Void())) { |
1093 | current_function_builder_->Emit(kExprDrop); |
1094 | } |
1095 | SkipSemicolon(); |
1096 | } |
1097 | |
1098 | // 6.5.3 EmptyStatement |
1099 | void AsmJsParser::EmptyStatement() { EXPECT_TOKEN(';')do { if (scanner_.Token() != ';') { failed_ = true; failure_message_ = "Unexpected token"; failure_location_ = static_cast<int >(scanner_.Position()); return ;; } scanner_.Next(); } while (false); } |
1100 | |
1101 | // 6.5.4 IfStatement |
1102 | void AsmJsParser::IfStatement() { |
1103 | EXPECT_TOKEN(TOK(if))do { if (scanner_.Token() != AsmJsScanner::kToken_if) { failed_ = true; failure_message_ = "Unexpected token"; failure_location_ = static_cast<int>(scanner_.Position()); return ;; } scanner_ .Next(); } while (false); |
1104 | EXPECT_TOKEN('(')do { if (scanner_.Token() != '(') { failed_ = true; failure_message_ = "Unexpected token"; failure_location_ = static_cast<int >(scanner_.Position()); return ;; } scanner_.Next(); } while (false); |
1105 | RECURSE(Expression(AsmType::Int())); |
1106 | EXPECT_TOKEN(')')do { if (scanner_.Token() != ')') { failed_ = true; failure_message_ = "Unexpected token"; failure_location_ = static_cast<int >(scanner_.Position()); return ;; } scanner_.Next(); } while (false); |
1107 | BareBegin(BlockKind::kOther); |
1108 | current_function_builder_->EmitWithU8(kExprIf, kVoidCode); |
1109 | RECURSE(ValidateStatement()); |
1110 | if (Check(TOK(else)AsmJsScanner::kToken_else)) { |
1111 | current_function_builder_->Emit(kExprElse); |
1112 | RECURSE(ValidateStatement()); |
1113 | } |
1114 | current_function_builder_->Emit(kExprEnd); |
1115 | BareEnd(); |
1116 | } |
1117 | |
1118 | // 6.5.5 ReturnStatement |
1119 | void AsmJsParser::ReturnStatement() { |
1120 | // clang-format off |
1121 | EXPECT_TOKEN(TOK(return))do { if (scanner_.Token() != AsmJsScanner::kToken_return) { failed_ = true; failure_message_ = "Unexpected token"; failure_location_ = static_cast<int>(scanner_.Position()); return ;; } scanner_ .Next(); } while (false); |
1122 | // clang-format on |
1123 | if (!Peek(';') && !Peek('}')) { |
1124 | // TODO(bradnelson): See if this can be factored out. |
1125 | AsmType* ret; |
1126 | RECURSE(ret = Expression(return_type_)); |
1127 | if (ret->IsA(AsmType::Double())) { |
1128 | return_type_ = AsmType::Double(); |
1129 | } else if (ret->IsA(AsmType::Float())) { |
1130 | return_type_ = AsmType::Float(); |
1131 | } else if (ret->IsA(AsmType::Signed())) { |
1132 | return_type_ = AsmType::Signed(); |
1133 | } else { |
1134 | FAIL("Invalid return type")failed_ = true; failure_message_ = "Invalid return type"; failure_location_ = static_cast<int>(scanner_.Position()); return ;; |
1135 | } |
1136 | } else if (return_type_ == nullptr) { |
1137 | return_type_ = AsmType::Void(); |
1138 | } else if (!return_type_->IsA(AsmType::Void())) { |
1139 | FAIL("Invalid void return type")failed_ = true; failure_message_ = "Invalid void return type" ; failure_location_ = static_cast<int>(scanner_.Position ()); return ;; |
1140 | } |
1141 | current_function_builder_->Emit(kExprReturn); |
1142 | SkipSemicolon(); |
1143 | } |
1144 | |
1145 | // 6.5.6 IterationStatement |
1146 | bool AsmJsParser::IterationStatement() { |
1147 | if (Peek(TOK(while)AsmJsScanner::kToken_while)) { |
1148 | WhileStatement(); |
1149 | } else if (Peek(TOK(do)AsmJsScanner::kToken_do)) { |
1150 | DoStatement(); |
1151 | } else if (Peek(TOK(for)AsmJsScanner::kToken_for)) { |
1152 | ForStatement(); |
1153 | } else { |
1154 | return false; |
1155 | } |
1156 | return true; |
1157 | } |
1158 | |
1159 | // 6.5.6 IterationStatement - while |
1160 | void AsmJsParser::WhileStatement() { |
1161 | // a: block { |
1162 | Begin(pending_label_); |
1163 | // b: loop { |
1164 | Loop(pending_label_); |
1165 | pending_label_ = 0; |
1166 | EXPECT_TOKEN(TOK(while))do { if (scanner_.Token() != AsmJsScanner::kToken_while) { failed_ = true; failure_message_ = "Unexpected token"; failure_location_ = static_cast<int>(scanner_.Position()); return ;; } scanner_ .Next(); } while (false); |
1167 | EXPECT_TOKEN('(')do { if (scanner_.Token() != '(') { failed_ = true; failure_message_ = "Unexpected token"; failure_location_ = static_cast<int >(scanner_.Position()); return ;; } scanner_.Next(); } while (false); |
1168 | RECURSE(Expression(AsmType::Int())); |
1169 | EXPECT_TOKEN(')')do { if (scanner_.Token() != ')') { failed_ = true; failure_message_ = "Unexpected token"; failure_location_ = static_cast<int >(scanner_.Position()); return ;; } scanner_.Next(); } while (false); |
1170 | // if (!CONDITION) break a; |
1171 | current_function_builder_->Emit(kExprI32Eqz); |
1172 | current_function_builder_->EmitWithU8(kExprBrIf, 1); |
1173 | // BODY |
1174 | RECURSE(ValidateStatement()); |
1175 | // continue b; |
1176 | current_function_builder_->EmitWithU8(kExprBr, 0); |
1177 | End(); |
1178 | // } |
1179 | // } |
1180 | End(); |
1181 | } |
1182 | |
1183 | // 6.5.6 IterationStatement - do |
1184 | void AsmJsParser::DoStatement() { |
1185 | // a: block { |
1186 | Begin(pending_label_); |
1187 | // b: loop { |
1188 | Loop(); |
1189 | // c: block { // but treated like loop so continue works |
1190 | BareBegin(BlockKind::kLoop, pending_label_); |
1191 | current_function_builder_->EmitWithU8(kExprBlock, kVoidCode); |
1192 | pending_label_ = 0; |
1193 | EXPECT_TOKEN(TOK(do))do { if (scanner_.Token() != AsmJsScanner::kToken_do) { failed_ = true; failure_message_ = "Unexpected token"; failure_location_ = static_cast<int>(scanner_.Position()); return ;; } scanner_ .Next(); } while (false); |
1194 | // BODY |
1195 | RECURSE(ValidateStatement()); |
1196 | EXPECT_TOKEN(TOK(while))do { if (scanner_.Token() != AsmJsScanner::kToken_while) { failed_ = true; failure_message_ = "Unexpected token"; failure_location_ = static_cast<int>(scanner_.Position()); return ;; } scanner_ .Next(); } while (false); |
1197 | End(); |
1198 | // } // end c |
1199 | EXPECT_TOKEN('(')do { if (scanner_.Token() != '(') { failed_ = true; failure_message_ = "Unexpected token"; failure_location_ = static_cast<int >(scanner_.Position()); return ;; } scanner_.Next(); } while (false); |
1200 | RECURSE(Expression(AsmType::Int())); |
1201 | // if (!CONDITION) break a; |
1202 | current_function_builder_->Emit(kExprI32Eqz); |
1203 | current_function_builder_->EmitWithU8(kExprBrIf, 1); |
1204 | // continue b; |
1205 | current_function_builder_->EmitWithU8(kExprBr, 0); |
1206 | EXPECT_TOKEN(')')do { if (scanner_.Token() != ')') { failed_ = true; failure_message_ = "Unexpected token"; failure_location_ = static_cast<int >(scanner_.Position()); return ;; } scanner_.Next(); } while (false); |
1207 | // } // end b |
1208 | End(); |
1209 | // } // end a |
1210 | End(); |
1211 | SkipSemicolon(); |
1212 | } |
1213 | |
1214 | // 6.5.6 IterationStatement - for |
1215 | void AsmJsParser::ForStatement() { |
1216 | EXPECT_TOKEN(TOK(for))do { if (scanner_.Token() != AsmJsScanner::kToken_for) { failed_ = true; failure_message_ = "Unexpected token"; failure_location_ = static_cast<int>(scanner_.Position()); return ;; } scanner_ .Next(); } while (false); |
1217 | EXPECT_TOKEN('(')do { if (scanner_.Token() != '(') { failed_ = true; failure_message_ = "Unexpected token"; failure_location_ = static_cast<int >(scanner_.Position()); return ;; } scanner_.Next(); } while (false); |
1218 | if (!Peek(';')) { |
1219 | AsmType* ret; |
1220 | RECURSE(ret = Expression(nullptr)); |
1221 | if (!ret->IsA(AsmType::Void())) { |
1222 | current_function_builder_->Emit(kExprDrop); |
1223 | } |
1224 | } |
1225 | EXPECT_TOKEN(';')do { if (scanner_.Token() != ';') { failed_ = true; failure_message_ = "Unexpected token"; failure_location_ = static_cast<int >(scanner_.Position()); return ;; } scanner_.Next(); } while (false); |
1226 | // a: block { |
1227 | Begin(pending_label_); |
1228 | // b: loop { |
1229 | Loop(); |
1230 | // c: block { // but treated like loop so continue works |
1231 | BareBegin(BlockKind::kLoop, pending_label_); |
1232 | current_function_builder_->EmitWithU8(kExprBlock, kVoidCode); |
1233 | pending_label_ = 0; |
1234 | if (!Peek(';')) { |
1235 | // if (!CONDITION) break a; |
1236 | RECURSE(Expression(AsmType::Int())); |
1237 | current_function_builder_->Emit(kExprI32Eqz); |
1238 | current_function_builder_->EmitWithU8(kExprBrIf, 2); |
1239 | } |
1240 | EXPECT_TOKEN(';')do { if (scanner_.Token() != ';') { failed_ = true; failure_message_ = "Unexpected token"; failure_location_ = static_cast<int >(scanner_.Position()); return ;; } scanner_.Next(); } while (false); |
1241 | // Race past INCREMENT |
1242 | size_t increment_position = scanner_.Position(); |
1243 | ScanToClosingParenthesis(); |
1244 | EXPECT_TOKEN(')')do { if (scanner_.Token() != ')') { failed_ = true; failure_message_ = "Unexpected token"; failure_location_ = static_cast<int >(scanner_.Position()); return ;; } scanner_.Next(); } while (false); |
1245 | // BODY |
1246 | RECURSE(ValidateStatement()); |
1247 | // } // end c |
1248 | End(); |
1249 | // INCREMENT |
1250 | size_t end_position = scanner_.Position(); |
1251 | scanner_.Seek(increment_position); |
1252 | if (!Peek(')')) { |
1253 | RECURSE(Expression(nullptr)); |
1254 | // NOTE: No explicit drop because below break is an implicit drop. |
1255 | } |
1256 | // continue b; |
1257 | current_function_builder_->EmitWithU8(kExprBr, 0); |
1258 | scanner_.Seek(end_position); |
1259 | // } // end b |
1260 | End(); |
1261 | // } // end a |
1262 | End(); |
1263 | } |
1264 | |
1265 | // 6.5.7 BreakStatement |
1266 | void AsmJsParser::BreakStatement() { |
1267 | EXPECT_TOKEN(TOK(break))do { if (scanner_.Token() != AsmJsScanner::kToken_break) { failed_ = true; failure_message_ = "Unexpected token"; failure_location_ = static_cast<int>(scanner_.Position()); return ;; } scanner_ .Next(); } while (false); |
1268 | AsmJsScanner::token_t label_name = kTokenNone; |
1269 | if (scanner_.IsGlobal() || scanner_.IsLocal()) { |
1270 | // NOTE: Currently using globals/locals for labels too. |
1271 | label_name = Consume(); |
1272 | } |
1273 | int depth = FindBreakLabelDepth(label_name); |
1274 | if (depth < 0) { |
1275 | FAIL("Illegal break")failed_ = true; failure_message_ = "Illegal break"; failure_location_ = static_cast<int>(scanner_.Position()); return ;; |
1276 | } |
1277 | current_function_builder_->Emit(kExprBr); |
1278 | current_function_builder_->EmitI32V(depth); |
1279 | SkipSemicolon(); |
1280 | } |
1281 | |
1282 | // 6.5.8 ContinueStatement |
1283 | void AsmJsParser::ContinueStatement() { |
1284 | EXPECT_TOKEN(TOK(continue))do { if (scanner_.Token() != AsmJsScanner::kToken_continue) { failed_ = true; failure_message_ = "Unexpected token"; failure_location_ = static_cast<int>(scanner_.Position()); return ;; } scanner_ .Next(); } while (false); |
1285 | AsmJsScanner::token_t label_name = kTokenNone; |
1286 | if (scanner_.IsGlobal() || scanner_.IsLocal()) { |
1287 | // NOTE: Currently using globals/locals for labels too. |
1288 | label_name = Consume(); |
1289 | } |
1290 | int depth = FindContinueLabelDepth(label_name); |
1291 | if (depth < 0) { |
1292 | FAIL("Illegal continue")failed_ = true; failure_message_ = "Illegal continue"; failure_location_ = static_cast<int>(scanner_.Position()); return ;; |
1293 | } |
1294 | current_function_builder_->EmitWithI32V(kExprBr, depth); |
1295 | SkipSemicolon(); |
1296 | } |
1297 | |
1298 | // 6.5.9 LabelledStatement |
1299 | void AsmJsParser::LabelledStatement() { |
1300 | DCHECK(scanner_.IsGlobal() || scanner_.IsLocal())((void) 0); |
1301 | // NOTE: Currently using globals/locals for labels too. |
1302 | if (pending_label_ != 0) { |
1303 | FAIL("Double label unsupported")failed_ = true; failure_message_ = "Double label unsupported" ; failure_location_ = static_cast<int>(scanner_.Position ()); return ;; |
1304 | } |
1305 | pending_label_ = scanner_.Token(); |
1306 | scanner_.Next(); |
1307 | EXPECT_TOKEN(':')do { if (scanner_.Token() != ':') { failed_ = true; failure_message_ = "Unexpected token"; failure_location_ = static_cast<int >(scanner_.Position()); return ;; } scanner_.Next(); } while (false); |
1308 | RECURSE(ValidateStatement()); |
1309 | } |
1310 | |
1311 | // 6.5.10 SwitchStatement |
1312 | void AsmJsParser::SwitchStatement() { |
1313 | EXPECT_TOKEN(TOK(switch))do { if (scanner_.Token() != AsmJsScanner::kToken_switch) { failed_ = true; failure_message_ = "Unexpected token"; failure_location_ = static_cast<int>(scanner_.Position()); return ;; } scanner_ .Next(); } while (false); |
1314 | EXPECT_TOKEN('(')do { if (scanner_.Token() != '(') { failed_ = true; failure_message_ = "Unexpected token"; failure_location_ = static_cast<int >(scanner_.Position()); return ;; } scanner_.Next(); } while (false); |
1315 | AsmType* test; |
1316 | RECURSE(test = Expression(nullptr)); |
1317 | if (!test->IsA(AsmType::Signed())) { |
1318 | FAIL("Expected signed for switch value")failed_ = true; failure_message_ = "Expected signed for switch value" ; failure_location_ = static_cast<int>(scanner_.Position ()); return ;; |
1319 | } |
1320 | EXPECT_TOKEN(')')do { if (scanner_.Token() != ')') { failed_ = true; failure_message_ = "Unexpected token"; failure_location_ = static_cast<int >(scanner_.Position()); return ;; } scanner_.Next(); } while (false); |
1321 | uint32_t tmp = TempVariable(0); |
1322 | current_function_builder_->EmitSetLocal(tmp); |
1323 | Begin(pending_label_); |
1324 | pending_label_ = 0; |
1325 | // TODO(bradnelson): Make less weird. |
1326 | CachedVector<int32_t> cases(&cached_int_vectors_); |
1327 | GatherCases(&cases); |
1328 | EXPECT_TOKEN('{')do { if (scanner_.Token() != '{') { failed_ = true; failure_message_ = "Unexpected token"; failure_location_ = static_cast<int >(scanner_.Position()); return ;; } scanner_.Next(); } while (false); |
1329 | size_t count = cases.size() + 1; |
1330 | for (size_t i = 0; i < count; ++i) { |
1331 | BareBegin(BlockKind::kOther); |
1332 | current_function_builder_->EmitWithU8(kExprBlock, kVoidCode); |
1333 | } |
1334 | int table_pos = 0; |
1335 | for (auto c : cases) { |
1336 | current_function_builder_->EmitGetLocal(tmp); |
1337 | current_function_builder_->EmitI32Const(c); |
1338 | current_function_builder_->Emit(kExprI32Eq); |
1339 | current_function_builder_->EmitWithI32V(kExprBrIf, table_pos++); |
1340 | } |
1341 | current_function_builder_->EmitWithI32V(kExprBr, table_pos++); |
1342 | while (!failed_ && Peek(TOK(case)AsmJsScanner::kToken_case)) { |
1343 | current_function_builder_->Emit(kExprEnd); |
1344 | BareEnd(); |
1345 | RECURSE(ValidateCase()); |
1346 | } |
1347 | current_function_builder_->Emit(kExprEnd); |
1348 | BareEnd(); |
1349 | if (Peek(TOK(default)AsmJsScanner::kToken_default)) { |
1350 | RECURSE(ValidateDefault()); |
1351 | } |
1352 | EXPECT_TOKEN('}')do { if (scanner_.Token() != '}') { failed_ = true; failure_message_ = "Unexpected token"; failure_location_ = static_cast<int >(scanner_.Position()); return ;; } scanner_.Next(); } while (false); |
1353 | End(); |
1354 | } |
1355 | |
1356 | // 6.6. ValidateCase |
1357 | void AsmJsParser::ValidateCase() { |
1358 | EXPECT_TOKEN(TOK(case))do { if (scanner_.Token() != AsmJsScanner::kToken_case) { failed_ = true; failure_message_ = "Unexpected token"; failure_location_ = static_cast<int>(scanner_.Position()); return ;; } scanner_ .Next(); } while (false); |
1359 | bool negate = false; |
1360 | if (Check('-')) { |
1361 | negate = true; |
1362 | } |
1363 | uint32_t uvalue; |
1364 | if (!CheckForUnsigned(&uvalue)) { |
1365 | FAIL("Expected numeric literal")failed_ = true; failure_message_ = "Expected numeric literal" ; failure_location_ = static_cast<int>(scanner_.Position ()); return ;; |
1366 | } |
1367 | // TODO(bradnelson): Share negation plumbing. |
1368 | if ((negate && uvalue > 0x80000000) || (!negate && uvalue > 0x7FFFFFFF)) { |
1369 | FAIL("Numeric literal out of range")failed_ = true; failure_message_ = "Numeric literal out of range" ; failure_location_ = static_cast<int>(scanner_.Position ()); return ;; |
1370 | } |
1371 | int32_t value = static_cast<int32_t>(uvalue); |
1372 | DCHECK_IMPLIES(negate && uvalue == 0x80000000, value == kMinInt)((void) 0); |
1373 | if (negate && value != kMinInt) { |
1374 | value = -value; |
Value stored to 'value' is never read | |
1375 | } |
1376 | EXPECT_TOKEN(':')do { if (scanner_.Token() != ':') { failed_ = true; failure_message_ = "Unexpected token"; failure_location_ = static_cast<int >(scanner_.Position()); return ;; } scanner_.Next(); } while (false); |
1377 | while (!failed_ && !Peek('}') && !Peek(TOK(case)AsmJsScanner::kToken_case) && !Peek(TOK(default)AsmJsScanner::kToken_default)) { |
1378 | RECURSE(ValidateStatement()); |
1379 | } |
1380 | } |
1381 | |
1382 | // 6.7 ValidateDefault |
1383 | void AsmJsParser::ValidateDefault() { |
1384 | EXPECT_TOKEN(TOK(default))do { if (scanner_.Token() != AsmJsScanner::kToken_default) { failed_ = true; failure_message_ = "Unexpected token"; failure_location_ = static_cast<int>(scanner_.Position()); return ;; } scanner_ .Next(); } while (false); |
1385 | EXPECT_TOKEN(':')do { if (scanner_.Token() != ':') { failed_ = true; failure_message_ = "Unexpected token"; failure_location_ = static_cast<int >(scanner_.Position()); return ;; } scanner_.Next(); } while (false); |
1386 | while (!failed_ && !Peek('}')) { |
1387 | RECURSE(ValidateStatement()); |
1388 | } |
1389 | } |
1390 | |
1391 | // 6.8 ValidateExpression |
1392 | AsmType* AsmJsParser::ValidateExpression() { |
1393 | AsmType* ret; |
1394 | RECURSEn(ret = Expression(nullptr))do { ((void) 0); if (GetCurrentStackPosition() < stack_limit_ ) { failed_ = true; failure_message_ = "Stack overflow while parsing asm.js module." ; failure_location_ = static_cast<int>(scanner_.Position ()); return nullptr;; } ret = Expression(nullptr); if (failed_ ) return nullptr; } while (false); |
1395 | return ret; |
1396 | } |
1397 | |
1398 | // 6.8.1 Expression |
1399 | AsmType* AsmJsParser::Expression(AsmType* expected) { |
1400 | AsmType* a; |
1401 | for (;;) { |
1402 | RECURSEn(a = AssignmentExpression())do { ((void) 0); if (GetCurrentStackPosition() < stack_limit_ ) { failed_ = true; failure_message_ = "Stack overflow while parsing asm.js module." ; failure_location_ = static_cast<int>(scanner_.Position ()); return nullptr;; } a = AssignmentExpression(); if (failed_ ) return nullptr; } while (false); |
1403 | if (Peek(',')) { |
1404 | if (a->IsA(AsmType::None())) { |
1405 | FAILn("Expected actual type")failed_ = true; failure_message_ = "Expected actual type"; failure_location_ = static_cast<int>(scanner_.Position()); return nullptr ;; |
1406 | } |
1407 | if (!a->IsA(AsmType::Void())) { |
1408 | current_function_builder_->Emit(kExprDrop); |
1409 | } |
1410 | EXPECT_TOKENn(',')do { if (scanner_.Token() != ',') { failed_ = true; failure_message_ = "Unexpected token"; failure_location_ = static_cast<int >(scanner_.Position()); return nullptr;; } scanner_.Next() ; } while (false); |
1411 | continue; |
1412 | } |
1413 | break; |
1414 | } |
1415 | if (expected != nullptr && !a->IsA(expected)) { |
1416 | FAILn("Unexpected type")failed_ = true; failure_message_ = "Unexpected type"; failure_location_ = static_cast<int>(scanner_.Position()); return nullptr ;; |
1417 | } |
1418 | return a; |
1419 | } |
1420 | |
1421 | // 6.8.2 NumericLiteral |
1422 | AsmType* AsmJsParser::NumericLiteral() { |
1423 | call_coercion_ = nullptr; |
1424 | double dvalue = 0.0; |
1425 | uint32_t uvalue = 0; |
1426 | if (CheckForDouble(&dvalue)) { |
1427 | current_function_builder_->EmitF64Const(dvalue); |
1428 | return AsmType::Double(); |
1429 | } else if (CheckForUnsigned(&uvalue)) { |
1430 | if (uvalue <= 0x7FFFFFFF) { |
1431 | current_function_builder_->EmitI32Const(static_cast<int32_t>(uvalue)); |
1432 | return AsmType::FixNum(); |
1433 | } else { |
1434 | current_function_builder_->EmitI32Const(static_cast<int32_t>(uvalue)); |
1435 | return AsmType::Unsigned(); |
1436 | } |
1437 | } else { |
1438 | FAILn("Expected numeric literal.")failed_ = true; failure_message_ = "Expected numeric literal." ; failure_location_ = static_cast<int>(scanner_.Position ()); return nullptr;; |
1439 | } |
1440 | } |
1441 | |
1442 | // 6.8.3 Identifier |
1443 | AsmType* AsmJsParser::Identifier() { |
1444 | call_coercion_ = nullptr; |
1445 | if (scanner_.IsLocal()) { |
1446 | VarInfo* info = GetVarInfo(Consume()); |
1447 | if (info->kind != VarKind::kLocal) { |
1448 | FAILn("Undefined local variable")failed_ = true; failure_message_ = "Undefined local variable" ; failure_location_ = static_cast<int>(scanner_.Position ()); return nullptr;; |
1449 | } |
1450 | current_function_builder_->EmitGetLocal(info->index); |
1451 | return info->type; |
1452 | } else if (scanner_.IsGlobal()) { |
1453 | VarInfo* info = GetVarInfo(Consume()); |
1454 | if (info->kind != VarKind::kGlobal) { |
1455 | FAILn("Undefined global variable")failed_ = true; failure_message_ = "Undefined global variable" ; failure_location_ = static_cast<int>(scanner_.Position ()); return nullptr;; |
1456 | } |
1457 | current_function_builder_->EmitWithI32V(kExprGlobalGet, VarIndex(info)); |
1458 | return info->type; |
1459 | } |
1460 | UNREACHABLE()V8_Fatal("unreachable code"); |
1461 | } |
1462 | |
1463 | // 6.8.4 CallExpression |
1464 | AsmType* AsmJsParser::CallExpression() { |
1465 | AsmType* ret; |
1466 | if (scanner_.IsGlobal() && |
1467 | GetVarInfo(scanner_.Token())->type->IsA(stdlib_fround_)) { |
1468 | ValidateFloatCoercion(); |
1469 | return AsmType::Float(); |
1470 | } else if (scanner_.IsGlobal() && |
1471 | GetVarInfo(scanner_.Token())->type->IsA(AsmType::Heap())) { |
1472 | RECURSEn(ret = MemberExpression())do { ((void) 0); if (GetCurrentStackPosition() < stack_limit_ ) { failed_ = true; failure_message_ = "Stack overflow while parsing asm.js module." ; failure_location_ = static_cast<int>(scanner_.Position ()); return nullptr;; } ret = MemberExpression(); if (failed_ ) return nullptr; } while (false); |
1473 | } else if (Peek('(')) { |
1474 | RECURSEn(ret = ParenthesizedExpression())do { ((void) 0); if (GetCurrentStackPosition() < stack_limit_ ) { failed_ = true; failure_message_ = "Stack overflow while parsing asm.js module." ; failure_location_ = static_cast<int>(scanner_.Position ()); return nullptr;; } ret = ParenthesizedExpression(); if ( failed_) return nullptr; } while (false); |
1475 | } else if (PeekCall()) { |
1476 | RECURSEn(ret = ValidateCall())do { ((void) 0); if (GetCurrentStackPosition() < stack_limit_ ) { failed_ = true; failure_message_ = "Stack overflow while parsing asm.js module." ; failure_location_ = static_cast<int>(scanner_.Position ()); return nullptr;; } ret = ValidateCall(); if (failed_) return nullptr; } while (false); |
1477 | } else if (scanner_.IsLocal() || scanner_.IsGlobal()) { |
1478 | RECURSEn(ret = Identifier())do { ((void) 0); if (GetCurrentStackPosition() < stack_limit_ ) { failed_ = true; failure_message_ = "Stack overflow while parsing asm.js module." ; failure_location_ = static_cast<int>(scanner_.Position ()); return nullptr;; } ret = Identifier(); if (failed_) return nullptr; } while (false); |
1479 | } else { |
1480 | RECURSEn(ret = NumericLiteral())do { ((void) 0); if (GetCurrentStackPosition() < stack_limit_ ) { failed_ = true; failure_message_ = "Stack overflow while parsing asm.js module." ; failure_location_ = static_cast<int>(scanner_.Position ()); return nullptr;; } ret = NumericLiteral(); if (failed_) return nullptr; } while (false); |
1481 | } |
1482 | return ret; |
1483 | } |
1484 | |
1485 | // 6.8.5 MemberExpression |
1486 | AsmType* AsmJsParser::MemberExpression() { |
1487 | call_coercion_ = nullptr; |
1488 | RECURSEn(ValidateHeapAccess())do { ((void) 0); if (GetCurrentStackPosition() < stack_limit_ ) { failed_ = true; failure_message_ = "Stack overflow while parsing asm.js module." ; failure_location_ = static_cast<int>(scanner_.Position ()); return nullptr;; } ValidateHeapAccess(); if (failed_) return nullptr; } while (false); |
1489 | DCHECK_NOT_NULL(heap_access_type_)((void) 0); |
1490 | if (Peek('=')) { |
1491 | inside_heap_assignment_ = true; |
1492 | return heap_access_type_->StoreType(); |
1493 | } else { |
1494 | #define V(array_type, wasmload, wasmstore, type) \ |
1495 | if (heap_access_type_->IsA(AsmType::array_type())) { \ |
1496 | current_function_builder_->Emit(kExpr##type##AsmjsLoad##wasmload); \ |
1497 | return heap_access_type_->LoadType(); \ |
1498 | } |
1499 | STDLIB_ARRAY_TYPE_LIST(V)V(Int8Array, Mem8S, Mem8, I32) V(Uint8Array, Mem8U, Mem8, I32 ) V(Int16Array, Mem16S, Mem16, I32) V(Uint16Array, Mem16U, Mem16 , I32) V(Int32Array, Mem, Mem, I32) V(Uint32Array, Mem, Mem, I32 ) V(Float32Array, Mem, Mem, F32) V(Float64Array, Mem, Mem, F64 ) |
1500 | #undef V |
1501 | FAILn("Expected valid heap load")failed_ = true; failure_message_ = "Expected valid heap load" ; failure_location_ = static_cast<int>(scanner_.Position ()); return nullptr;; |
1502 | } |
1503 | } |
1504 | |
1505 | // 6.8.6 AssignmentExpression |
1506 | AsmType* AsmJsParser::AssignmentExpression() { |
1507 | AsmType* ret; |
1508 | if (scanner_.IsGlobal() && |
1509 | GetVarInfo(scanner_.Token())->type->IsA(AsmType::Heap())) { |
1510 | RECURSEn(ret = ConditionalExpression())do { ((void) 0); if (GetCurrentStackPosition() < stack_limit_ ) { failed_ = true; failure_message_ = "Stack overflow while parsing asm.js module." ; failure_location_ = static_cast<int>(scanner_.Position ()); return nullptr;; } ret = ConditionalExpression(); if (failed_ ) return nullptr; } while (false); |
1511 | if (Peek('=')) { |
1512 | if (!inside_heap_assignment_) { |
1513 | FAILn("Invalid assignment target")failed_ = true; failure_message_ = "Invalid assignment target" ; failure_location_ = static_cast<int>(scanner_.Position ()); return nullptr;; |
1514 | } |
1515 | inside_heap_assignment_ = false; |
1516 | DCHECK_NOT_NULL(heap_access_type_)((void) 0); |
1517 | AsmType* heap_type = heap_access_type_; |
1518 | EXPECT_TOKENn('=')do { if (scanner_.Token() != '=') { failed_ = true; failure_message_ = "Unexpected token"; failure_location_ = static_cast<int >(scanner_.Position()); return nullptr;; } scanner_.Next() ; } while (false); |
1519 | AsmType* value; |
1520 | RECURSEn(value = AssignmentExpression())do { ((void) 0); if (GetCurrentStackPosition() < stack_limit_ ) { failed_ = true; failure_message_ = "Stack overflow while parsing asm.js module." ; failure_location_ = static_cast<int>(scanner_.Position ()); return nullptr;; } value = AssignmentExpression(); if (failed_ ) return nullptr; } while (false); |
1521 | if (!value->IsA(ret)) { |
1522 | FAILn("Illegal type stored to heap view")failed_ = true; failure_message_ = "Illegal type stored to heap view" ; failure_location_ = static_cast<int>(scanner_.Position ()); return nullptr;; |
1523 | } |
1524 | ret = value; |
1525 | if (heap_type->IsA(AsmType::Float32Array()) && |
1526 | value->IsA(AsmType::DoubleQ())) { |
1527 | // Assignment to a float32 heap can be used to convert doubles. |
1528 | current_function_builder_->Emit(kExprF32ConvertF64); |
1529 | ret = AsmType::FloatQ(); |
1530 | } |
1531 | if (heap_type->IsA(AsmType::Float64Array()) && |
1532 | value->IsA(AsmType::FloatQ())) { |
1533 | // Assignment to a float64 heap can be used to convert floats. |
1534 | current_function_builder_->Emit(kExprF64ConvertF32); |
1535 | ret = AsmType::DoubleQ(); |
1536 | } |
1537 | #define V(array_type, wasmload, wasmstore, type) \ |
1538 | if (heap_type->IsA(AsmType::array_type())) { \ |
1539 | current_function_builder_->Emit(kExpr##type##AsmjsStore##wasmstore); \ |
1540 | return ret; \ |
1541 | } |
1542 | STDLIB_ARRAY_TYPE_LIST(V)V(Int8Array, Mem8S, Mem8, I32) V(Uint8Array, Mem8U, Mem8, I32 ) V(Int16Array, Mem16S, Mem16, I32) V(Uint16Array, Mem16U, Mem16 , I32) V(Int32Array, Mem, Mem, I32) V(Uint32Array, Mem, Mem, I32 ) V(Float32Array, Mem, Mem, F32) V(Float64Array, Mem, Mem, F64 ) |
1543 | #undef V |
1544 | } |
1545 | } else if (scanner_.IsLocal() || scanner_.IsGlobal()) { |
1546 | bool is_local = scanner_.IsLocal(); |
1547 | VarInfo* info = GetVarInfo(scanner_.Token()); |
1548 | USE(is_local)do { ::v8::base::Use unused_tmp_array_for_use_macro[]{is_local }; (void)unused_tmp_array_for_use_macro; } while (false); |
1549 | ret = info->type; |
1550 | scanner_.Next(); |
1551 | if (Check('=')) { |
1552 | // NOTE: Before this point, this might have been VarKind::kUnused even in |
1553 | // valid code, as it might be a label. |
1554 | if (info->kind == VarKind::kUnused) { |
1555 | FAILn("Undeclared assignment target")failed_ = true; failure_message_ = "Undeclared assignment target" ; failure_location_ = static_cast<int>(scanner_.Position ()); return nullptr;; |
1556 | } |
1557 | if (!info->mutable_variable) { |
1558 | FAILn("Expected mutable variable in assignment")failed_ = true; failure_message_ = "Expected mutable variable in assignment" ; failure_location_ = static_cast<int>(scanner_.Position ()); return nullptr;; |
1559 | } |
1560 | DCHECK(is_local ? info->kind == VarKind::kLocal((void) 0) |
1561 | : info->kind == VarKind::kGlobal)((void) 0); |
1562 | AsmType* value; |
1563 | RECURSEn(value = AssignmentExpression())do { ((void) 0); if (GetCurrentStackPosition() < stack_limit_ ) { failed_ = true; failure_message_ = "Stack overflow while parsing asm.js module." ; failure_location_ = static_cast<int>(scanner_.Position ()); return nullptr;; } value = AssignmentExpression(); if (failed_ ) return nullptr; } while (false); |
1564 | if (!value->IsA(ret)) { |
1565 | FAILn("Type mismatch in assignment")failed_ = true; failure_message_ = "Type mismatch in assignment" ; failure_location_ = static_cast<int>(scanner_.Position ()); return nullptr;; |
1566 | } |
1567 | if (info->kind == VarKind::kLocal) { |
1568 | current_function_builder_->EmitTeeLocal(info->index); |
1569 | } else if (info->kind == VarKind::kGlobal) { |
1570 | current_function_builder_->EmitWithU32V(kExprGlobalSet, VarIndex(info)); |
1571 | current_function_builder_->EmitWithU32V(kExprGlobalGet, VarIndex(info)); |
1572 | } else { |
1573 | UNREACHABLE()V8_Fatal("unreachable code"); |
1574 | } |
1575 | return ret; |
1576 | } |
1577 | scanner_.Rewind(); |
1578 | RECURSEn(ret = ConditionalExpression())do { ((void) 0); if (GetCurrentStackPosition() < stack_limit_ ) { failed_ = true; failure_message_ = "Stack overflow while parsing asm.js module." ; failure_location_ = static_cast<int>(scanner_.Position ()); return nullptr;; } ret = ConditionalExpression(); if (failed_ ) return nullptr; } while (false); |
1579 | } else { |
1580 | RECURSEn(ret = ConditionalExpression())do { ((void) 0); if (GetCurrentStackPosition() < stack_limit_ ) { failed_ = true; failure_message_ = "Stack overflow while parsing asm.js module." ; failure_location_ = static_cast<int>(scanner_.Position ()); return nullptr;; } ret = ConditionalExpression(); if (failed_ ) return nullptr; } while (false); |
1581 | } |
1582 | return ret; |
1583 | } |
1584 | |
1585 | // 6.8.7 UnaryExpression |
1586 | AsmType* AsmJsParser::UnaryExpression() { |
1587 | AsmType* ret; |
1588 | if (Check('-')) { |
1589 | uint32_t uvalue; |
1590 | if (CheckForUnsigned(&uvalue)) { |
1591 | if (uvalue == 0) { |
1592 | current_function_builder_->EmitF64Const(-0.0); |
1593 | ret = AsmType::Double(); |
1594 | } else if (uvalue <= 0x80000000) { |
1595 | // TODO(bradnelson): was supposed to be 0x7FFFFFFF, check errata. |
1596 | current_function_builder_->EmitI32Const( |
1597 | base::NegateWithWraparound(static_cast<int32_t>(uvalue))); |
1598 | ret = AsmType::Signed(); |
1599 | } else { |
1600 | FAILn("Integer numeric literal out of range.")failed_ = true; failure_message_ = "Integer numeric literal out of range." ; failure_location_ = static_cast<int>(scanner_.Position ()); return nullptr;; |
1601 | } |
1602 | } else { |
1603 | RECURSEn(ret = UnaryExpression())do { ((void) 0); if (GetCurrentStackPosition() < stack_limit_ ) { failed_ = true; failure_message_ = "Stack overflow while parsing asm.js module." ; failure_location_ = static_cast<int>(scanner_.Position ()); return nullptr;; } ret = UnaryExpression(); if (failed_) return nullptr; } while (false); |
1604 | if (ret->IsA(AsmType::Int())) { |
1605 | TemporaryVariableScope tmp(this); |
1606 | current_function_builder_->EmitSetLocal(tmp.get()); |
1607 | current_function_builder_->EmitI32Const(0); |
1608 | current_function_builder_->EmitGetLocal(tmp.get()); |
1609 | current_function_builder_->Emit(kExprI32Sub); |
1610 | ret = AsmType::Intish(); |
1611 | } else if (ret->IsA(AsmType::DoubleQ())) { |
1612 | current_function_builder_->Emit(kExprF64Neg); |
1613 | ret = AsmType::Double(); |
1614 | } else if (ret->IsA(AsmType::FloatQ())) { |
1615 | current_function_builder_->Emit(kExprF32Neg); |
1616 | ret = AsmType::Floatish(); |
1617 | } else { |
1618 | FAILn("expected int/double?/float?")failed_ = true; failure_message_ = "expected int/double?/float?" ; failure_location_ = static_cast<int>(scanner_.Position ()); return nullptr;; |
1619 | } |
1620 | } |
1621 | } else if (Peek('+')) { |
1622 | call_coercion_ = AsmType::Double(); |
1623 | call_coercion_position_ = scanner_.Position(); |
1624 | scanner_.Next(); // Done late for correct position. |
1625 | RECURSEn(ret = UnaryExpression())do { ((void) 0); if (GetCurrentStackPosition() < stack_limit_ ) { failed_ = true; failure_message_ = "Stack overflow while parsing asm.js module." ; failure_location_ = static_cast<int>(scanner_.Position ()); return nullptr;; } ret = UnaryExpression(); if (failed_) return nullptr; } while (false); |
1626 | // TODO(bradnelson): Generalize. |
1627 | if (ret->IsA(AsmType::Signed())) { |
1628 | current_function_builder_->Emit(kExprF64SConvertI32); |
1629 | ret = AsmType::Double(); |
1630 | } else if (ret->IsA(AsmType::Unsigned())) { |
1631 | current_function_builder_->Emit(kExprF64UConvertI32); |
1632 | ret = AsmType::Double(); |
1633 | } else if (ret->IsA(AsmType::DoubleQ())) { |
1634 | ret = AsmType::Double(); |
1635 | } else if (ret->IsA(AsmType::FloatQ())) { |
1636 | current_function_builder_->Emit(kExprF64ConvertF32); |
1637 | ret = AsmType::Double(); |
1638 | } else { |
1639 | FAILn("expected signed/unsigned/double?/float?")failed_ = true; failure_message_ = "expected signed/unsigned/double?/float?" ; failure_location_ = static_cast<int>(scanner_.Position ()); return nullptr;; |
1640 | } |
1641 | } else if (Check('!')) { |
1642 | RECURSEn(ret = UnaryExpression())do { ((void) 0); if (GetCurrentStackPosition() < stack_limit_ ) { failed_ = true; failure_message_ = "Stack overflow while parsing asm.js module." ; failure_location_ = static_cast<int>(scanner_.Position ()); return nullptr;; } ret = UnaryExpression(); if (failed_) return nullptr; } while (false); |
1643 | if (!ret->IsA(AsmType::Int())) { |
1644 | FAILn("expected int")failed_ = true; failure_message_ = "expected int"; failure_location_ = static_cast<int>(scanner_.Position()); return nullptr ;; |
1645 | } |
1646 | current_function_builder_->Emit(kExprI32Eqz); |
1647 | } else if (Check('~')) { |
1648 | if (Check('~')) { |
1649 | RECURSEn(ret = UnaryExpression())do { ((void) 0); if (GetCurrentStackPosition() < stack_limit_ ) { failed_ = true; failure_message_ = "Stack overflow while parsing asm.js module." ; failure_location_ = static_cast<int>(scanner_.Position ()); return nullptr;; } ret = UnaryExpression(); if (failed_) return nullptr; } while (false); |
1650 | if (ret->IsA(AsmType::Double())) { |
1651 | current_function_builder_->Emit(kExprI32AsmjsSConvertF64); |
1652 | } else if (ret->IsA(AsmType::FloatQ())) { |
1653 | current_function_builder_->Emit(kExprI32AsmjsSConvertF32); |
1654 | } else { |
1655 | FAILn("expected double or float?")failed_ = true; failure_message_ = "expected double or float?" ; failure_location_ = static_cast<int>(scanner_.Position ()); return nullptr;; |
1656 | } |
1657 | ret = AsmType::Signed(); |
1658 | } else { |
1659 | RECURSEn(ret = UnaryExpression())do { ((void) 0); if (GetCurrentStackPosition() < stack_limit_ ) { failed_ = true; failure_message_ = "Stack overflow while parsing asm.js module." ; failure_location_ = static_cast<int>(scanner_.Position ()); return nullptr;; } ret = UnaryExpression(); if (failed_) return nullptr; } while (false); |
1660 | if (!ret->IsA(AsmType::Intish())) { |
1661 | FAILn("operator ~ expects intish")failed_ = true; failure_message_ = "operator ~ expects intish" ; failure_location_ = static_cast<int>(scanner_.Position ()); return nullptr;; |
1662 | } |
1663 | current_function_builder_->EmitI32Const(0xFFFFFFFF); |
1664 | current_function_builder_->Emit(kExprI32Xor); |
1665 | ret = AsmType::Signed(); |
1666 | } |
1667 | } else { |
1668 | RECURSEn(ret = CallExpression())do { ((void) 0); if (GetCurrentStackPosition() < stack_limit_ ) { failed_ = true; failure_message_ = "Stack overflow while parsing asm.js module." ; failure_location_ = static_cast<int>(scanner_.Position ()); return nullptr;; } ret = CallExpression(); if (failed_) return nullptr; } while (false); |
1669 | } |
1670 | return ret; |
1671 | } |
1672 | |
1673 | // 6.8.8 MultiplicativeExpression |
1674 | AsmType* AsmJsParser::MultiplicativeExpression() { |
1675 | AsmType* a; |
1676 | uint32_t uvalue; |
1677 | if (CheckForUnsignedBelow(0x100000, &uvalue)) { |
1678 | if (Check('*')) { |
1679 | AsmType* type; |
1680 | RECURSEn(type = UnaryExpression())do { ((void) 0); if (GetCurrentStackPosition() < stack_limit_ ) { failed_ = true; failure_message_ = "Stack overflow while parsing asm.js module." ; failure_location_ = static_cast<int>(scanner_.Position ()); return nullptr;; } type = UnaryExpression(); if (failed_ ) return nullptr; } while (false); |
1681 | if (!type->IsA(AsmType::Int())) { |
1682 | FAILn("Expected int")failed_ = true; failure_message_ = "Expected int"; failure_location_ = static_cast<int>(scanner_.Position()); return nullptr ;; |
1683 | } |
1684 | int32_t value = static_cast<int32_t>(uvalue); |
1685 | current_function_builder_->EmitI32Const(value); |
1686 | current_function_builder_->Emit(kExprI32Mul); |
1687 | return AsmType::Intish(); |
1688 | } else { |
1689 | scanner_.Rewind(); |
1690 | RECURSEn(a = UnaryExpression())do { ((void) 0); if (GetCurrentStackPosition() < stack_limit_ ) { failed_ = true; failure_message_ = "Stack overflow while parsing asm.js module." ; failure_location_ = static_cast<int>(scanner_.Position ()); return nullptr;; } a = UnaryExpression(); if (failed_) return nullptr; } while (false); |
1691 | } |
1692 | } else if (Check('-')) { |
1693 | if (!PeekForZero() && CheckForUnsignedBelow(0x100000, &uvalue)) { |
1694 | int32_t value = -static_cast<int32_t>(uvalue); |
1695 | current_function_builder_->EmitI32Const(value); |
1696 | if (Check('*')) { |
1697 | AsmType* type; |
1698 | RECURSEn(type = UnaryExpression())do { ((void) 0); if (GetCurrentStackPosition() < stack_limit_ ) { failed_ = true; failure_message_ = "Stack overflow while parsing asm.js module." ; failure_location_ = static_cast<int>(scanner_.Position ()); return nullptr;; } type = UnaryExpression(); if (failed_ ) return nullptr; } while (false); |
1699 | if (!type->IsA(AsmType::Int())) { |
1700 | FAILn("Expected int")failed_ = true; failure_message_ = "Expected int"; failure_location_ = static_cast<int>(scanner_.Position()); return nullptr ;; |
1701 | } |
1702 | current_function_builder_->Emit(kExprI32Mul); |
1703 | return AsmType::Intish(); |
1704 | } |
1705 | a = AsmType::Signed(); |
1706 | } else { |
1707 | scanner_.Rewind(); |
1708 | RECURSEn(a = UnaryExpression())do { ((void) 0); if (GetCurrentStackPosition() < stack_limit_ ) { failed_ = true; failure_message_ = "Stack overflow while parsing asm.js module." ; failure_location_ = static_cast<int>(scanner_.Position ()); return nullptr;; } a = UnaryExpression(); if (failed_) return nullptr; } while (false); |
1709 | } |
1710 | } else { |
1711 | RECURSEn(a = UnaryExpression())do { ((void) 0); if (GetCurrentStackPosition() < stack_limit_ ) { failed_ = true; failure_message_ = "Stack overflow while parsing asm.js module." ; failure_location_ = static_cast<int>(scanner_.Position ()); return nullptr;; } a = UnaryExpression(); if (failed_) return nullptr; } while (false); |
1712 | } |
1713 | for (;;) { |
1714 | if (Check('*')) { |
1715 | if (Check('-')) { |
1716 | if (!PeekForZero() && CheckForUnsigned(&uvalue)) { |
1717 | if (uvalue >= 0x100000) { |
1718 | FAILn("Constant multiple out of range")failed_ = true; failure_message_ = "Constant multiple out of range" ; failure_location_ = static_cast<int>(scanner_.Position ()); return nullptr;; |
1719 | } |
1720 | if (!a->IsA(AsmType::Int())) { |
1721 | FAILn("Integer multiply of expects int")failed_ = true; failure_message_ = "Integer multiply of expects int" ; failure_location_ = static_cast<int>(scanner_.Position ()); return nullptr;; |
1722 | } |
1723 | int32_t value = -static_cast<int32_t>(uvalue); |
1724 | current_function_builder_->EmitI32Const(value); |
1725 | current_function_builder_->Emit(kExprI32Mul); |
1726 | return AsmType::Intish(); |
1727 | } |
1728 | scanner_.Rewind(); |
1729 | } else if (CheckForUnsigned(&uvalue)) { |
1730 | if (uvalue >= 0x100000) { |
1731 | FAILn("Constant multiple out of range")failed_ = true; failure_message_ = "Constant multiple out of range" ; failure_location_ = static_cast<int>(scanner_.Position ()); return nullptr;; |
1732 | } |
1733 | if (!a->IsA(AsmType::Int())) { |
1734 | FAILn("Integer multiply of expects int")failed_ = true; failure_message_ = "Integer multiply of expects int" ; failure_location_ = static_cast<int>(scanner_.Position ()); return nullptr;; |
1735 | } |
1736 | int32_t value = static_cast<int32_t>(uvalue); |
1737 | current_function_builder_->EmitI32Const(value); |
1738 | current_function_builder_->Emit(kExprI32Mul); |
1739 | return AsmType::Intish(); |
1740 | } |
1741 | AsmType* b; |
1742 | RECURSEn(b = UnaryExpression())do { ((void) 0); if (GetCurrentStackPosition() < stack_limit_ ) { failed_ = true; failure_message_ = "Stack overflow while parsing asm.js module." ; failure_location_ = static_cast<int>(scanner_.Position ()); return nullptr;; } b = UnaryExpression(); if (failed_) return nullptr; } while (false); |
1743 | if (a->IsA(AsmType::DoubleQ()) && b->IsA(AsmType::DoubleQ())) { |
1744 | current_function_builder_->Emit(kExprF64Mul); |
1745 | a = AsmType::Double(); |
1746 | } else if (a->IsA(AsmType::FloatQ()) && b->IsA(AsmType::FloatQ())) { |
1747 | current_function_builder_->Emit(kExprF32Mul); |
1748 | a = AsmType::Floatish(); |
1749 | } else { |
1750 | FAILn("expected doubles or floats")failed_ = true; failure_message_ = "expected doubles or floats" ; failure_location_ = static_cast<int>(scanner_.Position ()); return nullptr;; |
1751 | } |
1752 | } else if (Check('/')) { |
1753 | AsmType* b; |
1754 | RECURSEn(b = UnaryExpression())do { ((void) 0); if (GetCurrentStackPosition() < stack_limit_ ) { failed_ = true; failure_message_ = "Stack overflow while parsing asm.js module." ; failure_location_ = static_cast<int>(scanner_.Position ()); return nullptr;; } b = UnaryExpression(); if (failed_) return nullptr; } while (false); |
1755 | if (a->IsA(AsmType::DoubleQ()) && b->IsA(AsmType::DoubleQ())) { |
1756 | current_function_builder_->Emit(kExprF64Div); |
1757 | a = AsmType::Double(); |
1758 | } else if (a->IsA(AsmType::FloatQ()) && b->IsA(AsmType::FloatQ())) { |
1759 | current_function_builder_->Emit(kExprF32Div); |
1760 | a = AsmType::Floatish(); |
1761 | } else if (a->IsA(AsmType::Signed()) && b->IsA(AsmType::Signed())) { |
1762 | current_function_builder_->Emit(kExprI32AsmjsDivS); |
1763 | a = AsmType::Intish(); |
1764 | } else if (a->IsA(AsmType::Unsigned()) && b->IsA(AsmType::Unsigned())) { |
1765 | current_function_builder_->Emit(kExprI32AsmjsDivU); |
1766 | a = AsmType::Intish(); |
1767 | } else { |
1768 | FAILn("expected doubles or floats")failed_ = true; failure_message_ = "expected doubles or floats" ; failure_location_ = static_cast<int>(scanner_.Position ()); return nullptr;; |
1769 | } |
1770 | } else if (Check('%')) { |
1771 | AsmType* b; |
1772 | RECURSEn(b = UnaryExpression())do { ((void) 0); if (GetCurrentStackPosition() < stack_limit_ ) { failed_ = true; failure_message_ = "Stack overflow while parsing asm.js module." ; failure_location_ = static_cast<int>(scanner_.Position ()); return nullptr;; } b = UnaryExpression(); if (failed_) return nullptr; } while (false); |
1773 | if (a->IsA(AsmType::DoubleQ()) && b->IsA(AsmType::DoubleQ())) { |
1774 | current_function_builder_->Emit(kExprF64Mod); |
1775 | a = AsmType::Double(); |
1776 | } else if (a->IsA(AsmType::Signed()) && b->IsA(AsmType::Signed())) { |
1777 | current_function_builder_->Emit(kExprI32AsmjsRemS); |
1778 | a = AsmType::Intish(); |
1779 | } else if (a->IsA(AsmType::Unsigned()) && b->IsA(AsmType::Unsigned())) { |
1780 | current_function_builder_->Emit(kExprI32AsmjsRemU); |
1781 | a = AsmType::Intish(); |
1782 | } else { |
1783 | FAILn("expected doubles or floats")failed_ = true; failure_message_ = "expected doubles or floats" ; failure_location_ = static_cast<int>(scanner_.Position ()); return nullptr;; |
1784 | } |
1785 | } else { |
1786 | break; |
1787 | } |
1788 | } |
1789 | return a; |
1790 | } |
1791 | |
1792 | // 6.8.9 AdditiveExpression |
1793 | AsmType* AsmJsParser::AdditiveExpression() { |
1794 | AsmType* a; |
1795 | RECURSEn(a = MultiplicativeExpression())do { ((void) 0); if (GetCurrentStackPosition() < stack_limit_ ) { failed_ = true; failure_message_ = "Stack overflow while parsing asm.js module." ; failure_location_ = static_cast<int>(scanner_.Position ()); return nullptr;; } a = MultiplicativeExpression(); if (failed_ ) return nullptr; } while (false); |
1796 | int n = 0; |
1797 | for (;;) { |
1798 | if (Check('+')) { |
1799 | AsmType* b; |
1800 | RECURSEn(b = MultiplicativeExpression())do { ((void) 0); if (GetCurrentStackPosition() < stack_limit_ ) { failed_ = true; failure_message_ = "Stack overflow while parsing asm.js module." ; failure_location_ = static_cast<int>(scanner_.Position ()); return nullptr;; } b = MultiplicativeExpression(); if (failed_ ) return nullptr; } while (false); |
1801 | if (a->IsA(AsmType::Double()) && b->IsA(AsmType::Double())) { |
1802 | current_function_builder_->Emit(kExprF64Add); |
1803 | a = AsmType::Double(); |
1804 | } else if (a->IsA(AsmType::FloatQ()) && b->IsA(AsmType::FloatQ())) { |
1805 | current_function_builder_->Emit(kExprF32Add); |
1806 | a = AsmType::Floatish(); |
1807 | } else if (a->IsA(AsmType::Int()) && b->IsA(AsmType::Int())) { |
1808 | current_function_builder_->Emit(kExprI32Add); |
1809 | a = AsmType::Intish(); |
1810 | n = 2; |
1811 | } else if (a->IsA(AsmType::Intish()) && b->IsA(AsmType::Intish())) { |
1812 | // TODO(bradnelson): b should really only be Int. |
1813 | // specialize intish to capture count. |
1814 | ++n; |
1815 | if (n > (1 << 20)) { |
1816 | FAILn("more than 2^20 additive values")failed_ = true; failure_message_ = "more than 2^20 additive values" ; failure_location_ = static_cast<int>(scanner_.Position ()); return nullptr;; |
1817 | } |
1818 | current_function_builder_->Emit(kExprI32Add); |
1819 | } else { |
1820 | FAILn("illegal types for +")failed_ = true; failure_message_ = "illegal types for +"; failure_location_ = static_cast<int>(scanner_.Position()); return nullptr ;; |
1821 | } |
1822 | } else if (Check('-')) { |
1823 | AsmType* b; |
1824 | RECURSEn(b = MultiplicativeExpression())do { ((void) 0); if (GetCurrentStackPosition() < stack_limit_ ) { failed_ = true; failure_message_ = "Stack overflow while parsing asm.js module." ; failure_location_ = static_cast<int>(scanner_.Position ()); return nullptr;; } b = MultiplicativeExpression(); if (failed_ ) return nullptr; } while (false); |
1825 | if (a->IsA(AsmType::Double()) && b->IsA(AsmType::Double())) { |
1826 | current_function_builder_->Emit(kExprF64Sub); |
1827 | a = AsmType::Double(); |
1828 | } else if (a->IsA(AsmType::FloatQ()) && b->IsA(AsmType::FloatQ())) { |
1829 | current_function_builder_->Emit(kExprF32Sub); |
1830 | a = AsmType::Floatish(); |
1831 | } else if (a->IsA(AsmType::Int()) && b->IsA(AsmType::Int())) { |
1832 | current_function_builder_->Emit(kExprI32Sub); |
1833 | a = AsmType::Intish(); |
1834 | n = 2; |
1835 | } else if (a->IsA(AsmType::Intish()) && b->IsA(AsmType::Intish())) { |
1836 | // TODO(bradnelson): b should really only be Int. |
1837 | // specialize intish to capture count. |
1838 | ++n; |
1839 | if (n > (1 << 20)) { |
1840 | FAILn("more than 2^20 additive values")failed_ = true; failure_message_ = "more than 2^20 additive values" ; failure_location_ = static_cast<int>(scanner_.Position ()); return nullptr;; |
1841 | } |
1842 | current_function_builder_->Emit(kExprI32Sub); |
1843 | } else { |
1844 | FAILn("illegal types for +")failed_ = true; failure_message_ = "illegal types for +"; failure_location_ = static_cast<int>(scanner_.Position()); return nullptr ;; |
1845 | } |
1846 | } else { |
1847 | break; |
1848 | } |
1849 | } |
1850 | return a; |
1851 | } |
1852 | |
1853 | // 6.8.10 ShiftExpression |
1854 | AsmType* AsmJsParser::ShiftExpression() { |
1855 | AsmType* a = nullptr; |
1856 | RECURSEn(a = AdditiveExpression())do { ((void) 0); if (GetCurrentStackPosition() < stack_limit_ ) { failed_ = true; failure_message_ = "Stack overflow while parsing asm.js module." ; failure_location_ = static_cast<int>(scanner_.Position ()); return nullptr;; } a = AdditiveExpression(); if (failed_ ) return nullptr; } while (false); |
1857 | heap_access_shift_position_ = kNoHeapAccessShift; |
1858 | // TODO(bradnelson): Implement backtracking to avoid emitting code |
1859 | // for the x >>> 0 case (similar to what's there for |0). |
1860 | for (;;) { |
1861 | switch (scanner_.Token()) { |
1862 | case TOK(SAR)AsmJsScanner::kToken_SAR: { |
1863 | EXPECT_TOKENn(TOK(SAR))do { if (scanner_.Token() != AsmJsScanner::kToken_SAR) { failed_ = true; failure_message_ = "Unexpected token"; failure_location_ = static_cast<int>(scanner_.Position()); return nullptr ;; } scanner_.Next(); } while (false); |
1864 | heap_access_shift_position_ = kNoHeapAccessShift; |
1865 | // Remember position allowing this shift-expression to be used as part |
1866 | // of a heap access operation expecting `a >> n:NumericLiteral`. |
1867 | bool imm = false; |
1868 | size_t old_pos; |
1869 | size_t old_code; |
1870 | uint32_t shift_imm; |
1871 | if (a->IsA(AsmType::Intish()) && CheckForUnsigned(&shift_imm)) { |
1872 | old_pos = scanner_.Position(); |
1873 | old_code = current_function_builder_->GetPosition(); |
1874 | scanner_.Rewind(); |
1875 | imm = true; |
1876 | } |
1877 | AsmType* b = nullptr; |
1878 | RECURSEn(b = AdditiveExpression())do { ((void) 0); if (GetCurrentStackPosition() < stack_limit_ ) { failed_ = true; failure_message_ = "Stack overflow while parsing asm.js module." ; failure_location_ = static_cast<int>(scanner_.Position ()); return nullptr;; } b = AdditiveExpression(); if (failed_ ) return nullptr; } while (false); |
1879 | // Check for `a >> n:NumericLiteral` pattern. |
1880 | if (imm && old_pos == scanner_.Position()) { |
1881 | heap_access_shift_position_ = old_code; |
1882 | heap_access_shift_value_ = shift_imm; |
1883 | } |
1884 | if (!(a->IsA(AsmType::Intish()) && b->IsA(AsmType::Intish()))) { |
1885 | FAILn("Expected intish for operator >>.")failed_ = true; failure_message_ = "Expected intish for operator >>." ; failure_location_ = static_cast<int>(scanner_.Position ()); return nullptr;; |
1886 | } |
1887 | current_function_builder_->Emit(kExprI32ShrS); |
1888 | a = AsmType::Signed(); |
1889 | continue; |
1890 | } |
1891 | #define HANDLE_CASE(op, opcode, name, result) \ |
1892 | case TOK(op)AsmJsScanner::kToken_op: { \ |
1893 | EXPECT_TOKENn(TOK(op))do { if (scanner_.Token() != AsmJsScanner::kToken_op) { failed_ = true; failure_message_ = "Unexpected token"; failure_location_ = static_cast<int>(scanner_.Position()); return nullptr ;; } scanner_.Next(); } while (false); \ |
1894 | heap_access_shift_position_ = kNoHeapAccessShift; \ |
1895 | AsmType* b = nullptr; \ |
1896 | RECURSEn(b = AdditiveExpression())do { ((void) 0); if (GetCurrentStackPosition() < stack_limit_ ) { failed_ = true; failure_message_ = "Stack overflow while parsing asm.js module." ; failure_location_ = static_cast<int>(scanner_.Position ()); return nullptr;; } b = AdditiveExpression(); if (failed_ ) return nullptr; } while (false); \ |
1897 | if (!(a->IsA(AsmType::Intish()) && b->IsA(AsmType::Intish()))) { \ |
1898 | FAILn("Expected intish for operator " #name ".")failed_ = true; failure_message_ = "Expected intish for operator " #name "."; failure_location_ = static_cast<int>(scanner_ .Position()); return nullptr;; \ |
1899 | } \ |
1900 | current_function_builder_->Emit(kExpr##opcode); \ |
1901 | a = AsmType::result(); \ |
1902 | continue; \ |
1903 | } |
1904 | HANDLE_CASE(SHL, I32Shl, "<<", Signed); |
1905 | HANDLE_CASE(SHR, I32ShrU, ">>>", Unsigned); |
1906 | #undef HANDLE_CASE |
1907 | default: |
1908 | return a; |
1909 | } |
1910 | } |
1911 | } |
1912 | |
1913 | // 6.8.11 RelationalExpression |
1914 | AsmType* AsmJsParser::RelationalExpression() { |
1915 | AsmType* a = nullptr; |
1916 | RECURSEn(a = ShiftExpression())do { ((void) 0); if (GetCurrentStackPosition() < stack_limit_ ) { failed_ = true; failure_message_ = "Stack overflow while parsing asm.js module." ; failure_location_ = static_cast<int>(scanner_.Position ()); return nullptr;; } a = ShiftExpression(); if (failed_) return nullptr; } while (false); |
1917 | for (;;) { |
1918 | switch (scanner_.Token()) { |
1919 | #define HANDLE_CASE(op, sop, uop, dop, fop, name) \ |
1920 | case op: { \ |
1921 | EXPECT_TOKENn(op)do { if (scanner_.Token() != op) { failed_ = true; failure_message_ = "Unexpected token"; failure_location_ = static_cast<int >(scanner_.Position()); return nullptr;; } scanner_.Next() ; } while (false); \ |
1922 | AsmType* b = nullptr; \ |
1923 | RECURSEn(b = ShiftExpression())do { ((void) 0); if (GetCurrentStackPosition() < stack_limit_ ) { failed_ = true; failure_message_ = "Stack overflow while parsing asm.js module." ; failure_location_ = static_cast<int>(scanner_.Position ()); return nullptr;; } b = ShiftExpression(); if (failed_) return nullptr; } while (false); \ |
1924 | if (a->IsA(AsmType::Signed()) && b->IsA(AsmType::Signed())) { \ |
1925 | current_function_builder_->Emit(kExpr##sop); \ |
1926 | } else if (a->IsA(AsmType::Unsigned()) && b->IsA(AsmType::Unsigned())) { \ |
1927 | current_function_builder_->Emit(kExpr##uop); \ |
1928 | } else if (a->IsA(AsmType::Double()) && b->IsA(AsmType::Double())) { \ |
1929 | current_function_builder_->Emit(kExpr##dop); \ |
1930 | } else if (a->IsA(AsmType::Float()) && b->IsA(AsmType::Float())) { \ |
1931 | current_function_builder_->Emit(kExpr##fop); \ |
1932 | } else { \ |
1933 | FAILn("Expected signed, unsigned, double, or float for operator " #name \failed_ = true; failure_message_ = "Expected signed, unsigned, double, or float for operator " #name "."; failure_location_ = static_cast<int>(scanner_ .Position()); return nullptr; |
1934 | ".")failed_ = true; failure_message_ = "Expected signed, unsigned, double, or float for operator " #name "."; failure_location_ = static_cast<int>(scanner_ .Position()); return nullptr;; \ |
1935 | } \ |
1936 | a = AsmType::Int(); \ |
1937 | continue; \ |
1938 | } |
1939 | HANDLE_CASE('<', I32LtS, I32LtU, F64Lt, F32Lt, "<"); |
1940 | HANDLE_CASE(TOK(LE)AsmJsScanner::kToken_LE, I32LeS, I32LeU, F64Le, F32Le, "<="); |
1941 | HANDLE_CASE('>', I32GtS, I32GtU, F64Gt, F32Gt, ">"); |
1942 | HANDLE_CASE(TOK(GE)AsmJsScanner::kToken_GE, I32GeS, I32GeU, F64Ge, F32Ge, ">="); |
1943 | #undef HANDLE_CASE |
1944 | default: |
1945 | return a; |
1946 | } |
1947 | } |
1948 | } |
1949 | |
1950 | // 6.8.12 EqualityExpression |
1951 | AsmType* AsmJsParser::EqualityExpression() { |
1952 | AsmType* a = nullptr; |
1953 | RECURSEn(a = RelationalExpression())do { ((void) 0); if (GetCurrentStackPosition() < stack_limit_ ) { failed_ = true; failure_message_ = "Stack overflow while parsing asm.js module." ; failure_location_ = static_cast<int>(scanner_.Position ()); return nullptr;; } a = RelationalExpression(); if (failed_ ) return nullptr; } while (false); |
1954 | for (;;) { |
1955 | switch (scanner_.Token()) { |
1956 | #define HANDLE_CASE(op, sop, uop, dop, fop, name) \ |
1957 | case op: { \ |
1958 | EXPECT_TOKENn(op)do { if (scanner_.Token() != op) { failed_ = true; failure_message_ = "Unexpected token"; failure_location_ = static_cast<int >(scanner_.Position()); return nullptr;; } scanner_.Next() ; } while (false); \ |
1959 | AsmType* b = nullptr; \ |
1960 | RECURSEn(b = RelationalExpression())do { ((void) 0); if (GetCurrentStackPosition() < stack_limit_ ) { failed_ = true; failure_message_ = "Stack overflow while parsing asm.js module." ; failure_location_ = static_cast<int>(scanner_.Position ()); return nullptr;; } b = RelationalExpression(); if (failed_ ) return nullptr; } while (false); \ |
1961 | if (a->IsA(AsmType::Signed()) && b->IsA(AsmType::Signed())) { \ |
1962 | current_function_builder_->Emit(kExpr##sop); \ |
1963 | } else if (a->IsA(AsmType::Unsigned()) && b->IsA(AsmType::Unsigned())) { \ |
1964 | current_function_builder_->Emit(kExpr##uop); \ |
1965 | } else if (a->IsA(AsmType::Double()) && b->IsA(AsmType::Double())) { \ |
1966 | current_function_builder_->Emit(kExpr##dop); \ |
1967 | } else if (a->IsA(AsmType::Float()) && b->IsA(AsmType::Float())) { \ |
1968 | current_function_builder_->Emit(kExpr##fop); \ |
1969 | } else { \ |
1970 | FAILn("Expected signed, unsigned, double, or float for operator " #name \failed_ = true; failure_message_ = "Expected signed, unsigned, double, or float for operator " #name "."; failure_location_ = static_cast<int>(scanner_ .Position()); return nullptr; |
1971 | ".")failed_ = true; failure_message_ = "Expected signed, unsigned, double, or float for operator " #name "."; failure_location_ = static_cast<int>(scanner_ .Position()); return nullptr;; \ |
1972 | } \ |
1973 | a = AsmType::Int(); \ |
1974 | continue; \ |
1975 | } |
1976 | HANDLE_CASE(TOK(EQ)AsmJsScanner::kToken_EQ, I32Eq, I32Eq, F64Eq, F32Eq, "=="); |
1977 | HANDLE_CASE(TOK(NE)AsmJsScanner::kToken_NE, I32Ne, I32Ne, F64Ne, F32Ne, "!="); |
1978 | #undef HANDLE_CASE |
1979 | default: |
1980 | return a; |
1981 | } |
1982 | } |
1983 | } |
1984 | |
1985 | // 6.8.13 BitwiseANDExpression |
1986 | AsmType* AsmJsParser::BitwiseANDExpression() { |
1987 | AsmType* a = nullptr; |
1988 | RECURSEn(a = EqualityExpression())do { ((void) 0); if (GetCurrentStackPosition() < stack_limit_ ) { failed_ = true; failure_message_ = "Stack overflow while parsing asm.js module." ; failure_location_ = static_cast<int>(scanner_.Position ()); return nullptr;; } a = EqualityExpression(); if (failed_ ) return nullptr; } while (false); |
1989 | while (Check('&')) { |
1990 | AsmType* b = nullptr; |
1991 | RECURSEn(b = EqualityExpression())do { ((void) 0); if (GetCurrentStackPosition() < stack_limit_ ) { failed_ = true; failure_message_ = "Stack overflow while parsing asm.js module." ; failure_location_ = static_cast<int>(scanner_.Position ()); return nullptr;; } b = EqualityExpression(); if (failed_ ) return nullptr; } while (false); |
1992 | if (a->IsA(AsmType::Intish()) && b->IsA(AsmType::Intish())) { |
1993 | current_function_builder_->Emit(kExprI32And); |
1994 | a = AsmType::Signed(); |
1995 | } else { |
1996 | FAILn("Expected intish for operator &.")failed_ = true; failure_message_ = "Expected intish for operator &." ; failure_location_ = static_cast<int>(scanner_.Position ()); return nullptr;; |
1997 | } |
1998 | } |
1999 | return a; |
2000 | } |
2001 | |
2002 | // 6.8.14 BitwiseXORExpression |
2003 | AsmType* AsmJsParser::BitwiseXORExpression() { |
2004 | AsmType* a = nullptr; |
2005 | RECURSEn(a = BitwiseANDExpression())do { ((void) 0); if (GetCurrentStackPosition() < stack_limit_ ) { failed_ = true; failure_message_ = "Stack overflow while parsing asm.js module." ; failure_location_ = static_cast<int>(scanner_.Position ()); return nullptr;; } a = BitwiseANDExpression(); if (failed_ ) return nullptr; } while (false); |
2006 | while (Check('^')) { |
2007 | AsmType* b = nullptr; |
2008 | RECURSEn(b = BitwiseANDExpression())do { ((void) 0); if (GetCurrentStackPosition() < stack_limit_ ) { failed_ = true; failure_message_ = "Stack overflow while parsing asm.js module." ; failure_location_ = static_cast<int>(scanner_.Position ()); return nullptr;; } b = BitwiseANDExpression(); if (failed_ ) return nullptr; } while (false); |
2009 | if (a->IsA(AsmType::Intish()) && b->IsA(AsmType::Intish())) { |
2010 | current_function_builder_->Emit(kExprI32Xor); |
2011 | a = AsmType::Signed(); |
2012 | } else { |
2013 | FAILn("Expected intish for operator &.")failed_ = true; failure_message_ = "Expected intish for operator &." ; failure_location_ = static_cast<int>(scanner_.Position ()); return nullptr;; |
2014 | } |
2015 | } |
2016 | return a; |
2017 | } |
2018 | |
2019 | // 6.8.15 BitwiseORExpression |
2020 | AsmType* AsmJsParser::BitwiseORExpression() { |
2021 | AsmType* a = nullptr; |
2022 | call_coercion_deferred_position_ = scanner_.Position(); |
2023 | RECURSEn(a = BitwiseXORExpression())do { ((void) 0); if (GetCurrentStackPosition() < stack_limit_ ) { failed_ = true; failure_message_ = "Stack overflow while parsing asm.js module." ; failure_location_ = static_cast<int>(scanner_.Position ()); return nullptr;; } a = BitwiseXORExpression(); if (failed_ ) return nullptr; } while (false); |
2024 | while (Check('|')) { |
2025 | AsmType* b = nullptr; |
2026 | // Remember whether the first operand to this OR-expression has requested |
2027 | // deferred validation of the |0 annotation. |
2028 | // NOTE: This has to happen here to work recursively. |
2029 | bool requires_zero = |
2030 | AsmType::IsExactly(call_coercion_deferred_, AsmType::Signed()); |
2031 | call_coercion_deferred_ = nullptr; |
2032 | // TODO(bradnelson): Make it prettier. |
2033 | bool zero = false; |
2034 | size_t old_pos; |
2035 | size_t old_code; |
2036 | if (a->IsA(AsmType::Intish()) && CheckForZero()) { |
2037 | old_pos = scanner_.Position(); |
2038 | old_code = current_function_builder_->GetPosition(); |
2039 | scanner_.Rewind(); |
2040 | zero = true; |
2041 | } |
2042 | RECURSEn(b = BitwiseXORExpression())do { ((void) 0); if (GetCurrentStackPosition() < stack_limit_ ) { failed_ = true; failure_message_ = "Stack overflow while parsing asm.js module." ; failure_location_ = static_cast<int>(scanner_.Position ()); return nullptr;; } b = BitwiseXORExpression(); if (failed_ ) return nullptr; } while (false); |
2043 | // Handle |0 specially. |
2044 | if (zero && old_pos == scanner_.Position()) { |
2045 | current_function_builder_->DeleteCodeAfter(old_code); |
2046 | a = AsmType::Signed(); |
2047 | continue; |
2048 | } |
2049 | // Anything not matching |0 breaks the lookahead in {ValidateCall}. |
2050 | if (requires_zero) { |
2051 | FAILn("Expected |0 type annotation for call")failed_ = true; failure_message_ = "Expected |0 type annotation for call" ; failure_location_ = static_cast<int>(scanner_.Position ()); return nullptr;; |
2052 | } |
2053 | if (a->IsA(AsmType::Intish()) && b->IsA(AsmType::Intish())) { |
2054 | current_function_builder_->Emit(kExprI32Ior); |
2055 | a = AsmType::Signed(); |
2056 | } else { |
2057 | FAILn("Expected intish for operator |.")failed_ = true; failure_message_ = "Expected intish for operator |." ; failure_location_ = static_cast<int>(scanner_.Position ()); return nullptr;; |
2058 | } |
2059 | } |
2060 | DCHECK_NULL(call_coercion_deferred_)((void) 0); |
2061 | return a; |
2062 | } |
2063 | |
2064 | // 6.8.16 ConditionalExpression |
2065 | AsmType* AsmJsParser::ConditionalExpression() { |
2066 | AsmType* test = nullptr; |
2067 | RECURSEn(test = BitwiseORExpression())do { ((void) 0); if (GetCurrentStackPosition() < stack_limit_ ) { failed_ = true; failure_message_ = "Stack overflow while parsing asm.js module." ; failure_location_ = static_cast<int>(scanner_.Position ()); return nullptr;; } test = BitwiseORExpression(); if (failed_ ) return nullptr; } while (false); |
2068 | if (Check('?')) { |
2069 | if (!test->IsA(AsmType::Int())) { |
2070 | FAILn("Expected int in condition of ternary operator.")failed_ = true; failure_message_ = "Expected int in condition of ternary operator." ; failure_location_ = static_cast<int>(scanner_.Position ()); return nullptr;; |
2071 | } |
2072 | current_function_builder_->EmitWithU8(kExprIf, kI32Code); |
2073 | size_t fixup = current_function_builder_->GetPosition() - |
2074 | 1; // Assumes encoding knowledge. |
2075 | AsmType* cons = nullptr; |
2076 | RECURSEn(cons = AssignmentExpression())do { ((void) 0); if (GetCurrentStackPosition() < stack_limit_ ) { failed_ = true; failure_message_ = "Stack overflow while parsing asm.js module." ; failure_location_ = static_cast<int>(scanner_.Position ()); return nullptr;; } cons = AssignmentExpression(); if (failed_ ) return nullptr; } while (false); |
2077 | current_function_builder_->Emit(kExprElse); |
2078 | EXPECT_TOKENn(':')do { if (scanner_.Token() != ':') { failed_ = true; failure_message_ = "Unexpected token"; failure_location_ = static_cast<int >(scanner_.Position()); return nullptr;; } scanner_.Next() ; } while (false); |
2079 | AsmType* alt = nullptr; |
2080 | RECURSEn(alt = AssignmentExpression())do { ((void) 0); if (GetCurrentStackPosition() < stack_limit_ ) { failed_ = true; failure_message_ = "Stack overflow while parsing asm.js module." ; failure_location_ = static_cast<int>(scanner_.Position ()); return nullptr;; } alt = AssignmentExpression(); if (failed_ ) return nullptr; } while (false); |
2081 | current_function_builder_->Emit(kExprEnd); |
2082 | if (cons->IsA(AsmType::Int()) && alt->IsA(AsmType::Int())) { |
2083 | current_function_builder_->FixupByte(fixup, kI32Code); |
2084 | return AsmType::Int(); |
2085 | } else if (cons->IsA(AsmType::Double()) && alt->IsA(AsmType::Double())) { |
2086 | current_function_builder_->FixupByte(fixup, kF64Code); |
2087 | return AsmType::Double(); |
2088 | } else if (cons->IsA(AsmType::Float()) && alt->IsA(AsmType::Float())) { |
2089 | current_function_builder_->FixupByte(fixup, kF32Code); |
2090 | return AsmType::Float(); |
2091 | } else { |
2092 | FAILn("Type mismatch in ternary operator.")failed_ = true; failure_message_ = "Type mismatch in ternary operator." ; failure_location_ = static_cast<int>(scanner_.Position ()); return nullptr;; |
2093 | } |
2094 | } else { |
2095 | return test; |
2096 | } |
2097 | } |
2098 | |
2099 | // 6.8.17 ParenthesiedExpression |
2100 | AsmType* AsmJsParser::ParenthesizedExpression() { |
2101 | call_coercion_ = nullptr; |
2102 | AsmType* ret; |
2103 | EXPECT_TOKENn('(')do { if (scanner_.Token() != '(') { failed_ = true; failure_message_ = "Unexpected token"; failure_location_ = static_cast<int >(scanner_.Position()); return nullptr;; } scanner_.Next() ; } while (false); |
2104 | RECURSEn(ret = Expression(nullptr))do { ((void) 0); if (GetCurrentStackPosition() < stack_limit_ ) { failed_ = true; failure_message_ = "Stack overflow while parsing asm.js module." ; failure_location_ = static_cast<int>(scanner_.Position ()); return nullptr;; } ret = Expression(nullptr); if (failed_ ) return nullptr; } while (false); |
2105 | EXPECT_TOKENn(')')do { if (scanner_.Token() != ')') { failed_ = true; failure_message_ = "Unexpected token"; failure_location_ = static_cast<int >(scanner_.Position()); return nullptr;; } scanner_.Next() ; } while (false); |
2106 | return ret; |
2107 | } |
2108 | |
2109 | // 6.9 ValidateCall |
2110 | AsmType* AsmJsParser::ValidateCall() { |
2111 | AsmType* return_type = call_coercion_; |
2112 | call_coercion_ = nullptr; |
2113 | size_t call_pos = scanner_.Position(); |
2114 | size_t to_number_pos = call_coercion_position_; |
2115 | bool allow_peek = (call_coercion_deferred_position_ == scanner_.Position()); |
2116 | AsmJsScanner::token_t function_name = Consume(); |
2117 | |
2118 | // Distinguish between ordinary function calls and function table calls. In |
2119 | // both cases we might be seeing the {function_name} for the first time and |
2120 | // hence allocate a {VarInfo} here, all subsequent uses of the same name then |
2121 | // need to match the information stored at this point. |
2122 | base::Optional<TemporaryVariableScope> tmp_scope; |
2123 | if (Check('[')) { |
2124 | AsmType* index = nullptr; |
2125 | RECURSEn(index = EqualityExpression())do { ((void) 0); if (GetCurrentStackPosition() < stack_limit_ ) { failed_ = true; failure_message_ = "Stack overflow while parsing asm.js module." ; failure_location_ = static_cast<int>(scanner_.Position ()); return nullptr;; } index = EqualityExpression(); if (failed_ ) return nullptr; } while (false); |
2126 | if (!index->IsA(AsmType::Intish())) { |
2127 | FAILn("Expected intish index")failed_ = true; failure_message_ = "Expected intish index"; failure_location_ = static_cast<int>(scanner_.Position()); return nullptr ;; |
2128 | } |
2129 | EXPECT_TOKENn('&')do { if (scanner_.Token() != '&') { failed_ = true; failure_message_ = "Unexpected token"; failure_location_ = static_cast<int >(scanner_.Position()); return nullptr;; } scanner_.Next() ; } while (false); |
2130 | uint32_t mask = 0; |
2131 | if (!CheckForUnsigned(&mask)) { |
2132 | FAILn("Expected mask literal")failed_ = true; failure_message_ = "Expected mask literal"; failure_location_ = static_cast<int>(scanner_.Position()); return nullptr ;; |
2133 | } |
2134 | if (!base::bits::IsPowerOfTwo(mask + 1)) { |
2135 | FAILn("Expected power of 2 mask")failed_ = true; failure_message_ = "Expected power of 2 mask" ; failure_location_ = static_cast<int>(scanner_.Position ()); return nullptr;; |
2136 | } |
2137 | current_function_builder_->EmitI32Const(mask); |
2138 | current_function_builder_->Emit(kExprI32And); |
2139 | EXPECT_TOKENn(']')do { if (scanner_.Token() != ']') { failed_ = true; failure_message_ = "Unexpected token"; failure_location_ = static_cast<int >(scanner_.Position()); return nullptr;; } scanner_.Next() ; } while (false); |
2140 | VarInfo* function_info = GetVarInfo(function_name); |
2141 | if (function_info->kind == VarKind::kUnused) { |
2142 | if (module_builder_->NumTables() == 0) { |
2143 | module_builder_->AddTable(kWasmFuncRef, 0); |
2144 | } |
2145 | uint32_t func_index = module_builder_->IncreaseTableMinSize(0, mask + 1); |
2146 | if (func_index == std::numeric_limits<uint32_t>::max()) { |
2147 | FAILn("Exceeded maximum function table size")failed_ = true; failure_message_ = "Exceeded maximum function table size" ; failure_location_ = static_cast<int>(scanner_.Position ()); return nullptr;; |
2148 | } |
2149 | function_info->kind = VarKind::kTable; |
2150 | function_info->mask = mask; |
2151 | function_info->index = func_index; |
2152 | function_info->mutable_variable = false; |
2153 | } else { |
2154 | if (function_info->kind != VarKind::kTable) { |
2155 | FAILn("Expected call table")failed_ = true; failure_message_ = "Expected call table"; failure_location_ = static_cast<int>(scanner_.Position()); return nullptr ;; |
2156 | } |
2157 | if (function_info->mask != mask) { |
2158 | FAILn("Mask size mismatch")failed_ = true; failure_message_ = "Mask size mismatch"; failure_location_ = static_cast<int>(scanner_.Position()); return nullptr ;; |
2159 | } |
2160 | } |
2161 | current_function_builder_->EmitI32Const(function_info->index); |
2162 | current_function_builder_->Emit(kExprI32Add); |
2163 | // We have to use a temporary for the correct order of evaluation. |
2164 | tmp_scope.emplace(this); |
2165 | current_function_builder_->EmitSetLocal(tmp_scope->get()); |
2166 | // The position of function table calls is after the table lookup. |
2167 | call_pos = scanner_.Position(); |
2168 | } else { |
2169 | VarInfo* function_info = GetVarInfo(function_name); |
2170 | if (function_info->kind == VarKind::kUnused) { |
2171 | function_info->kind = VarKind::kFunction; |
2172 | function_info->function_builder = module_builder_->AddFunction(); |
2173 | function_info->index = function_info->function_builder->func_index(); |
2174 | function_info->mutable_variable = false; |
2175 | } else { |
2176 | if (function_info->kind != VarKind::kFunction && |
2177 | function_info->kind < VarKind::kImportedFunction) { |
2178 | FAILn("Expected function as call target")failed_ = true; failure_message_ = "Expected function as call target" ; failure_location_ = static_cast<int>(scanner_.Position ()); return nullptr;; |
2179 | } |
2180 | } |
2181 | } |
2182 | |
2183 | // Parse argument list and gather types. |
2184 | CachedVector<AsmType*> param_types(&cached_asm_type_p_vectors_); |
2185 | CachedVector<AsmType*> param_specific_types(&cached_asm_type_p_vectors_); |
2186 | EXPECT_TOKENn('(')do { if (scanner_.Token() != '(') { failed_ = true; failure_message_ = "Unexpected token"; failure_location_ = static_cast<int >(scanner_.Position()); return nullptr;; } scanner_.Next() ; } while (false); |
2187 | while (!failed_ && !Peek(')')) { |
2188 | AsmType* t; |
2189 | RECURSEn(t = AssignmentExpression())do { ((void) 0); if (GetCurrentStackPosition() < stack_limit_ ) { failed_ = true; failure_message_ = "Stack overflow while parsing asm.js module." ; failure_location_ = static_cast<int>(scanner_.Position ()); return nullptr;; } t = AssignmentExpression(); if (failed_ ) return nullptr; } while (false); |
2190 | param_specific_types.push_back(t); |
2191 | if (t->IsA(AsmType::Int())) { |
2192 | param_types.push_back(AsmType::Int()); |
2193 | } else if (t->IsA(AsmType::Float())) { |
2194 | param_types.push_back(AsmType::Float()); |
2195 | } else if (t->IsA(AsmType::Double())) { |
2196 | param_types.push_back(AsmType::Double()); |
2197 | } else { |
2198 | FAILn("Bad function argument type")failed_ = true; failure_message_ = "Bad function argument type" ; failure_location_ = static_cast<int>(scanner_.Position ()); return nullptr;; |
2199 | } |
2200 | if (!Peek(')')) { |
2201 | EXPECT_TOKENn(',')do { if (scanner_.Token() != ',') { failed_ = true; failure_message_ = "Unexpected token"; failure_location_ = static_cast<int >(scanner_.Position()); return nullptr;; } scanner_.Next() ; } while (false); |
2202 | } |
2203 | } |
2204 | EXPECT_TOKENn(')')do { if (scanner_.Token() != ')') { failed_ = true; failure_message_ = "Unexpected token"; failure_location_ = static_cast<int >(scanner_.Position()); return nullptr;; } scanner_.Next() ; } while (false); |
2205 | |
2206 | // Reload {VarInfo} after parsing arguments as table might have grown. |
2207 | VarInfo* function_info = GetVarInfo(function_name); |
2208 | |
2209 | // We potentially use lookahead in order to determine the return type in case |
2210 | // it is not yet clear from the call context. Special care has to be taken to |
2211 | // ensure the non-contextual lookahead is valid. The following restrictions |
2212 | // substantiate the validity of the lookahead implemented below: |
2213 | // - All calls (except stdlib calls) require some sort of type annotation. |
2214 | // - The coercion to "signed" is part of the {BitwiseORExpression}, any |
2215 | // intermittent expressions like parenthesis in `(callsite(..))|0` are |
2216 | // syntactically not considered coercions. |
2217 | // - The coercion to "double" as part of the {UnaryExpression} has higher |
2218 | // precedence and wins in `+callsite(..)|0` cases. Only "float" return |
2219 | // types are overridden in `fround(callsite(..)|0)` expressions. |
2220 | // - Expected coercions to "signed" are flagged via {call_coercion_deferred} |
2221 | // and later on validated as part of {BitwiseORExpression} to ensure they |
2222 | // indeed apply to the current call expression. |
2223 | // - The deferred validation is only allowed if {BitwiseORExpression} did |
2224 | // promise to fulfill the request via {call_coercion_deferred_position}. |
2225 | if (allow_peek && Peek('|') && |
2226 | function_info->kind <= VarKind::kImportedFunction && |
2227 | (return_type == nullptr || return_type->IsA(AsmType::Float()))) { |
2228 | DCHECK_NULL(call_coercion_deferred_)((void) 0); |
2229 | call_coercion_deferred_ = AsmType::Signed(); |
2230 | to_number_pos = scanner_.Position(); |
2231 | return_type = AsmType::Signed(); |
2232 | } else if (return_type == nullptr) { |
2233 | to_number_pos = call_pos; // No conversion. |
2234 | return_type = AsmType::Void(); |
2235 | } |
2236 | |
2237 | // Compute function type and signature based on gathered types. |
2238 | AsmType* function_type = AsmType::Function(zone(), return_type); |
2239 | for (auto t : param_types) { |
2240 | function_type->AsFunctionType()->AddArgument(t); |
2241 | } |
2242 | FunctionSig* sig = ConvertSignature(return_type, param_types); |
2243 | uint32_t signature_index = module_builder_->AddSignature(sig); |
2244 | |
2245 | // Emit actual function invocation depending on the kind. At this point we |
2246 | // also determined the complete function type and can perform checking against |
2247 | // the expected type or update the expected type in case of first occurrence. |
2248 | if (function_info->kind == VarKind::kImportedFunction) { |
2249 | if (param_types.size() > kV8MaxWasmFunctionParams) { |
2250 | FAILn("Number of parameters exceeds internal limit")failed_ = true; failure_message_ = "Number of parameters exceeds internal limit" ; failure_location_ = static_cast<int>(scanner_.Position ()); return nullptr;; |
2251 | } |
2252 | for (auto t : param_specific_types) { |
2253 | if (!t->IsA(AsmType::Extern())) { |
2254 | FAILn("Imported function args must be type extern")failed_ = true; failure_message_ = "Imported function args must be type extern" ; failure_location_ = static_cast<int>(scanner_.Position ()); return nullptr;; |
2255 | } |
2256 | } |
2257 | if (return_type->IsA(AsmType::Float())) { |
2258 | FAILn("Imported function can't be called as float")failed_ = true; failure_message_ = "Imported function can't be called as float" ; failure_location_ = static_cast<int>(scanner_.Position ()); return nullptr;; |
2259 | } |
2260 | DCHECK_NOT_NULL(function_info->import)((void) 0); |
2261 | // TODO(bradnelson): Factor out. |
2262 | uint32_t index; |
2263 | auto it = function_info->import->cache.find(*sig); |
2264 | if (it != function_info->import->cache.end()) { |
2265 | index = it->second; |
2266 | DCHECK(function_info->function_defined)((void) 0); |
2267 | } else { |
2268 | index = |
2269 | module_builder_->AddImport(function_info->import->function_name, sig); |
2270 | function_info->import->cache[*sig] = index; |
2271 | function_info->function_defined = true; |
2272 | } |
2273 | current_function_builder_->AddAsmWasmOffset(call_pos, to_number_pos); |
2274 | current_function_builder_->EmitWithU32V(kExprCallFunction, index); |
2275 | } else if (function_info->kind > VarKind::kImportedFunction) { |
2276 | AsmCallableType* callable = function_info->type->AsCallableType(); |
2277 | if (!callable) { |
2278 | FAILn("Expected callable function")failed_ = true; failure_message_ = "Expected callable function" ; failure_location_ = static_cast<int>(scanner_.Position ()); return nullptr;; |
2279 | } |
2280 | // TODO(bradnelson): Refactor AsmType to not need this. |
2281 | if (callable->CanBeInvokedWith(return_type, param_specific_types)) { |
2282 | // Return type ok. |
2283 | } else if (callable->CanBeInvokedWith(AsmType::Float(), |
2284 | param_specific_types)) { |
2285 | return_type = AsmType::Float(); |
2286 | } else if (callable->CanBeInvokedWith(AsmType::Floatish(), |
2287 | param_specific_types)) { |
2288 | return_type = AsmType::Floatish(); |
2289 | } else if (callable->CanBeInvokedWith(AsmType::Double(), |
2290 | param_specific_types)) { |
2291 | return_type = AsmType::Double(); |
2292 | } else if (callable->CanBeInvokedWith(AsmType::Signed(), |
2293 | param_specific_types)) { |
2294 | return_type = AsmType::Signed(); |
2295 | } else if (callable->CanBeInvokedWith(AsmType::Unsigned(), |
2296 | param_specific_types)) { |
2297 | return_type = AsmType::Unsigned(); |
2298 | } else { |
2299 | FAILn("Function use doesn't match definition")failed_ = true; failure_message_ = "Function use doesn't match definition" ; failure_location_ = static_cast<int>(scanner_.Position ()); return nullptr;; |
2300 | } |
2301 | switch (function_info->kind) { |
2302 | #define V(name, Name, op, sig) \ |
2303 | case VarKind::kMath##Name: \ |
2304 | current_function_builder_->Emit(op); \ |
2305 | break; |
2306 | STDLIB_MATH_FUNCTION_MONOMORPHIC_LIST(V)V(acos, Acos, kExprF64Acos, dq2d) V(asin, Asin, kExprF64Asin, dq2d) V(atan, Atan, kExprF64Atan, dq2d) V(cos, Cos, kExprF64Cos , dq2d) V(sin, Sin, kExprF64Sin, dq2d) V(tan, Tan, kExprF64Tan , dq2d) V(exp, Exp, kExprF64Exp, dq2d) V(log, Log, kExprF64Log , dq2d) V(atan2, Atan2, kExprF64Atan2, dqdq2d) V(pow, Pow, kExprF64Pow , dqdq2d) V(imul, Imul, kExprI32Mul, ii2s) V(clz32, Clz32, kExprI32Clz , i2s) |
2307 | #undef V |
2308 | #define V(name, Name, op, sig) \ |
2309 | case VarKind::kMath##Name: \ |
2310 | if (param_specific_types[0]->IsA(AsmType::DoubleQ())) { \ |
2311 | current_function_builder_->Emit(kExprF64##Name); \ |
2312 | } else if (param_specific_types[0]->IsA(AsmType::FloatQ())) { \ |
2313 | current_function_builder_->Emit(kExprF32##Name); \ |
2314 | } else { \ |
2315 | UNREACHABLE()V8_Fatal("unreachable code"); \ |
2316 | } \ |
2317 | break; |
2318 | STDLIB_MATH_FUNCTION_CEIL_LIKE_LIST(V)V(ceil, Ceil, x, ceil_like) V(floor, Floor, x, ceil_like) V(sqrt , Sqrt, x, ceil_like) |
2319 | #undef V |
2320 | case VarKind::kMathMin: |
2321 | case VarKind::kMathMax: |
2322 | if (param_specific_types[0]->IsA(AsmType::Double())) { |
2323 | for (size_t i = 1; i < param_specific_types.size(); ++i) { |
2324 | if (function_info->kind == VarKind::kMathMin) { |
2325 | current_function_builder_->Emit(kExprF64Min); |
2326 | } else { |
2327 | current_function_builder_->Emit(kExprF64Max); |
2328 | } |
2329 | } |
2330 | } else if (param_specific_types[0]->IsA(AsmType::Float())) { |
2331 | // NOTE: Not technically part of the asm.js spec, but Firefox |
2332 | // accepts it. |
2333 | for (size_t i = 1; i < param_specific_types.size(); ++i) { |
2334 | if (function_info->kind == VarKind::kMathMin) { |
2335 | current_function_builder_->Emit(kExprF32Min); |
2336 | } else { |
2337 | current_function_builder_->Emit(kExprF32Max); |
2338 | } |
2339 | } |
2340 | } else if (param_specific_types[0]->IsA(AsmType::Signed())) { |
2341 | TemporaryVariableScope tmp_x(this); |
2342 | TemporaryVariableScope tmp_y(this); |
2343 | for (size_t i = 1; i < param_specific_types.size(); ++i) { |
2344 | current_function_builder_->EmitSetLocal(tmp_x.get()); |
2345 | current_function_builder_->EmitTeeLocal(tmp_y.get()); |
2346 | current_function_builder_->EmitGetLocal(tmp_x.get()); |
2347 | if (function_info->kind == VarKind::kMathMin) { |
2348 | current_function_builder_->Emit(kExprI32GeS); |
2349 | } else { |
2350 | current_function_builder_->Emit(kExprI32LeS); |
2351 | } |
2352 | current_function_builder_->EmitWithU8(kExprIf, kI32Code); |
2353 | current_function_builder_->EmitGetLocal(tmp_x.get()); |
2354 | current_function_builder_->Emit(kExprElse); |
2355 | current_function_builder_->EmitGetLocal(tmp_y.get()); |
2356 | current_function_builder_->Emit(kExprEnd); |
2357 | } |
2358 | } else { |
2359 | UNREACHABLE()V8_Fatal("unreachable code"); |
2360 | } |
2361 | break; |
2362 | |
2363 | case VarKind::kMathAbs: |
2364 | if (param_specific_types[0]->IsA(AsmType::Signed())) { |
2365 | TemporaryVariableScope tmp(this); |
2366 | current_function_builder_->EmitTeeLocal(tmp.get()); |
2367 | current_function_builder_->EmitGetLocal(tmp.get()); |
2368 | current_function_builder_->EmitI32Const(31); |
2369 | current_function_builder_->Emit(kExprI32ShrS); |
2370 | current_function_builder_->EmitTeeLocal(tmp.get()); |
2371 | current_function_builder_->Emit(kExprI32Xor); |
2372 | current_function_builder_->EmitGetLocal(tmp.get()); |
2373 | current_function_builder_->Emit(kExprI32Sub); |
2374 | } else if (param_specific_types[0]->IsA(AsmType::DoubleQ())) { |
2375 | current_function_builder_->Emit(kExprF64Abs); |
2376 | } else if (param_specific_types[0]->IsA(AsmType::FloatQ())) { |
2377 | current_function_builder_->Emit(kExprF32Abs); |
2378 | } else { |
2379 | UNREACHABLE()V8_Fatal("unreachable code"); |
2380 | } |
2381 | break; |
2382 | |
2383 | case VarKind::kMathFround: |
2384 | // NOTE: Handled in {AsmJsParser::CallExpression} specially and treated |
2385 | // as a coercion to "float" type. Cannot be reached as a call here. |
2386 | UNREACHABLE()V8_Fatal("unreachable code"); |
2387 | |
2388 | default: |
2389 | UNREACHABLE()V8_Fatal("unreachable code"); |
2390 | } |
2391 | } else { |
2392 | DCHECK(function_info->kind == VarKind::kFunction ||((void) 0) |
2393 | function_info->kind == VarKind::kTable)((void) 0); |
2394 | if (function_info->type->IsA(AsmType::None())) { |
2395 | function_info->type = function_type; |
2396 | } else { |
2397 | AsmCallableType* callable = function_info->type->AsCallableType(); |
2398 | if (!callable || |
2399 | !callable->CanBeInvokedWith(return_type, param_specific_types)) { |
2400 | FAILn("Function use doesn't match definition")failed_ = true; failure_message_ = "Function use doesn't match definition" ; failure_location_ = static_cast<int>(scanner_.Position ()); return nullptr;; |
2401 | } |
2402 | } |
2403 | if (function_info->kind == VarKind::kTable) { |
2404 | current_function_builder_->EmitGetLocal(tmp_scope->get()); |
2405 | current_function_builder_->AddAsmWasmOffset(call_pos, to_number_pos); |
2406 | current_function_builder_->Emit(kExprCallIndirect); |
2407 | current_function_builder_->EmitU32V(signature_index); |
2408 | current_function_builder_->EmitU32V(0); // table index |
2409 | } else { |
2410 | current_function_builder_->AddAsmWasmOffset(call_pos, to_number_pos); |
2411 | current_function_builder_->Emit(kExprCallFunction); |
2412 | current_function_builder_->EmitDirectCallIndex(function_info->index); |
2413 | } |
2414 | } |
2415 | |
2416 | return return_type; |
2417 | } |
2418 | |
2419 | // 6.9 ValidateCall - helper |
2420 | bool AsmJsParser::PeekCall() { |
2421 | if (!scanner_.IsGlobal()) { |
2422 | return false; |
2423 | } |
2424 | if (GetVarInfo(scanner_.Token())->kind == VarKind::kFunction) { |
2425 | return true; |
2426 | } |
2427 | if (GetVarInfo(scanner_.Token())->kind >= VarKind::kImportedFunction) { |
2428 | return true; |
2429 | } |
2430 | if (GetVarInfo(scanner_.Token())->kind == VarKind::kUnused || |
2431 | GetVarInfo(scanner_.Token())->kind == VarKind::kTable) { |
2432 | scanner_.Next(); |
2433 | if (Peek('(') || Peek('[')) { |
2434 | scanner_.Rewind(); |
2435 | return true; |
2436 | } |
2437 | scanner_.Rewind(); |
2438 | } |
2439 | return false; |
2440 | } |
2441 | |
2442 | // 6.10 ValidateHeapAccess |
2443 | void AsmJsParser::ValidateHeapAccess() { |
2444 | VarInfo* info = GetVarInfo(Consume()); |
2445 | int32_t size = info->type->ElementSizeInBytes(); |
2446 | EXPECT_TOKEN('[')do { if (scanner_.Token() != '[') { failed_ = true; failure_message_ = "Unexpected token"; failure_location_ = static_cast<int >(scanner_.Position()); return ;; } scanner_.Next(); } while (false); |
2447 | uint32_t offset; |
2448 | if (CheckForUnsigned(&offset)) { |
2449 | // TODO(bradnelson): Check more things. |
2450 | // TODO(asmjs): Clarify and explain where this limit is coming from, |
2451 | // as it is not mandated by the spec directly. |
2452 | if (offset > 0x7FFFFFFF || |
2453 | static_cast<uint64_t>(offset) * static_cast<uint64_t>(size) > |
2454 | 0x7FFFFFFF) { |
2455 | FAIL("Heap access out of range")failed_ = true; failure_message_ = "Heap access out of range" ; failure_location_ = static_cast<int>(scanner_.Position ()); return ;; |
2456 | } |
2457 | if (Check(']')) { |
2458 | current_function_builder_->EmitI32Const( |
2459 | static_cast<uint32_t>(offset * size)); |
2460 | // NOTE: This has to happen here to work recursively. |
2461 | heap_access_type_ = info->type; |
2462 | return; |
2463 | } else { |
2464 | scanner_.Rewind(); |
2465 | } |
2466 | } |
2467 | AsmType* index_type; |
2468 | if (info->type->IsA(AsmType::Int8Array()) || |
2469 | info->type->IsA(AsmType::Uint8Array())) { |
2470 | RECURSE(index_type = Expression(nullptr)); |
2471 | } else { |
2472 | RECURSE(index_type = ShiftExpression()); |
2473 | if (heap_access_shift_position_ == kNoHeapAccessShift) { |
2474 | FAIL("Expected shift of word size")failed_ = true; failure_message_ = "Expected shift of word size" ; failure_location_ = static_cast<int>(scanner_.Position ()); return ;; |
2475 | } |
2476 | if (heap_access_shift_value_ > 3) { |
2477 | FAIL("Expected valid heap access shift")failed_ = true; failure_message_ = "Expected valid heap access shift" ; failure_location_ = static_cast<int>(scanner_.Position ()); return ;; |
2478 | } |
2479 | if ((1 << heap_access_shift_value_) != size) { |
2480 | FAIL("Expected heap access shift to match heap view")failed_ = true; failure_message_ = "Expected heap access shift to match heap view" ; failure_location_ = static_cast<int>(scanner_.Position ()); return ;; |
2481 | } |
2482 | // Delete the code of the actual shift operation. |
2483 | current_function_builder_->DeleteCodeAfter(heap_access_shift_position_); |
2484 | // Mask bottom bits to match asm.js behavior. |
2485 | current_function_builder_->EmitI32Const(~(size - 1)); |
2486 | current_function_builder_->Emit(kExprI32And); |
2487 | } |
2488 | if (!index_type->IsA(AsmType::Intish())) { |
2489 | FAIL("Expected intish index")failed_ = true; failure_message_ = "Expected intish index"; failure_location_ = static_cast<int>(scanner_.Position()); return ;; |
2490 | } |
2491 | EXPECT_TOKEN(']')do { if (scanner_.Token() != ']') { failed_ = true; failure_message_ = "Unexpected token"; failure_location_ = static_cast<int >(scanner_.Position()); return ;; } scanner_.Next(); } while (false); |
2492 | // NOTE: This has to happen here to work recursively. |
2493 | heap_access_type_ = info->type; |
2494 | } |
2495 | |
2496 | // 6.11 ValidateFloatCoercion |
2497 | void AsmJsParser::ValidateFloatCoercion() { |
2498 | if (!scanner_.IsGlobal() || |
2499 | !GetVarInfo(scanner_.Token())->type->IsA(stdlib_fround_)) { |
2500 | FAIL("Expected fround")failed_ = true; failure_message_ = "Expected fround"; failure_location_ = static_cast<int>(scanner_.Position()); return ;; |
2501 | } |
2502 | scanner_.Next(); |
2503 | EXPECT_TOKEN('(')do { if (scanner_.Token() != '(') { failed_ = true; failure_message_ = "Unexpected token"; failure_location_ = static_cast<int >(scanner_.Position()); return ;; } scanner_.Next(); } while (false); |
2504 | call_coercion_ = AsmType::Float(); |
2505 | // NOTE: The coercion position to float is not observable from JavaScript, |
2506 | // because imported functions are not allowed to have float return type. |
2507 | call_coercion_position_ = scanner_.Position(); |
2508 | AsmType* ret; |
2509 | RECURSE(ret = AssignmentExpression()); |
2510 | if (ret->IsA(AsmType::Floatish())) { |
2511 | // Do nothing, as already a float. |
2512 | } else if (ret->IsA(AsmType::DoubleQ())) { |
2513 | current_function_builder_->Emit(kExprF32ConvertF64); |
2514 | } else if (ret->IsA(AsmType::Signed())) { |
2515 | current_function_builder_->Emit(kExprF32SConvertI32); |
2516 | } else if (ret->IsA(AsmType::Unsigned())) { |
2517 | current_function_builder_->Emit(kExprF32UConvertI32); |
2518 | } else { |
2519 | FAIL("Illegal conversion to float")failed_ = true; failure_message_ = "Illegal conversion to float" ; failure_location_ = static_cast<int>(scanner_.Position ()); return ;; |
2520 | } |
2521 | EXPECT_TOKEN(')')do { if (scanner_.Token() != ')') { failed_ = true; failure_message_ = "Unexpected token"; failure_location_ = static_cast<int >(scanner_.Position()); return ;; } scanner_.Next(); } while (false); |
2522 | } |
2523 | |
2524 | void AsmJsParser::ScanToClosingParenthesis() { |
2525 | int depth = 0; |
2526 | for (;;) { |
2527 | if (Peek('(')) { |
2528 | ++depth; |
2529 | } else if (Peek(')')) { |
2530 | --depth; |
2531 | if (depth < 0) { |
2532 | break; |
2533 | } |
2534 | } else if (Peek(AsmJsScanner::kEndOfInput)) { |
2535 | break; |
2536 | } |
2537 | scanner_.Next(); |
2538 | } |
2539 | } |
2540 | |
2541 | void AsmJsParser::GatherCases(ZoneVector<int32_t>* cases) { |
2542 | size_t start = scanner_.Position(); |
2543 | int depth = 0; |
2544 | for (;;) { |
2545 | if (Peek('{')) { |
2546 | ++depth; |
2547 | } else if (Peek('}')) { |
2548 | --depth; |
2549 | if (depth <= 0) { |
2550 | break; |
2551 | } |
2552 | } else if (depth == 1 && Peek(TOK(case)AsmJsScanner::kToken_case)) { |
2553 | scanner_.Next(); |
2554 | uint32_t uvalue; |
2555 | bool negate = false; |
2556 | if (Check('-')) negate = true; |
2557 | if (!CheckForUnsigned(&uvalue)) { |
2558 | break; |
2559 | } |
2560 | int32_t value = static_cast<int32_t>(uvalue); |
2561 | DCHECK_IMPLIES(negate && uvalue == 0x80000000, value == kMinInt)((void) 0); |
2562 | if (negate && value != kMinInt) { |
2563 | value = -value; |
2564 | } |
2565 | cases->push_back(value); |
2566 | } else if (Peek(AsmJsScanner::kEndOfInput) || |
2567 | Peek(AsmJsScanner::kParseError)) { |
2568 | break; |
2569 | } |
2570 | scanner_.Next(); |
2571 | } |
2572 | scanner_.Seek(start); |
2573 | } |
2574 | |
2575 | } // namespace wasm |
2576 | } // namespace internal |
2577 | } // namespace v8 |
2578 | |
2579 | #undef RECURSE |