Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(62)

Side by Side Diff: src/wasm/asm-typer.cc

Issue 2071343003: V8. ASM-2-WASM. Validator V2. (Closed) Base URL: https://chromium.googlesource.com/v8/v8.git@master
Patch Set: tests function tables and module exports. Created 4 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2016 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/wasm/asm-typer.h"
6
7 #include <limits>
8 #include <string>
9
10 #include "src/v8.h"
11
12 #include "src/ast/ast.h"
13 #include "src/ast/scopes.h"
14 #include "src/base/bits.h"
15 #include "src/codegen.h"
16 #include "src/type-cache.h"
17 #include "src/wasm/asm-types.h"
18
19 #ifndef DEBUG
20
21 #define FAIL(node, msg) \
22 do { \
23 int line = node->position() == RelocInfo::kNoPosition \
24 ? -1 \
25 : script_->GetLineNumber(node->position()); \
26 base::OS::SNPrintF(error_message_, sizeof(error_message_), \
27 "asm: line %d: %s\n", line + 1, msg); \
28 return AsmType::None(); \
29 } while (false)
30
31 #else // defined(DEBUG)
32
33 #define FAIL(node, msg) \
34 do { \
35 int line = node->position() == RelocInfo::kNoPosition \
36 ? -1 \
37 : script_->GetLineNumber(node->position()); \
38 base::OS::SNPrintF(error_message_, sizeof(error_message_), \
39 "asm: line %d: %s\n", line + 1, msg); \
40 /*(node)->Print(isolate_);*/ \
41 return AsmType::None(); \
42 } while (false)
43
44 #endif // DEBUG
45
46 #define RECURSE(call) \
bradnelson 2016/06/24 00:19:00 Put a TODO in about adding the stack checks.
John 2016/06/24 17:47:57 Done.
47 do { \
48 if ((call) == AsmType::None()) { \
49 return AsmType::None(); \
50 } \
51 } while (false)
52
53 namespace v8 {
54 namespace internal {
55 namespace wasm {
56
57 using v8::internal::AstNode;
58
59 // ----------------------------------------------------------------------------
60 // Implementation of AsmTyper::FlattenedStatements
61
62 AsmTyper::FlattenedStatements::FlattenedStatements(Zone* zone,
63 ZoneList<Statement*>* s)
64 : context_stack_(zone) {
65 context_stack_.emplace_back(Context(s));
66 }
67
68 Statement* AsmTyper::FlattenedStatements::Next() {
69 for (;;) {
70 if (context_stack_.empty()) {
71 return nullptr;
72 }
73
74 Context* current = &context_stack_.back();
75
76 if (current->statements_->length() <= current->next_index_) {
77 context_stack_.pop_back();
78 continue;
79 }
80
81 Statement* current_statement =
82 current->statements_->at(current->next_index_++);
83 if (current_statement->IsBlock()) {
84 context_stack_.emplace_back(
85 Context(current_statement->AsBlock()->statements()));
86 continue;
87 }
88
89 return current_statement;
90 }
91 }
92
93 // ----------------------------------------------------------------------------
94 // Implementation of AsmTyper::VariableInfo
95
96 AsmTyper::VariableInfo* AsmTyper::VariableInfo::Clone(Zone* zone) const {
97 CHECK(standard_member_ != kNone);
98 auto* new_var_info = new (zone) VariableInfo(type_);
99 new_var_info->standard_member_ = standard_member_;
100 new_var_info->mutability_ = mutability_;
101 return new_var_info;
102 }
103
104 void AsmTyper::VariableInfo::FirstForwardUseIs(VariableProxy* var) {
105 missing_definition_ = true;
106 first_forward_use_ = var;
bradnelson 2016/06/24 00:19:00 Assert only set once?
John 2016/06/24 17:47:56 Done.
107 }
108
109 // ----------------------------------------------------------------------------
110 // Implementation of AsmTyper
111
112 AsmTyper::AsmTyper(Isolate* isolate, Zone* zone, Script* script,
113 FunctionLiteral* root)
114 : isolate_(isolate),
115 zone_(zone),
116 script_(script),
117 root_(root),
118 forward_definitions_(zone),
119 stdlib_types_(zone),
120 stdlib_math_types_(zone),
121 global_scope_(ZoneHashMap::PointersMatch,
122 ZoneHashMap::kDefaultHashMapCapacity,
123 ZoneAllocationPolicy(zone)),
124 local_scope_(ZoneHashMap::PointersMatch,
125 ZoneHashMap::kDefaultHashMapCapacity,
126 ZoneAllocationPolicy(zone)) {
127 (void)isolate_;
bradnelson 2016/06/24 00:19:00 Space in between cast. (Which compiler catches thi
John 2016/06/24 17:47:58 LLVM was complaining locally. It stopped -- maybe
128 InitializeStdlib();
129 module_info_.set_standard_member(kModule);
130 }
131
132 namespace {
133 bool ValidAsmIdentifier(Handle<String> name) {
134 static const char* kInvalidAsmNames[] = {"eval", "arguments"};
bradnelson 2016/06/24 00:19:03 Ooh didn't catch these!. Nice
John 2016/06/24 17:47:58 Acknowledged.
135
136 for (size_t ii = 0; ii < arraysize(kInvalidAsmNames); ++ii) {
137 if (strcmp(name->ToCString().get(), kInvalidAsmNames[ii]) == 0) {
138 return false;
139 }
140 }
141 return true;
142 }
143 } // namespace
144
145 void AsmTyper::InitializeStdlib() {
146 auto* d = AsmType::Double();
147 auto* dq = AsmType::DoubleQ();
148 auto* dq2d = AsmType::Function(zone_, d);
149 dq2d->AsFunctionType()->AddArgument(dq);
150 auto* dqdq2d = AsmType::Function(zone_, d);
151 dqdq2d->AsFunctionType()->AddArgument(dq);
bradnelson 2016/06/24 00:19:01 Worth having a helper in asm-types.h to make 1/2 a
John 2016/06/24 17:47:57 I don't know why the empty line's missing here. A
152 dqdq2d->AsFunctionType()->AddArgument(dq);
153
154 auto* f = AsmType::Float();
155 auto* fq = AsmType::FloatQ();
156 auto* fq2f = AsmType::Function(zone_, f);
157 fq2f->AsFunctionType()->AddArgument(fq);
158
159 auto* s = AsmType::Signed();
160 auto* s2s = AsmType::Function(zone_, s);
161 s2s->AsFunctionType()->AddArgument(s);
162
163 auto* i = AsmType::Int();
164 auto* ii2s = AsmType::Function(zone_, s);
165 ii2s->AsFunctionType()->AddArgument(i);
166 ii2s->AsFunctionType()->AddArgument(i);
167
168 auto* minmax_d = AsmType::MinMaxType(zone_, d, d);
169 auto* minmax_i = AsmType::MinMaxType(zone_, s, i);
170 auto* minmax = AsmType::OverloadedFunction(zone_);
171 minmax->AsOverloadedFunctionType()->AddOverload(minmax_i);
172 minmax->AsOverloadedFunctionType()->AddOverload(minmax_d);
173
174 auto* fround = AsmType::FroundType(zone_);
175
176 auto* abs = AsmType::OverloadedFunction(zone_);
177 abs->AsOverloadedFunctionType()->AddOverload(s2s);
178 abs->AsOverloadedFunctionType()->AddOverload(dq2d);
179 abs->AsOverloadedFunctionType()->AddOverload(fq2f);
180
181 auto* ceil = AsmType::OverloadedFunction(zone_);
182 ceil->AsOverloadedFunctionType()->AddOverload(dq2d);
183 ceil->AsOverloadedFunctionType()->AddOverload(fq2f);
184
185 auto* floor = ceil;
186 auto* sqrt = ceil;
187
188 struct StandardMemberInitializer {
189 const char* name;
190 StandardMember standard_member;
191 AsmType* type;
192 };
193
194 const StandardMemberInitializer math[] = {
195 {"PI", kMathPI, d},
196 {"E", kMathE, d},
197 {"LN2", kMathLN2, d},
198 {"LN10", kMathLN10, d},
199 {"LOG2E", kMathLOG2E, d},
200 {"LOG10E", kMathLOG10E, d},
201 {"SQRT2", kMathSQRT2, d},
202 {"SQRT1_2", kMathSQRT1_2, d},
203 {"imul", kMathImul, ii2s},
204 {"abs", kMathAbs, abs},
205 {"ceil", kMathCeil, ceil},
206 {"floor", kMathFloor, floor},
207 {"fround", kMathFround, fround},
208 {"pow", kMathPow, dqdq2d},
209 {"exp", kMathExp, dq2d},
210 {"log", kMathLog, dq2d},
211 {"min", kMathMin, minmax},
212 {"max", kMathMax, minmax},
213 {"sqrt", kMathSqrt, sqrt},
214 {"cos", kMathCos, dq2d},
215 {"sin", kMathSin, dq2d},
216 {"tan", kMathTan, dq2d},
217 {"acos", kMathAcos, dq2d},
218 {"asin", kMathAsin, dq2d},
219 {"atan", kMathAtan, dq2d},
220 {"atan2", kMathAtan2, dqdq2d},
221 };
222
223 const StandardMemberInitializer stdlib[] = {{"Infinity", kInfinity, d},
224 {"NaN", kNaN, d},
225 #define ASM_TYPED_ARRAYS(V) \
226 V(Uint8) \
227 V(Int8) \
228 V(Uint16) \
229 V(Int16) \
230 V(Uint32) \
231 V(Int32) \
232 V(Float32) \
233 V(Float64)
234
235 #define ASM_TYPED_ARRAY(TypeName) \
236 {#TypeName "Array", kNone, AsmType::TypeName##Array()},
237 ASM_TYPED_ARRAYS(ASM_TYPED_ARRAY)
238 #undef ASM_TYPED_ARRAY
239 };
240
241 for (unsigned ii = 0; ii < arraysize(stdlib); ++ii) {
bradnelson 2016/06/24 00:19:01 https://google.github.io/styleguide/cppguide.html#
John 2016/06/24 17:47:56 In all honesty, I copied this for from typing-asm.
242 stdlib_types_[stdlib[ii].name] = new (zone_) VariableInfo(stdlib[ii].type);
243 stdlib_types_[stdlib[ii].name]->set_standard_member(
244 stdlib[ii].standard_member);
245 stdlib_types_[stdlib[ii].name]->set_mutability(
246 VariableInfo::kImmutableGlobal);
247 }
248
249 for (unsigned ii = 0; ii < arraysize(math); ++ii) {
bradnelson 2016/06/24 00:19:02 put this closer to the list above?
John 2016/06/24 17:47:57 Done.
250 stdlib_math_types_[math[ii].name] = new (zone_) VariableInfo(math[ii].type);
251 stdlib_math_types_[math[ii].name]->set_standard_member(
252 math[ii].standard_member);
253 stdlib_math_types_[math[ii].name]->set_mutability(
254 VariableInfo::kImmutableGlobal);
255 }
256 }
257
258 // Used for 5.5 GlobalVariableTypeAnnotations
259 AsmTyper::VariableInfo* AsmTyper::ImportLookup(Property* import) {
260 auto* obj = import->obj();
261 auto* key = import->key()->AsLiteral();
262
263 ObjectTypeMap* stdlib = &stdlib_types_;
264 if (auto* obj_as_property = obj->AsProperty()) {
265 // This can only be stdlib.Math
266 auto* math_name = obj_as_property->key()->AsLiteral();
267 if (math_name == nullptr || !math_name->IsPropertyName()) {
268 return nullptr;
269 }
270
271 if (!math_name->AsPropertyName()->IsUtf8EqualTo(CStrVector("Math"))) {
272 return nullptr;
273 }
274
275 auto* stdlib_var_proxy = obj_as_property->obj()->AsVariableProxy();
276 if (stdlib_var_proxy == nullptr) {
277 return nullptr;
278 }
279 obj = stdlib_var_proxy;
280 stdlib = &stdlib_math_types_;
281 }
282
283 auto* obj_as_var_proxy = obj->AsVariableProxy();
284 if (obj_as_var_proxy == nullptr) {
285 return nullptr;
286 }
287
288 auto* obj_info = Lookup(obj_as_var_proxy->var());
289 if (obj_info == nullptr) {
290 return nullptr;
291 }
292
293 if (obj_info->IsFFI()) {
294 // For FFI we can't validate import->key, so assume this is OK.
295 return obj_info;
296 }
297
298 base::SmartArrayPointer<char> aname = key->AsPropertyName()->ToCString();
299 ObjectTypeMap::iterator i = stdlib->find(std::string(aname.get()));
300 if (i == stdlib->end()) {
301 return nullptr;
302 }
303 return i->second;
304 }
305
306 AsmTyper::VariableInfo* AsmTyper::Lookup(Variable* variable) {
307 ZoneHashMap* scope = in_function_ ? &local_scope_ : &global_scope_;
308 ZoneHashMap::Entry* entry =
309 scope->Lookup(variable, ComputePointerHash(variable));
310 if (!entry && in_function_) {
bradnelson 2016/06/24 00:19:01 entry == nullptr
John 2016/06/24 17:47:57 This was copied from typing-asm.cc, too. :) Done.
311 entry = global_scope_.Lookup(variable, ComputePointerHash(variable));
312 }
313
314 if (entry == nullptr && module_name_->Equals(*variable->name())) {
315 return &module_info_;
316 }
317
318 return entry ? reinterpret_cast<VariableInfo*>(entry->value) : nullptr;
319 }
320
321 void AsmTyper::AddForwardReference(VariableProxy* proxy, VariableInfo* info) {
322 info->FirstForwardUseIs(proxy);
323 forward_definitions_.push_back(info);
324 }
325
326 bool AsmTyper::AddGlobal(Variable* variable, VariableInfo* info) {
327 // We can't DCHECK(!in_function_) because function may actually install global
328 // names (forward defined functions and function tables.)
329 DCHECK(info->mutability() != VariableInfo::kInvalidMutability);
330 DCHECK(info->IsGlobal());
331 DCHECK(ValidAsmIdentifier(variable->name()));
332
333 if (!module_name_.is_null() && module_name_->Equals(*variable->name())) {
334 return false;
335 }
336
337 ZoneHashMap::Entry* entry = global_scope_.LookupOrInsert(
338 variable, ComputePointerHash(variable), ZoneAllocationPolicy(zone_));
339
340 if (entry->value != nullptr) {
341 return false;
342 }
343
344 entry->value = info;
345 return true;
346 }
347
348 bool AsmTyper::AddLocal(Variable* variable, VariableInfo* info) {
349 DCHECK(in_function_);
350 DCHECK(info->mutability() != VariableInfo::kInvalidMutability);
351 DCHECK(!info->IsGlobal());
352 DCHECK(ValidAsmIdentifier(variable->name()));
353
354 ZoneHashMap::Entry* entry = global_scope_.LookupOrInsert(
355 variable, ComputePointerHash(variable), ZoneAllocationPolicy(zone_));
356
357 if (entry->value != nullptr) {
358 return false;
359 }
360
361 entry->value = info;
362 return true;
363 }
364
365 bool AsmTyper::Validate() {
366 if (!AsmType::None()->IsExactly(ValidateModule(root_))) {
bradnelson 2016/06/24 00:19:02 Maybe note in the header that None() is used as a
John 2016/06/24 17:47:58 Done.
367 return true;
368 }
369 return false;
370 }
371
372 namespace {
373 bool HasUseAsmDirective(FunctionLiteral* fun) {
374 if (fun->body() == nullptr) {
bradnelson 2016/06/24 00:19:02 Why not get this out of the flattened traversal? (
John 2016/06/24 17:47:56 I copied this over from the previous validator. do
375 return false;
376 }
377 ZoneList<Statement*>* body = fun->body();
378 if (body == nullptr || body->is_empty()) {
379 return false;
380 }
381 ExpressionStatement* use_asm = body->first()->AsExpressionStatement();
382 if (use_asm == nullptr) return false;
383 Literal* use_asm_literal = use_asm->expression()->AsLiteral();
384 if (use_asm_literal == nullptr) return false;
385 if (!use_asm_literal->raw_value()->AsString()->IsOneByteEqualTo("use asm"))
386 return false;
387 return true;
388 }
389
390 Assignment* ExtractInitializerExpression(Statement* statement) {
391 auto* expr_stmt = statement->AsExpressionStatement();
392 if (expr_stmt == nullptr) {
393 // Done with initializers.
394 return nullptr;
395 }
396 auto* assign = expr_stmt->expression()->AsAssignment();
397 if (assign == nullptr) {
398 // Done with initializers.
399 return nullptr;
400 }
401 if (assign->op() != Token::INIT) {
402 // Done with initializers.
403 return nullptr;
404 }
405 return assign;
406 }
407
408 } // namespace
409
410 // 6.1 ValidateModule
411 AsmType* AsmTyper::ValidateModule(FunctionLiteral* fun) {
412 Scope* scope = fun->scope();
413 if (!scope->is_function_scope()) FAIL(fun, "not at function scope.");
414 if (!HasUseAsmDirective(fun)) FAIL(fun, "missing \"use asm\".");
415 if (!ValidAsmIdentifier(fun->name()))
416 FAIL(fun, "Invalid ASM.js identifier in module name.");
417 module_name_ = fun->name();
418
419 // Allowed parameters: Stdlib, FFI, Mem
420 static constexpr uint32_t MaxModuleParameters = 3;
421 if (scope->num_parameters() > MaxModuleParameters) {
422 FAIL(fun, "ASM.js modules may not have more than three parameters.");
423 }
424
425 struct {
426 StandardMember standard_member;
427 AsmType* type;
428 } kModuleParamInfo[3] = {
429 {kStdlib, AsmType::None()},
430 {kFFI, AsmType::FFIType(zone_)},
431 {kHeap, AsmType::None()},
432 };
433
434 for (int ii = 0; ii < scope->num_parameters(); ++ii) {
435 Variable* param = scope->parameter(ii);
436 DCHECK(param);
437
438 if (!ValidAsmIdentifier(param->name())) {
439 FAIL(fun, "Invalid ASM.js identifier in module parameter.");
bradnelson 2016/06/24 00:19:00 I had been using Asm.js everywhere, but actually t
John 2016/06/24 17:47:56 Done.
440 }
441
442 auto* param_info = new (zone_) VariableInfo();
443 param_info->set_mutability(VariableInfo::kImmutableGlobal);
444 param_info->set_standard_member(kModuleParamInfo[ii].standard_member);
445 param_info->set_type(kModuleParamInfo[ii].type);
446
447 if (!AddGlobal(param, param_info)) {
448 FAIL(fun, "Redeclared identifier in module parameter.");
449 }
450 }
451
452 ZoneVector<Assignment*> function_pointer_tables(zone_);
453 FlattenedStatements iter(zone_, fun->body());
454 iter.Next(); // skips the "use asm" directive.
bradnelson 2016/06/24 00:19:01 why not just check for use asm here?
John 2016/06/24 17:47:56 Done.
455 ReturnStatement* module_return = nullptr;
456
457 // *VIOLATION* The spec states that globals should be followed by function
458 // declarations, which should be followed by function pointer tables, followed
459 // by the module export (return) statement. Our AST might be rearraged by the
460 // parser, so we can't rely on it being in source code order.
461 while (Statement* current = iter.Next()) {
462 if (auto* assign = ExtractInitializerExpression(current)) {
463 if (assign->value()->IsArrayLiteral()) {
464 // Save function tables for later validation.
465 function_pointer_tables.push_back(assign);
466 } else {
467 RECURSE(ValidateGlobalDeclaration(assign));
468 }
469 continue;
470 }
471
472 if (auto* current_as_return = current->AsReturnStatement()) {
473 if (module_return != nullptr) {
474 FAIL(fun, "Multiple export statements.");
475 }
476 module_return = current_as_return;
477 continue;
478 }
479
480 FAIL(current, "Invalid top-level statement in ASM.js module.");
481 }
482
483 ZoneList<Declaration*>* decls = scope->declarations();
484
485 for (int ii = 0; ii < decls->length(); ++ii) {
486 Declaration* decl = decls->at(ii);
487
488 if (FunctionDeclaration* fun_decl = decl->AsFunctionDeclaration()) {
489 RECURSE(ValidateFunction(fun_decl));
490 continue;
491 }
492 }
493
494 for (auto* function_table : function_pointer_tables) {
495 RECURSE(ValidateFunctionTable(function_table));
496 }
497
498 for (int ii = 0; ii < decls->length(); ++ii) {
499 Declaration* decl = decls->at(ii);
500
501 if (decl->IsFunctionDeclaration()) {
502 continue;
503 }
504
505 VariableDeclaration* var_decl = decl->AsVariableDeclaration();
506 if (var_decl == nullptr) {
507 FAIL(decl, "Invalid ASM.js declaration.");
bradnelson 2016/06/24 00:19:01 asm.js
John 2016/06/24 17:47:56 Done.
508 }
509
510 auto* var_proxy = var_decl->proxy();
511 if (var_proxy == nullptr) {
512 FAIL(decl, "Invalid ASM.js declaration.");
bradnelson 2016/06/24 00:19:01 asm.js
John 2016/06/24 17:47:57 Done.
513 }
514
515 if (Lookup(var_proxy->var()) == nullptr) {
516 FAIL(decl, "Global variable missing initializer in ASM.js module.");
517 }
518 }
519
520 // 6.2 ValidateExport
521 if (module_return == nullptr) {
522 FAIL(fun, "Missing ASM.js module export.");
523 }
524
525 for (auto* forward_def : forward_definitions_) {
526 if (forward_def->missing_definition()) {
527 FAIL(forward_def->first_forward_use(),
528 "Missing definition for forward declared identifier.");
529 }
530 }
531
532 RECURSE(ValidateExport(module_return));
533
534 return AsmType::Int(); // Any type that is not AsmType::None();
bradnelson 2016/06/24 00:19:01 Actually, any reason to make this return a type? W
John 2016/06/24 17:47:56 Consistency is not usually a good reason, but it i
bradn 2016/06/24 18:57:03 Meh. Kind of like it as is too. What if we changed
John 2016/07/07 16:46:56 Failure() makes sense in the context of validation
535 }
536
537 // 6.? ValidateGlobalDeclaration
538 namespace {
539 bool MayBeDoubleAnnotation(BinaryOperation* binop) {
bradnelson 2016/06/24 00:19:02 While agreed that the current representation in th
John 2016/06/24 17:47:57 Done.
540 // *VIOLATION* The parser replaces uses of +x with x*1.0.
541 if (binop->op() != Token::MUL) {
542 return false;
543 }
544
545 auto* right_as_literal = binop->right()->AsLiteral();
546 if (right_as_literal == nullptr) {
547 return false;
548 }
549
550 return right_as_literal->raw_value()->ContainsDot() &&
551 right_as_literal->raw_value()->AsNumber() == 1.0;
552 }
553
554 bool IsIntAnnotation(BinaryOperation* binop) {
555 if (binop->op() != Token::BIT_OR) {
556 return false;
557 }
558
559 auto* right_as_literal = binop->right()->AsLiteral();
560 if (right_as_literal == nullptr) {
561 return false;
562 }
563
564 return !right_as_literal->raw_value()->ContainsDot() &&
565 right_as_literal->raw_value()->AsNumber() == 0.0;
566 }
567 } // namespace
568
569 AsmType* AsmTyper::ValidateGlobalDeclaration(Assignment* assign) {
570 DCHECK(!assign->is_compound());
571 if (assign->is_compound()) {
572 FAIL(assign,
573 "Compound assignment not supported when declaring global variables.");
574 }
575
576 auto* target = assign->target();
577 if (!target->IsVariableProxy()) {
578 FAIL(target, "Module assignments may only assign to globals.");
579 }
580 auto* target_variable = target->AsVariableProxy()->var();
581 auto* target_info = Lookup(target_variable);
582
583 if (target_info != nullptr) {
584 FAIL(target, "Redefined global variable.");
585 }
586
587 auto* value = assign->value();
588 // Not all types of assignment are allowed by asm.js. See
589 // 5.5 Global Variable Type Annotations.
590 if (value->IsLiteral() || value->IsCall()) {
591 AsmType* type = nullptr;
592 RECURSE(type = VariableTypeAnnotations(value));
593 target_info = new (zone_) VariableInfo(type);
594 target_info->set_mutability(VariableInfo::kMutableGlobal);
595 } else if (value->IsProperty()) {
596 target_info = ImportLookup(value->AsProperty());
597 if (target_info == nullptr) {
598 FAIL(assign, "Invalid import.");
599 }
600 CHECK(target_info->mutability() == VariableInfo::kImmutableGlobal);
601 if (!target_info->IsFFI()) {
602 target_info = target_info->Clone(zone_);
603 } else {
604 // create a new target info that represents a foreign function.
605 DCHECK(target_info->type()->AsFFIType() != nullptr);
606 target_info = new (zone_) VariableInfo(target_info->type());
607 target_info->set_mutability(VariableInfo::kImmutableGlobal);
608 }
609 } else if (value->IsBinaryOperation()) {
610 // This should either be:
611 //
612 // var <> = ffi.<>|0
613 //
614 // or
615 //
616 // var <> = +ffi.<>
617 auto* value_binop = value->AsBinaryOperation();
618 auto* left = value_binop->left();
619 AsmType* import_type = nullptr;
620
621 if (MayBeDoubleAnnotation(value_binop)) {
622 // *VIOLATION* if the asm source was
623 //
bradnelson 2016/06/24 00:19:00 This seems off?
John 2016/06/24 17:47:58 I don't know what I had in mind. Done.
624 // var foreign_func = +foreign.func
625 //
626 // we will validate, even though we shouldn't.
627 import_type = AsmType::Double();
628 } else if (IsIntAnnotation(value_binop)) {
629 import_type = AsmType::Int();
630 } else {
631 FAIL(value,
632 "Invalid initializer for foreign import - unrecognized annotation.");
633 }
634
635 if (!left->IsProperty()) {
636 FAIL(value,
637 "Invalid initializer for foreign import - must import member.");
638 }
639 target_info = ImportLookup(left->AsProperty());
640 if (target_info == nullptr) {
641 // TODO(jpp): this error message is innacurate: this may fail if the
642 // object lookup fails, or if the property lookup fails, or even if the
643 // import is bogus like a().c.
644 FAIL(value,
645 "Invalid initializer for foreign import - object lookup failed.");
646 }
647 CHECK(target_info->mutability() == VariableInfo::kImmutableGlobal);
648 if (!target_info->IsFFI()) {
649 FAIL(value,
650 "Invalid initializer for foreign import - object is not the ffi.");
651 }
652
653 // Create a new target info that represents the foreign import.
bradnelson 2016/06/24 00:19:02 variable
John 2016/06/24 17:47:56 Done.
654 DCHECK(target_info->type()->AsFFIType() != nullptr);
655 target_info = new (zone_) VariableInfo(import_type);
656 target_info->set_mutability(VariableInfo::kMutableGlobal);
657 } else if (value->IsCallNew()) {
658 AsmType* type = nullptr;
659 RECURSE(type = NewHeapView(value->AsCallNew()));
660 target_info = new (zone_) VariableInfo(type);
661 target_info->set_mutability(VariableInfo::kImmutableGlobal);
662 }
663
664 if (target_info == nullptr) {
665 FAIL(assign, "Invalid global variable initializer.");
666 }
667
668 if (!ValidAsmIdentifier(target_variable->name())) {
669 FAIL(target, "Invalid ASM.js identifier in global variable.");
670 }
671
672 if (!AddGlobal(target_variable, target_info)) {
673 FAIL(assign, "Redeclared global identifier");
674 }
675
676 DCHECK(target_info->type() != AsmType::None());
677 return target_info->type();
678 }
679
680 // 6.3 ValidateFunctionTable
681 AsmType* AsmTyper::ValidateFunctionTable(Assignment* assign) {
682 if (assign->is_compound()) {
683 FAIL(assign,
684 "Compound assignment not supported when declaring global variables.");
685 }
686
687 auto* target = assign->target();
688 if (!target->IsVariableProxy()) {
689 FAIL(target, "Module assignments may only assign to globals.");
690 }
691 auto* target_variable = target->AsVariableProxy()->var();
692
693 auto* value = assign->value()->AsArrayLiteral();
694 CHECK(value != nullptr);
695 ZoneList<Expression*>* pointers = value->values();
696
697 // The function table size must be n = 2 ** m, for m >= 0;
bradnelson 2016/06/24 00:19:02 Not in the spec, but think we should cap this? Nah
John 2016/06/24 17:47:57 Maybe. I added a TODO here
698 if (!base::bits::IsPowerOfTwo32(pointers->length())) {
699 FAIL(assign, "Invalid length for function pointer table.");
700 }
701
702 AsmType* table_element_type = nullptr;
703 AsmCallableType* callable_type = nullptr;
704 for (auto* initializer : *pointers) {
705 auto* var_proxy = initializer->AsVariableProxy();
706 if (var_proxy == nullptr) {
707 FAIL(initializer,
708 "Function pointer table initializer must be a function name.");
709 }
710
711 auto* var_info = Lookup(var_proxy->var());
712 if (var_info == nullptr) {
713 FAIL(var_proxy,
714 "Undefined identifier in function pointer table initializer.");
715 }
716
717 if (var_info->standard_member() != kNone) {
718 FAIL(initializer,
719 "Function pointer table must not be a member of the standard "
720 "library.");
721 }
722
723 auto* initializer_callable = var_info->type()->AsFunctionType();
bradnelson 2016/06/24 00:19:01 Don't we need to ban FFI ones?
John 2016/06/24 17:47:57 From function tables? Yes, but the FFI type is not
bradn 2016/06/24 18:57:03 A confused the inheritance relationship. Makes sen
724 if (initializer_callable == nullptr) {
725 FAIL(initializer,
726 "Function pointer table initializer must be an ASM.js function.");
727 }
728
729 if (callable_type == nullptr) {
730 table_element_type = var_info->type();
731 callable_type = initializer_callable;
732 } else if (callable_type->ValidateCall(initializer_callable->ReturnType(),
733 initializer_callable->Arguments()) ==
734 AsmType::None()) {
735 FAIL(initializer, "Type mismatch in function pointer table initializer.");
736 }
737 }
738
739 auto* target_info = Lookup(target_variable);
740 if (target_info == nullptr) {
741 // Function pointer tables are the last entities to be validates, so this is
742 // unlikely to happen: only unreferenced function tables will not already
743 // have an entry in the global scope.
744 target_info = new (zone_) VariableInfo(AsmType::FunctionTableType(
745 zone_, pointers->length(), table_element_type));
746 target_info->set_mutability(VariableInfo::kImmutableGlobal);
747 if (!ValidAsmIdentifier(target_variable->name())) {
748 FAIL(target, "Invalid ASM.js identifier in function table name.");
749 }
750 if (!AddGlobal(target_variable, target_info)) {
751 DCHECK(false);
752 FAIL(assign, "Redeclared global identifier in function table name.");
753 }
754 return target_info->type();
755 }
756
757 if (!target_info->missing_definition()) {
758 FAIL(assign, "Redefined identifier in function table name.");
759 }
760
761 target_info->MarkDefined();
762
763 auto* target_info_table = target_info->type()->AsFunctionTableType();
764 if (target_info_table == nullptr) {
765 FAIL(assign, "Identifier redefined as function pointer table.");
766 }
767
768 if (target_info_table->length() != pointers->length()) {
769 FAIL(assign, "Function table size mismatch.");
770 }
771
772 auto* function_type = callable_type->AsFunctionType();
773 if (target_info_table->ValidateCall(function_type->ReturnType(),
774 function_type->Arguments()) ==
775 AsmType::None()) {
776 FAIL(assign, "Function table initializer does not match previous type.");
777 }
778
779 return target_info->type();
bradnelson 2016/06/24 00:19:02 DCHECK non-None?
John 2016/06/24 17:47:57 We know that by construction: if target_info->type
780 }
781
782 // 6.2 ValidateExport
783 AsmType* AsmTyper::ValidateExport(ReturnStatement* exports) {
784 // ASM.js modules can export single functions, or multiple functions in an
785 // object literal.
786 if (auto* fun_export = exports->expression()->AsVariableProxy()) {
787 // Exporting single function.
788 auto* fun_info = Lookup(fun_export->var());
789 if (fun_info == nullptr) {
790 FAIL(fun_export, "Undefined identifier in ASM.js module export.");
791 }
792
793 if (fun_info->type()->AsFunctionType() == nullptr) {
794 FAIL(fun_export, "ASM.js module export is not an ASM.js function.");
795 }
796
797 if (fun_info->standard_member() != kNone) {
798 FAIL(fun_export, "ASM.js module export must be an ASM.js function.");
799 }
800
801 return fun_info->type();
802 }
803
804 if (auto* obj_export = exports->expression()->AsObjectLiteral()) {
805 // Exporting object literal.
806 for (auto* prop : *obj_export->properties()) {
807 if (!prop->key()->IsLiteral()) {
808 FAIL(prop->key(),
809 "Only normal object properties may be used in the export object "
810 "literal.");
811 }
812 auto* export_obj = prop->value()->AsVariableProxy();
813 if (export_obj == nullptr) {
814 FAIL(prop->value(), "Exported value must be an ASM.js function name.");
815 }
816
817 auto* fun_info = Lookup(export_obj->var());
818 if (fun_info == nullptr) {
819 FAIL(export_obj,
820 "Undefined identifier in ASM.js module export property.");
821 }
822
823 if (fun_info->standard_member() != kNone) {
824 FAIL(export_obj,
825 "ASM.js module export cannot contain standard library functions.");
bradnelson 2016/06/24 00:19:03 Forbid foreign imports
John 2016/06/24 17:47:58 Are you asking me to change the error message, or
826 }
827
828 if (fun_info->type()->AsFunctionType() == nullptr) {
829 FAIL(export_obj,
830 "ASM.js module export property is not an ASM.js function.");
831 }
832 }
833 return AsmType::Int();
834 }
835
836 FAIL(exports, "Unrecognized expression in ASM.js module export expression.");
837 }
838
839 // 6.4 ValidateFunction
840 AsmType* AsmTyper::ValidateFunction(FunctionDeclaration* fun_decl) {
841 FunctionScope _(this);
842
843 // Extract parameter types.
844 auto* fun = fun_decl->fun();
845
846 auto* fun_decl_proxy = fun_decl->proxy();
847 if (fun_decl_proxy == nullptr) {
848 FAIL(fun_decl, "Anonymous functions are not support in ASM.js.");
849 }
850
851 Statement* current;
852 FlattenedStatements iter(zone_, fun->body());
853
854 size_t annotated_parameters = 0;
855
856 // 5.3 Function type annotations
857 // * parameters
858 ZoneVector<AsmType*> parameter_types(zone_);
859 for (; (current = iter.Next()) != nullptr; ++annotated_parameters) {
860 auto* stmt = current->AsExpressionStatement();
861 if (stmt == nullptr) {
862 // Done with parameters.
863 break;
864 }
865 auto* expr = stmt->expression()->AsAssignment();
866 if (expr == nullptr || expr->is_compound()) {
867 // Done with parameters.
868 break;
869 }
870 auto* proxy = expr->target()->AsVariableProxy();
871 if (proxy == nullptr) {
872 // Done with parameters.
873 break;
874 }
875 auto* param = proxy->var();
876 if (param->location() != VariableLocation::PARAMETER ||
877 param->index() != annotated_parameters) {
878 // Done with parameters.
879 break;
880 }
881
882 AsmType* type;
883 RECURSE(type = ParameterTypeAnnotations(param, expr->value()));
884 DCHECK(type->IsParameterType());
885 auto* param_info = new (zone_) VariableInfo(type);
886 param_info->set_mutability(VariableInfo::kLocal);
887 if (!ValidAsmIdentifier(proxy->name())) {
888 FAIL(proxy, "Invalid ASM.js identifier in parameter name.");
889 }
890
891 if (!AddLocal(param, param_info)) {
892 FAIL(proxy, "Redeclared local.");
893 }
894 parameter_types.push_back(type);
895 }
896
897 if (annotated_parameters != fun->parameter_count()) {
898 FAIL(fun_decl, "Incorrect parameter type annotations.");
899 }
900
901 // 5.3 Function type annotations
902 // * locals
903 for (; current; current = iter.Next()) {
904 auto* initializer = ExtractInitializerExpression(current);
905 if (initializer == nullptr) {
906 // Done with locals.
907 break;
908 }
909
910 auto* local = initializer->target()->AsVariableProxy();
911 if (local == nullptr) {
912 // Done with locals... or maybe an error?
bradnelson 2016/06/24 00:19:03 I get your meaning, but maybe clarify you mean tha
John 2016/06/24 17:47:58 Done.
913 break;
914 }
915
916 AsmType* type;
917 RECURSE(type = VariableTypeAnnotations(initializer->value()));
918 auto* local_info = new (zone_) VariableInfo(type);
919 local_info->set_mutability(VariableInfo::kLocal);
920 if (!ValidAsmIdentifier(local->name())) {
921 FAIL(local, "Invalid ASM.js identifier in local variable.");
922 }
923
924 if (!AddLocal(local->var(), local_info)) {
925 FAIL(initializer, "Redeclared local.");
926 }
927 }
928
929 // 5.2 Return Type Annotations
930 // *VIOLATION* we peel blocks to find the last statement in the ASM module
931 // because the parser may introduce synthetic blocks.
932 if (fun->body()->length() == 0) {
933 return_type_ = AsmType::Void();
934 } else {
935 auto* last_statement = fun->body()->last();
bradnelson 2016/06/24 00:19:01 Since we walk this all anyway, maybe no need to do
John 2016/06/24 17:47:56 That's what I thought, but then validating: funct
bradn 2016/06/24 18:57:03 This is ok. Maybe put a TODO / NOTE that this won'
936 while (auto* as_block = last_statement->AsBlock()) {
937 last_statement = as_block->statements()->last();
938 }
939 // We don't check whether AsReturnStatement() below returns non-null -- we
940 // leave that to the ReturnTypeAnnotations method.
941 return_type_ = ReturnTypeAnnotations(last_statement->AsReturnStatement());
942 }
943 DCHECK(return_type_);
944 DCHECK(return_type_->IsReturnType());
945
946 for (auto* decl : *fun->scope()->declarations()) {
bradnelson 2016/06/24 00:19:03 Maybe comment this revisits the ones above?
John 2016/06/24 17:47:56 Does it? This visits the declarations for the curr
bradn 2016/06/24 18:57:02 Oops, misread. You're right.
947 auto* var_decl = decl->AsVariableDeclaration();
948 if (var_decl == nullptr) {
949 FAIL(decl, "Functions may only define inner variables.");
950 }
951
952 auto* var_proxy = var_decl->proxy();
953 if (var_proxy == nullptr) {
954 FAIL(decl, "Invalid local declaration declaration.");
955 }
956
957 auto* var_info = Lookup(var_proxy->var());
958 if (var_info == nullptr || var_info->IsGlobal()) {
959 FAIL(decl, "Local variable missing initializer in ASM.js module.");
960 }
961 }
962
963 for (; current; current = iter.Next()) {
964 AsmType* current_type;
965 RECURSE(current_type = ValidateStatement(current));
966 }
967
968 auto* fun_type = AsmType::Function(zone_, return_type_);
969 auto* fun_type_as_function = fun_type->AsFunctionType();
970 for (auto* param_type : parameter_types) {
971 fun_type_as_function->AddArgument(param_type);
972 }
973
974 auto* fun_var = fun_decl_proxy->var();
975 auto* fun_info = new (zone_) VariableInfo(fun_type);
976 fun_info->set_mutability(VariableInfo::kImmutableGlobal);
977 auto* old_fun_type = Lookup(fun_var);
978 if (old_fun_type == nullptr) {
979 if (!ValidAsmIdentifier(fun_var->name())) {
980 FAIL(fun_decl_proxy, "Invalid ASM.js identifier in function name.");
981 }
982 if (!AddGlobal(fun_var, fun_info)) {
983 DCHECK(false);
984 FAIL(fun_decl, "Redeclared global identifier.");
985 }
986 return fun_type;
987 }
988
989 // Not necessarily an error -- fun_decl might have been used before being
990 // defined. If that's the case, then the type in the global environment must
991 // be the same as the type inferred by the parameter/return type annotations.
992 old_fun_type->MarkDefined();
993 auto* old_fun_callable = old_fun_type->type()->AsCallableType();
994 if (old_fun_callable == nullptr) {
995 FAIL(fun_decl, "Redefinition of global name with different type.");
996 }
997 if (old_fun_callable->ValidateCall(fun_type_as_function->ReturnType(),
998 fun_type_as_function->Arguments()) ==
999 AsmType::None()) {
1000 FAIL(fun_decl, "Signature mismatch when declaring function.");
1001 }
1002
1003 return fun_type;
1004 }
1005
1006 // 6.5 ValidateStatement
1007 AsmType* AsmTyper::ValidateStatement(Statement* statement) {
1008 switch (statement->node_type()) {
1009 default:
1010 FAIL(statement, "Invalid ASM.js statement.");
bradnelson 2016/06/24 00:19:00 Statement type invalid for asm.js ?
John 2016/06/24 17:47:57 Done.
1011 case AstNode::kBlock:
bradnelson 2016/06/24 00:19:02 Shouldn't the Flattener eat these?
John 2016/06/24 17:47:57 that's what I thought, until I tried to validate t
1012 return ValidateBlockStatement(statement->AsBlock());
1013 case AstNode::kExpressionStatement:
1014 return ValidateExpressionStatement(statement->AsExpressionStatement());
1015 case AstNode::kEmptyStatement:
1016 return ValidateEmptyStatement(statement->AsEmptyStatement());
1017 case AstNode::kIfStatement:
1018 return ValidateIfStatement(statement->AsIfStatement());
1019 case AstNode::kReturnStatement:
1020 return ValidateReturnStatement(statement->AsReturnStatement());
1021 case AstNode::kWhileStatement:
1022 return ValidateWhileStatement(statement->AsWhileStatement());
1023 case AstNode::kDoWhileStatement:
1024 return ValidateDoWhileStatement(statement->AsDoWhileStatement());
1025 case AstNode::kForStatement:
1026 return ValidateForStatement(statement->AsForStatement());
1027 case AstNode::kBreakStatement:
1028 return ValidateBreakStatement(statement->AsBreakStatement());
1029 case AstNode::kContinueStatement:
1030 return ValidateContinueStatement(statement->AsContinueStatement());
1031 case AstNode::kSwitchStatement:
1032 return ValidateSwitchStatement(statement->AsSwitchStatement());
1033 }
1034
1035 return AsmType::Void();
1036 }
1037
1038 // 6.5.1 BlockStatement
1039 AsmType* AsmTyper::ValidateBlockStatement(Block* block) {
1040 FlattenedStatements iter(zone_, block->statements());
1041
1042 while (auto* current = iter.Next()) {
1043 RECURSE(ValidateStatement(current));
1044 }
1045
1046 return AsmType::Void();
1047 }
1048
1049 // 6.5.2 ExpressionStatement
1050 AsmType* AsmTyper::ValidateExpressionStatement(ExpressionStatement* expr) {
1051 auto* expression = expr->expression();
1052 if (auto* call = expression->AsCall()) {
1053 RECURSE(ValidateCall(AsmType::Void(), call));
1054 } else {
1055 RECURSE(ValidateExpression(expression));
1056 }
1057
1058 return AsmType::Void();
1059 }
1060
1061 // 6.5.3 EmptyStatement
1062 AsmType* AsmTyper::ValidateEmptyStatement(EmptyStatement* empty) {
1063 return AsmType::Void();
1064 }
1065
1066 // 6.5.4 IfStatement
1067 AsmType* AsmTyper::ValidateIfStatement(IfStatement* if_stmt) {
1068 AsmType* cond_type;
1069 RECURSE(cond_type = ValidateExpression(if_stmt->condition()));
1070 if (!cond_type->IsA(AsmType::Int())) {
1071 FAIL(if_stmt->condition(), "If condition must be int.");
bradnelson 2016/06/24 00:19:02 be type int?
John 2016/06/24 17:47:56 Done.
1072 }
1073 RECURSE(ValidateStatement(if_stmt->then_statement()));
1074 RECURSE(ValidateStatement(if_stmt->else_statement()));
1075 return AsmType::Void();
1076 }
1077
1078 // 6.5.5 ReturnStatement
1079 AsmType* AsmTyper::ValidateReturnStatement(ReturnStatement* ret_stmt) {
1080 AsmType* ret_expr_type = AsmType::Void();
1081 if (auto* ret_expr = ret_stmt->expression()) {
1082 RECURSE(ret_expr_type = ValidateExpression(ret_expr));
1083 if (ret_expr_type == AsmType::Void()) {
1084 // *VIOLATION* The parser modifies the source code so that expressionless
1085 // returns will return undefined, so we need to allow that.
1086 if (!ret_expr->IsUndefinedLiteral()) {
1087 FAIL(ret_stmt, "Return statement expression can't be void.");
1088 }
1089 }
1090 }
1091
1092 if (!ret_expr_type->IsA(return_type_)) {
1093 FAIL(ret_stmt, "Type mismatch in return statement.");
1094 }
1095
1096 return ret_expr_type;
1097 }
1098
1099 // 6.5.6 IterationStatement
1100 // 6.5.6.a WhileStatement
1101 AsmType* AsmTyper::ValidateWhileStatement(WhileStatement* while_stmt) {
1102 AsmType* cond_type;
1103 RECURSE(cond_type = ValidateExpression(while_stmt->cond()));
1104 if (!cond_type->IsA(AsmType::Int())) {
1105 FAIL(while_stmt->cond(), "While condition must be int.");
1106 }
1107
1108 if (auto* body = while_stmt->body()) {
1109 RECURSE(ValidateStatement(body));
1110 }
1111 return AsmType::Void();
1112 }
1113
1114 // 6.5.6.b DoWhileStatement
1115 AsmType* AsmTyper::ValidateDoWhileStatement(DoWhileStatement* do_while) {
1116 AsmType* cond_type;
1117 RECURSE(cond_type = ValidateExpression(do_while->cond()));
1118 if (!cond_type->IsA(AsmType::Int())) {
1119 FAIL(do_while->cond(), "Do {} While condition must be int.");
1120 }
1121
1122 if (auto* body = do_while->body()) {
1123 RECURSE(ValidateStatement(body));
1124 }
1125 return AsmType::Void();
1126 }
1127
1128 // 6.5.6.c ForStatement
1129 AsmType* AsmTyper::ValidateForStatement(ForStatement* for_stmt) {
1130 if (auto* init = for_stmt->init()) {
1131 RECURSE(ValidateStatement(init));
1132 }
1133
1134 if (auto* cond = for_stmt->cond()) {
1135 AsmType* cond_type;
1136 RECURSE(cond_type = ValidateExpression(cond));
1137 if (!cond_type->IsA(AsmType::Int())) {
1138 FAIL(cond, "For condition must be int.");
1139 }
1140 }
1141
1142 if (auto* next = for_stmt->next()) {
1143 RECURSE(ValidateStatement(next));
1144 }
1145
1146 if (auto* body = for_stmt->body()) {
1147 RECURSE(ValidateStatement(body));
1148 }
1149
1150 return AsmType::Void();
1151 }
1152
1153 // 6.5.7 BreakStatement
1154 AsmType* AsmTyper::ValidateBreakStatement(BreakStatement* brk_stmt) {
bradnelson 2016/06/24 00:19:02 Starting to feel kind of odd that the statements r
John 2016/06/24 17:47:57 I agree, but see above. There's a solution, but th
bradn 2016/06/24 18:57:03 See above.
1155 return AsmType::Void();
1156 }
1157
1158 // 6.5.8 ContinueStatement
1159 AsmType* AsmTyper::ValidateContinueStatement(ContinueStatement* cont_stmt) {
1160 return AsmType::Void();
1161 }
1162
1163 // 6.5.9 LabelledStatement
1164 // JPP here, not sure what to do.
bradnelson 2016/06/24 00:19:02 These are kind of like BreakableStatements in our
John 2016/06/24 17:47:56 Done.
1165
1166 // 6.5.10 SwitchStatement
1167 namespace {
1168 bool ExtractInt32CaseLabel(CaseClause* clause, int32_t* lbl) {
1169 auto* lbl_expr = clause->label()->AsLiteral();
1170
1171 if (lbl_expr == nullptr) {
1172 return false;
1173 }
1174
1175 if (lbl_expr->raw_value()->ContainsDot()) {
1176 return false;
1177 }
1178
1179 return lbl_expr->value()->ToInt32(lbl);
1180 }
1181 } // namespace
1182
1183 AsmType* AsmTyper::ValidateSwitchStatement(SwitchStatement* stmt) {
1184 AsmType* cond_type;
1185 RECURSE(cond_type = ValidateExpression(stmt->tag()));
1186 if (!cond_type->IsA(AsmType::Signed())) {
1187 FAIL(stmt, "Switch tag must be signed.");
1188 }
1189
1190 bool has_default = false;
1191
1192 ZoneSet<int32_t> cases_seen(zone_);
1193 for (auto* a_case : *stmt->cases()) {
1194 if (a_case->is_default()) {
1195 CHECK(!has_default);
1196 RECURSE(ValidateDefault(a_case));
1197 has_default = true;
1198 }
1199
1200 // This checks some of 6.6 ValidateCase.
1201 int32_t case_lbl;
1202 if (!ExtractInt32CaseLabel(a_case, &case_lbl)) {
bradnelson 2016/06/24 00:19:00 Would mirror the spec better if this was inside Va
John 2016/06/24 17:47:57 I was trying to avoid adding an output parameter t
1203 FAIL(a_case, "Case label must be a 32-bit signed integer.");
1204 }
1205
1206 auto case_lbl_pos = cases_seen.find(case_lbl);
1207 if (case_lbl_pos != cases_seen.end() && *case_lbl_pos == case_lbl) {
1208 FAIL(a_case, "Duplicated case label.");
1209 }
1210 cases_seen.insert(case_lbl);
1211
1212 RECURSE(ValidateCase(a_case));
1213 }
1214
1215 if (!cases_seen.empty()) {
1216 const int64_t max_lbl = *cases_seen.rbegin();
1217 const int64_t min_lbl = *cases_seen.begin();
1218 if (max_lbl - min_lbl > std::numeric_limits<int32_t>::max()) {
1219 FAIL(stmt, "Out-of-bounds case label range.");
1220 }
1221 }
1222
1223 return AsmType::Void();
1224 }
1225
1226 // 6.6 ValidateCase
1227 AsmType* AsmTyper::ValidateCase(CaseClause* label) {
1228 FlattenedStatements iter(zone_, label->statements());
1229 while (auto* current = iter.Next()) {
1230 RECURSE(ValidateStatement(current));
1231 }
1232 return AsmType::Void();
1233 }
1234
1235 // 6.7 ValidateDefault
1236 AsmType* AsmTyper::ValidateDefault(CaseClause* label) {
1237 FlattenedStatements iter(zone_, label->statements());
1238 while (auto* current = iter.Next()) {
1239 RECURSE(ValidateStatement(current));
1240 }
1241 return AsmType::Void();
1242 }
1243
1244 // 6.8 ValidateExpression
1245 AsmType* AsmTyper::ValidateExpression(Expression* expr) {
1246 switch (expr->node_type()) {
1247 default:
1248 FAIL(expr, "Invalid ASM.js expression.");
1249 case AstNode::kLiteral:
1250 return ValidateNumericLiteral(expr->AsLiteral());
1251 case AstNode::kVariableProxy:
1252 return ValidateIdentifier(expr->AsVariableProxy());
1253 case AstNode::kCall:
1254 return ValidateCallExpression(expr->AsCall());
1255 case AstNode::kProperty:
1256 return ValidateMemberExpression(expr->AsProperty());
1257 case AstNode::kAssignment:
1258 return ValidateAssignmentExpression(expr->AsAssignment());
1259 case AstNode::kUnaryOperation:
1260 return ValidateUnaryExpression(expr->AsUnaryOperation());
1261 case AstNode::kConditional:
1262 return ValidateConditionalExpression(expr->AsConditional());
1263 case AstNode::kCompareOperation:
1264 return ValidateCompareOperation(expr->AsCompareOperation());
1265 case AstNode::kBinaryOperation:
1266 return ValidateBinaryOperation(expr->AsBinaryOperation());
1267 }
1268 }
1269
1270 AsmType* AsmTyper::ValidateCompareOperation(CompareOperation* cmp) {
1271 switch (cmp->op()) {
1272 default:
1273 FAIL(cmp, "Invalid ASM.js comparison operator.");
1274 case Token::LT:
1275 case Token::LTE:
1276 case Token::GT:
1277 case Token::GTE:
1278 return ValidateRelationalExpression(cmp);
1279 case Token::EQ:
1280 case Token::NE:
1281 return ValidateEqualityExpression(cmp);
1282 }
1283
1284 UNREACHABLE();
1285 }
1286
1287 namespace {
1288 bool MayBeNeg(BinaryOperation* binop) {
bradnelson 2016/06/24 00:19:00 Again, the maybe seems confusing. (There's places
bradnelson 2016/06/24 00:19:03 Probably worth spelling out Negate (to clarify fro
John 2016/06/24 17:47:56 Done.
John 2016/06/24 17:47:58 Acknowledged.
1289 if (binop->op() != Token::BIT_XOR) {
1290 return false;
1291 }
1292
1293 auto* right_as_literal = binop->right()->AsLiteral();
1294 if (right_as_literal == nullptr) {
1295 return false;
1296 }
1297
1298 return !right_as_literal->raw_value()->ContainsDot() &&
1299 right_as_literal->raw_value()->AsNumber() == -1.0;
1300 }
1301 } // namespace
1302
1303 AsmType* AsmTyper::ValidateBinaryOperation(BinaryOperation* expr) {
1304 #define UNOP_OVERLOAD(Src, Dest) \
1305 do { \
1306 if (left_type->IsA(AsmType::Src())) { \
1307 return AsmType::Dest(); \
1308 } \
1309 } while (0)
1310
1311 switch (expr->op()) {
1312 default:
1313 FAIL(expr, "Invalid ASM.js binary expression.");
1314 case Token::COMMA:
1315 return ValidateCommaExpression(expr);
1316 case Token::MUL:
1317 if (MayBeDoubleAnnotation(expr)) {
1318 // *VIOLATION* We can't be 100% sure this really IS a unary + in the ASM
1319 // source so we have to be lenient, and treat this as a unary +.
1320 if (auto* Call = expr->left()->AsCall()) {
1321 return ValidateCall(AsmType::Double(), Call);
1322 }
1323 AsmType* left_type;
1324 RECURSE(left_type = ValidateExpression(expr->left()));
bradnelson 2016/06/24 00:19:01 Maybe highlight this is from 8.1 for + What about
John 2016/06/24 17:47:57 I did not know the parser would do this... :( Don
1325 UNOP_OVERLOAD(Signed, Double);
1326 UNOP_OVERLOAD(Unsigned, Double);
1327 UNOP_OVERLOAD(DoubleQ, Double);
1328 UNOP_OVERLOAD(FloatQ, Double);
1329 FAIL(expr, "Invalid type for conversion to double.");
1330 }
1331 // FALTHROUGH
1332 case Token::DIV:
1333 case Token::MOD:
1334 return ValidateMultiplicativeExpression(expr);
1335 case Token::ADD:
1336 case Token::SUB: {
1337 static const uint32_t kInitialIntishCount = 0;
1338 return ValidateAdditiveExpression(expr, kInitialIntishCount);
1339 }
1340 case Token::SAR:
1341 case Token::SHL:
1342 case Token::SHR:
1343 return ValidateShiftExpression(expr);
1344 case Token::BIT_AND:
1345 return ValidateBitwiseANDExpression(expr);
1346 case Token::BIT_XOR:
1347 if (MayBeNeg(expr)) {
1348 auto* left = expr->left();
1349 auto* left_as_binop = left->AsBinaryOperation();
1350
1351 if (left_as_binop != nullptr && MayBeNeg(left_as_binop)) {
1352 // This is the special ~~ operator.
1353 AsmType* left_type;
1354 RECURSE(left_type = ValidateExpression(left_as_binop->left()));
1355 UNOP_OVERLOAD(Double, Signed);
1356 UNOP_OVERLOAD(FloatQ, Signed);
1357 FAIL(left_as_binop, "Invalid type for conversion to signed.");
1358 }
1359
1360 AsmType* left_type;
1361 RECURSE(left_type = ValidateExpression(left));
1362 UNOP_OVERLOAD(Intish, Signed);
1363 FAIL(left, "Invalid type for ~");
1364 }
1365
1366 return ValidateBitwiseXORExpression(expr);
1367 case Token::BIT_OR:
1368 return ValidateBitwiseORExpression(expr);
1369 }
1370 #undef UNOP_OVERLOAD
1371 UNREACHABLE();
1372 }
1373
1374 // 6.8.1 Expression
1375 AsmType* AsmTyper::ValidateCommaExpression(BinaryOperation* comma) {
1376 // The AST looks like:
1377 // (expr COMMA (expr COMMA (expr COMMA (... ))))
1378
1379 auto* left = comma->left();
1380 auto* left_as_binop = left->AsBinaryOperation();
1381 if (left_as_binop && left_as_binop->op() == Token::COMMA) {
1382 ValidateCommaExpression(left_as_binop);
1383 } else if (auto* left_as_call = left->AsCall()) {
1384 ValidateCall(AsmType::Void(), left_as_call);
1385 } else {
1386 ValidateExpression(left);
1387 }
1388
1389 auto* right = comma->right();
1390 auto* right_as_binop = right->AsBinaryOperation();
1391 if (right_as_binop && right_as_binop->op() == Token::COMMA) {
1392 return ValidateCommaExpression(right_as_binop);
1393 } else {
1394 return ValidateExpression(right);
1395 }
1396
1397 UNREACHABLE();
1398 }
1399
1400 // 6.8.2 NumericLiteral
1401 AsmType* AsmTyper::ValidateNumericLiteral(Literal* literal) {
1402 // *VIOLATION* ASM.js does not allow the use of undefined, but our parser
1403 // inserts them, so we have to handle them.
1404 if (literal->IsUndefinedLiteral()) {
1405 return AsmType::Void();
1406 }
1407
1408 if (literal->raw_value()->ContainsDot()) {
1409 return AsmType::Double();
1410 }
1411
1412 uint32_t value;
1413 if (!literal->value()->ToUint32(&value)) {
1414 int32_t value;
1415 if (!literal->value()->ToInt32(&value)) {
1416 FAIL(literal, "Integer literal is out of range.");
1417 }
1418 // *VIOLATION* Not really a violation, but rather a different in the
1419 // validation. The spec handles -NumericLiteral in ValidateUnaryExpression,
1420 // but V8's AST represents the negative literals as Literals.
1421 return AsmType::Signed();
1422 }
1423
1424 static const uint32_t LargestFixNum = 0x80000000u - 1;
bradnelson 2016/06/24 00:19:01 You used numeric_limit of int32_t elsewhere for th
John 2016/06/24 17:47:57 Done.
1425 if (value <= LargestFixNum) {
1426 return AsmType::FixNum();
1427 }
1428
1429 return AsmType::Unsigned();
1430 }
1431
1432 // 6.8.3 Identifier
1433 AsmType* AsmTyper::ValidateIdentifier(VariableProxy* proxy) {
1434 auto* proxy_info = Lookup(proxy->var());
1435 if (proxy_info == nullptr) {
1436 FAIL(proxy, "Undeclared identifier");
1437 }
1438 DCHECK(!proxy_info->type()->IsA(AsmType::None()));
1439 return proxy_info->type();
1440 }
1441
1442 // 6.8.4 CallExpression
1443 AsmType* AsmTyper::ValidateCallExpression(Call* call) {
1444 AsmType* return_type;
1445 RECURSE(return_type = ValidateFloatCoercion(call));
1446 if (return_type == nullptr) {
1447 FAIL(call, "Call to fround was expected.");
1448 }
1449 return return_type;
1450 }
1451
1452 // 6.8.5 MemberExpression
1453 AsmType* AsmTyper::ValidateMemberExpression(Property* prop) {
1454 AsmType* return_type;
1455 RECURSE(return_type = ValidateHeapAccess(prop, LoadFromHeap));
1456 return return_type;
1457 }
1458
1459 // 6.8.6 AssignmentExpression
1460 AsmType* AsmTyper::ValidateAssignmentExpression(Assignment* assignment) {
1461 AsmType* value_type;
1462 RECURSE(value_type = ValidateExpression(assignment->value()));
1463
1464 if (auto* target_as_proxy = assignment->target()->AsVariableProxy()) {
1465 auto* var = target_as_proxy->var();
1466 auto* target_info = Lookup(var);
1467
1468 if (target_info == nullptr) {
1469 if (var->mode() != TEMPORARY) {
1470 FAIL(target_as_proxy, "Undeclared identifier.");
1471 }
1472 // Temporary variables are special: we add them to the local symbol table
1473 // as we see them, with the exact type of the variable's initializer. This
1474 // means that temporary variables might have nonsensical types (i.e.,
1475 // intish, float?, fixnum, and not just the "canonical" types.)
1476 auto* var_info = new (zone_) VariableInfo(value_type);
1477 var_info->set_mutability(VariableInfo::kLocal);
1478 if (!ValidAsmIdentifier(target_as_proxy->name())) {
1479 FAIL(target_as_proxy,
1480 "Invalid ASM.js identifier in temporary variable.");
1481 }
1482
1483 if (!AddLocal(var, var_info)) {
1484 FAIL(assignment, "Failed to add temporary variable to symbol table.");
1485 }
1486 return value_type;
1487 }
1488
1489 DCHECK(target_info->type() != AsmType::None());
1490 if (!value_type->IsA(target_info->type())) {
1491 FAIL(assignment, "Invalid assignment.");
1492 }
1493
1494 return value_type;
1495 }
1496
1497 if (auto* target_as_property = assignment->target()->AsProperty()) {
1498 AsmType* allowed_store_types;
1499 RECURSE(allowed_store_types =
1500 ValidateHeapAccess(target_as_property, StoreToHeap));
1501
1502 if (!value_type->IsA(allowed_store_types)) {
1503 FAIL(assignment, "Invalid assignment.");
1504 }
1505
1506 return value_type;
1507 }
1508
1509 FAIL(assignment, "Invalid ASM.js assignment.");
1510 }
1511
1512 // 6.8.7 UnaryExpression
1513 AsmType* AsmTyper::ValidateUnaryExpression(UnaryOperation* unop) {
1514 // *VIOLATION* -NumericLiteral is validated in ValidateLiteral.
1515 // *VIOLATION* +UnaryExpression is validated in ValidateBinaryOperation.
1516 // *VIOLATION* ~UnaryOperation is validated in ValidateBinaryOperation.
1517 // *VIOLATION* ~~UnaryOperation is validated in ValidateBinaryOperation.
1518 DCHECK(unop->op() != Token::BIT_NOT);
1519 DCHECK(unop->op() != Token::ADD);
1520 AsmType* exp_type;
1521 RECURSE(exp_type = ValidateExpression(unop->expression()));
1522 #define UNOP_OVERLOAD(Src, Dest) \
1523 do { \
1524 if (exp_type->IsA(AsmType::Src())) { \
1525 return AsmType::Dest(); \
1526 } \
1527 } while (0)
1528 switch (unop->op()) {
1529 default:
1530 FAIL(unop, "Invalid unary operator.");
bradnelson 2016/06/24 00:19:01 Minor nit, you're inconsistent about periods at th
John 2016/06/24 17:47:57 I thought I was being consistent... :( I like the
bradn 2016/06/24 18:57:02 Wow.
1531 case Token::ADD:
1532 UNOP_OVERLOAD(Signed, Double);
bradnelson 2016/06/24 00:19:01 These are in practice unreachable. Maybe we should
John 2016/06/24 17:47:58 Yeah, that's a good idea. I left three DCHECKs beh
1533 UNOP_OVERLOAD(Unsigned, Double);
1534 UNOP_OVERLOAD(DoubleQ, Double);
1535 UNOP_OVERLOAD(FloatQ, Double);
1536 FAIL(unop, "Invalid type for unary +");
1537 case Token::SUB:
1538 UNOP_OVERLOAD(Int, Intish);
1539 UNOP_OVERLOAD(DoubleQ, Double);
1540 UNOP_OVERLOAD(FloatQ, Floatish);
1541 FAIL(unop, "Invalid type for unary -");
1542 case Token::BIT_NOT:
1543 UNOP_OVERLOAD(Intish, Signed);
1544 FAIL(unop, "Invalid type for ~");
1545 case Token::NOT:
1546 UNOP_OVERLOAD(Int, Int);
1547 FAIL(unop, "Invalid type for !");
1548 }
1549 #undef UNOP_OVERLOAD
1550
1551 UNREACHABLE();
1552 }
1553
1554 // 6.8.8 MultiplicativeExpression
1555 namespace {
1556 bool IsIntishLiteralFactor(Expression* expr) {
1557 auto* literal = expr->AsLiteral();
1558 if (literal == nullptr) {
1559 return false;
1560 }
1561
1562 if (literal->raw_value()->ContainsDot()) {
1563 return false;
1564 }
1565
1566 int32_t value;
1567 if (!literal->value()->ToInt32(&value)) {
1568 return false;
1569 }
1570 static const int32_t kIntishBound = 1 << 20;
1571 return -kIntishBound < value && value < kIntishBound;
1572 }
1573 } // namespace
1574
1575 AsmType* AsmTyper::ValidateMultiplicativeExpression(BinaryOperation* binop) {
1576 DCHECK(!MayBeDoubleAnnotation(binop));
1577
1578 auto* left = binop->left();
1579 auto* right = binop->right();
1580
1581 if (binop->op() == Token::MUL) {
1582 if (IsIntishLiteralFactor(left)) {
1583 AsmType* right_type;
1584 RECURSE(right_type = ValidateExpression(right));
1585 if (right_type->IsA(AsmType::Int())) {
1586 return AsmType::Intish();
1587 }
1588 FAIL(binop, "Invalid types for intish *");
1589 }
1590
1591 if (IsIntishLiteralFactor(right)) {
1592 AsmType* left_type;
1593 RECURSE(left_type = ValidateExpression(left));
1594 if (left_type->IsA(AsmType::Int())) {
1595 return AsmType::Intish();
1596 }
1597 FAIL(binop, "Invalid types for intish *");
1598 }
1599 }
1600
1601 AsmType* left_type;
1602 AsmType* right_type;
1603 RECURSE(left_type = ValidateExpression(left));
1604 RECURSE(right_type = ValidateExpression(right));
1605
1606 #define BINOP_OVERLOAD(Src0, Src1, Dest) \
1607 do { \
1608 if (left_type->IsA(AsmType::Src0()) && right_type->IsA(AsmType::Src1())) { \
1609 return AsmType::Dest(); \
1610 } \
1611 } while (0)
1612 switch (binop->op()) {
1613 default:
1614 FAIL(binop, "Invalid multiplicative expression.");
1615 case Token::MUL:
1616 BINOP_OVERLOAD(DoubleQ, DoubleQ, Double);
bradnelson 2016/06/24 00:19:00 Comment referencing table 8.2 ?
John 2016/06/24 17:47:56 Done.
1617 BINOP_OVERLOAD(FloatQ, FloatQ, Floatish);
1618 FAIL(binop, "Invalida operands for *");
bradnelson 2016/06/24 00:19:01 typo
John 2016/06/24 17:47:57 Done.
1619 case Token::DIV:
1620 BINOP_OVERLOAD(Signed, Signed, Intish);
1621 BINOP_OVERLOAD(Unsigned, Unsigned, Intish);
1622 BINOP_OVERLOAD(DoubleQ, DoubleQ, Double);
1623 BINOP_OVERLOAD(FloatQ, FloatQ, Floatish);
1624 FAIL(binop, "Invalida operands for /");
bradnelson 2016/06/24 00:19:03 typo
John 2016/06/24 17:47:57 Done.
1625 case Token::MOD:
1626 BINOP_OVERLOAD(Signed, Signed, Intish);
1627 BINOP_OVERLOAD(DoubleQ, DoubleQ, Double);
1628 BINOP_OVERLOAD(FloatQ, FloatQ, Floatish);
1629 FAIL(binop, "Invalida operands for %");
bradnelson 2016/06/24 00:19:02 typo
John 2016/06/24 17:47:56 Done.
1630 }
1631 #undef BINOP_OVERLOAD
1632
1633 UNREACHABLE();
1634 }
1635
1636 // 6.8.9 AdditiveExpression
1637 AsmType* AsmTyper::ValidateAdditiveExpression(BinaryOperation* binop,
1638 uint32_t intish_count) {
1639 static const uint32_t kMaxIntish = 1 << 20;
1640
1641 auto* left = binop->left();
1642 auto* left_as_binop = left->AsBinaryOperation();
1643 AsmType* left_type;
1644
1645 if (left_as_binop != nullptr && (left_as_binop->op() == Token::ADD ||
bradnelson 2016/06/24 00:19:01 Maybe a TODO to consider handling this iteratively
John 2016/06/24 17:47:57 I was going for iterative here, but then I ran int
bradn 2016/06/24 18:57:03 Ick. Ok. I've never seen more than 100s in practic
1646 left_as_binop->op() == Token::SUB)) {
1647 RECURSE(left_type =
1648 ValidateAdditiveExpression(left_as_binop, intish_count + 1));
1649 } else {
1650 RECURSE(left_type = ValidateExpression(left));
1651 }
1652
1653 auto* right = binop->right();
1654 auto* right_as_binop = right->AsBinaryOperation();
1655 AsmType* right_type;
1656
1657 if (right_as_binop != nullptr && (right_as_binop->op() == Token::ADD ||
1658 right_as_binop->op() == Token::SUB)) {
1659 RECURSE(right_type =
1660 ValidateAdditiveExpression(right_as_binop, intish_count + 1));
1661 } else {
1662 RECURSE(right_type = ValidateExpression(right));
1663 }
1664
1665 if (left_type->IsA(AsmType::FloatQ()) && right_type->IsA(AsmType::FloatQ())) {
1666 return AsmType::Floatish();
1667 }
1668
1669 if (left_type->IsA(AsmType::Int()) && right_type->IsA(AsmType::Int())) {
1670 if (intish_count == 0) {
1671 return AsmType::Intish();
1672 }
1673 if (intish_count < kMaxIntish) {
1674 return AsmType::Int();
1675 }
1676 FAIL(binop, "Too many uncoerced integer additive expressions.");
1677 }
1678
1679 if (left_type->IsA(AsmType::Double()) && right_type->IsA(AsmType::Double())) {
1680 return AsmType::Double();
1681 }
1682
1683 if (binop->op() == Token::SUB) {
1684 if (left_type->IsA(AsmType::DoubleQ()) &&
1685 right_type->IsA(AsmType::DoubleQ())) {
1686 return AsmType::Double();
1687 }
1688 }
1689
1690 FAIL(binop, "Invalid operands for additive expression.");
1691 }
1692
1693 // 6.8.10 ShiftExpression
1694 AsmType* AsmTyper::ValidateShiftExpression(BinaryOperation* binop) {
1695 auto* left = binop->left();
1696 auto* right = binop->right();
1697
1698 AsmType* left_type;
1699 AsmType* right_type;
1700 RECURSE(left_type = ValidateExpression(left));
1701 RECURSE(right_type = ValidateExpression(right));
1702
1703 #define BINOP_OVERLOAD(Src0, Src1, Dest) \
1704 do { \
1705 if (left_type->IsA(AsmType::Src0()) && right_type->IsA(AsmType::Src1())) { \
1706 return AsmType::Dest(); \
1707 } \
1708 } while (0)
1709 switch (binop->op()) {
1710 default:
1711 FAIL(binop, "Invalid shift expression.");
1712 case Token::SHL:
1713 BINOP_OVERLOAD(Intish, Intish, Signed);
1714 FAIL(binop, "Invalida operands for <<");
bradnelson 2016/06/24 00:19:02 typos
John 2016/06/24 17:47:56 Done.
1715 case Token::SAR:
1716 BINOP_OVERLOAD(Intish, Intish, Signed);
1717 FAIL(binop, "Invalida operands for >>");
1718 case Token::SHR:
1719 BINOP_OVERLOAD(Intish, Intish, Unsigned);
1720 FAIL(binop, "Invalida operands for >>>");
1721 }
1722 #undef BINOP_OVERLOAD
1723
1724 UNREACHABLE();
1725 }
1726
1727 // 6.8.11 RelationalExpression
1728 AsmType* AsmTyper::ValidateRelationalExpression(CompareOperation* cmpop) {
1729 auto* left = cmpop->left();
1730 auto* right = cmpop->right();
1731
1732 AsmType* left_type;
1733 AsmType* right_type;
1734 RECURSE(left_type = ValidateExpression(left));
1735 RECURSE(right_type = ValidateExpression(right));
1736
1737 #define CMPOP_OVERLOAD(Src0, Src1, Dest) \
1738 do { \
1739 if (left_type->IsA(AsmType::Src0()) && right_type->IsA(AsmType::Src1())) { \
1740 return AsmType::Dest(); \
1741 } \
1742 } while (0)
1743 switch (cmpop->op()) {
1744 default:
1745 FAIL(cmpop, "Invalid relational expression.");
1746 case Token::LT:
1747 CMPOP_OVERLOAD(Signed, Signed, Int);
1748 CMPOP_OVERLOAD(Unsigned, Unsigned, Int);
1749 CMPOP_OVERLOAD(Float, Float, Int);
1750 CMPOP_OVERLOAD(Double, Double, Int);
1751 FAIL(cmpop, "Invalida operands for <");
bradnelson 2016/06/24 00:19:02 typos
John 2016/06/24 17:47:58 Done.
1752 case Token::GT:
1753 CMPOP_OVERLOAD(Signed, Signed, Int);
1754 CMPOP_OVERLOAD(Unsigned, Unsigned, Int);
1755 CMPOP_OVERLOAD(Float, Float, Int);
1756 CMPOP_OVERLOAD(Double, Double, Int);
1757 FAIL(cmpop, "Invalida operands for >");
1758 case Token::LTE:
1759 CMPOP_OVERLOAD(Signed, Signed, Int);
1760 CMPOP_OVERLOAD(Unsigned, Unsigned, Int);
1761 CMPOP_OVERLOAD(Float, Float, Int);
1762 CMPOP_OVERLOAD(Double, Double, Int);
1763 FAIL(cmpop, "Invalida operands for <=");
1764 case Token::GTE:
1765 CMPOP_OVERLOAD(Signed, Signed, Int);
1766 CMPOP_OVERLOAD(Unsigned, Unsigned, Int);
1767 CMPOP_OVERLOAD(Float, Float, Int);
1768 CMPOP_OVERLOAD(Double, Double, Int);
1769 FAIL(cmpop, "Invalida operands for >=");
1770 }
1771 #undef CMPOP_OVERLOAD
1772
1773 UNREACHABLE();
1774 }
1775
1776 // 6.8.12 EqualityExpression
1777 AsmType* AsmTyper::ValidateEqualityExpression(CompareOperation* cmpop) {
1778 auto* left = cmpop->left();
1779 auto* right = cmpop->right();
1780
1781 AsmType* left_type;
1782 AsmType* right_type;
1783 RECURSE(left_type = ValidateExpression(left));
1784 RECURSE(right_type = ValidateExpression(right));
1785
1786 #define CMPOP_OVERLOAD(Src0, Src1, Dest) \
1787 do { \
1788 if (left_type->IsA(AsmType::Src0()) && right_type->IsA(AsmType::Src1())) { \
1789 return AsmType::Dest(); \
1790 } \
1791 } while (0)
1792 switch (cmpop->op()) {
1793 default:
1794 FAIL(cmpop, "Invalid equality expression.");
1795 case Token::EQ:
1796 CMPOP_OVERLOAD(Signed, Signed, Int);
1797 CMPOP_OVERLOAD(Unsigned, Unsigned, Int);
1798 CMPOP_OVERLOAD(Float, Float, Int);
1799 CMPOP_OVERLOAD(Double, Double, Int);
1800 FAIL(cmpop, "Invalida operands for ==");
bradnelson 2016/06/24 00:19:03 typos
John 2016/06/24 17:47:57 Done.
1801 case Token::NE:
1802 CMPOP_OVERLOAD(Signed, Signed, Int);
1803 CMPOP_OVERLOAD(Unsigned, Unsigned, Int);
1804 CMPOP_OVERLOAD(Float, Float, Int);
1805 CMPOP_OVERLOAD(Double, Double, Int);
1806 FAIL(cmpop, "Invalida operands for !=");
1807 }
1808 #undef CMPOP_OVERLOAD
1809
1810 UNREACHABLE();
1811 }
1812
1813 // 6.8.13 BitwiseANDExpression
1814 AsmType* AsmTyper::ValidateBitwiseANDExpression(BinaryOperation* binop) {
1815 auto* left = binop->left();
1816 auto* right = binop->right();
1817
1818 AsmType* left_type;
1819 AsmType* right_type;
1820 RECURSE(left_type = ValidateExpression(left));
1821 RECURSE(right_type = ValidateExpression(right));
1822
1823 if (binop->op() != Token::BIT_AND) {
1824 FAIL(binop, "Invalid & expression.");
1825 }
1826
1827 #define BINOP_OVERLOAD(Src0, Src1, Dest) \
1828 do { \
1829 if (left_type->IsA(AsmType::Src0()) && right_type->IsA(AsmType::Src1())) { \
1830 return AsmType::Dest(); \
1831 } \
1832 } while (0)
1833 BINOP_OVERLOAD(Intish, Intish, Signed);
1834 FAIL(binop, "Invalida operands for &");
1835 #undef BINOP_OVERLOAD
1836
1837 UNREACHABLE();
1838 }
1839
1840 // 6.8.14 BitwiseXORExpression
1841 AsmType* AsmTyper::ValidateBitwiseXORExpression(BinaryOperation* binop) {
1842 auto* left = binop->left();
1843 auto* right = binop->right();
1844
1845 AsmType* left_type;
1846 AsmType* right_type;
1847 RECURSE(left_type = ValidateExpression(left));
1848 RECURSE(right_type = ValidateExpression(right));
1849
1850 if (binop->op() != Token::BIT_XOR) {
1851 FAIL(binop, "Invalid ^ expression.");
1852 }
1853
1854 #define BINOP_OVERLOAD(Src0, Src1, Dest) \
1855 do { \
1856 if (left_type->IsA(AsmType::Src0()) && right_type->IsA(AsmType::Src1())) { \
1857 return AsmType::Dest(); \
1858 } \
1859 } while (0)
1860 BINOP_OVERLOAD(Intish, Intish, Signed);
1861 FAIL(binop, "Invalida operands for ^");
1862 #undef BINOP_OVERLOAD
1863
1864 UNREACHABLE();
1865 }
1866
1867 // 6.8.15 BitwiseORExpression
1868 AsmType* AsmTyper::ValidateBitwiseORExpression(BinaryOperation* binop) {
1869 auto* left = binop->left();
1870 if (IsIntAnnotation(binop)) {
bradnelson 2016/06/24 00:19:02 Maybe comment this peeks in twice?
John 2016/06/24 17:47:57 Done.
1871 if (auto* left_as_call = left->AsCall()) {
1872 AsmType* type;
1873 RECURSE(type = ValidateCall(AsmType::Signed(), left_as_call));
1874 return type;
1875 }
1876 }
1877
1878 auto* right = binop->right();
1879 AsmType* left_type;
1880 AsmType* right_type;
1881 RECURSE(left_type = ValidateExpression(left));
1882 RECURSE(right_type = ValidateExpression(right));
1883
1884 if (binop->op() != Token::BIT_XOR) {
1885 FAIL(binop, "Invalid ^ expression.");
1886 }
1887
1888 #define BINOP_OVERLOAD(Src0, Src1, Dest) \
1889 do { \
1890 if (left_type->IsA(AsmType::Src0()) && right_type->IsA(AsmType::Src1())) { \
1891 return AsmType::Dest(); \
1892 } \
1893 } while (0)
1894 BINOP_OVERLOAD(Intish, Intish, Signed);
1895 FAIL(binop, "Invalida operands for ^");
1896 #undef BINOP_OVERLOAD
1897
1898 UNREACHABLE();
1899 }
1900
1901 // 6.8.16 ContionalExpression
1902 AsmType* AsmTyper::ValidateConditionalExpression(Conditional* cond) {
1903 AsmType* cond_type;
1904 RECURSE(cond_type = ValidateExpression(cond->condition()));
1905 if (!cond_type->IsA(AsmType::Int())) {
1906 FAIL(cond, "Ternary operation condition should be int.");
1907 }
1908
1909 AsmType* then_type;
1910 RECURSE(then_type = ValidateExpression(cond->then_expression()));
1911 AsmType* else_type;
1912 RECURSE(else_type = ValidateExpression(cond->else_expression()));
1913
1914 #define SUCCEED_IF_BOTH_ARE(type) \
1915 do { \
1916 if (then_type->IsA(AsmType::type())) { \
1917 if (!else_type->IsA(AsmType::type())) { \
1918 FAIL(cond, "Type mismatch for ternary operation result type."); \
1919 } \
1920 } \
1921 } while (0)
1922 SUCCEED_IF_BOTH_ARE(Int);
1923 SUCCEED_IF_BOTH_ARE(Float);
1924 SUCCEED_IF_BOTH_ARE(Double);
1925 #undef SUCCEED_IF_BOTH_ARE
1926
1927 FAIL(cond, "Ternary operator must return int, float, or double.");
1928 }
1929
1930 // 6.9 ValidateCall
1931 namespace {
1932 bool ExtractIndirectCallMask(Expression* expr, uint32_t* value) {
1933 auto* as_literal = expr->AsLiteral();
1934 if (as_literal == nullptr) {
1935 return false;
1936 }
1937
1938 if (as_literal->raw_value()->ContainsDot()) {
1939 return false;
1940 }
1941
1942 if (!as_literal->value()->ToUint32(value)) {
1943 return false;
1944 }
1945
1946 return base::bits::IsPowerOfTwo32(1 + *value);
bradnelson 2016/06/24 00:19:01 This will do the wrong thing for 2^31, no?
John 2016/06/24 17:47:58 IsPowerOfTwo32(2 ^ 31) === return (2 ^ 31) && !(
bradn 2016/06/24 18:57:03 Sorry meant 2^32 - 1
1947 }
1948 } // namespace
1949
1950 AsmType* AsmTyper::ValidateCall(AsmType* return_type, Call* call) {
1951 AsmType* float_coercion_type;
1952 RECURSE(float_coercion_type = ValidateFloatCoercion(call));
1953 if (float_coercion_type == AsmType::Float()) {
1954 return AsmType::Float();
1955 }
1956
1957 auto* call_type = AsmType::Function(zone_, return_type)->AsFunctionType();
1958 for (auto* arg : *call->arguments()) {
1959 AsmType* arg_type;
1960 RECURSE(arg_type = ValidateExpression(arg));
1961 call_type->AddArgument(arg_type->ToParameterType());
1962 }
1963
1964 auto* call_expr = call->expression();
1965
1966 // identifier(Expression...)
1967 if (auto* call_var_proxy = call_expr->AsVariableProxy()) {
1968 auto* call_var_info = Lookup(call_var_proxy->var());
1969
1970 if (call_var_info == nullptr) {
1971 // We can't fail here: the validator performs a single pass over the AST,
1972 // so it is possible for some calls to be currently unresolved. We eagerly
1973 // add the function to the table of globals.
1974 auto* fun_info =
1975 new (zone_) VariableInfo(reinterpret_cast<AsmType*>(call_type));
1976 fun_info->set_mutability(VariableInfo::kImmutableGlobal);
1977 AddForwardReference(call_var_proxy, fun_info);
1978 if (!ValidAsmIdentifier(call_var_proxy->name())) {
1979 FAIL(call_var_proxy,
1980 "Invalid ASM.js identifier in (forward) function name.");
1981 }
1982 if (!AddGlobal(call_var_proxy->var(), fun_info)) {
1983 DCHECK(false);
1984 FAIL(call, "Redeclared global identifier");
1985 }
1986 return return_type;
1987 }
1988
1989 auto* callee_type = call_var_info->type()->AsCallableType();
1990 if (callee_type == nullptr) {
1991 FAIL(call, "Calling something that's not a function.");
1992 }
1993
1994 if (callee_type->AsFFIType() != nullptr &&
1995 return_type == AsmType::Float()) {
1996 FAIL(call, "FFI functions can't return float.");
1997 }
1998
1999 if (callee_type->ValidateCall(call_type->ReturnType(),
2000 call_type->Arguments()) == AsmType::None()) {
2001 FAIL(call, "Argument type mismatch.");
2002 }
2003
2004 return return_type;
2005 }
2006
2007 // identifier[expr & n](Expression...)
2008 if (auto* call_property = call_expr->AsProperty()) {
2009 auto* index = call_property->key()->AsBinaryOperation();
2010 if (index == nullptr || index->op() != Token::BIT_AND) {
2011 FAIL(index, "Indirect call index must be in the expr & mask form");
2012 }
2013
2014 auto* right = index->right();
2015 uint32_t mask;
2016 if (!ExtractIndirectCallMask(right, &mask)) {
2017 FAIL(right, "Invalid indirect call mask");
2018 }
2019 auto* table_type = AsmType::FunctionTableType(
2020 zone_, mask + 1, reinterpret_cast<AsmType*>(call_type));
2021
2022 auto* left = index->left();
2023 AsmType* left_type;
2024 RECURSE(left_type = ValidateExpression(left));
2025 if (!left_type->IsA(AsmType::Intish())) {
2026 FAIL(left, "Indirect call index should be an intish.");
2027 }
2028
2029 auto* name_var = call_property->obj()->AsVariableProxy();
2030
2031 if (name_var == nullptr) {
2032 FAIL(call_property, "Invalid call.");
2033 }
2034
2035 auto* name_info = Lookup(name_var->var());
2036 if (name_info == nullptr) {
2037 // We can't fail here -- just like above.
2038 auto* fun_info =
2039 new (zone_) VariableInfo(reinterpret_cast<AsmType*>(table_type));
2040 fun_info->set_mutability(VariableInfo::kImmutableGlobal);
2041 AddForwardReference(name_var, fun_info);
2042 if (!ValidAsmIdentifier(name_var->name())) {
2043 FAIL(name_var,
2044 "Invalid ASM.js identifier in (forward) function table name.");
2045 }
2046 if (!AddGlobal(name_var->var(), fun_info)) {
2047 DCHECK(false);
2048 FAIL(call, "Redeclared global identifier.");
2049 }
2050 return return_type;
2051 }
2052
2053 auto* previous_type = name_info->type()->AsFunctionTableType();
2054 if (previous_type == nullptr) {
2055 FAIL(call, "Expression type does not match previous type");
2056 }
2057
2058 auto* table_type_as_function_table = table_type->AsFunctionTableType();
2059 if (table_type_as_function_table->length() != previous_type->length()) {
2060 FAIL(call, "Function table size does not previous size.");
2061 }
2062
2063 auto* previous_type_signature =
2064 previous_type->signature()->AsFunctionType();
2065 if (call_type->ValidateCall(previous_type_signature->ReturnType(),
2066 previous_type_signature->Arguments()) ==
2067 AsmType::None()) {
2068 FAIL(call,
2069 "Function pointer table signature does not match previous "
2070 "signature.");
2071 }
2072
2073 return return_type;
2074 }
2075
2076 FAIL(call, "Invalid call.");
2077 }
2078
2079 // 6.10 ValidateHeapAccess
2080 namespace {
2081 bool ExtractHeapAccessShift(Expression* expr, uint32_t* value) {
2082 auto* as_literal = expr->AsLiteral();
2083 if (as_literal == nullptr) {
2084 return false;
2085 }
2086
2087 if (as_literal->raw_value()->ContainsDot()) {
2088 return false;
2089 }
2090
2091 return as_literal->value()->ToUint32(value);
2092 }
2093 } // namespace
2094
2095 AsmType* AsmTyper::ValidateHeapAccess(Property* heap,
2096 HeapAccessType access_type) {
2097 auto* obj = heap->obj()->AsVariableProxy();
2098 if (obj == nullptr) {
2099 FAIL(heap, "Invalid heap access.");
2100 }
2101
2102 auto* obj_info = Lookup(obj->var());
2103 if (obj_info == nullptr) {
2104 FAIL(heap, "Undeclared identifier.");
2105 }
2106
2107 auto* obj_type = obj_info->type();
2108 if (!obj_type->IsA(AsmType::Heap())) {
2109 FAIL(heap, "Identifier does not represent a heap view.");
2110 }
2111
2112 if (auto* key_as_literal = heap->key()->AsLiteral()) {
2113 if (key_as_literal->raw_value()->ContainsDot()) {
2114 FAIL(key_as_literal, "Heap access index must be intish");
2115 }
2116
2117 uint32_t _;
2118 if (!key_as_literal->value()->ToUint32(&_)) {
2119 FAIL(key_as_literal,
2120 "Heap access index must be a 32-bit unsigned integer.");
2121 }
2122
2123 if (access_type == LoadFromHeap) {
2124 return obj_type->LoadType();
2125 }
2126 return obj_type->StoreType();
2127 }
2128
2129 if (auto* key_as_binop = heap->key()->AsBinaryOperation()) {
2130 uint32_t shift;
2131 if (key_as_binop->op() == Token::SAR &&
2132 ExtractHeapAccessShift(key_as_binop->right(), &shift) &&
2133 (1 << shift) == obj_type->ElementSizeInBytes()) {
2134 AsmType* type;
2135 RECURSE(type = ValidateExpression(key_as_binop->left()));
2136 if (type->IsA(AsmType::Intish())) {
2137 if (access_type == LoadFromHeap) {
2138 return obj_type->LoadType();
2139 }
2140 return obj_type->StoreType();
2141 }
2142 }
2143 }
2144
2145 if (obj_type->ElementSizeInBytes() == 1) {
2146 // Leniency: if this is a byte array, we don't require the shift operation
2147 // to be present.
2148 AsmType* index_type;
2149 RECURSE(index_type = ValidateExpression(heap->key()));
2150 if (!index_type->IsA(AsmType::Intish())) {
2151 FAIL(heap, "Invalid heap access index for byte array.");
2152 }
2153 }
2154
2155 FAIL(heap, "Invalid heap access index.");
2156 }
2157
2158 // 6.11 ValidateFloatCoercion
2159 bool AsmTyper::IsCallToFround(Call* call) {
2160 if (call->arguments()->length() != 1) {
2161 return false;
2162 }
2163
2164 auto* call_var_proxy = call->expression()->AsVariableProxy();
2165 if (call_var_proxy == nullptr) {
2166 return false;
2167 }
2168
2169 auto* call_var_info = Lookup(call_var_proxy->var());
2170 if (call_var_info == nullptr) {
2171 return false;
2172 }
2173
2174 return call_var_info->standard_member() == kMathFround;
2175 }
2176
2177 AsmType* AsmTyper::ValidateFloatCoercion(Call* call) {
2178 if (!IsCallToFround(call)) {
2179 return nullptr;
2180 }
2181
2182 auto* arg = call->arguments()->at(0);
2183 // call is a fround() node. From now, there can be two possible outcomes:
2184 // 1. fround is used as a return type annotation.
2185 if (auto* arg_as_call = arg->AsCall()) {
2186 RECURSE(ValidateCall(AsmType::Float(), arg_as_call));
2187 return AsmType::Float();
2188 }
2189
2190 // 2. fround is used for converting to float.
2191 AsmType* arg_type;
2192 RECURSE(arg_type = ValidateExpression(arg));
2193 if (arg_type->IsA(AsmType::Floatish()) || arg_type->IsA(AsmType::DoubleQ()) ||
2194 arg_type->IsA(AsmType::Signed()) || arg_type->IsA(AsmType::Unsigned())) {
2195 return AsmType::Float();
2196 }
2197
2198 FAIL(call, "Invalid argument type to fround.");
2199 }
2200
2201 // 5.1 ParameterTypeAnnotations
2202 AsmType* AsmTyper::ParameterTypeAnnotations(Variable* parameter,
2203 Expression* annotation) {
2204 if (auto* binop = annotation->AsBinaryOperation()) {
2205 // Must be:
2206 // * x|0
2207 // * x*1 (*VIOLATION* i.e.,, +x)
2208 auto* left = binop->left()->AsVariableProxy();
2209 if (left == nullptr) {
2210 FAIL(binop->left(), "Invalid parameter type annotation");
2211 }
2212 if (left->var() != parameter) {
2213 FAIL(binop->left(), "Invalid parameter type annotation");
2214 }
2215 auto* right = binop->right()->AsLiteral();
2216 if (right == nullptr) {
2217 FAIL(binop->left(), "Invalid parameter type annotation");
2218 }
2219 switch (binop->op()) {
2220 default:
2221 FAIL(binop->left(), "Invalid parameter type annotation");
2222 case Token::MUL:
2223 if (right->raw_value()->ContainsDot() &&
2224 right->raw_value()->AsNumber() == 1.0) {
2225 return AsmType::Double();
2226 }
2227 FAIL(binop, "Invalid parameter type annotation");
2228 case Token::BIT_OR:
2229 if (!right->raw_value()->ContainsDot() &&
2230 right->raw_value()->AsNumber() == 0.0) {
2231 return AsmType::Int();
2232 }
2233 FAIL(binop, "Invalid parameter type annotation");
2234 }
2235 }
2236
2237 auto* call = annotation->AsCall();
2238 if (call == nullptr) {
2239 FAIL(annotation, "Invalid float parameter type annotation.");
2240 }
2241
2242 if (call->arguments()->length() != 1) {
2243 FAIL(annotation, "Invalid float parameter type annotation.");
2244 }
2245
2246 auto* src_expr = call->arguments()->at(0)->AsVariableProxy();
2247 if (src_expr == nullptr) {
2248 FAIL(annotation, "Invalid float parameter type annotation.");
2249 }
2250
2251 if (src_expr->var() != parameter) {
2252 FAIL(annotation, "Invalid float parameter type annotation.");
2253 }
2254
2255 auto* call_var_proxy = call->expression()->AsVariableProxy();
2256 if (call_var_proxy == nullptr) {
2257 FAIL(annotation, "Invalid float parameter type annotation.");
2258 }
2259
2260 auto* call_var_info = Lookup(call_var_proxy->var());
bradnelson 2016/06/24 00:19:03 Isn't this one a DCHECK?
John 2016/06/24 17:47:57 I don't think so. Why?
bradn 2016/06/24 18:57:03 Oh wait, nevermind, you're right.
2261 if (call_var_info == nullptr) {
2262 FAIL(annotation, "Invalid float parameter type annotation.");
2263 }
2264
2265 if (call_var_info->standard_member() != kMathFround) {
2266 FAIL(annotation, "Invalid float parameter type annotation.");
2267 }
2268
2269 return AsmType::Float();
2270 }
2271
2272 // 5.2 ReturnTypeAnnotations
2273 AsmType* AsmTyper::ReturnTypeAnnotations(ReturnStatement* statement) {
2274 if (statement == nullptr) {
2275 return AsmType::Void();
2276 }
2277
2278 auto* ret_expr = statement->expression();
2279 if (ret_expr == nullptr) {
2280 return AsmType::Void();
2281 }
2282
2283 if (auto* binop = ret_expr->AsBinaryOperation()) {
2284 if (MayBeDoubleAnnotation(binop)) {
2285 return AsmType::Double();
2286 } else if (IsIntAnnotation(binop)) {
2287 return AsmType::Signed();
2288 }
2289 FAIL(statement, "Invalid return type annotation.");
2290 }
2291
2292 if (auto* call = ret_expr->AsCall()) {
2293 if (IsCallToFround(call)) {
2294 return AsmType::Float();
2295 }
2296 FAIL(statement, "Invalid function call in return statement.");
2297 }
2298
2299 if (auto* literal = ret_expr->AsLiteral()) {
2300 int32_t _;
2301 if (literal->raw_value()->ContainsDot()) {
2302 return AsmType::Double();
2303 } else if (literal->value()->ToInt32(&_)) {
2304 return AsmType::Signed();
2305 } else if (literal->IsUndefinedLiteral()) {
2306 // *VIOLATION* The parser changes
2307 //
2308 // return;
2309 //
2310 // into
2311 //
2312 // return undefined
2313 return AsmType::Void();
2314 }
2315 FAIL(statement, "Invalid literal in return statement.");
2316 }
2317
2318 FAIL(statement, "Invalid return type expression.");
2319 }
2320
2321 // 5.4 VariableTypeAnnotations
2322 // Also used for 5.5 GlobalVariableTypeAnnotations
2323 AsmType* AsmTyper::VariableTypeAnnotations(Expression* initializer) {
2324 if (auto* literal = initializer->AsLiteral()) {
2325 if (literal->raw_value()->ContainsDot()) {
2326 return AsmType::Double();
2327 }
2328 int32_t i32;
2329 uint32_t u32;
2330 if (literal->value()->ToInt32(&i32) || literal->value()->ToUint32(&u32)) {
2331 return AsmType::Int();
2332 }
2333 FAIL(initializer, "Invalid type annotation - forbidden literal.");
2334 }
2335
2336 auto* call = initializer->AsCall();
2337 DCHECK(call != nullptr);
2338 if (call == nullptr) {
2339 FAIL(initializer,
2340 "Invalid variable initialization - it should be a literal, or "
2341 "fround(literal).");
2342 }
2343
2344 if (!IsCallToFround(call)) {
2345 FAIL(initializer,
2346 "Invalid float coercion - expected call fround(literal).");
2347 }
2348
2349 auto* src_expr = call->arguments()->at(0)->AsLiteral();
2350 if (src_expr == nullptr) {
2351 FAIL(initializer,
2352 "Invalid float type annotation - expected literal argument for call "
2353 "to fround.");
2354 }
2355
2356 if (!src_expr->raw_value()->ContainsDot()) {
2357 FAIL(initializer,
2358 "Invalid float type annotation - expected literal argument to be a "
2359 "floating point literal.");
2360 }
2361
2362 return AsmType::Float();
2363 }
2364
2365 // 5.5 GlobalVariableTypeAnnotations
2366 AsmType* AsmTyper::NewHeapView(CallNew* new_heap_view) {
2367 auto* heap_type = new_heap_view->expression()->AsProperty();
2368 if (heap_type == nullptr) {
2369 FAIL(new_heap_view, "Invalid type after new.");
2370 }
2371 auto* heap_view_info = ImportLookup(heap_type);
2372
2373 if (heap_view_info == nullptr) {
2374 FAIL(new_heap_view, "Unknown stdlib member in heap view declaration.");
2375 }
2376
2377 if (!heap_view_info->type()->IsA(AsmType::Heap())) {
2378 FAIL(new_heap_view, "Type is not a heap view type.");
2379 }
2380
2381 if (new_heap_view->arguments()->length() != 1) {
2382 FAIL(new_heap_view, "Invalid number of arguments when creating heap view.");
2383 }
2384
2385 auto* heap = new_heap_view->arguments()->at(0);
2386 auto* heap_var_proxy = heap->AsVariableProxy();
2387
2388 if (heap_var_proxy == nullptr) {
2389 FAIL(heap,
2390 "Heap view creation parameter should be the module's heap parameter.");
2391 }
2392
2393 auto* heap_var_info = Lookup(heap_var_proxy->var());
2394
2395 if (heap_var_info == nullptr) {
2396 FAIL(heap, "Undeclared identifier instead of heap parameter.");
2397 }
2398
2399 if (!heap_var_info->IsHeap()) {
2400 FAIL(heap,
2401 "Heap view creation parameter should be the module's heap parameter.");
2402 }
2403
2404 DCHECK(heap_view_info->type()->IsA(AsmType::Heap()));
2405 return heap_view_info->type();
2406 }
2407
2408 bool IsValidAsm(Isolate* isolate, Zone* zone, Script* script,
2409 FunctionLiteral* root, std::string* error_message) {
2410 error_message->clear();
2411
2412 AsmTyper typer(isolate, zone, script, root);
2413 if (typer.Validate()) {
2414 return true;
2415 }
2416
2417 *error_message = typer.error_message();
2418 return false;
2419 }
2420
2421 } // namespace wasm
2422 } // namespace internal
2423 } // namespace v8
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698