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