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

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

Issue 2398023002: [wasm] asm.js - Parse and convert asm.js to wasm a function at a time. (Closed)
Patch Set: Created 4 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright 2016 the V8 project authors. All rights reserved. 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "src/asmjs/asm-typer.h" 5 #include "src/asmjs/asm-typer.h"
6 6
7 #include <algorithm> 7 #include <algorithm>
8 #include <limits> 8 #include <limits>
9 #include <memory> 9 #include <memory>
10 #include <string> 10 #include <string>
(...skipping 73 matching lines...) Expand 10 before | Expand all | Expand 10 after
84 context_stack_.emplace_back( 84 context_stack_.emplace_back(
85 Context(current_statement->AsBlock()->statements())); 85 Context(current_statement->AsBlock()->statements()));
86 continue; 86 continue;
87 } 87 }
88 88
89 return current_statement; 89 return current_statement;
90 } 90 }
91 } 91 }
92 92
93 // ---------------------------------------------------------------------------- 93 // ----------------------------------------------------------------------------
94 // Implementation of AsmTyper::SourceLayoutTracker
95
96 bool AsmTyper::SourceLayoutTracker::IsValid() const {
97 const Section* kAllSections[] = {&use_asm_, &globals_, &functions_, &tables_,
98 &exports_};
99 for (size_t ii = 0; ii < arraysize(kAllSections); ++ii) {
100 const auto& curr_section = *kAllSections[ii];
101 for (size_t jj = ii + 1; jj < arraysize(kAllSections); ++jj) {
102 if (curr_section.OverlapsWith(*kAllSections[jj])) {
103 return false;
104 }
105 }
106 }
107 return true;
108 }
109
110 void AsmTyper::SourceLayoutTracker::Section::AddNewElement(
111 const AstNode& node) {
112 const int node_pos = node.position();
113 if (start_ == kNoSourcePosition) {
114 start_ = node_pos;
115 } else {
116 start_ = std::max(start_, node_pos);
117 }
118 if (end_ == kNoSourcePosition) {
119 end_ = node_pos;
120 } else {
121 end_ = std::max(end_, node_pos);
122 }
123 }
124
125 bool AsmTyper::SourceLayoutTracker::Section::OverlapsWith(
126 const Section& other) const {
127 if (start_ == kNoSourcePosition) {
128 DCHECK_EQ(end_, kNoSourcePosition);
129 return false;
130 }
131 if (other.start_ == kNoSourcePosition) {
132 DCHECK_EQ(other.end_, kNoSourcePosition);
133 return false;
134 }
135 return other.start_ < end_ || other.end_ < start_;
136 }
137
138 // ----------------------------------------------------------------------------
94 // Implementation of AsmTyper::VariableInfo 139 // Implementation of AsmTyper::VariableInfo
95 140
96 AsmTyper::VariableInfo* AsmTyper::VariableInfo::ForSpecialSymbol( 141 AsmTyper::VariableInfo* AsmTyper::VariableInfo::ForSpecialSymbol(
97 Zone* zone, StandardMember standard_member) { 142 Zone* zone, StandardMember standard_member) {
98 DCHECK(standard_member == kStdlib || standard_member == kFFI || 143 DCHECK(standard_member == kStdlib || standard_member == kFFI ||
99 standard_member == kHeap || standard_member == kModule); 144 standard_member == kHeap || standard_member == kModule);
100 auto* new_var_info = new (zone) VariableInfo(AsmType::None()); 145 auto* new_var_info = new (zone) VariableInfo(AsmType::None());
101 new_var_info->standard_member_ = standard_member; 146 new_var_info->standard_member_ = standard_member;
102 new_var_info->mutability_ = kImmutableGlobal; 147 new_var_info->mutability_ = kImmutableGlobal;
103 return new_var_info; 148 return new_var_info;
(...skipping 28 matching lines...) Expand all
132 stdlib_types_(zone), 177 stdlib_types_(zone),
133 stdlib_math_types_(zone), 178 stdlib_math_types_(zone),
134 module_info_(VariableInfo::ForSpecialSymbol(zone_, kModule)), 179 module_info_(VariableInfo::ForSpecialSymbol(zone_, kModule)),
135 global_scope_(ZoneHashMap::kDefaultHashMapCapacity, 180 global_scope_(ZoneHashMap::kDefaultHashMapCapacity,
136 ZoneAllocationPolicy(zone)), 181 ZoneAllocationPolicy(zone)),
137 local_scope_(ZoneHashMap::kDefaultHashMapCapacity, 182 local_scope_(ZoneHashMap::kDefaultHashMapCapacity,
138 ZoneAllocationPolicy(zone)), 183 ZoneAllocationPolicy(zone)),
139 stack_limit_(isolate->stack_guard()->real_climit()), 184 stack_limit_(isolate->stack_guard()->real_climit()),
140 node_types_(zone_), 185 node_types_(zone_),
141 fround_type_(AsmType::FroundType(zone_)), 186 fround_type_(AsmType::FroundType(zone_)),
142 ffi_type_(AsmType::FFIType(zone_)) { 187 ffi_type_(AsmType::FFIType(zone_)),
188 function_pointer_tables_(zone_) {
143 InitializeStdlib(); 189 InitializeStdlib();
144 } 190 }
145 191
146 namespace { 192 namespace {
147 bool ValidAsmIdentifier(Handle<String> name) { 193 bool ValidAsmIdentifier(Handle<String> name) {
148 static const char* kInvalidAsmNames[] = {"eval", "arguments"}; 194 static const char* kInvalidAsmNames[] = {"eval", "arguments"};
149 195
150 for (size_t ii = 0; ii < arraysize(kInvalidAsmNames); ++ii) { 196 for (size_t ii = 0; ii < arraysize(kInvalidAsmNames); ++ii) {
151 if (strcmp(name->ToCString().get(), kInvalidAsmNames[ii]) == 0) { 197 if (strcmp(name->ToCString().get(), kInvalidAsmNames[ii]) == 0) {
152 return false; 198 return false;
(...skipping 274 matching lines...) Expand 10 before | Expand all | Expand 10 after
427 AsmTyper::StandardMember AsmTyper::VariableAsStandardMember(Variable* var) { 473 AsmTyper::StandardMember AsmTyper::VariableAsStandardMember(Variable* var) {
428 auto* var_info = Lookup(var); 474 auto* var_info = Lookup(var);
429 if (var_info == nullptr) { 475 if (var_info == nullptr) {
430 return kNone; 476 return kNone;
431 } 477 }
432 StandardMember member = var_info->standard_member(); 478 StandardMember member = var_info->standard_member();
433 return member; 479 return member;
434 } 480 }
435 481
436 bool AsmTyper::Validate() { 482 bool AsmTyper::Validate() {
437 if (!AsmType::None()->IsExactly(ValidateModule(root_))) { 483 return ValidatePhase1of2() &&
484 !AsmType::None()->IsExactly(ValidateModuleFunctions(root_)) &&
485 ValidatePhase2of2();
486 }
487
488 bool AsmTyper::ValidatePhase1of2() {
489 if (!AsmType::None()->IsExactly(ValidateModulePhase1of2(root_))) {
438 return true; 490 return true;
439 } 491 }
440 return false; 492 return false;
493 }
494
495 bool AsmTyper::ValidateInnerFunction(FunctionDeclaration* fun_decl) {
496 if (!AsmType::None()->IsExactly(ValidateModuleFunction(fun_decl))) {
497 return true;
498 }
499 return false;
500 }
501
502 bool AsmTyper::ValidatePhase2of2() {
503 if (!AsmType::None()->IsExactly(ValidateModulePhase2of2(root_))) {
504 return true;
505 }
506 return false;
441 } 507 }
442 508
443 namespace { 509 namespace {
444 bool IsUseAsmDirective(Statement* first_statement) { 510 bool IsUseAsmDirective(Statement* first_statement) {
445 ExpressionStatement* use_asm = first_statement->AsExpressionStatement(); 511 ExpressionStatement* use_asm = first_statement->AsExpressionStatement();
446 if (use_asm == nullptr) { 512 if (use_asm == nullptr) {
447 return false; 513 return false;
448 } 514 }
449 515
450 Literal* use_asm_literal = use_asm->expression()->AsLiteral(); 516 Literal* use_asm_literal = use_asm->expression()->AsLiteral();
(...skipping 19 matching lines...) Expand all
470 if (assign->op() != Token::INIT) { 536 if (assign->op() != Token::INIT) {
471 // Done with initializers. 537 // Done with initializers.
472 return nullptr; 538 return nullptr;
473 } 539 }
474 return assign; 540 return assign;
475 } 541 }
476 542
477 } // namespace 543 } // namespace
478 544
479 // 6.1 ValidateModule 545 // 6.1 ValidateModule
480 namespace { 546 AsmType* AsmTyper::ValidateModulePhase1of2(FunctionLiteral* fun) {
481 // SourceLayoutTracker keeps track of the start and end positions of each
482 // section in the asm.js source. The sections should not overlap, otherwise the
483 // asm.js source is invalid.
484 class SourceLayoutTracker {
485 public:
486 SourceLayoutTracker() = default;
487
488 bool IsValid() const {
489 const Section* kAllSections[] = {&use_asm_, &globals_, &functions_,
490 &tables_, &exports_};
491 for (size_t ii = 0; ii < arraysize(kAllSections); ++ii) {
492 const auto& curr_section = *kAllSections[ii];
493 for (size_t jj = ii + 1; jj < arraysize(kAllSections); ++jj) {
494 if (curr_section.OverlapsWith(*kAllSections[jj])) {
495 return false;
496 }
497 }
498 }
499 return true;
500 }
501
502 void AddUseAsm(const AstNode& node) { use_asm_.AddNewElement(node); }
503
504 void AddGlobal(const AstNode& node) { globals_.AddNewElement(node); }
505
506 void AddFunction(const AstNode& node) { functions_.AddNewElement(node); }
507
508 void AddTable(const AstNode& node) { tables_.AddNewElement(node); }
509
510 void AddExport(const AstNode& node) { exports_.AddNewElement(node); }
511
512 private:
513 class Section {
514 public:
515 Section() = default;
516 Section(const Section&) = default;
517 Section& operator=(const Section&) = default;
518
519 void AddNewElement(const AstNode& node) {
520 const int node_pos = node.position();
521 if (start_ == kNoSourcePosition) {
522 start_ = node_pos;
523 } else {
524 start_ = std::max(start_, node_pos);
525 }
526 if (end_ == kNoSourcePosition) {
527 end_ = node_pos;
528 } else {
529 end_ = std::max(end_, node_pos);
530 }
531 }
532
533 bool OverlapsWith(const Section& other) const {
534 if (start_ == kNoSourcePosition) {
535 DCHECK_EQ(end_, kNoSourcePosition);
536 return false;
537 }
538 if (other.start_ == kNoSourcePosition) {
539 DCHECK_EQ(other.end_, kNoSourcePosition);
540 return false;
541 }
542 return other.start_ < end_ || other.end_ < start_;
543 }
544
545 private:
546 int start_ = kNoSourcePosition;
547 int end_ = kNoSourcePosition;
548 };
549
550 Section use_asm_;
551 Section globals_;
552 Section functions_;
553 Section tables_;
554 Section exports_;
555
556 DISALLOW_COPY_AND_ASSIGN(SourceLayoutTracker);
557 };
558 } // namespace
559
560 AsmType* AsmTyper::ValidateModule(FunctionLiteral* fun) {
561 SourceLayoutTracker source_layout;
562
563 DeclarationScope* scope = fun->scope(); 547 DeclarationScope* scope = fun->scope();
564 if (!scope->is_function_scope()) FAIL(fun, "Not at function scope."); 548 if (!scope->is_function_scope()) FAIL(fun, "Not at function scope.");
565 if (!ValidAsmIdentifier(fun->name())) 549 if (!ValidAsmIdentifier(fun->name()))
566 FAIL(fun, "Invalid asm.js identifier in module name."); 550 FAIL(fun, "Invalid asm.js identifier in module name.");
567 module_name_ = fun->name(); 551 module_name_ = fun->name();
568 552
569 // Allowed parameters: Stdlib, FFI, Mem 553 // Allowed parameters: Stdlib, FFI, Mem
570 static const uint32_t MaxModuleParameters = 3; 554 static const uint32_t MaxModuleParameters = 3;
571 if (scope->num_parameters() > MaxModuleParameters) { 555 if (scope->num_parameters() > MaxModuleParameters) {
572 FAIL(fun, "asm.js modules may not have more than three parameters."); 556 FAIL(fun, "asm.js modules may not have more than three parameters.");
(...skipping 14 matching lines...) Expand all
587 } 571 }
588 572
589 auto* param_info = VariableInfo::ForSpecialSymbol( 573 auto* param_info = VariableInfo::ForSpecialSymbol(
590 zone_, kModuleParamInfo[ii].standard_member); 574 zone_, kModuleParamInfo[ii].standard_member);
591 575
592 if (!AddGlobal(param, param_info)) { 576 if (!AddGlobal(param, param_info)) {
593 FAIL(fun, "Redeclared identifier in module parameter."); 577 FAIL(fun, "Redeclared identifier in module parameter.");
594 } 578 }
595 } 579 }
596 580
597 ZoneVector<Assignment*> function_pointer_tables(zone_);
598 FlattenedStatements iter(zone_, fun->body()); 581 FlattenedStatements iter(zone_, fun->body());
599 auto* use_asm_directive = iter.Next(); 582 auto* use_asm_directive = iter.Next();
600 if (use_asm_directive == nullptr) { 583 if (use_asm_directive == nullptr) {
601 FAIL(fun, "Missing \"use asm\"."); 584 FAIL(fun, "Missing \"use asm\".");
602 } 585 }
603 // Check for extra assignment inserted by the parser when in this form: 586 // Check for extra assignment inserted by the parser when in this form:
604 // (function Module(a, b, c) {... }) 587 // (function Module(a, b, c) {... })
605 ExpressionStatement* estatement = use_asm_directive->AsExpressionStatement(); 588 ExpressionStatement* estatement = use_asm_directive->AsExpressionStatement();
606 if (estatement != nullptr) { 589 if (estatement != nullptr) {
607 Assignment* assignment = estatement->expression()->AsAssignment(); 590 Assignment* assignment = estatement->expression()->AsAssignment();
608 if (assignment != nullptr && assignment->target()->IsVariableProxy() && 591 if (assignment != nullptr && assignment->target()->IsVariableProxy() &&
609 assignment->target() 592 assignment->target()
610 ->AsVariableProxy() 593 ->AsVariableProxy()
611 ->var() 594 ->var()
612 ->is_sloppy_function_name()) { 595 ->is_sloppy_function_name()) {
613 use_asm_directive = iter.Next(); 596 use_asm_directive = iter.Next();
614 } 597 }
615 } 598 }
616 if (!IsUseAsmDirective(use_asm_directive)) { 599 if (!IsUseAsmDirective(use_asm_directive)) {
617 FAIL(fun, "Missing \"use asm\"."); 600 FAIL(fun, "Missing \"use asm\".");
618 } 601 }
619 source_layout.AddUseAsm(*use_asm_directive); 602 source_layout_.AddUseAsm(*use_asm_directive);
620 ReturnStatement* module_return = nullptr; 603 module_return_ = nullptr;
621 604
622 // *VIOLATION* The spec states that globals should be followed by function 605 // *VIOLATION* The spec states that globals should be followed by function
623 // declarations, which should be followed by function pointer tables, followed 606 // declarations, which should be followed by function pointer tables, followed
624 // by the module export (return) statement. Our AST might be rearraged by the 607 // by the module export (return) statement. Our AST might be rearraged by the
625 // parser, so we can't rely on it being in source code order. 608 // parser, so we can't rely on it being in source code order.
626 while (Statement* current = iter.Next()) { 609 while (Statement* current = iter.Next()) {
627 if (auto* assign = ExtractInitializerExpression(current)) { 610 if (auto* assign = ExtractInitializerExpression(current)) {
628 if (assign->value()->IsArrayLiteral()) { 611 if (assign->value()->IsArrayLiteral()) {
629 // Save function tables for later validation. 612 // Save function tables for later validation.
630 function_pointer_tables.push_back(assign); 613 function_pointer_tables_.push_back(assign);
631 } else { 614 } else {
632 RECURSE(ValidateGlobalDeclaration(assign)); 615 RECURSE(ValidateGlobalDeclaration(assign));
633 source_layout.AddGlobal(*assign); 616 source_layout_.AddGlobal(*assign);
634 } 617 }
635 continue; 618 continue;
636 } 619 }
637 620
638 if (auto* current_as_return = current->AsReturnStatement()) { 621 if (auto* current_as_return = current->AsReturnStatement()) {
639 if (module_return != nullptr) { 622 if (module_return_ != nullptr) {
640 FAIL(fun, "Multiple export statements."); 623 FAIL(fun, "Multiple export statements.");
641 } 624 }
642 module_return = current_as_return; 625 module_return_ = current_as_return;
643 source_layout.AddExport(*module_return); 626 source_layout_.AddExport(*module_return_);
644 continue; 627 continue;
645 } 628 }
646 629
647 FAIL(current, "Invalid top-level statement in asm.js module."); 630 FAIL(current, "Invalid top-level statement in asm.js module.");
648 } 631 }
649 632
633 return AsmType::Int(); // Any type that is not AsmType::None();
634 }
635
636 AsmType* AsmTyper::ValidateModuleFunction(FunctionDeclaration* fun_decl) {
637 RECURSE(ValidateFunction(fun_decl));
638 source_layout_.AddFunction(*fun_decl);
639
640 return AsmType::Int(); // Any type that is not AsmType::None();
641 }
642
643 AsmType* AsmTyper::ValidateModuleFunctions(FunctionLiteral* fun) {
644 DeclarationScope* scope = fun->scope();
650 ZoneList<Declaration*>* decls = scope->declarations(); 645 ZoneList<Declaration*>* decls = scope->declarations();
651 646
652 for (int ii = 0; ii < decls->length(); ++ii) { 647 for (int ii = 0; ii < decls->length(); ++ii) {
653 Declaration* decl = decls->at(ii); 648 Declaration* decl = decls->at(ii);
654 649
655 if (FunctionDeclaration* fun_decl = decl->AsFunctionDeclaration()) { 650 if (FunctionDeclaration* fun_decl = decl->AsFunctionDeclaration()) {
656 RECURSE(ValidateFunction(fun_decl)); 651 RECURSE(ValidateModuleFunction(fun_decl));
657 source_layout.AddFunction(*fun_decl);
658 continue; 652 continue;
659 } 653 }
660 } 654 }
661 655
662 for (auto* function_table : function_pointer_tables) { 656 return AsmType::Int(); // Any type that is not AsmType::None();
657 }
658
659 AsmType* AsmTyper::ValidateModulePhase2of2(FunctionLiteral* fun) {
660 for (auto* function_table : function_pointer_tables_) {
663 RECURSE(ValidateFunctionTable(function_table)); 661 RECURSE(ValidateFunctionTable(function_table));
664 source_layout.AddTable(*function_table); 662 source_layout_.AddTable(*function_table);
665 } 663 }
666 664
665 DeclarationScope* scope = fun->scope();
666 ZoneList<Declaration*>* decls = scope->declarations();
667 for (int ii = 0; ii < decls->length(); ++ii) { 667 for (int ii = 0; ii < decls->length(); ++ii) {
668 Declaration* decl = decls->at(ii); 668 Declaration* decl = decls->at(ii);
669 669
670 if (decl->IsFunctionDeclaration()) { 670 if (decl->IsFunctionDeclaration()) {
671 continue; 671 continue;
672 } 672 }
673 673
674 VariableDeclaration* var_decl = decl->AsVariableDeclaration(); 674 VariableDeclaration* var_decl = decl->AsVariableDeclaration();
675 if (var_decl == nullptr) { 675 if (var_decl == nullptr) {
676 FAIL(decl, "Invalid asm.js declaration."); 676 FAIL(decl, "Invalid asm.js declaration.");
677 } 677 }
678 678
679 auto* var_proxy = var_decl->proxy(); 679 auto* var_proxy = var_decl->proxy();
680 if (var_proxy == nullptr) { 680 if (var_proxy == nullptr) {
681 FAIL(decl, "Invalid asm.js declaration."); 681 FAIL(decl, "Invalid asm.js declaration.");
682 } 682 }
683 683
684 if (Lookup(var_proxy->var()) == nullptr) { 684 if (Lookup(var_proxy->var()) == nullptr) {
685 FAIL(decl, "Global variable missing initializer in asm.js module."); 685 FAIL(decl, "Global variable missing initializer in asm.js module.");
686 } 686 }
687 } 687 }
688 688
689 // 6.2 ValidateExport 689 // 6.2 ValidateExport
690 if (module_return == nullptr) { 690 if (module_return_ == nullptr) {
691 FAIL(fun, "Missing asm.js module export."); 691 FAIL(fun, "Missing asm.js module export.");
692 } 692 }
693 693
694 for (auto* forward_def : forward_definitions_) { 694 for (auto* forward_def : forward_definitions_) {
695 if (forward_def->missing_definition()) { 695 if (forward_def->missing_definition()) {
696 FAIL(forward_def->first_forward_use(), 696 FAIL(forward_def->first_forward_use(),
697 "Missing definition for forward declared identifier."); 697 "Missing definition for forward declared identifier.");
698 } 698 }
699 } 699 }
700 700
701 RECURSE(ValidateExport(module_return)); 701 RECURSE(ValidateExport(module_return_));
702 702
703 if (!source_layout.IsValid()) { 703 if (!source_layout_.IsValid()) {
704 FAIL(fun, "Invalid asm.js source code layout."); 704 FAIL(fun, "Invalid asm.js source code layout.");
705 } 705 }
706 706
707 return AsmType::Int(); // Any type that is not AsmType::None(); 707 return AsmType::Int(); // Any type that is not AsmType::None();
708 } 708 }
709 709
710 namespace { 710 namespace {
711 bool IsDoubleAnnotation(BinaryOperation* binop) { 711 bool IsDoubleAnnotation(BinaryOperation* binop) {
712 // *VIOLATION* The parser replaces uses of +x with x*1.0. 712 // *VIOLATION* The parser replaces uses of +x with x*1.0.
713 if (binop->op() != Token::MUL) { 713 if (binop->op() != Token::MUL) {
(...skipping 135 matching lines...) Expand 10 before | Expand all | Expand 10 after
849 } 849 }
850 SetTypeOf(assign, target_info->type()); 850 SetTypeOf(assign, target_info->type());
851 SetTypeOf(target, target_info->type()); 851 SetTypeOf(target, target_info->type());
852 return target_info->type(); 852 return target_info->type();
853 } 853 }
854 854
855 // 6.2 ValidateExport 855 // 6.2 ValidateExport
856 AsmType* AsmTyper::ExportType(VariableProxy* fun_export) { 856 AsmType* AsmTyper::ExportType(VariableProxy* fun_export) {
857 auto* fun_info = Lookup(fun_export->var()); 857 auto* fun_info = Lookup(fun_export->var());
858 if (fun_info == nullptr) { 858 if (fun_info == nullptr) {
859 UNREACHABLE();
859 FAIL(fun_export, "Undefined identifier in asm.js module export."); 860 FAIL(fun_export, "Undefined identifier in asm.js module export.");
860 } 861 }
861 862
862 if (fun_info->standard_member() != kNone) { 863 if (fun_info->standard_member() != kNone) {
863 FAIL(fun_export, "Module cannot export standard library functions."); 864 FAIL(fun_export, "Module cannot export standard library functions.");
864 } 865 }
865 866
866 auto* type = fun_info->type(); 867 auto* type = fun_info->type();
867 if (type->AsFFIType() != nullptr) { 868 if (type->AsFFIType() != nullptr) {
868 FAIL(fun_export, "Module cannot export foreign functions."); 869 FAIL(fun_export, "Module cannot export foreign functions.");
(...skipping 1914 matching lines...) Expand 10 before | Expand all | Expand 10 after
2783 return true; 2784 return true;
2784 } 2785 }
2785 2786
2786 *error_message = typer.error_message(); 2787 *error_message = typer.error_message();
2787 return false; 2788 return false;
2788 } 2789 }
2789 2790
2790 } // namespace wasm 2791 } // namespace wasm
2791 } // namespace internal 2792 } // namespace internal
2792 } // namespace v8 2793 } // namespace v8
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698