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/asmjs/asm-typer.h"
6
7 #include <algorithm>
8 #include <limits>
9 #include <memory>
10 #include <string>
11
12 #include "src/v8.h"
13
14 #include "src/asmjs/asm-types.h"
15 #include "src/ast/ast.h"
16 #include "src/ast/scopes.h"
17 #include "src/base/bits.h"
18 #include "src/codegen.h"
19 #include "src/globals.h"
20 #include "src/utils.h"
21
22 #define FAIL(node, msg) \
23 do { \
24 int line = node->position() == kNoSourcePosition \
25 ? -1 \
26 : script_->GetLineNumber(node->position()); \
27 base::OS::SNPrintF(error_message_, sizeof(error_message_), \
28 "asm: line %d: %s\n", line + 1, msg); \
29 return AsmType::None(); \
30 } while (false)
31
32 #define RECURSE(call) \
33 do { \
34 if (GetCurrentStackPosition() < stack_limit_) { \
35 stack_overflow_ = true; \
36 FAIL(root_, "Stack overflow while parsing asm.js module."); \
37 } \
38 \
39 AsmType* result = (call); \
40 if (stack_overflow_) { \
41 return AsmType::None(); \
42 } \
43 \
44 if (result == AsmType::None()) { \
45 return AsmType::None(); \
46 } \
47 } while (false)
48
49 namespace v8 {
50 namespace internal {
51 namespace wasm {
52 namespace {
53 static const uint32_t LargestFixNum = std::numeric_limits<int32_t>::max();
54 } // namespace
55
56 using v8::internal::AstNode;
57 using v8::internal::GetCurrentStackPosition;
58
59 // ----------------------------------------------------------------------------
60 // Implementation of AsmTyper::FlattenedStatements
61
FlattenedStatements(Zone * zone,ZoneList<Statement * > * s)62 AsmTyper::FlattenedStatements::FlattenedStatements(Zone* zone,
63 ZoneList<Statement*>* s)
64 : context_stack_(zone) {
65 context_stack_.emplace_back(Context(s));
66 }
67
Next()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
ForSpecialSymbol(Zone * zone,StandardMember standard_member)96 AsmTyper::VariableInfo* AsmTyper::VariableInfo::ForSpecialSymbol(
97 Zone* zone, StandardMember standard_member) {
98 DCHECK(standard_member == kStdlib || standard_member == kFFI ||
99 standard_member == kHeap || standard_member == kModule);
100 auto* new_var_info = new (zone) VariableInfo(AsmType::None());
101 new_var_info->standard_member_ = standard_member;
102 new_var_info->mutability_ = kImmutableGlobal;
103 return new_var_info;
104 }
105
Clone(Zone * zone) const106 AsmTyper::VariableInfo* AsmTyper::VariableInfo::Clone(Zone* zone) const {
107 CHECK(standard_member_ != kNone);
108 CHECK(!type_->IsA(AsmType::None()));
109 auto* new_var_info = new (zone) VariableInfo(type_);
110 new_var_info->standard_member_ = standard_member_;
111 new_var_info->mutability_ = mutability_;
112 return new_var_info;
113 }
114
FirstForwardUseIs(VariableProxy * var)115 void AsmTyper::VariableInfo::FirstForwardUseIs(VariableProxy* var) {
116 DCHECK(first_forward_use_ == nullptr);
117 missing_definition_ = true;
118 first_forward_use_ = var;
119 }
120
121 // ----------------------------------------------------------------------------
122 // Implementation of AsmTyper
123
AsmTyper(Isolate * isolate,Zone * zone,Script * script,FunctionLiteral * root)124 AsmTyper::AsmTyper(Isolate* isolate, Zone* zone, Script* script,
125 FunctionLiteral* root)
126 : isolate_(isolate),
127 zone_(zone),
128 script_(script),
129 root_(root),
130 forward_definitions_(zone),
131 ffi_use_signatures_(zone),
132 stdlib_types_(zone),
133 stdlib_math_types_(zone),
134 module_info_(VariableInfo::ForSpecialSymbol(zone_, kModule)),
135 global_scope_(ZoneHashMap::kDefaultHashMapCapacity,
136 ZoneAllocationPolicy(zone)),
137 local_scope_(ZoneHashMap::kDefaultHashMapCapacity,
138 ZoneAllocationPolicy(zone)),
139 stack_limit_(isolate->stack_guard()->real_climit()),
140 node_types_(zone_),
141 fround_type_(AsmType::FroundType(zone_)),
142 ffi_type_(AsmType::FFIType(zone_)) {
143 InitializeStdlib();
144 }
145
146 namespace {
ValidAsmIdentifier(Handle<String> name)147 bool ValidAsmIdentifier(Handle<String> name) {
148 static const char* kInvalidAsmNames[] = {"eval", "arguments"};
149
150 for (size_t ii = 0; ii < arraysize(kInvalidAsmNames); ++ii) {
151 if (strcmp(name->ToCString().get(), kInvalidAsmNames[ii]) == 0) {
152 return false;
153 }
154 }
155 return true;
156 }
157 } // namespace
158
InitializeStdlib()159 void AsmTyper::InitializeStdlib() {
160 auto* d = AsmType::Double();
161 auto* dq = AsmType::DoubleQ();
162 auto* dq2d = AsmType::Function(zone_, d);
163 dq2d->AsFunctionType()->AddArgument(dq);
164
165 auto* dqdq2d = AsmType::Function(zone_, d);
166 dqdq2d->AsFunctionType()->AddArgument(dq);
167 dqdq2d->AsFunctionType()->AddArgument(dq);
168
169 auto* f = AsmType::Float();
170 auto* fq = AsmType::FloatQ();
171 auto* fq2f = AsmType::Function(zone_, f);
172 fq2f->AsFunctionType()->AddArgument(fq);
173
174 auto* s = AsmType::Signed();
175 auto* s2s = AsmType::Function(zone_, s);
176 s2s->AsFunctionType()->AddArgument(s);
177
178 auto* i = AsmType::Int();
179 auto* i2s = AsmType::Function(zone_, s);
180 i2s->AsFunctionType()->AddArgument(i);
181
182 auto* ii2s = AsmType::Function(zone_, s);
183 ii2s->AsFunctionType()->AddArgument(i);
184 ii2s->AsFunctionType()->AddArgument(i);
185
186 auto* minmax_d = AsmType::MinMaxType(zone_, d, d);
187 // *VIOLATION* The float variant is not part of the spec, but firefox accepts
188 // it.
189 auto* minmax_f = AsmType::MinMaxType(zone_, f, f);
190 auto* minmax_i = AsmType::MinMaxType(zone_, s, i);
191 auto* minmax = AsmType::OverloadedFunction(zone_);
192 minmax->AsOverloadedFunctionType()->AddOverload(minmax_i);
193 minmax->AsOverloadedFunctionType()->AddOverload(minmax_f);
194 minmax->AsOverloadedFunctionType()->AddOverload(minmax_d);
195
196 auto* fround = fround_type_;
197
198 auto* abs = AsmType::OverloadedFunction(zone_);
199 abs->AsOverloadedFunctionType()->AddOverload(s2s);
200 abs->AsOverloadedFunctionType()->AddOverload(dq2d);
201 abs->AsOverloadedFunctionType()->AddOverload(fq2f);
202
203 auto* ceil = AsmType::OverloadedFunction(zone_);
204 ceil->AsOverloadedFunctionType()->AddOverload(dq2d);
205 ceil->AsOverloadedFunctionType()->AddOverload(fq2f);
206
207 auto* floor = ceil;
208 auto* sqrt = ceil;
209
210 struct StandardMemberInitializer {
211 const char* name;
212 StandardMember standard_member;
213 AsmType* type;
214 };
215
216 const StandardMemberInitializer stdlib[] = {{"Infinity", kInfinity, d},
217 {"NaN", kNaN, d},
218 #define ASM_TYPED_ARRAYS(V) \
219 V(Uint8) \
220 V(Int8) \
221 V(Uint16) \
222 V(Int16) \
223 V(Uint32) \
224 V(Int32) \
225 V(Float32) \
226 V(Float64)
227
228 #define ASM_TYPED_ARRAY(TypeName) \
229 {#TypeName "Array", kNone, AsmType::TypeName##Array()},
230 ASM_TYPED_ARRAYS(ASM_TYPED_ARRAY)
231 #undef ASM_TYPED_ARRAY
232 };
233 for (size_t ii = 0; ii < arraysize(stdlib); ++ii) {
234 stdlib_types_[stdlib[ii].name] = new (zone_) VariableInfo(stdlib[ii].type);
235 stdlib_types_[stdlib[ii].name]->set_standard_member(
236 stdlib[ii].standard_member);
237 stdlib_types_[stdlib[ii].name]->set_mutability(
238 VariableInfo::kImmutableGlobal);
239 }
240
241 const StandardMemberInitializer math[] = {
242 {"PI", kMathPI, d},
243 {"E", kMathE, d},
244 {"LN2", kMathLN2, d},
245 {"LN10", kMathLN10, d},
246 {"LOG2E", kMathLOG2E, d},
247 {"LOG10E", kMathLOG10E, d},
248 {"SQRT2", kMathSQRT2, d},
249 {"SQRT1_2", kMathSQRT1_2, d},
250 {"imul", kMathImul, ii2s},
251 {"abs", kMathAbs, abs},
252 // NOTE: clz32 should return fixnum. The current typer can only return
253 // Signed, Float, or Double, so it returns Signed in our version of
254 // asm.js.
255 {"clz32", kMathClz32, i2s},
256 {"ceil", kMathCeil, ceil},
257 {"floor", kMathFloor, floor},
258 {"fround", kMathFround, fround},
259 {"pow", kMathPow, dqdq2d},
260 {"exp", kMathExp, dq2d},
261 {"log", kMathLog, dq2d},
262 {"min", kMathMin, minmax},
263 {"max", kMathMax, minmax},
264 {"sqrt", kMathSqrt, sqrt},
265 {"cos", kMathCos, dq2d},
266 {"sin", kMathSin, dq2d},
267 {"tan", kMathTan, dq2d},
268 {"acos", kMathAcos, dq2d},
269 {"asin", kMathAsin, dq2d},
270 {"atan", kMathAtan, dq2d},
271 {"atan2", kMathAtan2, dqdq2d},
272 };
273 for (size_t ii = 0; ii < arraysize(math); ++ii) {
274 stdlib_math_types_[math[ii].name] = new (zone_) VariableInfo(math[ii].type);
275 stdlib_math_types_[math[ii].name]->set_standard_member(
276 math[ii].standard_member);
277 stdlib_math_types_[math[ii].name]->set_mutability(
278 VariableInfo::kImmutableGlobal);
279 }
280 }
281
282 // Used for 5.5 GlobalVariableTypeAnnotations
ImportLookup(Property * import)283 AsmTyper::VariableInfo* AsmTyper::ImportLookup(Property* import) {
284 auto* obj = import->obj();
285 auto* key = import->key()->AsLiteral();
286
287 ObjectTypeMap* stdlib = &stdlib_types_;
288 if (auto* obj_as_property = obj->AsProperty()) {
289 // This can only be stdlib.Math
290 auto* math_name = obj_as_property->key()->AsLiteral();
291 if (math_name == nullptr || !math_name->IsPropertyName()) {
292 return nullptr;
293 }
294
295 if (!math_name->AsPropertyName()->IsUtf8EqualTo(CStrVector("Math"))) {
296 return nullptr;
297 }
298
299 auto* stdlib_var_proxy = obj_as_property->obj()->AsVariableProxy();
300 if (stdlib_var_proxy == nullptr) {
301 return nullptr;
302 }
303 obj = stdlib_var_proxy;
304 stdlib = &stdlib_math_types_;
305 }
306
307 auto* obj_as_var_proxy = obj->AsVariableProxy();
308 if (obj_as_var_proxy == nullptr) {
309 return nullptr;
310 }
311
312 auto* obj_info = Lookup(obj_as_var_proxy->var());
313 if (obj_info == nullptr) {
314 return nullptr;
315 }
316
317 if (obj_info->IsFFI()) {
318 // For FFI we can't validate import->key, so assume this is OK.
319 return obj_info;
320 }
321
322 std::unique_ptr<char[]> aname = key->AsPropertyName()->ToCString();
323 ObjectTypeMap::iterator i = stdlib->find(std::string(aname.get()));
324 if (i == stdlib->end()) {
325 return nullptr;
326 }
327 stdlib_uses_.insert(i->second->standard_member());
328 return i->second;
329 }
330
Lookup(Variable * variable) const331 AsmTyper::VariableInfo* AsmTyper::Lookup(Variable* variable) const {
332 const ZoneHashMap* scope = in_function_ ? &local_scope_ : &global_scope_;
333 ZoneHashMap::Entry* entry =
334 scope->Lookup(variable, ComputePointerHash(variable));
335 if (entry == nullptr && in_function_) {
336 entry = global_scope_.Lookup(variable, ComputePointerHash(variable));
337 }
338
339 if (entry == nullptr && !module_name_.is_null() &&
340 module_name_->Equals(*variable->name())) {
341 return module_info_;
342 }
343
344 return entry ? reinterpret_cast<VariableInfo*>(entry->value) : nullptr;
345 }
346
AddForwardReference(VariableProxy * proxy,VariableInfo * info)347 void AsmTyper::AddForwardReference(VariableProxy* proxy, VariableInfo* info) {
348 info->FirstForwardUseIs(proxy);
349 forward_definitions_.push_back(info);
350 }
351
AddGlobal(Variable * variable,VariableInfo * info)352 bool AsmTyper::AddGlobal(Variable* variable, VariableInfo* info) {
353 // We can't DCHECK(!in_function_) because function may actually install global
354 // names (forward defined functions and function tables.)
355 DCHECK(info->mutability() != VariableInfo::kInvalidMutability);
356 DCHECK(info->IsGlobal());
357 DCHECK(ValidAsmIdentifier(variable->name()));
358
359 if (!module_name_.is_null() && module_name_->Equals(*variable->name())) {
360 return false;
361 }
362
363 ZoneHashMap::Entry* entry = global_scope_.LookupOrInsert(
364 variable, ComputePointerHash(variable), ZoneAllocationPolicy(zone_));
365
366 if (entry->value != nullptr) {
367 return false;
368 }
369
370 entry->value = info;
371 return true;
372 }
373
AddLocal(Variable * variable,VariableInfo * info)374 bool AsmTyper::AddLocal(Variable* variable, VariableInfo* info) {
375 DCHECK(in_function_);
376 DCHECK(info->mutability() != VariableInfo::kInvalidMutability);
377 DCHECK(!info->IsGlobal());
378 DCHECK(ValidAsmIdentifier(variable->name()));
379
380 ZoneHashMap::Entry* entry = local_scope_.LookupOrInsert(
381 variable, ComputePointerHash(variable), ZoneAllocationPolicy(zone_));
382
383 if (entry->value != nullptr) {
384 return false;
385 }
386
387 entry->value = info;
388 return true;
389 }
390
SetTypeOf(AstNode * node,AsmType * type)391 void AsmTyper::SetTypeOf(AstNode* node, AsmType* type) {
392 DCHECK_NE(type, AsmType::None());
393 DCHECK(node_types_.find(node) == node_types_.end());
394 node_types_.insert(std::make_pair(node, type));
395 }
396
TypeOf(AstNode * node) const397 AsmType* AsmTyper::TypeOf(AstNode* node) const {
398 auto node_type_iter = node_types_.find(node);
399 if (node_type_iter != node_types_.end()) {
400 return node_type_iter->second;
401 }
402
403 // Sometimes literal nodes are not added to the node_type_ map simply because
404 // their are not visited with ValidateExpression().
405 if (auto* literal = node->AsLiteral()) {
406 if (literal->raw_value()->ContainsDot()) {
407 return AsmType::Double();
408 }
409 uint32_t u;
410 if (literal->value()->ToUint32(&u)) {
411 if (u > LargestFixNum) {
412 return AsmType::Unsigned();
413 }
414 return AsmType::FixNum();
415 }
416 int32_t i;
417 if (literal->value()->ToInt32(&i)) {
418 return AsmType::Signed();
419 }
420 }
421
422 return AsmType::None();
423 }
424
TypeOf(Variable * v) const425 AsmType* AsmTyper::TypeOf(Variable* v) const { return Lookup(v)->type(); }
426
VariableAsStandardMember(Variable * var)427 AsmTyper::StandardMember AsmTyper::VariableAsStandardMember(Variable* var) {
428 auto* var_info = Lookup(var);
429 if (var_info == nullptr) {
430 return kNone;
431 }
432 StandardMember member = var_info->standard_member();
433 return member;
434 }
435
Validate()436 bool AsmTyper::Validate() {
437 if (!AsmType::None()->IsExactly(ValidateModule(root_))) {
438 return true;
439 }
440 return false;
441 }
442
443 namespace {
IsUseAsmDirective(Statement * first_statement)444 bool IsUseAsmDirective(Statement* first_statement) {
445 ExpressionStatement* use_asm = first_statement->AsExpressionStatement();
446 if (use_asm == nullptr) {
447 return false;
448 }
449
450 Literal* use_asm_literal = use_asm->expression()->AsLiteral();
451
452 if (use_asm_literal == nullptr) {
453 return false;
454 }
455
456 return use_asm_literal->raw_value()->AsString()->IsOneByteEqualTo("use asm");
457 }
458
ExtractInitializerExpression(Statement * statement)459 Assignment* ExtractInitializerExpression(Statement* statement) {
460 auto* expr_stmt = statement->AsExpressionStatement();
461 if (expr_stmt == nullptr) {
462 // Done with initializers.
463 return nullptr;
464 }
465 auto* assign = expr_stmt->expression()->AsAssignment();
466 if (assign == nullptr) {
467 // Done with initializers.
468 return nullptr;
469 }
470 if (assign->op() != Token::INIT) {
471 // Done with initializers.
472 return nullptr;
473 }
474 return assign;
475 }
476
477 } // namespace
478
479 // 6.1 ValidateModule
480 namespace {
481 // SourceLayoutTracker keeps track of the start and end positions of each
482 // section in the asm.js source. The sections should not overlap, otherwise the
483 // asm.js source is invalid.
484 class SourceLayoutTracker {
485 public:
486 SourceLayoutTracker() = default;
487
IsValid() const488 bool IsValid() const {
489 const Section* kAllSections[] = {&use_asm_, &globals_, &functions_,
490 &tables_, &exports_};
491 for (size_t ii = 0; ii < arraysize(kAllSections); ++ii) {
492 const auto& curr_section = *kAllSections[ii];
493 for (size_t jj = ii + 1; jj < arraysize(kAllSections); ++jj) {
494 if (curr_section.OverlapsWith(*kAllSections[jj])) {
495 return false;
496 }
497 }
498 }
499 return true;
500 }
501
AddUseAsm(const AstNode & node)502 void AddUseAsm(const AstNode& node) { use_asm_.AddNewElement(node); }
503
AddGlobal(const AstNode & node)504 void AddGlobal(const AstNode& node) { globals_.AddNewElement(node); }
505
AddFunction(const AstNode & node)506 void AddFunction(const AstNode& node) { functions_.AddNewElement(node); }
507
AddTable(const AstNode & node)508 void AddTable(const AstNode& node) { tables_.AddNewElement(node); }
509
AddExport(const AstNode & node)510 void AddExport(const AstNode& node) { exports_.AddNewElement(node); }
511
512 private:
513 class Section {
514 public:
515 Section() = default;
516 Section(const Section&) = default;
517 Section& operator=(const Section&) = default;
518
AddNewElement(const AstNode & node)519 void AddNewElement(const AstNode& node) {
520 const int node_pos = node.position();
521 if (start_ == kNoSourcePosition) {
522 start_ = node_pos;
523 } else {
524 start_ = std::max(start_, node_pos);
525 }
526 if (end_ == kNoSourcePosition) {
527 end_ = node_pos;
528 } else {
529 end_ = std::max(end_, node_pos);
530 }
531 }
532
OverlapsWith(const Section & other) const533 bool OverlapsWith(const Section& other) const {
534 if (start_ == kNoSourcePosition) {
535 DCHECK_EQ(end_, kNoSourcePosition);
536 return false;
537 }
538 if (other.start_ == kNoSourcePosition) {
539 DCHECK_EQ(other.end_, kNoSourcePosition);
540 return false;
541 }
542 return other.start_ < end_ || other.end_ < start_;
543 }
544
545 private:
546 int start_ = kNoSourcePosition;
547 int end_ = kNoSourcePosition;
548 };
549
550 Section use_asm_;
551 Section globals_;
552 Section functions_;
553 Section tables_;
554 Section exports_;
555
556 DISALLOW_COPY_AND_ASSIGN(SourceLayoutTracker);
557 };
558 } // namespace
559
ValidateModule(FunctionLiteral * fun)560 AsmType* AsmTyper::ValidateModule(FunctionLiteral* fun) {
561 SourceLayoutTracker source_layout;
562
563 DeclarationScope* scope = fun->scope();
564 if (!scope->is_function_scope()) FAIL(fun, "Not at function scope.");
565 if (!ValidAsmIdentifier(fun->name()))
566 FAIL(fun, "Invalid asm.js identifier in module name.");
567 module_name_ = fun->name();
568
569 // Allowed parameters: Stdlib, FFI, Mem
570 static const int MaxModuleParameters = 3;
571 if (scope->num_parameters() > MaxModuleParameters) {
572 FAIL(fun, "asm.js modules may not have more than three parameters.");
573 }
574
575 struct {
576 StandardMember standard_member;
577 } kModuleParamInfo[3] = {
578 {kStdlib}, {kFFI}, {kHeap},
579 };
580
581 for (int ii = 0; ii < scope->num_parameters(); ++ii) {
582 Variable* param = scope->parameter(ii);
583 DCHECK(param);
584
585 if (!ValidAsmIdentifier(param->name())) {
586 FAIL(fun, "Invalid asm.js identifier in module parameter.");
587 }
588
589 auto* param_info = VariableInfo::ForSpecialSymbol(
590 zone_, kModuleParamInfo[ii].standard_member);
591
592 if (!AddGlobal(param, param_info)) {
593 FAIL(fun, "Redeclared identifier in module parameter.");
594 }
595 }
596
597 ZoneVector<Assignment*> function_pointer_tables(zone_);
598 FlattenedStatements iter(zone_, fun->body());
599 auto* use_asm_directive = iter.Next();
600 if (use_asm_directive == nullptr) {
601 FAIL(fun, "Missing \"use asm\".");
602 }
603 // Check for extra assignment inserted by the parser when in this form:
604 // (function Module(a, b, c) {... })
605 ExpressionStatement* estatement = use_asm_directive->AsExpressionStatement();
606 if (estatement != nullptr) {
607 Assignment* assignment = estatement->expression()->AsAssignment();
608 if (assignment != nullptr && assignment->target()->IsVariableProxy() &&
609 assignment->target()
610 ->AsVariableProxy()
611 ->var()
612 ->is_sloppy_function_name()) {
613 use_asm_directive = iter.Next();
614 }
615 }
616 if (!IsUseAsmDirective(use_asm_directive)) {
617 FAIL(fun, "Missing \"use asm\".");
618 }
619 source_layout.AddUseAsm(*use_asm_directive);
620 ReturnStatement* module_return = nullptr;
621
622 // *VIOLATION* The spec states that globals should be followed by function
623 // declarations, which should be followed by function pointer tables, followed
624 // by the module export (return) statement. Our AST might be rearraged by the
625 // parser, so we can't rely on it being in source code order.
626 while (Statement* current = iter.Next()) {
627 if (auto* assign = ExtractInitializerExpression(current)) {
628 if (assign->value()->IsArrayLiteral()) {
629 // Save function tables for later validation.
630 function_pointer_tables.push_back(assign);
631 } else {
632 RECURSE(ValidateGlobalDeclaration(assign));
633 source_layout.AddGlobal(*assign);
634 }
635 continue;
636 }
637
638 if (auto* current_as_return = current->AsReturnStatement()) {
639 if (module_return != nullptr) {
640 FAIL(fun, "Multiple export statements.");
641 }
642 module_return = current_as_return;
643 source_layout.AddExport(*module_return);
644 continue;
645 }
646
647 FAIL(current, "Invalid top-level statement in asm.js module.");
648 }
649
650 Declaration::List* decls = scope->declarations();
651 for (Declaration* decl : *decls) {
652 if (FunctionDeclaration* fun_decl = decl->AsFunctionDeclaration()) {
653 RECURSE(ValidateFunction(fun_decl));
654 source_layout.AddFunction(*fun_decl);
655 continue;
656 }
657 }
658
659 for (auto* function_table : function_pointer_tables) {
660 RECURSE(ValidateFunctionTable(function_table));
661 source_layout.AddTable(*function_table);
662 }
663
664 for (Declaration* decl : *decls) {
665 if (decl->IsFunctionDeclaration()) {
666 continue;
667 }
668
669 VariableDeclaration* var_decl = decl->AsVariableDeclaration();
670 if (var_decl == nullptr) {
671 FAIL(decl, "Invalid asm.js declaration.");
672 }
673
674 auto* var_proxy = var_decl->proxy();
675 if (var_proxy == nullptr) {
676 FAIL(decl, "Invalid asm.js declaration.");
677 }
678
679 if (Lookup(var_proxy->var()) == nullptr) {
680 FAIL(decl, "Global variable missing initializer in asm.js module.");
681 }
682 }
683
684 // 6.2 ValidateExport
685 if (module_return == nullptr) {
686 FAIL(fun, "Missing asm.js module export.");
687 }
688
689 for (auto* forward_def : forward_definitions_) {
690 if (forward_def->missing_definition()) {
691 FAIL(forward_def->first_forward_use(),
692 "Missing definition for forward declared identifier.");
693 }
694 }
695
696 RECURSE(ValidateExport(module_return));
697
698 if (!source_layout.IsValid()) {
699 FAIL(fun, "Invalid asm.js source code layout.");
700 }
701
702 return AsmType::Int(); // Any type that is not AsmType::None();
703 }
704
705 namespace {
IsDoubleAnnotation(BinaryOperation * binop)706 bool IsDoubleAnnotation(BinaryOperation* binop) {
707 // *VIOLATION* The parser replaces uses of +x with x*1.0.
708 if (binop->op() != Token::MUL) {
709 return false;
710 }
711
712 auto* right_as_literal = binop->right()->AsLiteral();
713 if (right_as_literal == nullptr) {
714 return false;
715 }
716
717 return right_as_literal->raw_value()->ContainsDot() &&
718 right_as_literal->raw_value()->AsNumber() == 1.0;
719 }
720
IsIntAnnotation(BinaryOperation * binop)721 bool IsIntAnnotation(BinaryOperation* binop) {
722 if (binop->op() != Token::BIT_OR) {
723 return false;
724 }
725
726 auto* right_as_literal = binop->right()->AsLiteral();
727 if (right_as_literal == nullptr) {
728 return false;
729 }
730
731 return !right_as_literal->raw_value()->ContainsDot() &&
732 right_as_literal->raw_value()->AsNumber() == 0.0;
733 }
734 } // namespace
735
ValidateGlobalDeclaration(Assignment * assign)736 AsmType* AsmTyper::ValidateGlobalDeclaration(Assignment* assign) {
737 DCHECK(!assign->is_compound());
738 if (assign->is_compound()) {
739 FAIL(assign,
740 "Compound assignment not supported when declaring global variables.");
741 }
742
743 auto* target = assign->target();
744 if (!target->IsVariableProxy()) {
745 FAIL(target, "Module assignments may only assign to globals.");
746 }
747 auto* target_variable = target->AsVariableProxy()->var();
748 auto* target_info = Lookup(target_variable);
749
750 if (target_info != nullptr) {
751 FAIL(target, "Redefined global variable.");
752 }
753
754 auto* value = assign->value();
755 // Not all types of assignment are allowed by asm.js. See
756 // 5.5 Global Variable Type Annotations.
757 bool global_variable = false;
758 if (value->IsLiteral() || value->IsCall()) {
759 AsmType* type = nullptr;
760 VariableInfo::Mutability mutability;
761 if (target_variable->mode() == CONST) {
762 mutability = VariableInfo::kConstGlobal;
763 } else {
764 mutability = VariableInfo::kMutableGlobal;
765 }
766 RECURSE(type = VariableTypeAnnotations(value, mutability));
767 target_info = new (zone_) VariableInfo(type);
768 target_info->set_mutability(mutability);
769 global_variable = true;
770 } else if (value->IsProperty()) {
771 target_info = ImportLookup(value->AsProperty());
772 if (target_info == nullptr) {
773 FAIL(assign, "Invalid import.");
774 }
775 CHECK(target_info->mutability() == VariableInfo::kImmutableGlobal);
776 if (target_info->IsFFI()) {
777 // create a new target info that represents a foreign variable.
778 target_info = new (zone_) VariableInfo(ffi_type_);
779 target_info->set_mutability(VariableInfo::kImmutableGlobal);
780 } else if (target_info->type()->IsA(AsmType::Heap())) {
781 FAIL(assign, "Heap view types can not be aliased.");
782 } else {
783 target_info = target_info->Clone(zone_);
784 }
785 } else if (value->IsBinaryOperation()) {
786 // This should either be:
787 //
788 // var <> = ffi.<>|0
789 //
790 // or
791 //
792 // var <> = +ffi.<>
793 auto* value_binop = value->AsBinaryOperation();
794 auto* left = value_binop->left();
795 AsmType* import_type = nullptr;
796
797 if (IsDoubleAnnotation(value_binop)) {
798 import_type = AsmType::Double();
799 } else if (IsIntAnnotation(value_binop)) {
800 import_type = AsmType::Int();
801 } else {
802 FAIL(value,
803 "Invalid initializer for foreign import - unrecognized annotation.");
804 }
805
806 if (!left->IsProperty()) {
807 FAIL(value,
808 "Invalid initializer for foreign import - must import member.");
809 }
810 target_info = ImportLookup(left->AsProperty());
811 if (target_info == nullptr) {
812 // TODO(jpp): this error message is innacurate: this may fail if the
813 // object lookup fails, or if the property lookup fails, or even if the
814 // import is bogus like a().c.
815 FAIL(value,
816 "Invalid initializer for foreign import - object lookup failed.");
817 }
818 CHECK(target_info->mutability() == VariableInfo::kImmutableGlobal);
819 if (!target_info->IsFFI()) {
820 FAIL(value,
821 "Invalid initializer for foreign import - object is not the ffi.");
822 }
823
824 // Create a new target info that represents the foreign import.
825 target_info = new (zone_) VariableInfo(import_type);
826 target_info->set_mutability(VariableInfo::kMutableGlobal);
827 } else if (value->IsCallNew()) {
828 AsmType* type = nullptr;
829 RECURSE(type = NewHeapView(value->AsCallNew()));
830 target_info = new (zone_) VariableInfo(type);
831 target_info->set_mutability(VariableInfo::kImmutableGlobal);
832 } else if (auto* proxy = value->AsVariableProxy()) {
833 auto* var_info = Lookup(proxy->var());
834
835 if (var_info == nullptr) {
836 FAIL(value, "Undeclared identifier in global initializer");
837 }
838
839 if (var_info->mutability() != VariableInfo::kConstGlobal) {
840 FAIL(value, "Identifier used to initialize a global must be a const");
841 }
842
843 target_info = new (zone_) VariableInfo(var_info->type());
844 if (target_variable->mode() == CONST) {
845 target_info->set_mutability(VariableInfo::kConstGlobal);
846 } else {
847 target_info->set_mutability(VariableInfo::kMutableGlobal);
848 }
849 }
850
851 if (target_info == nullptr) {
852 FAIL(assign, "Invalid global variable initializer.");
853 }
854
855 if (!ValidAsmIdentifier(target_variable->name())) {
856 FAIL(target, "Invalid asm.js identifier in global variable.");
857 }
858
859 if (!AddGlobal(target_variable, target_info)) {
860 FAIL(assign, "Redeclared global identifier.");
861 }
862
863 DCHECK(target_info->type() != AsmType::None());
864 if (!global_variable) {
865 // Global variables have their types set in VariableTypeAnnotations.
866 SetTypeOf(value, target_info->type());
867 }
868 SetTypeOf(assign, target_info->type());
869 SetTypeOf(target, target_info->type());
870 return target_info->type();
871 }
872
873 // 6.2 ValidateExport
ExportType(VariableProxy * fun_export)874 AsmType* AsmTyper::ExportType(VariableProxy* fun_export) {
875 auto* fun_info = Lookup(fun_export->var());
876 if (fun_info == nullptr) {
877 FAIL(fun_export, "Undefined identifier in asm.js module export.");
878 }
879
880 if (fun_info->standard_member() != kNone) {
881 FAIL(fun_export, "Module cannot export standard library functions.");
882 }
883
884 auto* type = fun_info->type();
885 if (type->AsFFIType() != nullptr) {
886 FAIL(fun_export, "Module cannot export foreign functions.");
887 }
888
889 if (type->AsFunctionTableType() != nullptr) {
890 FAIL(fun_export, "Module cannot export function tables.");
891 }
892
893 if (fun_info->type()->AsFunctionType() == nullptr) {
894 FAIL(fun_export, "Module export is not an asm.js function.");
895 }
896
897 return type;
898 }
899
ValidateExport(ReturnStatement * exports)900 AsmType* AsmTyper::ValidateExport(ReturnStatement* exports) {
901 // asm.js modules can export single functions, or multiple functions in an
902 // object literal.
903 if (auto* fun_export = exports->expression()->AsVariableProxy()) {
904 // Exporting single function.
905 AsmType* export_type;
906 RECURSE(export_type = ExportType(fun_export));
907 return export_type;
908 }
909
910 if (auto* obj_export = exports->expression()->AsObjectLiteral()) {
911 // Exporting object literal.
912 for (auto* prop : *obj_export->properties()) {
913 if (!prop->key()->IsLiteral()) {
914 FAIL(prop->key(),
915 "Only normal object properties may be used in the export object "
916 "literal.");
917 }
918
919 auto* export_obj = prop->value()->AsVariableProxy();
920 if (export_obj == nullptr) {
921 FAIL(prop->value(), "Exported value must be an asm.js function name.");
922 }
923
924 RECURSE(ExportType(export_obj));
925 }
926
927 return AsmType::Int();
928 }
929
930 FAIL(exports, "Unrecognized expression in asm.js module export expression.");
931 }
932
933 // 6.3 ValidateFunctionTable
ValidateFunctionTable(Assignment * assign)934 AsmType* AsmTyper::ValidateFunctionTable(Assignment* assign) {
935 if (assign->is_compound()) {
936 FAIL(assign,
937 "Compound assignment not supported when declaring global variables.");
938 }
939
940 auto* target = assign->target();
941 if (!target->IsVariableProxy()) {
942 FAIL(target, "Module assignments may only assign to globals.");
943 }
944 auto* target_variable = target->AsVariableProxy()->var();
945
946 auto* value = assign->value()->AsArrayLiteral();
947 CHECK(value != nullptr);
948 ZoneList<Expression*>* pointers = value->values();
949
950 // The function table size must be n = 2 ** m, for m >= 0;
951 // TODO(jpp): should this be capped?
952 if (!base::bits::IsPowerOfTwo32(pointers->length())) {
953 FAIL(assign, "Invalid length for function pointer table.");
954 }
955
956 AsmType* table_element_type = nullptr;
957 for (auto* initializer : *pointers) {
958 auto* var_proxy = initializer->AsVariableProxy();
959 if (var_proxy == nullptr) {
960 FAIL(initializer,
961 "Function pointer table initializer must be a function name.");
962 }
963
964 auto* var_info = Lookup(var_proxy->var());
965 if (var_info == nullptr) {
966 FAIL(var_proxy,
967 "Undefined identifier in function pointer table initializer.");
968 }
969
970 if (var_info->standard_member() != kNone) {
971 FAIL(initializer,
972 "Function pointer table must not be a member of the standard "
973 "library.");
974 }
975
976 auto* initializer_type = var_info->type();
977 if (initializer_type->AsFunctionType() == nullptr) {
978 FAIL(initializer,
979 "Function pointer table initializer must be an asm.js function.");
980 }
981
982 DCHECK(var_info->type()->AsFFIType() == nullptr);
983 DCHECK(var_info->type()->AsFunctionTableType() == nullptr);
984
985 if (table_element_type == nullptr) {
986 table_element_type = initializer_type;
987 } else if (!initializer_type->IsA(table_element_type)) {
988 FAIL(initializer, "Type mismatch in function pointer table initializer.");
989 }
990 }
991
992 auto* target_info = Lookup(target_variable);
993 if (target_info == nullptr) {
994 // Function pointer tables are the last entities to be validates, so this is
995 // unlikely to happen: only unreferenced function tables will not already
996 // have an entry in the global scope.
997 target_info = new (zone_) VariableInfo(AsmType::FunctionTableType(
998 zone_, pointers->length(), table_element_type));
999 target_info->set_mutability(VariableInfo::kImmutableGlobal);
1000 if (!ValidAsmIdentifier(target_variable->name())) {
1001 FAIL(target, "Invalid asm.js identifier in function table name.");
1002 }
1003 if (!AddGlobal(target_variable, target_info)) {
1004 DCHECK(false);
1005 FAIL(assign, "Redeclared global identifier in function table name.");
1006 }
1007 SetTypeOf(value, target_info->type());
1008 return target_info->type();
1009 }
1010
1011 auto* target_info_table = target_info->type()->AsFunctionTableType();
1012 if (target_info_table == nullptr) {
1013 FAIL(assign, "Identifier redefined as function pointer table.");
1014 }
1015
1016 if (!target_info->missing_definition()) {
1017 FAIL(assign, "Identifier redefined (function table name).");
1018 }
1019
1020 if (static_cast<int>(target_info_table->length()) != pointers->length()) {
1021 FAIL(assign, "Function table size mismatch.");
1022 }
1023
1024 DCHECK(target_info_table->signature()->AsFunctionType());
1025 if (!table_element_type->IsA(target_info_table->signature())) {
1026 FAIL(assign, "Function table initializer does not match previous type.");
1027 }
1028
1029 target_info->MarkDefined();
1030 DCHECK(target_info->type() != AsmType::None());
1031 SetTypeOf(value, target_info->type());
1032
1033 return target_info->type();
1034 }
1035
1036 // 6.4 ValidateFunction
ValidateFunction(FunctionDeclaration * fun_decl)1037 AsmType* AsmTyper::ValidateFunction(FunctionDeclaration* fun_decl) {
1038 FunctionScope _(this);
1039
1040 // Extract parameter types.
1041 auto* fun = fun_decl->fun();
1042
1043 auto* fun_decl_proxy = fun_decl->proxy();
1044 if (fun_decl_proxy == nullptr) {
1045 FAIL(fun_decl, "Anonymous functions are not support in asm.js.");
1046 }
1047
1048 Statement* current;
1049 FlattenedStatements iter(zone_, fun->body());
1050
1051 size_t annotated_parameters = 0;
1052
1053 // 5.3 Function type annotations
1054 // * parameters
1055 ZoneVector<AsmType*> parameter_types(zone_);
1056 for (; (current = iter.Next()) != nullptr; ++annotated_parameters) {
1057 auto* stmt = current->AsExpressionStatement();
1058 if (stmt == nullptr) {
1059 // Done with parameters.
1060 break;
1061 }
1062 auto* expr = stmt->expression()->AsAssignment();
1063 if (expr == nullptr || expr->is_compound()) {
1064 // Done with parameters.
1065 break;
1066 }
1067 auto* proxy = expr->target()->AsVariableProxy();
1068 if (proxy == nullptr) {
1069 // Done with parameters.
1070 break;
1071 }
1072 auto* param = proxy->var();
1073 if (param->location() != VariableLocation::PARAMETER ||
1074 param->index() != static_cast<int>(annotated_parameters)) {
1075 // Done with parameters.
1076 break;
1077 }
1078
1079 AsmType* type;
1080 RECURSE(type = ParameterTypeAnnotations(param, expr->value()));
1081 DCHECK(type->IsParameterType());
1082 auto* param_info = new (zone_) VariableInfo(type);
1083 param_info->set_mutability(VariableInfo::kLocal);
1084 if (!ValidAsmIdentifier(proxy->name())) {
1085 FAIL(proxy, "Invalid asm.js identifier in parameter name.");
1086 }
1087
1088 if (!AddLocal(param, param_info)) {
1089 FAIL(proxy, "Redeclared parameter.");
1090 }
1091 parameter_types.push_back(type);
1092 SetTypeOf(proxy, type);
1093 SetTypeOf(expr, type);
1094 }
1095
1096 if (static_cast<int>(annotated_parameters) != fun->parameter_count()) {
1097 FAIL(fun_decl, "Incorrect parameter type annotations.");
1098 }
1099
1100 // 5.3 Function type annotations
1101 // * locals
1102 for (; current; current = iter.Next()) {
1103 auto* initializer = ExtractInitializerExpression(current);
1104 if (initializer == nullptr) {
1105 // Done with locals.
1106 break;
1107 }
1108
1109 auto* local = initializer->target()->AsVariableProxy();
1110 if (local == nullptr) {
1111 // Done with locals. It should never happen. Even if it does, the asm.js
1112 // code should not declare any other locals after this point, so we assume
1113 // this is OK. If any other variable declaration is found we report a
1114 // validation error.
1115 DCHECK(false);
1116 break;
1117 }
1118
1119 AsmType* type;
1120 RECURSE(type = VariableTypeAnnotations(initializer->value()));
1121 auto* local_info = new (zone_) VariableInfo(type);
1122 local_info->set_mutability(VariableInfo::kLocal);
1123 if (!ValidAsmIdentifier(local->name())) {
1124 FAIL(local, "Invalid asm.js identifier in local variable.");
1125 }
1126
1127 if (!AddLocal(local->var(), local_info)) {
1128 FAIL(initializer, "Redeclared local.");
1129 }
1130
1131 SetTypeOf(local, type);
1132 SetTypeOf(initializer, type);
1133 }
1134
1135 // 5.2 Return Type Annotations
1136 // *VIOLATION* we peel blocks to find the last statement in the asm module
1137 // because the parser may introduce synthetic blocks.
1138 ZoneList<Statement*>* statements = fun->body();
1139
1140 do {
1141 if (statements->length() == 0) {
1142 return_type_ = AsmType::Void();
1143 } else {
1144 auto* last_statement = statements->last();
1145 auto* as_block = last_statement->AsBlock();
1146 if (as_block != nullptr) {
1147 statements = as_block->statements();
1148 } else {
1149 // We don't check whether AsReturnStatement() below returns non-null --
1150 // we leave that to the ReturnTypeAnnotations method.
1151 RECURSE(return_type_ =
1152 ReturnTypeAnnotations(last_statement->AsReturnStatement()));
1153 }
1154 }
1155 } while (return_type_ == AsmType::None());
1156
1157 DCHECK(return_type_->IsReturnType());
1158
1159 for (Declaration* decl : *fun->scope()->declarations()) {
1160 auto* var_decl = decl->AsVariableDeclaration();
1161 if (var_decl == nullptr) {
1162 FAIL(decl, "Functions may only define inner variables.");
1163 }
1164
1165 auto* var_proxy = var_decl->proxy();
1166 if (var_proxy == nullptr) {
1167 FAIL(decl, "Invalid local declaration declaration.");
1168 }
1169
1170 auto* var_info = Lookup(var_proxy->var());
1171 if (var_info == nullptr || var_info->IsGlobal()) {
1172 FAIL(decl, "Local variable missing initializer in asm.js module.");
1173 }
1174 }
1175
1176 for (; current; current = iter.Next()) {
1177 AsmType* current_type;
1178 RECURSE(current_type = ValidateStatement(current));
1179 }
1180
1181 auto* fun_type = AsmType::Function(zone_, return_type_);
1182 auto* fun_type_as_function = fun_type->AsFunctionType();
1183 for (auto* param_type : parameter_types) {
1184 fun_type_as_function->AddArgument(param_type);
1185 }
1186
1187 auto* fun_var = fun_decl_proxy->var();
1188 auto* fun_info = new (zone_) VariableInfo(fun_type);
1189 fun_info->set_mutability(VariableInfo::kImmutableGlobal);
1190 auto* old_fun_info = Lookup(fun_var);
1191 if (old_fun_info == nullptr) {
1192 if (!ValidAsmIdentifier(fun_var->name())) {
1193 FAIL(fun_decl_proxy, "Invalid asm.js identifier in function name.");
1194 }
1195 if (!AddGlobal(fun_var, fun_info)) {
1196 DCHECK(false);
1197 FAIL(fun_decl, "Redeclared global identifier.");
1198 }
1199
1200 SetTypeOf(fun, fun_type);
1201 return fun_type;
1202 }
1203
1204 // Not necessarily an error -- fun_decl might have been used before being
1205 // defined. If that's the case, then the type in the global environment must
1206 // be the same as the type inferred by the parameter/return type annotations.
1207 auto* old_fun_type = old_fun_info->type();
1208 if (old_fun_type->AsFunctionType() == nullptr) {
1209 FAIL(fun_decl, "Identifier redefined as function.");
1210 }
1211
1212 if (!old_fun_info->missing_definition()) {
1213 FAIL(fun_decl, "Identifier redefined (function name).");
1214 }
1215
1216 if (!fun_type->IsA(old_fun_type)) {
1217 FAIL(fun_decl, "Signature mismatch when defining function.");
1218 }
1219
1220 old_fun_info->MarkDefined();
1221 SetTypeOf(fun, fun_type);
1222
1223 return fun_type;
1224 }
1225
1226 // 6.5 ValidateStatement
ValidateStatement(Statement * statement)1227 AsmType* AsmTyper::ValidateStatement(Statement* statement) {
1228 switch (statement->node_type()) {
1229 default:
1230 FAIL(statement, "Statement type invalid for asm.js.");
1231 case AstNode::kBlock:
1232 return ValidateBlockStatement(statement->AsBlock());
1233 case AstNode::kExpressionStatement:
1234 return ValidateExpressionStatement(statement->AsExpressionStatement());
1235 case AstNode::kEmptyStatement:
1236 return ValidateEmptyStatement(statement->AsEmptyStatement());
1237 case AstNode::kIfStatement:
1238 return ValidateIfStatement(statement->AsIfStatement());
1239 case AstNode::kReturnStatement:
1240 return ValidateReturnStatement(statement->AsReturnStatement());
1241 case AstNode::kWhileStatement:
1242 return ValidateWhileStatement(statement->AsWhileStatement());
1243 case AstNode::kDoWhileStatement:
1244 return ValidateDoWhileStatement(statement->AsDoWhileStatement());
1245 case AstNode::kForStatement:
1246 return ValidateForStatement(statement->AsForStatement());
1247 case AstNode::kBreakStatement:
1248 return ValidateBreakStatement(statement->AsBreakStatement());
1249 case AstNode::kContinueStatement:
1250 return ValidateContinueStatement(statement->AsContinueStatement());
1251 case AstNode::kSwitchStatement:
1252 return ValidateSwitchStatement(statement->AsSwitchStatement());
1253 }
1254
1255 return AsmType::Void();
1256 }
1257
1258 // 6.5.1 BlockStatement
ValidateBlockStatement(Block * block)1259 AsmType* AsmTyper::ValidateBlockStatement(Block* block) {
1260 FlattenedStatements iter(zone_, block->statements());
1261
1262 while (auto* current = iter.Next()) {
1263 RECURSE(ValidateStatement(current));
1264 }
1265
1266 return AsmType::Void();
1267 }
1268
1269 // 6.5.2 ExpressionStatement
ValidateExpressionStatement(ExpressionStatement * expr)1270 AsmType* AsmTyper::ValidateExpressionStatement(ExpressionStatement* expr) {
1271 auto* expression = expr->expression();
1272 if (auto* call = expression->AsCall()) {
1273 RECURSE(ValidateCall(AsmType::Void(), call));
1274 } else {
1275 RECURSE(ValidateExpression(expression));
1276 }
1277
1278 return AsmType::Void();
1279 }
1280
1281 // 6.5.3 EmptyStatement
ValidateEmptyStatement(EmptyStatement * empty)1282 AsmType* AsmTyper::ValidateEmptyStatement(EmptyStatement* empty) {
1283 return AsmType::Void();
1284 }
1285
1286 // 6.5.4 IfStatement
ValidateIfStatement(IfStatement * if_stmt)1287 AsmType* AsmTyper::ValidateIfStatement(IfStatement* if_stmt) {
1288 AsmType* cond_type;
1289 RECURSE(cond_type = ValidateExpression(if_stmt->condition()));
1290 if (!cond_type->IsA(AsmType::Int())) {
1291 FAIL(if_stmt->condition(), "If condition must be type int.");
1292 }
1293 RECURSE(ValidateStatement(if_stmt->then_statement()));
1294 RECURSE(ValidateStatement(if_stmt->else_statement()));
1295 return AsmType::Void();
1296 }
1297
1298 // 6.5.5 ReturnStatement
ValidateReturnStatement(ReturnStatement * ret_stmt)1299 AsmType* AsmTyper::ValidateReturnStatement(ReturnStatement* ret_stmt) {
1300 AsmType* ret_expr_type = AsmType::Void();
1301 if (auto* ret_expr = ret_stmt->expression()) {
1302 RECURSE(ret_expr_type = ValidateExpression(ret_expr));
1303 if (ret_expr_type == AsmType::Void()) {
1304 // *VIOLATION* The parser modifies the source code so that expressionless
1305 // returns will return undefined, so we need to allow that.
1306 if (!ret_expr->IsUndefinedLiteral()) {
1307 FAIL(ret_stmt, "Return statement expression can't be void.");
1308 }
1309 }
1310 }
1311
1312 if (!ret_expr_type->IsA(return_type_)) {
1313 FAIL(ret_stmt, "Type mismatch in return statement.");
1314 }
1315
1316 return ret_expr_type;
1317 }
1318
1319 // 6.5.6 IterationStatement
1320 // 6.5.6.a WhileStatement
ValidateWhileStatement(WhileStatement * while_stmt)1321 AsmType* AsmTyper::ValidateWhileStatement(WhileStatement* while_stmt) {
1322 AsmType* cond_type;
1323 RECURSE(cond_type = ValidateExpression(while_stmt->cond()));
1324 if (!cond_type->IsA(AsmType::Int())) {
1325 FAIL(while_stmt->cond(), "While condition must be type int.");
1326 }
1327
1328 if (auto* body = while_stmt->body()) {
1329 RECURSE(ValidateStatement(body));
1330 }
1331 return AsmType::Void();
1332 }
1333
1334 // 6.5.6.b DoWhileStatement
ValidateDoWhileStatement(DoWhileStatement * do_while)1335 AsmType* AsmTyper::ValidateDoWhileStatement(DoWhileStatement* do_while) {
1336 AsmType* cond_type;
1337 RECURSE(cond_type = ValidateExpression(do_while->cond()));
1338 if (!cond_type->IsA(AsmType::Int())) {
1339 FAIL(do_while->cond(), "Do {} While condition must be type int.");
1340 }
1341
1342 if (auto* body = do_while->body()) {
1343 RECURSE(ValidateStatement(body));
1344 }
1345 return AsmType::Void();
1346 }
1347
1348 // 6.5.6.c ForStatement
ValidateForStatement(ForStatement * for_stmt)1349 AsmType* AsmTyper::ValidateForStatement(ForStatement* for_stmt) {
1350 if (auto* init = for_stmt->init()) {
1351 RECURSE(ValidateStatement(init));
1352 }
1353
1354 if (auto* cond = for_stmt->cond()) {
1355 AsmType* cond_type;
1356 RECURSE(cond_type = ValidateExpression(cond));
1357 if (!cond_type->IsA(AsmType::Int())) {
1358 FAIL(cond, "For condition must be type int.");
1359 }
1360 }
1361
1362 if (auto* next = for_stmt->next()) {
1363 RECURSE(ValidateStatement(next));
1364 }
1365
1366 if (auto* body = for_stmt->body()) {
1367 RECURSE(ValidateStatement(body));
1368 }
1369
1370 return AsmType::Void();
1371 }
1372
1373 // 6.5.7 BreakStatement
ValidateBreakStatement(BreakStatement * brk_stmt)1374 AsmType* AsmTyper::ValidateBreakStatement(BreakStatement* brk_stmt) {
1375 return AsmType::Void();
1376 }
1377
1378 // 6.5.8 ContinueStatement
ValidateContinueStatement(ContinueStatement * cont_stmt)1379 AsmType* AsmTyper::ValidateContinueStatement(ContinueStatement* cont_stmt) {
1380 return AsmType::Void();
1381 }
1382
1383 // 6.5.9 LabelledStatement
1384 // No need to handle these here -- see the AsmTyper's definition.
1385
1386 // 6.5.10 SwitchStatement
ValidateSwitchStatement(SwitchStatement * stmt)1387 AsmType* AsmTyper::ValidateSwitchStatement(SwitchStatement* stmt) {
1388 AsmType* cond_type;
1389 RECURSE(cond_type = ValidateExpression(stmt->tag()));
1390 if (!cond_type->IsA(AsmType::Signed())) {
1391 FAIL(stmt, "Switch tag must be signed.");
1392 }
1393
1394 int default_pos = kNoSourcePosition;
1395 int last_case_pos = kNoSourcePosition;
1396 ZoneSet<int32_t> cases_seen(zone_);
1397 for (auto* a_case : *stmt->cases()) {
1398 if (a_case->is_default()) {
1399 CHECK(default_pos == kNoSourcePosition);
1400 RECURSE(ValidateDefault(a_case));
1401 default_pos = a_case->position();
1402 continue;
1403 }
1404
1405 if (last_case_pos == kNoSourcePosition) {
1406 last_case_pos = a_case->position();
1407 } else {
1408 last_case_pos = std::max(last_case_pos, a_case->position());
1409 }
1410
1411 int32_t case_lbl;
1412 RECURSE(ValidateCase(a_case, &case_lbl));
1413 auto case_lbl_pos = cases_seen.find(case_lbl);
1414 if (case_lbl_pos != cases_seen.end() && *case_lbl_pos == case_lbl) {
1415 FAIL(a_case, "Duplicated case label.");
1416 }
1417 cases_seen.insert(case_lbl);
1418 }
1419
1420 if (!cases_seen.empty()) {
1421 const int64_t max_lbl = *cases_seen.rbegin();
1422 const int64_t min_lbl = *cases_seen.begin();
1423 if (max_lbl - min_lbl > std::numeric_limits<int32_t>::max()) {
1424 FAIL(stmt, "Out-of-bounds case label range.");
1425 }
1426 }
1427
1428 if (last_case_pos != kNoSourcePosition && default_pos != kNoSourcePosition &&
1429 default_pos < last_case_pos) {
1430 FAIL(stmt, "Switch default must appear last.");
1431 }
1432
1433 return AsmType::Void();
1434 }
1435
1436 // 6.6 ValidateCase
1437 namespace {
ExtractInt32CaseLabel(CaseClause * clause,int32_t * lbl)1438 bool ExtractInt32CaseLabel(CaseClause* clause, int32_t* lbl) {
1439 auto* lbl_expr = clause->label()->AsLiteral();
1440
1441 if (lbl_expr == nullptr) {
1442 return false;
1443 }
1444
1445 if (lbl_expr->raw_value()->ContainsDot()) {
1446 return false;
1447 }
1448
1449 return lbl_expr->value()->ToInt32(lbl);
1450 }
1451 } // namespace
1452
ValidateCase(CaseClause * label,int32_t * case_lbl)1453 AsmType* AsmTyper::ValidateCase(CaseClause* label, int32_t* case_lbl) {
1454 if (!ExtractInt32CaseLabel(label, case_lbl)) {
1455 FAIL(label, "Case label must be a 32-bit signed integer.");
1456 }
1457
1458 FlattenedStatements iter(zone_, label->statements());
1459 while (auto* current = iter.Next()) {
1460 RECURSE(ValidateStatement(current));
1461 }
1462 return AsmType::Void();
1463 }
1464
1465 // 6.7 ValidateDefault
ValidateDefault(CaseClause * label)1466 AsmType* AsmTyper::ValidateDefault(CaseClause* label) {
1467 FlattenedStatements iter(zone_, label->statements());
1468 while (auto* current = iter.Next()) {
1469 RECURSE(ValidateStatement(current));
1470 }
1471 return AsmType::Void();
1472 }
1473
1474 // 6.8 ValidateExpression
ValidateExpression(Expression * expr)1475 AsmType* AsmTyper::ValidateExpression(Expression* expr) {
1476 AsmType* expr_ty = AsmType::None();
1477
1478 switch (expr->node_type()) {
1479 default:
1480 FAIL(expr, "Invalid asm.js expression.");
1481 case AstNode::kLiteral:
1482 RECURSE(expr_ty = ValidateNumericLiteral(expr->AsLiteral()));
1483 break;
1484 case AstNode::kVariableProxy:
1485 RECURSE(expr_ty = ValidateIdentifier(expr->AsVariableProxy()));
1486 break;
1487 case AstNode::kCall:
1488 RECURSE(expr_ty = ValidateCallExpression(expr->AsCall()));
1489 break;
1490 case AstNode::kProperty:
1491 RECURSE(expr_ty = ValidateMemberExpression(expr->AsProperty()));
1492 break;
1493 case AstNode::kAssignment:
1494 RECURSE(expr_ty = ValidateAssignmentExpression(expr->AsAssignment()));
1495 break;
1496 case AstNode::kUnaryOperation:
1497 RECURSE(expr_ty = ValidateUnaryExpression(expr->AsUnaryOperation()));
1498 break;
1499 case AstNode::kConditional:
1500 RECURSE(expr_ty = ValidateConditionalExpression(expr->AsConditional()));
1501 break;
1502 case AstNode::kCompareOperation:
1503 RECURSE(expr_ty = ValidateCompareOperation(expr->AsCompareOperation()));
1504 break;
1505 case AstNode::kBinaryOperation:
1506 RECURSE(expr_ty = ValidateBinaryOperation(expr->AsBinaryOperation()));
1507 break;
1508 }
1509
1510 SetTypeOf(expr, expr_ty);
1511 return expr_ty;
1512 }
1513
ValidateCompareOperation(CompareOperation * cmp)1514 AsmType* AsmTyper::ValidateCompareOperation(CompareOperation* cmp) {
1515 switch (cmp->op()) {
1516 default:
1517 FAIL(cmp, "Invalid asm.js comparison operator.");
1518 case Token::LT:
1519 case Token::LTE:
1520 case Token::GT:
1521 case Token::GTE:
1522 return ValidateRelationalExpression(cmp);
1523 case Token::EQ:
1524 case Token::NE:
1525 return ValidateEqualityExpression(cmp);
1526 }
1527
1528 UNREACHABLE();
1529 }
1530
1531 namespace {
IsInvert(BinaryOperation * binop)1532 bool IsInvert(BinaryOperation* binop) {
1533 if (binop->op() != Token::BIT_XOR) {
1534 return false;
1535 }
1536
1537 auto* right_as_literal = binop->right()->AsLiteral();
1538 if (right_as_literal == nullptr) {
1539 return false;
1540 }
1541
1542 return !right_as_literal->raw_value()->ContainsDot() &&
1543 right_as_literal->raw_value()->AsNumber() == -1.0;
1544 }
1545
IsUnaryMinus(BinaryOperation * binop)1546 bool IsUnaryMinus(BinaryOperation* binop) {
1547 // *VIOLATION* The parser replaces uses of -x with x*-1.
1548 if (binop->op() != Token::MUL) {
1549 return false;
1550 }
1551
1552 auto* right_as_literal = binop->right()->AsLiteral();
1553 if (right_as_literal == nullptr) {
1554 return false;
1555 }
1556
1557 return !right_as_literal->raw_value()->ContainsDot() &&
1558 right_as_literal->raw_value()->AsNumber() == -1.0;
1559 }
1560 } // namespace
1561
ValidateBinaryOperation(BinaryOperation * expr)1562 AsmType* AsmTyper::ValidateBinaryOperation(BinaryOperation* expr) {
1563 #define UNOP_OVERLOAD(Src, Dest) \
1564 do { \
1565 if (left_type->IsA(AsmType::Src())) { \
1566 return AsmType::Dest(); \
1567 } \
1568 } while (0)
1569
1570 switch (expr->op()) {
1571 default:
1572 FAIL(expr, "Invalid asm.js binary expression.");
1573 case Token::COMMA:
1574 return ValidateCommaExpression(expr);
1575 case Token::MUL:
1576 if (IsDoubleAnnotation(expr)) {
1577 // *VIOLATION* We can't be 100% sure this really IS a unary + in the asm
1578 // source so we have to be lenient, and treat this as a unary +.
1579 if (auto* Call = expr->left()->AsCall()) {
1580 return ValidateCall(AsmType::Double(), Call);
1581 }
1582 AsmType* left_type;
1583 RECURSE(left_type = ValidateExpression(expr->left()));
1584 SetTypeOf(expr->right(), AsmType::Double());
1585 UNOP_OVERLOAD(Signed, Double);
1586 UNOP_OVERLOAD(Unsigned, Double);
1587 UNOP_OVERLOAD(DoubleQ, Double);
1588 UNOP_OVERLOAD(FloatQ, Double);
1589 FAIL(expr, "Invalid type for conversion to double.");
1590 }
1591
1592 if (IsUnaryMinus(expr)) {
1593 // *VIOLATION* the parser converts -x to x * -1.
1594 AsmType* left_type;
1595 RECURSE(left_type = ValidateExpression(expr->left()));
1596 SetTypeOf(expr->right(), left_type);
1597 UNOP_OVERLOAD(Int, Intish);
1598 UNOP_OVERLOAD(DoubleQ, Double);
1599 UNOP_OVERLOAD(FloatQ, Floatish);
1600 FAIL(expr, "Invalid type for unary -.");
1601 }
1602 // FALTHROUGH
1603 case Token::DIV:
1604 case Token::MOD:
1605 return ValidateMultiplicativeExpression(expr);
1606 case Token::ADD:
1607 case Token::SUB: {
1608 static const uint32_t kInitialIntishCount = 0;
1609 return ValidateAdditiveExpression(expr, kInitialIntishCount);
1610 }
1611 case Token::SAR:
1612 case Token::SHL:
1613 case Token::SHR:
1614 return ValidateShiftExpression(expr);
1615 case Token::BIT_AND:
1616 return ValidateBitwiseANDExpression(expr);
1617 case Token::BIT_XOR:
1618 if (IsInvert(expr)) {
1619 auto* left = expr->left();
1620 auto* left_as_binop = left->AsBinaryOperation();
1621
1622 if (left_as_binop != nullptr && IsInvert(left_as_binop)) {
1623 // This is the special ~~ operator.
1624 AsmType* left_type;
1625 RECURSE(left_type = ValidateExpression(left_as_binop->left()));
1626 SetTypeOf(left_as_binop->right(), AsmType::FixNum());
1627 SetTypeOf(left_as_binop, AsmType::Signed());
1628 SetTypeOf(expr->right(), AsmType::FixNum());
1629 UNOP_OVERLOAD(Double, Signed);
1630 UNOP_OVERLOAD(FloatQ, Signed);
1631 FAIL(left_as_binop, "Invalid type for conversion to signed.");
1632 }
1633
1634 AsmType* left_type;
1635 RECURSE(left_type = ValidateExpression(left));
1636 UNOP_OVERLOAD(Intish, Signed);
1637 FAIL(left, "Invalid type for ~.");
1638 }
1639
1640 return ValidateBitwiseXORExpression(expr);
1641 case Token::BIT_OR:
1642 return ValidateBitwiseORExpression(expr);
1643 }
1644 #undef UNOP_OVERLOAD
1645 UNREACHABLE();
1646 }
1647
1648 // 6.8.1 Expression
ValidateCommaExpression(BinaryOperation * comma)1649 AsmType* AsmTyper::ValidateCommaExpression(BinaryOperation* comma) {
1650 // The AST looks like:
1651 // (expr COMMA (expr COMMA (expr COMMA (... ))))
1652
1653 auto* left = comma->left();
1654 if (auto* left_as_call = left->AsCall()) {
1655 RECURSE(ValidateCall(AsmType::Void(), left_as_call));
1656 } else {
1657 RECURSE(ValidateExpression(left));
1658 }
1659
1660 auto* right = comma->right();
1661 AsmType* right_type = nullptr;
1662 if (auto* right_as_call = right->AsCall()) {
1663 RECURSE(right_type = ValidateFloatCoercion(right_as_call));
1664 if (right_type != AsmType::Float()) {
1665 // right_type == nullptr <-> right_as_call is not a call to fround.
1666 DCHECK(right_type == nullptr);
1667 RECURSE(right_type = ValidateCall(AsmType::Void(), right_as_call));
1668 // Unnanotated function call to something that's not fround must be a call
1669 // to a void function.
1670 DCHECK_EQ(right_type, AsmType::Void());
1671 }
1672 } else {
1673 RECURSE(right_type = ValidateExpression(right));
1674 }
1675
1676 return right_type;
1677 }
1678
1679 // 6.8.2 NumericLiteral
ValidateNumericLiteral(Literal * literal)1680 AsmType* AsmTyper::ValidateNumericLiteral(Literal* literal) {
1681 // *VIOLATION* asm.js does not allow the use of undefined, but our parser
1682 // inserts them, so we have to handle them.
1683 if (literal->IsUndefinedLiteral()) {
1684 return AsmType::Void();
1685 }
1686
1687 if (literal->raw_value()->ContainsDot()) {
1688 return AsmType::Double();
1689 }
1690
1691 // The parser collapses expressions like !0 and !123 to true/false.
1692 // We therefore need to permit these as alternate versions of 0 / 1.
1693 if (literal->raw_value()->IsTrue() || literal->raw_value()->IsFalse()) {
1694 return AsmType::Int();
1695 }
1696
1697 uint32_t value;
1698 if (!literal->value()->ToUint32(&value)) {
1699 int32_t value;
1700 if (!literal->value()->ToInt32(&value)) {
1701 FAIL(literal, "Integer literal is out of range.");
1702 }
1703 // *VIOLATION* Not really a violation, but rather a difference in
1704 // validation. The spec handles -NumericLiteral in ValidateUnaryExpression,
1705 // but V8's AST represents the negative literals as Literals.
1706 return AsmType::Signed();
1707 }
1708
1709 if (value <= LargestFixNum) {
1710 return AsmType::FixNum();
1711 }
1712
1713 return AsmType::Unsigned();
1714 }
1715
1716 // 6.8.3 Identifier
ValidateIdentifier(VariableProxy * proxy)1717 AsmType* AsmTyper::ValidateIdentifier(VariableProxy* proxy) {
1718 auto* proxy_info = Lookup(proxy->var());
1719 if (proxy_info == nullptr) {
1720 FAIL(proxy, "Undeclared identifier.");
1721 }
1722 auto* type = proxy_info->type();
1723 if (type->IsA(AsmType::None()) || type->AsCallableType() != nullptr) {
1724 FAIL(proxy, "Identifier may not be accessed by ordinary expressions.");
1725 }
1726 return type;
1727 }
1728
1729 // 6.8.4 CallExpression
ValidateCallExpression(Call * call)1730 AsmType* AsmTyper::ValidateCallExpression(Call* call) {
1731 AsmType* return_type;
1732 RECURSE(return_type = ValidateFloatCoercion(call));
1733 if (return_type == nullptr) {
1734 FAIL(call, "Unanotated call to a function must be a call to fround.");
1735 }
1736 return return_type;
1737 }
1738
1739 // 6.8.5 MemberExpression
ValidateMemberExpression(Property * prop)1740 AsmType* AsmTyper::ValidateMemberExpression(Property* prop) {
1741 AsmType* return_type;
1742 RECURSE(return_type = ValidateHeapAccess(prop, LoadFromHeap));
1743 return return_type;
1744 }
1745
1746 // 6.8.6 AssignmentExpression
ValidateAssignmentExpression(Assignment * assignment)1747 AsmType* AsmTyper::ValidateAssignmentExpression(Assignment* assignment) {
1748 AsmType* value_type;
1749 RECURSE(value_type = ValidateExpression(assignment->value()));
1750
1751 if (assignment->op() == Token::INIT) {
1752 FAIL(assignment,
1753 "Local variable declaration must be at the top of the function.");
1754 }
1755
1756 if (auto* target_as_proxy = assignment->target()->AsVariableProxy()) {
1757 auto* var = target_as_proxy->var();
1758 auto* target_info = Lookup(var);
1759
1760 if (target_info == nullptr) {
1761 if (var->mode() != TEMPORARY) {
1762 FAIL(target_as_proxy, "Undeclared identifier.");
1763 }
1764 // Temporary variables are special: we add them to the local symbol table
1765 // as we see them, with the exact type of the variable's initializer. This
1766 // means that temporary variables might have nonsensical types (i.e.,
1767 // intish, float?, fixnum, and not just the "canonical" types.)
1768 auto* var_info = new (zone_) VariableInfo(value_type);
1769 var_info->set_mutability(VariableInfo::kLocal);
1770 if (!ValidAsmIdentifier(target_as_proxy->name())) {
1771 FAIL(target_as_proxy,
1772 "Invalid asm.js identifier in temporary variable.");
1773 }
1774
1775 if (!AddLocal(var, var_info)) {
1776 FAIL(assignment, "Failed to add temporary variable to symbol table.");
1777 }
1778 return value_type;
1779 }
1780
1781 if (!target_info->IsMutable()) {
1782 FAIL(assignment, "Can't assign to immutable symbol.");
1783 }
1784
1785 DCHECK_NE(AsmType::None(), target_info->type());
1786 if (!value_type->IsA(target_info->type())) {
1787 FAIL(assignment, "Type mismatch in assignment.");
1788 }
1789
1790 return value_type;
1791 }
1792
1793 if (auto* target_as_property = assignment->target()->AsProperty()) {
1794 AsmType* allowed_store_types;
1795 RECURSE(allowed_store_types =
1796 ValidateHeapAccess(target_as_property, StoreToHeap));
1797
1798 if (!value_type->IsA(allowed_store_types)) {
1799 FAIL(assignment, "Type mismatch in heap assignment.");
1800 }
1801
1802 return value_type;
1803 }
1804
1805 FAIL(assignment, "Invalid asm.js assignment.");
1806 }
1807
1808 // 6.8.7 UnaryExpression
ValidateUnaryExpression(UnaryOperation * unop)1809 AsmType* AsmTyper::ValidateUnaryExpression(UnaryOperation* unop) {
1810 // *VIOLATION* -NumericLiteral is validated in ValidateLiteral.
1811 // *VIOLATION* +UnaryExpression is validated in ValidateBinaryOperation.
1812 // *VIOLATION* ~UnaryOperation is validated in ValidateBinaryOperation.
1813 // *VIOLATION* ~~UnaryOperation is validated in ValidateBinaryOperation.
1814 DCHECK(unop->op() != Token::BIT_NOT);
1815 DCHECK(unop->op() != Token::ADD);
1816 AsmType* exp_type;
1817 RECURSE(exp_type = ValidateExpression(unop->expression()));
1818 #define UNOP_OVERLOAD(Src, Dest) \
1819 do { \
1820 if (exp_type->IsA(AsmType::Src())) { \
1821 return AsmType::Dest(); \
1822 } \
1823 } while (0)
1824
1825 // 8.1 Unary Operators
1826 switch (unop->op()) {
1827 default:
1828 FAIL(unop, "Invalid unary operator.");
1829 case Token::ADD:
1830 // We can't test this because of the +x -> x * 1.0 transformation.
1831 DCHECK(false);
1832 UNOP_OVERLOAD(Signed, Double);
1833 UNOP_OVERLOAD(Unsigned, Double);
1834 UNOP_OVERLOAD(DoubleQ, Double);
1835 UNOP_OVERLOAD(FloatQ, Double);
1836 FAIL(unop, "Invalid type for unary +.");
1837 case Token::SUB:
1838 // We can't test this because of the -x -> x * -1.0 transformation.
1839 DCHECK(false);
1840 UNOP_OVERLOAD(Int, Intish);
1841 UNOP_OVERLOAD(DoubleQ, Double);
1842 UNOP_OVERLOAD(FloatQ, Floatish);
1843 FAIL(unop, "Invalid type for unary -.");
1844 case Token::BIT_NOT:
1845 // We can't test this because of the ~x -> x ^ -1 transformation.
1846 DCHECK(false);
1847 UNOP_OVERLOAD(Intish, Signed);
1848 FAIL(unop, "Invalid type for ~.");
1849 case Token::NOT:
1850 UNOP_OVERLOAD(Int, Int);
1851 FAIL(unop, "Invalid type for !.");
1852 }
1853
1854 #undef UNOP_OVERLOAD
1855
1856 UNREACHABLE();
1857 }
1858
1859 // 6.8.8 MultiplicativeExpression
1860 namespace {
IsIntishLiteralFactor(Expression * expr,int32_t * factor)1861 bool IsIntishLiteralFactor(Expression* expr, int32_t* factor) {
1862 auto* literal = expr->AsLiteral();
1863 if (literal == nullptr) {
1864 return false;
1865 }
1866
1867 if (literal->raw_value()->ContainsDot()) {
1868 return false;
1869 }
1870
1871 if (!literal->value()->ToInt32(factor)) {
1872 return false;
1873 }
1874 static const int32_t kIntishBound = 1 << 20;
1875 return -kIntishBound < *factor && *factor < kIntishBound;
1876 }
1877 } // namespace
1878
ValidateMultiplicativeExpression(BinaryOperation * binop)1879 AsmType* AsmTyper::ValidateMultiplicativeExpression(BinaryOperation* binop) {
1880 DCHECK(!IsDoubleAnnotation(binop));
1881
1882 auto* left = binop->left();
1883 auto* right = binop->right();
1884
1885 bool intish_mul_failed = false;
1886 if (binop->op() == Token::MUL) {
1887 int32_t factor;
1888 if (IsIntishLiteralFactor(left, &factor)) {
1889 AsmType* right_type;
1890 RECURSE(right_type = ValidateExpression(right));
1891 if (right_type->IsA(AsmType::Int())) {
1892 return AsmType::Intish();
1893 }
1894 // Can't fail here, because the rhs might contain a valid intish factor.
1895 //
1896 // The solution is to flag that there was an error, and later on -- when
1897 // both lhs and rhs are evaluated -- complain.
1898 intish_mul_failed = true;
1899 }
1900
1901 if (IsIntishLiteralFactor(right, &factor)) {
1902 AsmType* left_type;
1903 RECURSE(left_type = ValidateExpression(left));
1904 if (left_type->IsA(AsmType::Int())) {
1905 // *VIOLATION* This will also (and correctly) handle -X, when X is an
1906 // integer. Therefore, we don't need to handle this case within the if
1907 // block below.
1908 return AsmType::Intish();
1909 }
1910 intish_mul_failed = true;
1911
1912 if (factor == -1) {
1913 // *VIOLATION* The frontend transforms -x into x * -1 (not -1.0, because
1914 // consistency is overrated.)
1915 if (left_type->IsA(AsmType::DoubleQ())) {
1916 return AsmType::Double();
1917 } else if (left_type->IsA(AsmType::FloatQ())) {
1918 return AsmType::Floatish();
1919 }
1920 }
1921 }
1922 }
1923
1924 if (intish_mul_failed) {
1925 FAIL(binop, "Invalid types for intish * (or unary -).");
1926 }
1927
1928 AsmType* left_type;
1929 AsmType* right_type;
1930 RECURSE(left_type = ValidateExpression(left));
1931 RECURSE(right_type = ValidateExpression(right));
1932
1933 #define BINOP_OVERLOAD(Src0, Src1, Dest) \
1934 do { \
1935 if (left_type->IsA(AsmType::Src0()) && right_type->IsA(AsmType::Src1())) { \
1936 return AsmType::Dest(); \
1937 } \
1938 } while (0)
1939 switch (binop->op()) {
1940 default:
1941 FAIL(binop, "Invalid multiplicative expression.");
1942 case Token::MUL:
1943 BINOP_OVERLOAD(DoubleQ, DoubleQ, Double);
1944 BINOP_OVERLOAD(FloatQ, FloatQ, Floatish);
1945 FAIL(binop, "Invalid operands for *.");
1946 case Token::DIV:
1947 BINOP_OVERLOAD(Signed, Signed, Intish);
1948 BINOP_OVERLOAD(Unsigned, Unsigned, Intish);
1949 BINOP_OVERLOAD(DoubleQ, DoubleQ, Double);
1950 BINOP_OVERLOAD(FloatQ, FloatQ, Floatish);
1951 FAIL(binop, "Invalid operands for /.");
1952 case Token::MOD:
1953 BINOP_OVERLOAD(Signed, Signed, Intish);
1954 BINOP_OVERLOAD(Unsigned, Unsigned, Intish);
1955 BINOP_OVERLOAD(DoubleQ, DoubleQ, Double);
1956 FAIL(binop, "Invalid operands for %.");
1957 }
1958 #undef BINOP_OVERLOAD
1959
1960 UNREACHABLE();
1961 }
1962
1963 // 6.8.9 AdditiveExpression
ValidateAdditiveExpression(BinaryOperation * binop,uint32_t intish_count)1964 AsmType* AsmTyper::ValidateAdditiveExpression(BinaryOperation* binop,
1965 uint32_t intish_count) {
1966 static const uint32_t kMaxIntish = 1 << 20;
1967
1968 auto* left = binop->left();
1969 auto* left_as_binop = left->AsBinaryOperation();
1970 AsmType* left_type;
1971
1972 // TODO(jpp): maybe use an iterative approach instead of the recursion to
1973 // ValidateAdditiveExpression.
1974 if (left_as_binop != nullptr && (left_as_binop->op() == Token::ADD ||
1975 left_as_binop->op() == Token::SUB)) {
1976 RECURSE(left_type =
1977 ValidateAdditiveExpression(left_as_binop, intish_count + 1));
1978 SetTypeOf(left_as_binop, left_type);
1979 } else {
1980 RECURSE(left_type = ValidateExpression(left));
1981 }
1982
1983 auto* right = binop->right();
1984 auto* right_as_binop = right->AsBinaryOperation();
1985 AsmType* right_type;
1986
1987 if (right_as_binop != nullptr && (right_as_binop->op() == Token::ADD ||
1988 right_as_binop->op() == Token::SUB)) {
1989 RECURSE(right_type =
1990 ValidateAdditiveExpression(right_as_binop, intish_count + 1));
1991 SetTypeOf(right_as_binop, right_type);
1992 } else {
1993 RECURSE(right_type = ValidateExpression(right));
1994 }
1995
1996 if (left_type->IsA(AsmType::FloatQ()) && right_type->IsA(AsmType::FloatQ())) {
1997 return AsmType::Floatish();
1998 }
1999
2000 if (left_type->IsA(AsmType::Int()) && right_type->IsA(AsmType::Int())) {
2001 if (intish_count == 0) {
2002 return AsmType::Intish();
2003 }
2004 if (intish_count < kMaxIntish) {
2005 return AsmType::Int();
2006 }
2007 FAIL(binop, "Too many uncoerced integer additive expressions.");
2008 }
2009
2010 if (left_type->IsA(AsmType::Double()) && right_type->IsA(AsmType::Double())) {
2011 return AsmType::Double();
2012 }
2013
2014 if (binop->op() == Token::SUB) {
2015 if (left_type->IsA(AsmType::DoubleQ()) &&
2016 right_type->IsA(AsmType::DoubleQ())) {
2017 return AsmType::Double();
2018 }
2019 }
2020
2021 FAIL(binop, "Invalid operands for additive expression.");
2022 }
2023
2024 // 6.8.10 ShiftExpression
ValidateShiftExpression(BinaryOperation * binop)2025 AsmType* AsmTyper::ValidateShiftExpression(BinaryOperation* binop) {
2026 auto* left = binop->left();
2027 auto* right = binop->right();
2028
2029 AsmType* left_type;
2030 AsmType* right_type;
2031 RECURSE(left_type = ValidateExpression(left));
2032 RECURSE(right_type = ValidateExpression(right));
2033
2034 #define BINOP_OVERLOAD(Src0, Src1, Dest) \
2035 do { \
2036 if (left_type->IsA(AsmType::Src0()) && right_type->IsA(AsmType::Src1())) { \
2037 return AsmType::Dest(); \
2038 } \
2039 } while (0)
2040 switch (binop->op()) {
2041 default:
2042 FAIL(binop, "Invalid shift expression.");
2043 case Token::SHL:
2044 BINOP_OVERLOAD(Intish, Intish, Signed);
2045 FAIL(binop, "Invalid operands for <<.");
2046 case Token::SAR:
2047 BINOP_OVERLOAD(Intish, Intish, Signed);
2048 FAIL(binop, "Invalid operands for >>.");
2049 case Token::SHR:
2050 BINOP_OVERLOAD(Intish, Intish, Unsigned);
2051 FAIL(binop, "Invalid operands for >>>.");
2052 }
2053 #undef BINOP_OVERLOAD
2054
2055 UNREACHABLE();
2056 }
2057
2058 // 6.8.11 RelationalExpression
ValidateRelationalExpression(CompareOperation * cmpop)2059 AsmType* AsmTyper::ValidateRelationalExpression(CompareOperation* cmpop) {
2060 auto* left = cmpop->left();
2061 auto* right = cmpop->right();
2062
2063 AsmType* left_type;
2064 AsmType* right_type;
2065 RECURSE(left_type = ValidateExpression(left));
2066 RECURSE(right_type = ValidateExpression(right));
2067
2068 #define CMPOP_OVERLOAD(Src0, Src1, Dest) \
2069 do { \
2070 if (left_type->IsA(AsmType::Src0()) && right_type->IsA(AsmType::Src1())) { \
2071 return AsmType::Dest(); \
2072 } \
2073 } while (0)
2074 switch (cmpop->op()) {
2075 default:
2076 FAIL(cmpop, "Invalid relational expression.");
2077 case Token::LT:
2078 CMPOP_OVERLOAD(Signed, Signed, Int);
2079 CMPOP_OVERLOAD(Unsigned, Unsigned, Int);
2080 CMPOP_OVERLOAD(Float, Float, Int);
2081 CMPOP_OVERLOAD(Double, Double, Int);
2082 FAIL(cmpop, "Invalid operands for <.");
2083 case Token::GT:
2084 CMPOP_OVERLOAD(Signed, Signed, Int);
2085 CMPOP_OVERLOAD(Unsigned, Unsigned, Int);
2086 CMPOP_OVERLOAD(Float, Float, Int);
2087 CMPOP_OVERLOAD(Double, Double, Int);
2088 FAIL(cmpop, "Invalid operands for >.");
2089 case Token::LTE:
2090 CMPOP_OVERLOAD(Signed, Signed, Int);
2091 CMPOP_OVERLOAD(Unsigned, Unsigned, Int);
2092 CMPOP_OVERLOAD(Float, Float, Int);
2093 CMPOP_OVERLOAD(Double, Double, Int);
2094 FAIL(cmpop, "Invalid operands for <=.");
2095 case Token::GTE:
2096 CMPOP_OVERLOAD(Signed, Signed, Int);
2097 CMPOP_OVERLOAD(Unsigned, Unsigned, Int);
2098 CMPOP_OVERLOAD(Float, Float, Int);
2099 CMPOP_OVERLOAD(Double, Double, Int);
2100 FAIL(cmpop, "Invalid operands for >=.");
2101 }
2102 #undef CMPOP_OVERLOAD
2103
2104 UNREACHABLE();
2105 }
2106
2107 // 6.8.12 EqualityExpression
ValidateEqualityExpression(CompareOperation * cmpop)2108 AsmType* AsmTyper::ValidateEqualityExpression(CompareOperation* cmpop) {
2109 auto* left = cmpop->left();
2110 auto* right = cmpop->right();
2111
2112 AsmType* left_type;
2113 AsmType* right_type;
2114 RECURSE(left_type = ValidateExpression(left));
2115 RECURSE(right_type = ValidateExpression(right));
2116
2117 #define CMPOP_OVERLOAD(Src0, Src1, Dest) \
2118 do { \
2119 if (left_type->IsA(AsmType::Src0()) && right_type->IsA(AsmType::Src1())) { \
2120 return AsmType::Dest(); \
2121 } \
2122 } while (0)
2123 switch (cmpop->op()) {
2124 default:
2125 FAIL(cmpop, "Invalid equality expression.");
2126 case Token::EQ:
2127 CMPOP_OVERLOAD(Signed, Signed, Int);
2128 CMPOP_OVERLOAD(Unsigned, Unsigned, Int);
2129 CMPOP_OVERLOAD(Float, Float, Int);
2130 CMPOP_OVERLOAD(Double, Double, Int);
2131 FAIL(cmpop, "Invalid operands for ==.");
2132 case Token::NE:
2133 CMPOP_OVERLOAD(Signed, Signed, Int);
2134 CMPOP_OVERLOAD(Unsigned, Unsigned, Int);
2135 CMPOP_OVERLOAD(Float, Float, Int);
2136 CMPOP_OVERLOAD(Double, Double, Int);
2137 FAIL(cmpop, "Invalid operands for !=.");
2138 }
2139 #undef CMPOP_OVERLOAD
2140
2141 UNREACHABLE();
2142 }
2143
2144 // 6.8.13 BitwiseANDExpression
ValidateBitwiseANDExpression(BinaryOperation * binop)2145 AsmType* AsmTyper::ValidateBitwiseANDExpression(BinaryOperation* binop) {
2146 auto* left = binop->left();
2147 auto* right = binop->right();
2148
2149 AsmType* left_type;
2150 AsmType* right_type;
2151 RECURSE(left_type = ValidateExpression(left));
2152 RECURSE(right_type = ValidateExpression(right));
2153
2154 if (binop->op() != Token::BIT_AND) {
2155 FAIL(binop, "Invalid & expression.");
2156 }
2157
2158 #define BINOP_OVERLOAD(Src0, Src1, Dest) \
2159 do { \
2160 if (left_type->IsA(AsmType::Src0()) && right_type->IsA(AsmType::Src1())) { \
2161 return AsmType::Dest(); \
2162 } \
2163 } while (0)
2164 BINOP_OVERLOAD(Intish, Intish, Signed);
2165 FAIL(binop, "Invalid operands for &.");
2166 #undef BINOP_OVERLOAD
2167
2168 UNREACHABLE();
2169 }
2170
2171 // 6.8.14 BitwiseXORExpression
ValidateBitwiseXORExpression(BinaryOperation * binop)2172 AsmType* AsmTyper::ValidateBitwiseXORExpression(BinaryOperation* binop) {
2173 auto* left = binop->left();
2174 auto* right = binop->right();
2175
2176 AsmType* left_type;
2177 AsmType* right_type;
2178 RECURSE(left_type = ValidateExpression(left));
2179 RECURSE(right_type = ValidateExpression(right));
2180
2181 if (binop->op() != Token::BIT_XOR) {
2182 FAIL(binop, "Invalid ^ expression.");
2183 }
2184
2185 #define BINOP_OVERLOAD(Src0, Src1, Dest) \
2186 do { \
2187 if (left_type->IsA(AsmType::Src0()) && right_type->IsA(AsmType::Src1())) { \
2188 return AsmType::Dest(); \
2189 } \
2190 } while (0)
2191 BINOP_OVERLOAD(Intish, Intish, Signed);
2192 FAIL(binop, "Invalid operands for ^.");
2193 #undef BINOP_OVERLOAD
2194
2195 UNREACHABLE();
2196 }
2197
2198 // 6.8.15 BitwiseORExpression
ValidateBitwiseORExpression(BinaryOperation * binop)2199 AsmType* AsmTyper::ValidateBitwiseORExpression(BinaryOperation* binop) {
2200 auto* left = binop->left();
2201 if (IsIntAnnotation(binop)) {
2202 if (auto* left_as_call = left->AsCall()) {
2203 AsmType* type;
2204 RECURSE(type = ValidateCall(AsmType::Signed(), left_as_call));
2205 return type;
2206 }
2207
2208 // TODO(jpp): at this point we know that binop is expr|0. We could sinply
2209 //
2210 // RECURSE(t = ValidateExpression(left));
2211 // FAIL_IF(t->IsNotA(Intish));
2212 // return Signed;
2213 }
2214
2215 auto* right = binop->right();
2216 AsmType* left_type;
2217 AsmType* right_type;
2218 RECURSE(left_type = ValidateExpression(left));
2219 RECURSE(right_type = ValidateExpression(right));
2220
2221 if (binop->op() != Token::BIT_OR) {
2222 FAIL(binop, "Invalid | expression.");
2223 }
2224
2225 #define BINOP_OVERLOAD(Src0, Src1, Dest) \
2226 do { \
2227 if (left_type->IsA(AsmType::Src0()) && right_type->IsA(AsmType::Src1())) { \
2228 return AsmType::Dest(); \
2229 } \
2230 } while (0)
2231 BINOP_OVERLOAD(Intish, Intish, Signed);
2232 FAIL(binop, "Invalid operands for |.");
2233 #undef BINOP_OVERLOAD
2234
2235 UNREACHABLE();
2236 }
2237
2238 // 6.8.16 ConditionalExpression
ValidateConditionalExpression(Conditional * cond)2239 AsmType* AsmTyper::ValidateConditionalExpression(Conditional* cond) {
2240 AsmType* cond_type;
2241 RECURSE(cond_type = ValidateExpression(cond->condition()));
2242 if (!cond_type->IsA(AsmType::Int())) {
2243 FAIL(cond, "Ternary operation condition should be int.");
2244 }
2245
2246 AsmType* then_type;
2247 RECURSE(then_type = ValidateExpression(cond->then_expression()));
2248 AsmType* else_type;
2249 RECURSE(else_type = ValidateExpression(cond->else_expression()));
2250
2251 #define SUCCEED_IF_BOTH_ARE(type) \
2252 do { \
2253 if (then_type->IsA(AsmType::type())) { \
2254 if (!else_type->IsA(AsmType::type())) { \
2255 FAIL(cond, "Type mismatch for ternary operation result type."); \
2256 } \
2257 return AsmType::type(); \
2258 } \
2259 } while (0)
2260 SUCCEED_IF_BOTH_ARE(Int);
2261 SUCCEED_IF_BOTH_ARE(Float);
2262 SUCCEED_IF_BOTH_ARE(Double);
2263 #undef SUCCEED_IF_BOTH_ARE
2264
2265 FAIL(cond, "Ternary operator must return int, float, or double.");
2266 }
2267
2268 // 6.9 ValidateCall
2269 namespace {
ExtractIndirectCallMask(Expression * expr,uint32_t * value)2270 bool ExtractIndirectCallMask(Expression* expr, uint32_t* value) {
2271 auto* as_literal = expr->AsLiteral();
2272 if (as_literal == nullptr) {
2273 return false;
2274 }
2275
2276 if (as_literal->raw_value()->ContainsDot()) {
2277 return false;
2278 }
2279
2280 if (!as_literal->value()->ToUint32(value)) {
2281 return false;
2282 }
2283
2284 return base::bits::IsPowerOfTwo32(1 + *value);
2285 }
2286 } // namespace
2287
ValidateCall(AsmType * return_type,Call * call)2288 AsmType* AsmTyper::ValidateCall(AsmType* return_type, Call* call) {
2289 AsmType* float_coercion_type;
2290 RECURSE(float_coercion_type = ValidateFloatCoercion(call));
2291 if (float_coercion_type == AsmType::Float()) {
2292 SetTypeOf(call, AsmType::Float());
2293 return return_type;
2294 }
2295
2296 // TODO(jpp): we should be able to reuse the args vector's storage space.
2297 ZoneVector<AsmType*> args(zone_);
2298 args.reserve(call->arguments()->length());
2299
2300 for (auto* arg : *call->arguments()) {
2301 AsmType* arg_type;
2302 RECURSE(arg_type = ValidateExpression(arg));
2303 args.emplace_back(arg_type);
2304 }
2305
2306 auto* call_expr = call->expression();
2307
2308 // identifier(Expression...)
2309 if (auto* call_var_proxy = call_expr->AsVariableProxy()) {
2310 auto* call_var_info = Lookup(call_var_proxy->var());
2311
2312 if (call_var_info == nullptr) {
2313 // We can't fail here: the validator performs a single pass over the AST,
2314 // so it is possible for some calls to be currently unresolved. We eagerly
2315 // add the function to the table of globals.
2316 auto* call_type = AsmType::Function(zone_, return_type)->AsFunctionType();
2317 for (auto* arg : args) {
2318 call_type->AddArgument(arg->ToParameterType());
2319 }
2320 auto* fun_info =
2321 new (zone_) VariableInfo(reinterpret_cast<AsmType*>(call_type));
2322 fun_info->set_mutability(VariableInfo::kImmutableGlobal);
2323 AddForwardReference(call_var_proxy, fun_info);
2324 if (!ValidAsmIdentifier(call_var_proxy->name())) {
2325 FAIL(call_var_proxy,
2326 "Invalid asm.js identifier in (forward) function name.");
2327 }
2328 if (!AddGlobal(call_var_proxy->var(), fun_info)) {
2329 DCHECK(false);
2330 FAIL(call, "Redeclared global identifier.");
2331 }
2332 SetTypeOf(call_var_proxy, reinterpret_cast<AsmType*>(call_type));
2333 SetTypeOf(call, return_type);
2334 return return_type;
2335 }
2336
2337 auto* callee_type = call_var_info->type()->AsCallableType();
2338 if (callee_type == nullptr) {
2339 FAIL(call, "Calling something that's not a function.");
2340 }
2341
2342 if (callee_type->AsFFIType() != nullptr) {
2343 if (return_type == AsmType::Float()) {
2344 FAIL(call, "Foreign functions can't return float.");
2345 }
2346 // Record FFI use signature, since the asm->wasm translator must know
2347 // all uses up-front.
2348 ffi_use_signatures_.emplace_back(
2349 FFIUseSignature(call_var_proxy->var(), zone_));
2350 FFIUseSignature* sig = &ffi_use_signatures_.back();
2351 sig->return_type_ = return_type;
2352 sig->arg_types_.reserve(args.size());
2353 for (size_t i = 0; i < args.size(); ++i) {
2354 sig->arg_types_.emplace_back(args[i]);
2355 }
2356 }
2357
2358 if (!callee_type->CanBeInvokedWith(return_type, args)) {
2359 FAIL(call, "Function invocation does not match function type.");
2360 }
2361
2362 SetTypeOf(call_var_proxy, call_var_info->type());
2363 SetTypeOf(call, return_type);
2364 return return_type;
2365 }
2366
2367 // identifier[expr & n](Expression...)
2368 if (auto* call_property = call_expr->AsProperty()) {
2369 auto* index = call_property->key()->AsBinaryOperation();
2370 if (index == nullptr || index->op() != Token::BIT_AND) {
2371 FAIL(call_property->key(),
2372 "Indirect call index must be in the expr & mask form.");
2373 }
2374
2375 auto* left = index->left();
2376 auto* right = index->right();
2377 uint32_t mask;
2378 if (!ExtractIndirectCallMask(right, &mask)) {
2379 if (!ExtractIndirectCallMask(left, &mask)) {
2380 FAIL(right, "Invalid indirect call mask.");
2381 } else {
2382 left = right;
2383 }
2384 }
2385 const uint32_t table_length = mask + 1;
2386
2387 AsmType* left_type;
2388 RECURSE(left_type = ValidateExpression(left));
2389 if (!left_type->IsA(AsmType::Intish())) {
2390 FAIL(left, "Indirect call index should be an intish.");
2391 }
2392
2393 auto* name_var = call_property->obj()->AsVariableProxy();
2394
2395 if (name_var == nullptr) {
2396 FAIL(call_property, "Invalid call.");
2397 }
2398
2399 auto* name_info = Lookup(name_var->var());
2400 if (name_info == nullptr) {
2401 // We can't fail here -- just like above.
2402 auto* call_type = AsmType::Function(zone_, return_type)->AsFunctionType();
2403 for (auto* arg : args) {
2404 call_type->AddArgument(arg->ToParameterType());
2405 }
2406 auto* table_type = AsmType::FunctionTableType(
2407 zone_, table_length, reinterpret_cast<AsmType*>(call_type));
2408 auto* fun_info =
2409 new (zone_) VariableInfo(reinterpret_cast<AsmType*>(table_type));
2410 fun_info->set_mutability(VariableInfo::kImmutableGlobal);
2411 AddForwardReference(name_var, fun_info);
2412 if (!ValidAsmIdentifier(name_var->name())) {
2413 FAIL(name_var,
2414 "Invalid asm.js identifier in (forward) function table name.");
2415 }
2416 if (!AddGlobal(name_var->var(), fun_info)) {
2417 DCHECK(false);
2418 FAIL(call, "Redeclared global identifier.");
2419 }
2420 SetTypeOf(call_property, reinterpret_cast<AsmType*>(call_type));
2421 SetTypeOf(call, return_type);
2422 return return_type;
2423 }
2424
2425 auto* previous_type = name_info->type()->AsFunctionTableType();
2426 if (previous_type == nullptr) {
2427 FAIL(call, "Identifier does not name a function table.");
2428 }
2429
2430 if (table_length != previous_type->length()) {
2431 FAIL(call, "Function table size does not match expected size.");
2432 }
2433
2434 auto* previous_type_signature =
2435 previous_type->signature()->AsFunctionType();
2436 DCHECK(previous_type_signature != nullptr);
2437 if (!previous_type_signature->CanBeInvokedWith(return_type, args)) {
2438 // TODO(jpp): better error messages.
2439 FAIL(call,
2440 "Function pointer table signature does not match previous "
2441 "signature.");
2442 }
2443
2444 SetTypeOf(call_property, previous_type->signature());
2445 SetTypeOf(call, return_type);
2446 return return_type;
2447 }
2448
2449 FAIL(call, "Invalid call.");
2450 }
2451
2452 // 6.10 ValidateHeapAccess
2453 namespace {
ExtractHeapAccessShift(Expression * expr,uint32_t * value)2454 bool ExtractHeapAccessShift(Expression* expr, uint32_t* value) {
2455 auto* as_literal = expr->AsLiteral();
2456 if (as_literal == nullptr) {
2457 return false;
2458 }
2459
2460 if (as_literal->raw_value()->ContainsDot()) {
2461 return false;
2462 }
2463
2464 return as_literal->value()->ToUint32(value);
2465 }
2466
2467 // Returns whether index is too large to access a heap with the given type.
LiteralIndexOutOfBounds(AsmType * obj_type,uint32_t index)2468 bool LiteralIndexOutOfBounds(AsmType* obj_type, uint32_t index) {
2469 switch (obj_type->ElementSizeInBytes()) {
2470 case 1:
2471 return false;
2472 case 2:
2473 return (index & 0x80000000u) != 0;
2474 case 4:
2475 return (index & 0xC0000000u) != 0;
2476 case 8:
2477 return (index & 0xE0000000u) != 0;
2478 }
2479 UNREACHABLE();
2480 return true;
2481 }
2482
2483 } // namespace
2484
ValidateHeapAccess(Property * heap,HeapAccessType access_type)2485 AsmType* AsmTyper::ValidateHeapAccess(Property* heap,
2486 HeapAccessType access_type) {
2487 auto* obj = heap->obj()->AsVariableProxy();
2488 if (obj == nullptr) {
2489 FAIL(heap, "Invalid heap access.");
2490 }
2491
2492 auto* obj_info = Lookup(obj->var());
2493 if (obj_info == nullptr) {
2494 FAIL(heap, "Undeclared identifier in heap access.");
2495 }
2496
2497 auto* obj_type = obj_info->type();
2498 if (!obj_type->IsA(AsmType::Heap())) {
2499 FAIL(heap, "Identifier does not represent a heap view.");
2500 }
2501 SetTypeOf(obj, obj_type);
2502
2503 if (auto* key_as_literal = heap->key()->AsLiteral()) {
2504 if (key_as_literal->raw_value()->ContainsDot()) {
2505 FAIL(key_as_literal, "Heap access index must be int.");
2506 }
2507
2508 uint32_t index;
2509 if (!key_as_literal->value()->ToUint32(&index)) {
2510 FAIL(key_as_literal,
2511 "Heap access index must be a 32-bit unsigned integer.");
2512 }
2513
2514 if (LiteralIndexOutOfBounds(obj_type, index)) {
2515 FAIL(key_as_literal, "Heap access index is out of bounds");
2516 }
2517
2518 if (access_type == LoadFromHeap) {
2519 return obj_type->LoadType();
2520 }
2521 return obj_type->StoreType();
2522 }
2523
2524 if (auto* key_as_binop = heap->key()->AsBinaryOperation()) {
2525 uint32_t shift;
2526 if (key_as_binop->op() == Token::SAR &&
2527 ExtractHeapAccessShift(key_as_binop->right(), &shift) &&
2528 (1 << shift) == obj_type->ElementSizeInBytes()) {
2529 AsmType* type;
2530 RECURSE(type = ValidateExpression(key_as_binop->left()));
2531 if (type->IsA(AsmType::Intish())) {
2532 if (access_type == LoadFromHeap) {
2533 return obj_type->LoadType();
2534 }
2535 return obj_type->StoreType();
2536 }
2537 FAIL(key_as_binop, "Invalid heap access index.");
2538 }
2539 }
2540
2541 if (obj_type->ElementSizeInBytes() == 1) {
2542 // Leniency: if this is a byte array, we don't require the shift operation
2543 // to be present.
2544 AsmType* index_type;
2545 RECURSE(index_type = ValidateExpression(heap->key()));
2546 if (!index_type->IsA(AsmType::Int())) {
2547 FAIL(heap, "Invalid heap access index for byte array.");
2548 }
2549 if (access_type == LoadFromHeap) {
2550 return obj_type->LoadType();
2551 }
2552 return obj_type->StoreType();
2553 }
2554
2555 FAIL(heap, "Invalid heap access index.");
2556 }
2557
2558 // 6.11 ValidateFloatCoercion
IsCallToFround(Call * call)2559 bool AsmTyper::IsCallToFround(Call* call) {
2560 if (call->arguments()->length() != 1) {
2561 return false;
2562 }
2563
2564 auto* call_var_proxy = call->expression()->AsVariableProxy();
2565 if (call_var_proxy == nullptr) {
2566 return false;
2567 }
2568
2569 auto* call_var_info = Lookup(call_var_proxy->var());
2570 if (call_var_info == nullptr) {
2571 return false;
2572 }
2573
2574 return call_var_info->standard_member() == kMathFround;
2575 }
2576
ValidateFloatCoercion(Call * call)2577 AsmType* AsmTyper::ValidateFloatCoercion(Call* call) {
2578 if (!IsCallToFround(call)) {
2579 return nullptr;
2580 }
2581
2582 auto* arg = call->arguments()->at(0);
2583 // call is a fround() node. From now, there can be two possible outcomes:
2584 // 1. fround is used as a return type annotation.
2585 if (auto* arg_as_call = arg->AsCall()) {
2586 RECURSE(ValidateCall(AsmType::Float(), arg_as_call));
2587 return AsmType::Float();
2588 }
2589
2590 // 2. fround is used for converting to float.
2591 AsmType* arg_type;
2592 RECURSE(arg_type = ValidateExpression(arg));
2593 if (arg_type->IsA(AsmType::Floatish()) || arg_type->IsA(AsmType::DoubleQ()) ||
2594 arg_type->IsA(AsmType::Signed()) || arg_type->IsA(AsmType::Unsigned())) {
2595 SetTypeOf(call->expression(), fround_type_);
2596 return AsmType::Float();
2597 }
2598
2599 FAIL(call, "Invalid argument type to fround.");
2600 }
2601
2602 // 5.1 ParameterTypeAnnotations
ParameterTypeAnnotations(Variable * parameter,Expression * annotation)2603 AsmType* AsmTyper::ParameterTypeAnnotations(Variable* parameter,
2604 Expression* annotation) {
2605 if (auto* binop = annotation->AsBinaryOperation()) {
2606 // Must be:
2607 // * x|0
2608 // * x*1 (*VIOLATION* i.e.,, +x)
2609 auto* left = binop->left()->AsVariableProxy();
2610 if (left == nullptr) {
2611 FAIL(
2612 binop->left(),
2613 "Invalid parameter type annotation - should annotate an identifier.");
2614 }
2615 if (left->var() != parameter) {
2616 FAIL(binop->left(),
2617 "Invalid parameter type annotation - should annotate a parameter.");
2618 }
2619 if (IsDoubleAnnotation(binop)) {
2620 SetTypeOf(left, AsmType::Double());
2621 return AsmType::Double();
2622 }
2623 if (IsIntAnnotation(binop)) {
2624 SetTypeOf(left, AsmType::Int());
2625 return AsmType::Int();
2626 }
2627 FAIL(binop, "Invalid parameter type annotation.");
2628 }
2629
2630 auto* call = annotation->AsCall();
2631 if (call == nullptr) {
2632 FAIL(
2633 annotation,
2634 "Invalid float parameter type annotation - must be fround(parameter).");
2635 }
2636
2637 if (!IsCallToFround(call)) {
2638 FAIL(annotation,
2639 "Invalid float parameter type annotation - must be call to fround.");
2640 }
2641
2642 auto* src_expr = call->arguments()->at(0)->AsVariableProxy();
2643 if (src_expr == nullptr) {
2644 FAIL(annotation,
2645 "Invalid float parameter type annotation - argument to fround is not "
2646 "an identifier.");
2647 }
2648
2649 if (src_expr->var() != parameter) {
2650 FAIL(annotation,
2651 "Invalid float parameter type annotation - argument to fround is not "
2652 "a parameter.");
2653 }
2654
2655 SetTypeOf(src_expr, AsmType::Float());
2656 return AsmType::Float();
2657 }
2658
2659 // 5.2 ReturnTypeAnnotations
ReturnTypeAnnotations(ReturnStatement * statement)2660 AsmType* AsmTyper::ReturnTypeAnnotations(ReturnStatement* statement) {
2661 if (statement == nullptr) {
2662 return AsmType::Void();
2663 }
2664
2665 auto* ret_expr = statement->expression();
2666 if (ret_expr == nullptr) {
2667 return AsmType::Void();
2668 }
2669
2670 if (auto* binop = ret_expr->AsBinaryOperation()) {
2671 if (IsDoubleAnnotation(binop)) {
2672 return AsmType::Double();
2673 } else if (IsIntAnnotation(binop)) {
2674 return AsmType::Signed();
2675 }
2676 FAIL(statement, "Invalid return type annotation.");
2677 }
2678
2679 if (auto* call = ret_expr->AsCall()) {
2680 if (IsCallToFround(call)) {
2681 return AsmType::Float();
2682 }
2683 FAIL(statement, "Invalid function call in return statement.");
2684 }
2685
2686 if (auto* literal = ret_expr->AsLiteral()) {
2687 int32_t _;
2688 if (literal->raw_value()->ContainsDot()) {
2689 return AsmType::Double();
2690 } else if (literal->value()->ToInt32(&_)) {
2691 return AsmType::Signed();
2692 } else if (literal->IsUndefinedLiteral()) {
2693 // *VIOLATION* The parser changes
2694 //
2695 // return;
2696 //
2697 // into
2698 //
2699 // return undefined
2700 return AsmType::Void();
2701 }
2702 FAIL(statement, "Invalid literal in return statement.");
2703 }
2704
2705 if (auto* proxy = ret_expr->AsVariableProxy()) {
2706 auto* var_info = Lookup(proxy->var());
2707
2708 if (var_info == nullptr) {
2709 FAIL(statement, "Undeclared identifier in return statement.");
2710 }
2711
2712 if (var_info->mutability() != VariableInfo::kConstGlobal) {
2713 FAIL(statement, "Identifier in return statement is not const.");
2714 }
2715
2716 if (!var_info->type()->IsReturnType()) {
2717 FAIL(statement, "Constant in return must be signed, float, or double.");
2718 }
2719
2720 return var_info->type();
2721 }
2722
2723 FAIL(statement, "Invalid return type expression.");
2724 }
2725
2726 // 5.4 VariableTypeAnnotations
2727 // Also used for 5.5 GlobalVariableTypeAnnotations
VariableTypeAnnotations(Expression * initializer,VariableInfo::Mutability mutability_type)2728 AsmType* AsmTyper::VariableTypeAnnotations(
2729 Expression* initializer, VariableInfo::Mutability mutability_type) {
2730 if (auto* literal = initializer->AsLiteral()) {
2731 if (literal->raw_value()->ContainsDot()) {
2732 SetTypeOf(initializer, AsmType::Double());
2733 return AsmType::Double();
2734 }
2735 int32_t i32;
2736 uint32_t u32;
2737
2738 AsmType* initializer_type = nullptr;
2739 if (literal->value()->ToUint32(&u32)) {
2740 if (u32 > LargestFixNum) {
2741 initializer_type = AsmType::Unsigned();
2742 SetTypeOf(initializer, initializer_type);
2743 } else {
2744 initializer_type = AsmType::FixNum();
2745 SetTypeOf(initializer, initializer_type);
2746 initializer_type = AsmType::Signed();
2747 }
2748 } else if (literal->value()->ToInt32(&i32)) {
2749 initializer_type = AsmType::Signed();
2750 SetTypeOf(initializer, initializer_type);
2751 } else {
2752 FAIL(initializer, "Invalid type annotation - forbidden literal.");
2753 }
2754 if (mutability_type != VariableInfo::kConstGlobal) {
2755 return AsmType::Int();
2756 }
2757 return initializer_type;
2758 }
2759
2760 if (auto* proxy = initializer->AsVariableProxy()) {
2761 auto* var_info = Lookup(proxy->var());
2762
2763 if (var_info == nullptr) {
2764 FAIL(initializer,
2765 "Undeclared identifier in variable declaration initializer.");
2766 }
2767
2768 if (var_info->mutability() != VariableInfo::kConstGlobal) {
2769 FAIL(initializer,
2770 "Identifier in variable declaration initializer must be const.");
2771 }
2772
2773 SetTypeOf(initializer, var_info->type());
2774 return var_info->type();
2775 }
2776
2777 auto* call = initializer->AsCall();
2778 if (call == nullptr) {
2779 FAIL(initializer,
2780 "Invalid variable initialization - it should be a literal, const, or "
2781 "fround(literal).");
2782 }
2783
2784 if (!IsCallToFround(call)) {
2785 FAIL(initializer,
2786 "Invalid float coercion - expected call fround(literal).");
2787 }
2788
2789 auto* src_expr = call->arguments()->at(0)->AsLiteral();
2790 if (src_expr == nullptr) {
2791 FAIL(initializer,
2792 "Invalid float type annotation - expected literal argument for call "
2793 "to fround.");
2794 }
2795
2796 // Float constants must contain dots in local, but not in globals.
2797 if (mutability_type == VariableInfo::kLocal) {
2798 if (!src_expr->raw_value()->ContainsDot()) {
2799 FAIL(initializer,
2800 "Invalid float type annotation - expected literal argument to be a "
2801 "floating point literal.");
2802 }
2803 }
2804
2805 return AsmType::Float();
2806 }
2807
2808 // 5.5 GlobalVariableTypeAnnotations
NewHeapView(CallNew * new_heap_view)2809 AsmType* AsmTyper::NewHeapView(CallNew* new_heap_view) {
2810 auto* heap_type = new_heap_view->expression()->AsProperty();
2811 if (heap_type == nullptr) {
2812 FAIL(new_heap_view, "Invalid type after new.");
2813 }
2814 auto* heap_view_info = ImportLookup(heap_type);
2815
2816 if (heap_view_info == nullptr) {
2817 FAIL(new_heap_view, "Unknown stdlib member in heap view declaration.");
2818 }
2819
2820 if (!heap_view_info->type()->IsA(AsmType::Heap())) {
2821 FAIL(new_heap_view, "Type is not a heap view type.");
2822 }
2823
2824 if (new_heap_view->arguments()->length() != 1) {
2825 FAIL(new_heap_view, "Invalid number of arguments when creating heap view.");
2826 }
2827
2828 auto* heap = new_heap_view->arguments()->at(0);
2829 auto* heap_var_proxy = heap->AsVariableProxy();
2830
2831 if (heap_var_proxy == nullptr) {
2832 FAIL(heap,
2833 "Heap view creation parameter should be the module's heap parameter.");
2834 }
2835
2836 auto* heap_var_info = Lookup(heap_var_proxy->var());
2837
2838 if (heap_var_info == nullptr) {
2839 FAIL(heap, "Undeclared identifier instead of heap parameter.");
2840 }
2841
2842 if (!heap_var_info->IsHeap()) {
2843 FAIL(heap,
2844 "Heap view creation parameter should be the module's heap parameter.");
2845 }
2846
2847 DCHECK(heap_view_info->type()->IsA(AsmType::Heap()));
2848 return heap_view_info->type();
2849 }
2850
IsValidAsm(Isolate * isolate,Zone * zone,Script * script,FunctionLiteral * root,std::string * error_message)2851 bool IsValidAsm(Isolate* isolate, Zone* zone, Script* script,
2852 FunctionLiteral* root, std::string* error_message) {
2853 error_message->clear();
2854
2855 AsmTyper typer(isolate, zone, script, root);
2856 if (typer.Validate()) {
2857 return true;
2858 }
2859
2860 *error_message = typer.error_message();
2861 return false;
2862 }
2863
2864 } // namespace wasm
2865 } // namespace internal
2866 } // namespace v8
2867