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

Side by Side Diff: src/typing-asm.cc

Issue 1471073003: Make typing-asm match spec more closely around load/store, add more tests. (Closed) Base URL: https://chromium.googlesource.com/v8/v8.git@master
Patch Set: revised Created 5 years 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
« no previous file with comments | « src/typing-asm.h ('k') | test/cctest/expression-type-collector-macros.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright 2015 the V8 project authors. All rights reserved. 1 // Copyright 2015 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/v8.h" 5 #include "src/v8.h"
6 6
7 #include "src/typing-asm.h" 7 #include "src/typing-asm.h"
8 8
9 #include "src/ast/ast.h" 9 #include "src/ast/ast.h"
10 #include "src/ast/scopes.h" 10 #include "src/ast/scopes.h"
(...skipping 23 matching lines...) Expand all
34 if (!valid_) return; \ 34 if (!valid_) return; \
35 } while (false) 35 } while (false)
36 36
37 37
38 AsmTyper::AsmTyper(Isolate* isolate, Zone* zone, Script* script, 38 AsmTyper::AsmTyper(Isolate* isolate, Zone* zone, Script* script,
39 FunctionLiteral* root) 39 FunctionLiteral* root)
40 : zone_(zone), 40 : zone_(zone),
41 script_(script), 41 script_(script),
42 root_(root), 42 root_(root),
43 valid_(true), 43 valid_(true),
44 intish_(0),
44 stdlib_types_(zone), 45 stdlib_types_(zone),
45 stdlib_heap_types_(zone), 46 stdlib_heap_types_(zone),
46 stdlib_math_types_(zone), 47 stdlib_math_types_(zone),
47 global_variable_type_(HashMap::PointersMatch, 48 global_variable_type_(HashMap::PointersMatch,
48 ZoneHashMap::kDefaultHashMapCapacity, 49 ZoneHashMap::kDefaultHashMapCapacity,
49 ZoneAllocationPolicy(zone)), 50 ZoneAllocationPolicy(zone)),
50 local_variable_type_(HashMap::PointersMatch, 51 local_variable_type_(HashMap::PointersMatch,
51 ZoneHashMap::kDefaultHashMapCapacity, 52 ZoneHashMap::kDefaultHashMapCapacity,
52 ZoneAllocationPolicy(zone)), 53 ZoneAllocationPolicy(zone)),
53 in_function_(false), 54 in_function_(false),
(...skipping 94 matching lines...) Expand 10 before | Expand all | Expand 10 after
148 Type* result_type = Type::Undefined(zone()); 149 Type* result_type = Type::Undefined(zone());
149 if (body->length() > 0) { 150 if (body->length() > 0) {
150 ReturnStatement* stmt = body->last()->AsReturnStatement(); 151 ReturnStatement* stmt = body->last()->AsReturnStatement();
151 if (stmt != NULL) { 152 if (stmt != NULL) {
152 Literal* literal = stmt->expression()->AsLiteral(); 153 Literal* literal = stmt->expression()->AsLiteral();
153 Type* old_expected = expected_type_; 154 Type* old_expected = expected_type_;
154 expected_type_ = Type::Any(); 155 expected_type_ = Type::Any();
155 if (literal) { 156 if (literal) {
156 RECURSE(VisitLiteral(literal, true)); 157 RECURSE(VisitLiteral(literal, true));
157 } else { 158 } else {
158 RECURSE(VisitExpressionAnnotation(stmt->expression(), true)); 159 RECURSE(VisitExpressionAnnotation(stmt->expression(), NULL, true));
159 } 160 }
160 expected_type_ = old_expected; 161 expected_type_ = old_expected;
161 result_type = computed_type_; 162 result_type = computed_type_;
162 } 163 }
163 } 164 }
164 Type::FunctionType* type = 165 Type::FunctionType* type =
165 Type::Function(result_type, Type::Any(), fun->parameter_count(), zone()) 166 Type::Function(result_type, Type::Any(), fun->parameter_count(), zone())
166 ->AsFunction(); 167 ->AsFunction();
167 168
168 // Extract parameter types. 169 // Extract parameter types.
169 bool good = true; 170 bool good = true;
170 for (int i = 0; i < fun->parameter_count(); ++i) { 171 for (int i = 0; i < fun->parameter_count(); ++i) {
171 good = false; 172 good = false;
172 if (i >= body->length()) break; 173 if (i >= body->length()) break;
173 ExpressionStatement* stmt = body->at(i)->AsExpressionStatement(); 174 ExpressionStatement* stmt = body->at(i)->AsExpressionStatement();
174 if (stmt == NULL) break; 175 if (stmt == NULL) break;
175 Assignment* expr = stmt->expression()->AsAssignment(); 176 Assignment* expr = stmt->expression()->AsAssignment();
176 if (expr == NULL || expr->is_compound()) break; 177 if (expr == NULL || expr->is_compound()) break;
177 VariableProxy* proxy = expr->target()->AsVariableProxy(); 178 VariableProxy* proxy = expr->target()->AsVariableProxy();
178 if (proxy == NULL) break; 179 if (proxy == NULL) break;
179 Variable* var = proxy->var(); 180 Variable* var = proxy->var();
180 if (var->location() != VariableLocation::PARAMETER || var->index() != i) 181 if (var->location() != VariableLocation::PARAMETER || var->index() != i)
181 break; 182 break;
182 RECURSE(VisitExpressionAnnotation(expr->value(), false)); 183 RECURSE(VisitExpressionAnnotation(expr->value(), var, false));
183 SetType(var, computed_type_); 184 SetType(var, computed_type_);
184 type->InitParameter(i, computed_type_); 185 type->InitParameter(i, computed_type_);
185 good = true; 186 good = true;
186 } 187 }
187 if (!good) FAIL(fun, "missing parameter type annotations"); 188 if (!good) FAIL(fun, "missing parameter type annotations");
188 189
189 SetResult(fun, type); 190 SetResult(fun, type);
190 } 191 }
191 192
192 193
193 void AsmTyper::VisitExpressionAnnotation(Expression* expr, bool is_return) { 194 void AsmTyper::VisitExpressionAnnotation(Expression* expr, Variable* var,
195 bool is_return) {
194 // Normal +x or x|0 annotations. 196 // Normal +x or x|0 annotations.
195 BinaryOperation* bin = expr->AsBinaryOperation(); 197 BinaryOperation* bin = expr->AsBinaryOperation();
196 if (bin != NULL) { 198 if (bin != NULL) {
199 if (var != NULL) {
200 VariableProxy* left = bin->left()->AsVariableProxy();
201 if (!left) {
202 FAIL(expr, "variable name expected in type annotation");
203 }
204 if (left->var() != var) {
205 FAIL(left, "variable type annotation references other variable");
206 }
207 }
197 Literal* right = bin->right()->AsLiteral(); 208 Literal* right = bin->right()->AsLiteral();
198 if (right != NULL) { 209 if (right != NULL) {
199 switch (bin->op()) { 210 switch (bin->op()) {
200 case Token::MUL: // We encode +x as x*1.0 211 case Token::MUL: // We encode +x as x*1.0
201 if (right->raw_value()->ContainsDot() && 212 if (right->raw_value()->ContainsDot() &&
202 right->raw_value()->AsNumber() == 1.0) { 213 right->raw_value()->AsNumber() == 1.0) {
203 SetResult(expr, cache_.kAsmDouble); 214 SetResult(expr, cache_.kAsmDouble);
204 return; 215 return;
205 } 216 }
206 break; 217 break;
(...skipping 379 matching lines...) Expand 10 before | Expand all | Expand 10 after
586 } else { 597 } else {
587 if (building_function_tables_) { 598 if (building_function_tables_) {
588 return; 599 return;
589 } 600 }
590 } 601 }
591 } 602 }
592 if (expr->is_compound()) FAIL(expr, "compound assignment encountered"); 603 if (expr->is_compound()) FAIL(expr, "compound assignment encountered");
593 Type* type = expected_type_; 604 Type* type = expected_type_;
594 RECURSE(VisitWithExpectation( 605 RECURSE(VisitWithExpectation(
595 expr->value(), type, "assignment value expected to match surrounding")); 606 expr->value(), type, "assignment value expected to match surrounding"));
607 Type* target_type = StorageType(computed_type_);
596 if (intish_ != 0) { 608 if (intish_ != 0) {
597 FAIL(expr, "value still an intish"); 609 FAIL(expr, "intish or floatish assignment");
598 } 610 }
599 Type* target_type = computed_type_; 611 if (expr->target()->IsVariableProxy()) {
600 if (target_type->Is(cache_.kAsmInt)) { 612 RECURSE(VisitWithExpectation(expr->target(), target_type,
601 target_type = cache_.kAsmInt; 613 "assignment target expected to match value"));
614 } else if (expr->target()->IsProperty()) {
615 Property* property = expr->target()->AsProperty();
616 RECURSE(VisitWithExpectation(property->obj(), Type::Any(),
617 "bad propety object"));
618 if (!computed_type_->IsArray()) {
619 FAIL(property->obj(), "array expected");
620 }
621 VisitHeapAccess(property, true, target_type);
602 } 622 }
603 RECURSE(VisitWithExpectation(expr->target(), target_type, 623 IntersectResult(expr, target_type);
604 "assignment target expected to match value"));
605 if (intish_ != 0) {
606 FAIL(expr, "value still an intish");
607 }
608 IntersectResult(expr, computed_type_);
609 } 624 }
610 625
611 626
612 void AsmTyper::VisitYield(Yield* expr) { 627 void AsmTyper::VisitYield(Yield* expr) {
613 FAIL(expr, "yield expression encountered"); 628 FAIL(expr, "yield expression encountered");
614 } 629 }
615 630
616 631
617 void AsmTyper::VisitThrow(Throw* expr) { 632 void AsmTyper::VisitThrow(Throw* expr) {
618 FAIL(expr, "throw statement encountered"); 633 FAIL(expr, "throw statement encountered");
(...skipping 11 matching lines...) Expand all
630 645
631 Type* AsmTyper::StorageType(Type* type) { 646 Type* AsmTyper::StorageType(Type* type) {
632 if (type->Is(cache_.kAsmInt)) { 647 if (type->Is(cache_.kAsmInt)) {
633 return cache_.kAsmInt; 648 return cache_.kAsmInt;
634 } else { 649 } else {
635 return type; 650 return type;
636 } 651 }
637 } 652 }
638 653
639 654
640 void AsmTyper::VisitHeapAccess(Property* expr) { 655 void AsmTyper::VisitHeapAccess(Property* expr, bool assigning,
656 Type* assignment_type) {
641 Type::ArrayType* array_type = computed_type_->AsArray(); 657 Type::ArrayType* array_type = computed_type_->AsArray();
642 size_t size = array_size_; 658 size_t size = array_size_;
643 Type* type = array_type->AsArray()->Element(); 659 Type* type = array_type->AsArray()->Element();
644 if (type->IsFunction()) { 660 if (type->IsFunction()) {
661 if (assigning) {
662 FAIL(expr, "assigning to function table is illegal");
663 }
645 BinaryOperation* bin = expr->key()->AsBinaryOperation(); 664 BinaryOperation* bin = expr->key()->AsBinaryOperation();
646 if (bin == NULL || bin->op() != Token::BIT_AND) { 665 if (bin == NULL || bin->op() != Token::BIT_AND) {
647 FAIL(expr->key(), "expected & in call"); 666 FAIL(expr->key(), "expected & in call");
648 } 667 }
649 RECURSE(VisitWithExpectation(bin->left(), cache_.kAsmSigned, 668 RECURSE(VisitWithExpectation(bin->left(), cache_.kAsmSigned,
650 "array index expected to be integer")); 669 "array index expected to be integer"));
651 Literal* right = bin->right()->AsLiteral(); 670 Literal* right = bin->right()->AsLiteral();
652 if (right == NULL || right->raw_value()->ContainsDot()) { 671 if (right == NULL || right->raw_value()->ContainsDot()) {
653 FAIL(right, "call mask must be integer"); 672 FAIL(right, "call mask must be integer");
654 } 673 }
655 RECURSE(VisitWithExpectation(bin->right(), cache_.kAsmSigned, 674 RECURSE(VisitWithExpectation(bin->right(), cache_.kAsmSigned,
656 "call mask expected to be integer")); 675 "call mask expected to be integer"));
657 if (static_cast<size_t>(right->raw_value()->AsNumber()) != size - 1) { 676 if (static_cast<size_t>(right->raw_value()->AsNumber()) != size - 1) {
658 FAIL(right, "call mask must match function table"); 677 FAIL(right, "call mask must match function table");
659 } 678 }
660 bin->set_bounds(Bounds(cache_.kAsmSigned)); 679 bin->set_bounds(Bounds(cache_.kAsmSigned));
680 IntersectResult(expr, type);
661 } else { 681 } else {
662 Literal* literal = expr->key()->AsLiteral(); 682 Literal* literal = expr->key()->AsLiteral();
663 if (literal) { 683 if (literal) {
664 RECURSE(VisitWithExpectation(literal, cache_.kAsmSigned, 684 RECURSE(VisitWithExpectation(literal, cache_.kAsmSigned,
665 "array index expected to be integer")); 685 "array index expected to be integer"));
666 } else { 686 } else {
667 BinaryOperation* bin = expr->key()->AsBinaryOperation(); 687 BinaryOperation* bin = expr->key()->AsBinaryOperation();
668 if (bin == NULL || bin->op() != Token::SAR) { 688 if (bin == NULL || bin->op() != Token::SAR) {
669 FAIL(expr->key(), "expected >> in heap access"); 689 FAIL(expr->key(), "expected >> in heap access");
670 } 690 }
671 RECURSE(VisitWithExpectation(bin->left(), cache_.kAsmSigned, 691 RECURSE(VisitWithExpectation(bin->left(), cache_.kAsmSigned,
672 "array index expected to be integer")); 692 "array index expected to be integer"));
673 Literal* right = bin->right()->AsLiteral(); 693 Literal* right = bin->right()->AsLiteral();
674 if (right == NULL || right->raw_value()->ContainsDot()) { 694 if (right == NULL || right->raw_value()->ContainsDot()) {
675 FAIL(right, "heap access shift must be integer"); 695 FAIL(right, "heap access shift must be integer");
676 } 696 }
677 RECURSE(VisitWithExpectation(bin->right(), cache_.kAsmSigned, 697 RECURSE(VisitWithExpectation(bin->right(), cache_.kAsmSigned,
678 "array shift expected to be integer")); 698 "array shift expected to be integer"));
679 int n = static_cast<int>(right->raw_value()->AsNumber()); 699 int n = static_cast<int>(right->raw_value()->AsNumber());
680 int expected_shift = ElementShiftSize(type); 700 int expected_shift = ElementShiftSize(type);
681 if (expected_shift < 0 || n != expected_shift) { 701 if (expected_shift < 0 || n != expected_shift) {
682 FAIL(right, "heap access shift must match element size"); 702 FAIL(right, "heap access shift must match element size");
683 } 703 }
684 bin->set_bounds(Bounds(cache_.kAsmSigned)); 704 bin->set_bounds(Bounds(cache_.kAsmSigned));
685 } 705 }
706 Type* result_type;
707 if (type->Is(cache_.kAsmIntArrayElement)) {
708 result_type = cache_.kAsmIntQ;
709 intish_ = kMaxUncombinedAdditiveSteps;
710 } else if (type->Is(cache_.kAsmFloat)) {
711 if (assigning) {
712 result_type = cache_.kAsmFloatDoubleQ;
713 } else {
714 result_type = cache_.kAsmFloatQ;
715 }
716 intish_ = 0;
717 } else if (type->Is(cache_.kAsmDouble)) {
718 if (assigning) {
719 result_type = cache_.kAsmFloatDoubleQ;
720 if (intish_ != 0) {
721 FAIL(expr, "Assignment of floatish to Float64Array");
722 }
723 } else {
724 result_type = cache_.kAsmDoubleQ;
725 }
726 intish_ = 0;
727 } else {
728 UNREACHABLE();
729 }
730 if (assigning) {
731 if (!assignment_type->Is(result_type)) {
732 FAIL(expr, "illegal type in assignment");
733 }
734 } else {
735 IntersectResult(expr, expected_type_);
736 IntersectResult(expr, result_type);
737 }
686 } 738 }
687 IntersectResult(expr, type);
688 } 739 }
689 740
690 741
691 void AsmTyper::VisitProperty(Property* expr) { 742 void AsmTyper::VisitProperty(Property* expr) {
692 // stdlib.Math.x 743 // stdlib.Math.x
693 Property* inner_prop = expr->obj()->AsProperty(); 744 Property* inner_prop = expr->obj()->AsProperty();
694 if (inner_prop != NULL) { 745 if (inner_prop != NULL) {
695 // Get property name. 746 // Get property name.
696 Literal* key = expr->key()->AsLiteral(); 747 Literal* key = expr->key()->AsLiteral();
697 if (key == NULL || !key->IsPropertyName()) 748 if (key == NULL || !key->IsPropertyName())
(...skipping 15 matching lines...) Expand all
713 764
714 // Look up library type. 765 // Look up library type.
715 Type* type = LibType(stdlib_math_types_, name); 766 Type* type = LibType(stdlib_math_types_, name);
716 if (type == NULL) FAIL(expr, "unknown standard function 3 "); 767 if (type == NULL) FAIL(expr, "unknown standard function 3 ");
717 SetResult(expr, type); 768 SetResult(expr, type);
718 return; 769 return;
719 } 770 }
720 771
721 // Only recurse at this point so that we avoid needing 772 // Only recurse at this point so that we avoid needing
722 // stdlib.Math to have a real type. 773 // stdlib.Math to have a real type.
723 RECURSE(VisitWithExpectation(expr->obj(), Type::Any(), 774 RECURSE(VisitWithExpectation(expr->obj(), Type::Any(), "bad propety object"));
724 "property holder expected to be object"));
725 775
726 // For heap view or function table access. 776 // For heap view or function table access.
727 if (computed_type_->IsArray()) { 777 if (computed_type_->IsArray()) {
728 VisitHeapAccess(expr); 778 VisitHeapAccess(expr, false, NULL);
729 return; 779 return;
730 } 780 }
731 781
732 // Get property name. 782 // Get property name.
733 Literal* key = expr->key()->AsLiteral(); 783 Literal* key = expr->key()->AsLiteral();
734 if (key == NULL || !key->IsPropertyName()) 784 if (key == NULL || !key->IsPropertyName())
735 FAIL(expr, "invalid type annotation on property 3"); 785 FAIL(expr, "invalid type annotation on property 3");
736 Handle<String> name = key->AsPropertyName(); 786 Handle<String> name = key->AsPropertyName();
737 787
738 // stdlib.x or foreign.x 788 // stdlib.x or foreign.x
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after
773 ZoneList<Expression*>* args = expr->arguments(); 823 ZoneList<Expression*>* args = expr->arguments();
774 if (fun_type->Arity() != args->length()) { 824 if (fun_type->Arity() != args->length()) {
775 FAIL(expr, "call with wrong arity"); 825 FAIL(expr, "call with wrong arity");
776 } 826 }
777 for (int i = 0; i < args->length(); ++i) { 827 for (int i = 0; i < args->length(); ++i) {
778 Expression* arg = args->at(i); 828 Expression* arg = args->at(i);
779 RECURSE(VisitWithExpectation( 829 RECURSE(VisitWithExpectation(
780 arg, fun_type->Parameter(i), 830 arg, fun_type->Parameter(i),
781 "call argument expected to match callee parameter")); 831 "call argument expected to match callee parameter"));
782 } 832 }
833 intish_ = 0;
783 IntersectResult(expr, fun_type->Result()); 834 IntersectResult(expr, fun_type->Result());
784 } else if (computed_type_->Is(Type::Any())) { 835 } else if (computed_type_->Is(Type::Any())) {
785 // For foreign calls. 836 // For foreign calls.
786 ZoneList<Expression*>* args = expr->arguments(); 837 ZoneList<Expression*>* args = expr->arguments();
787 for (int i = 0; i < args->length(); ++i) { 838 for (int i = 0; i < args->length(); ++i) {
788 Expression* arg = args->at(i); 839 Expression* arg = args->at(i);
789 RECURSE(VisitWithExpectation(arg, Type::Any(), 840 RECURSE(VisitWithExpectation(arg, Type::Any(),
790 "foreign call argument expected to be any")); 841 "foreign call argument expected to be any"));
791 } 842 }
843 intish_ = kMaxUncombinedAdditiveSteps;
792 IntersectResult(expr, Type::Number()); 844 IntersectResult(expr, Type::Number());
793 } else { 845 } else {
794 FAIL(expr, "invalid callee"); 846 FAIL(expr, "invalid callee");
795 } 847 }
796 } 848 }
797 849
798 850
799 void AsmTyper::VisitCallNew(CallNew* expr) { 851 void AsmTyper::VisitCallNew(CallNew* expr) {
800 if (in_function_) { 852 if (in_function_) {
801 FAIL(expr, "new not allowed in module function"); 853 FAIL(expr, "new not allowed in module function");
(...skipping 185 matching lines...) Expand 10 before | Expand all | Expand 10 after
987 FAIL(expr, "too many consecutive additive ops"); 1039 FAIL(expr, "too many consecutive additive ops");
988 } 1040 }
989 } else { 1041 } else {
990 if (intish_ > kMaxUncombinedMultiplicativeSteps) { 1042 if (intish_ > kMaxUncombinedMultiplicativeSteps) {
991 FAIL(expr, "too many consecutive multiplicative ops"); 1043 FAIL(expr, "too many consecutive multiplicative ops");
992 } 1044 }
993 } 1045 }
994 IntersectResult(expr, cache_.kAsmInt); 1046 IntersectResult(expr, cache_.kAsmInt);
995 return; 1047 return;
996 } 1048 }
997 } else if (expr->op() == Token::MUL && left_type->Is(cache_.kAsmInt) && 1049 } else if (expr->op() == Token::MUL && expr->right()->IsLiteral() &&
998 right_type->Is(cache_.kAsmDouble)) { 1050 right_type->Is(cache_.kAsmDouble)) {
999 // For unary +, expressed as x * 1.0 1051 // For unary +, expressed as x * 1.0
1000 IntersectResult(expr, cache_.kAsmDouble); 1052 IntersectResult(expr, cache_.kAsmDouble);
1001 return; 1053 return;
1002 } else if (type->Is(cache_.kAsmFloat) && expr->op() != Token::MOD) { 1054 } else if (type->Is(cache_.kAsmFloat) && expr->op() != Token::MOD) {
1055 if (left_intish != 0 || right_intish != 0) {
1056 FAIL(expr, "float operation before required fround");
1057 }
1003 IntersectResult(expr, cache_.kAsmFloat); 1058 IntersectResult(expr, cache_.kAsmFloat);
1059 intish_ = 1;
1004 return; 1060 return;
1005 } else if (type->Is(cache_.kAsmDouble)) { 1061 } else if (type->Is(cache_.kAsmDouble)) {
1006 IntersectResult(expr, cache_.kAsmDouble); 1062 IntersectResult(expr, cache_.kAsmDouble);
1007 return; 1063 return;
1008 } else { 1064 } else {
1009 FAIL(expr, "ill-typed arithmetic operation"); 1065 FAIL(expr, "ill-typed arithmetic operation");
1010 } 1066 }
1011 } 1067 }
1012 default: 1068 default:
1013 UNREACHABLE(); 1069 UNREACHABLE();
(...skipping 192 matching lines...) Expand 10 before | Expand all | Expand 10 after
1206 computed_type_->Print(); 1262 computed_type_->Print();
1207 PrintF("Expected type: "); 1263 PrintF("Expected type: ");
1208 expected_type_->Print(); 1264 expected_type_->Print();
1209 #endif 1265 #endif
1210 FAIL(expr, msg); 1266 FAIL(expr, msg);
1211 } 1267 }
1212 expected_type_ = save; 1268 expected_type_ = save;
1213 } 1269 }
1214 } // namespace internal 1270 } // namespace internal
1215 } // namespace v8 1271 } // namespace v8
OLDNEW
« no previous file with comments | « src/typing-asm.h ('k') | test/cctest/expression-type-collector-macros.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698