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

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

Issue 1322773002: Add asm.js typer / validator. (Closed) Base URL: https://chromium.googlesource.com/v8/v8.git@master
Patch Set: fixes Created 5 years, 3 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
« no previous file with comments | « src/typing-asm.h ('k') | test/cctest/cctest.gyp » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
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
3 // found in the LICENSE file.
4
5 #include "src/v8.h"
6
7 #include "src/typing-asm.h"
8
9 #include "src/ast.h"
10 #include "src/codegen.h"
11 #include "src/scopes.h"
12 #include "src/zone-type-cache.h"
13
14 namespace v8 {
15 namespace internal {
16 namespace {
17
18 base::LazyInstance<ZoneTypeCache>::type kCache = LAZY_INSTANCE_INITIALIZER;
19
20 } // namespace
21
22
23 #define FAIL(node, msg) \
24 do { \
25 valid_ = false; \
26 int line = node->position() == RelocInfo::kNoPosition \
27 ? -1 \
28 : info_->script()->GetLineNumber(node->position()); \
29 base::OS::SNPrintF(error_message_, sizeof(error_message_), \
30 "asm: line %d: %s\n", line + 1, msg); \
31 return; \
32 } while (false)
33
34
35 #define RECURSE(call) \
36 do { \
37 DCHECK(!HasStackOverflow()); \
38 call; \
39 if (HasStackOverflow()) return; \
40 if (!valid_) return; \
41 } while (false)
42
43
44 AsmTyper::AsmTyper(CompilationInfo* info)
45 : info_(info),
46 valid_(true),
47 stdlib_types_(info->zone()),
48 stdlib_heap_types_(info->zone()),
49 stdlib_math_types_(info->zone()),
50 global_variable_type_(HashMap::PointersMatch,
51 ZoneHashMap::kDefaultHashMapCapacity,
52 ZoneAllocationPolicy(info->zone())),
53 local_variable_type_(HashMap::PointersMatch,
54 ZoneHashMap::kDefaultHashMapCapacity,
55 ZoneAllocationPolicy(info->zone())),
56 in_function_(false),
57 building_function_tables_(false),
58 cache_(kCache.Get()) {
59 InitializeAstVisitor(info->isolate(), info->zone());
60 InitializeStdlib();
61 }
62
63
64 bool AsmTyper::Validate() {
65 VisitAsmModule(info_->literal());
66 return valid_ && !HasStackOverflow();
67 }
68
69
70 void AsmTyper::VisitAsmModule(FunctionLiteral* fun) {
71 Scope* scope = fun->scope();
72 if (!scope->is_function_scope()) FAIL(fun, "not at function scope");
73
74 // Module parameters.
75 for (int i = 0; i < scope->num_parameters(); ++i) {
76 Variable* param = scope->parameter(i);
77 DCHECK(GetType(param) == NULL);
78 SetType(param, Type::None(zone()));
79 }
80
81 ZoneList<Declaration*>* decls = scope->declarations();
82
83 // Set all globals to type Any.
84 VariableDeclaration* decl = scope->function();
85 if (decl != NULL) SetType(decl->proxy()->var(), Type::None());
86 RECURSE(VisitDeclarations(scope->declarations()));
87
88 // Validate global variables.
89 RECURSE(VisitStatements(fun->body()));
90
91 // Validate function annotations.
92 for (int i = 0; i < decls->length(); ++i) {
93 FunctionDeclaration* decl = decls->at(i)->AsFunctionDeclaration();
94 if (decl != NULL) {
95 RECURSE(VisitFunctionAnnotation(decl->fun()));
96 Variable* var = decl->proxy()->var();
97 DCHECK(GetType(var) == NULL);
98 SetType(var, computed_type_);
99 DCHECK(GetType(var) != NULL);
100 }
101 }
102
103 // Build function tables.
104 building_function_tables_ = true;
105 RECURSE(VisitStatements(fun->body()));
106 building_function_tables_ = false;
107
108 // Validate function bodies.
109 for (int i = 0; i < decls->length(); ++i) {
110 FunctionDeclaration* decl = decls->at(i)->AsFunctionDeclaration();
111 if (decl != NULL) {
112 RECURSE(
113 VisitWithExpectation(decl->fun(), Type::Any(zone()), "UNREACHABLE"));
114 if (!computed_type_->IsFunction()) {
115 FAIL(decl->fun(), "function literal expected to be a function");
116 }
117 }
118 }
119
120 // Validate exports.
121 ReturnStatement* stmt = fun->body()->last()->AsReturnStatement();
122 RECURSE(VisitWithExpectation(stmt->expression(), Type::Object(),
123 "expected object export"));
124 }
125
126
127 void AsmTyper::VisitVariableDeclaration(VariableDeclaration* decl) {
128 Variable* var = decl->proxy()->var();
129 if (var->location() != VariableLocation::PARAMETER) {
130 if (GetType(var) == NULL) {
131 SetType(var, Type::Any(zone()));
132 } else {
133 DCHECK(!GetType(var)->IsFunction());
134 }
135 }
136 DCHECK(GetType(var) != NULL);
137 intish_ = 0;
138 }
139
140
141 void AsmTyper::VisitFunctionDeclaration(FunctionDeclaration* decl) {
142 if (in_function_) {
143 FAIL(decl, "function declared inside another");
144 }
145 }
146
147
148 void AsmTyper::VisitFunctionAnnotation(FunctionLiteral* fun) {
149 // Extract result type.
150 ZoneList<Statement*>* body = fun->body();
151 Type* result_type = Type::Undefined(zone());
152 if (body->length() > 0) {
153 ReturnStatement* stmt = body->last()->AsReturnStatement();
154 if (stmt != NULL) {
155 RECURSE(VisitExpressionAnnotation(stmt->expression()));
156 result_type = computed_type_;
157 }
158 }
159 Type::FunctionType* type =
160 Type::Function(result_type, Type::Any(), fun->parameter_count(), zone())
161 ->AsFunction();
162
163 // Extract parameter types.
164 bool good = true;
165 for (int i = 0; i < fun->parameter_count(); ++i) {
166 good = false;
167 if (i >= body->length()) break;
168 ExpressionStatement* stmt = body->at(i)->AsExpressionStatement();
169 if (stmt == NULL) break;
170 Assignment* expr = stmt->expression()->AsAssignment();
171 if (expr == NULL || expr->is_compound()) break;
172 VariableProxy* proxy = expr->target()->AsVariableProxy();
173 if (proxy == NULL) break;
174 Variable* var = proxy->var();
175 if (var->location() != VariableLocation::PARAMETER || var->index() != i)
176 break;
177 RECURSE(VisitExpressionAnnotation(expr->value()));
178 SetType(var, computed_type_);
179 type->InitParameter(i, computed_type_);
180 good = true;
181 }
182 if (!good) FAIL(fun, "missing parameter type annotations");
183
184 SetResult(fun, type);
185 }
186
187
188 void AsmTyper::VisitExpressionAnnotation(Expression* expr) {
189 // Normal +x or x|0 annotations.
190 BinaryOperation* bin = expr->AsBinaryOperation();
191 if (bin != NULL) {
192 Literal* right = bin->right()->AsLiteral();
193 if (right != NULL) {
194 switch (bin->op()) {
195 case Token::MUL: // We encode +x as 1*x
196 if (right->raw_value()->ContainsDot() &&
197 right->raw_value()->AsNumber() == 1.0) {
198 SetResult(expr, cache_.kFloat64);
199 return;
200 }
201 break;
202 case Token::BIT_OR:
203 if (!right->raw_value()->ContainsDot() &&
204 right->raw_value()->AsNumber() == 0.0) {
205 SetResult(expr, cache_.kInt32);
206 return;
207 }
208 break;
209 default:
210 break;
211 }
212 }
213 FAIL(expr, "invalid type annotation on binary op");
214 }
215
216 // Numbers or the undefined literal (for empty returns).
217 if (expr->IsLiteral()) {
218 RECURSE(VisitWithExpectation(expr, Type::Any(), "invalid literal"));
219 return;
220 }
221
222 Call* call = expr->AsCall();
223 if (call != NULL) {
224 if (call->expression()->IsVariableProxy()) {
225 RECURSE(VisitWithExpectation(
226 call->expression(), Type::Any(zone()),
227 "only fround allowed on expression annotations"));
228 if (!computed_type_->Is(
229 Type::Function(cache_.kFloat32, Type::Number(zone()), zone()))) {
230 FAIL(call->expression(),
231 "only fround allowed on expression annotations");
232 }
233 if (call->arguments()->length() != 1) {
234 FAIL(call, "invalid argument count calling fround");
235 }
236 SetResult(expr, cache_.kFloat32);
237 return;
238 }
239 }
240
241 FAIL(expr, "invalid type annotation");
242 }
243
244
245 void AsmTyper::VisitStatements(ZoneList<Statement*>* stmts) {
246 for (int i = 0; i < stmts->length(); ++i) {
247 Statement* stmt = stmts->at(i);
248 RECURSE(Visit(stmt));
249 }
250 }
251
252
253 void AsmTyper::VisitBlock(Block* stmt) {
254 RECURSE(VisitStatements(stmt->statements()));
255 }
256
257
258 void AsmTyper::VisitExpressionStatement(ExpressionStatement* stmt) {
259 RECURSE(VisitWithExpectation(stmt->expression(), Type::Any(),
260 "expression statement expected to be any"));
261 }
262
263
264 void AsmTyper::VisitEmptyStatement(EmptyStatement* stmt) {}
265
266
267 void AsmTyper::VisitEmptyParentheses(EmptyParentheses* expr) { UNREACHABLE(); }
268
269
270 void AsmTyper::VisitIfStatement(IfStatement* stmt) {
271 if (!in_function_) {
272 FAIL(stmt, "if statement inside module body");
273 }
274 RECURSE(VisitWithExpectation(stmt->condition(), cache_.kInt32,
275 "if condition expected to be integer"));
276 RECURSE(Visit(stmt->then_statement()));
277 RECURSE(Visit(stmt->else_statement()));
278 }
279
280
281 void AsmTyper::VisitContinueStatement(ContinueStatement* stmt) {
282 if (!in_function_) {
283 FAIL(stmt, "continue statement inside module body");
284 }
285 }
286
287
288 void AsmTyper::VisitBreakStatement(BreakStatement* stmt) {
289 if (!in_function_) {
290 FAIL(stmt, "continue statement inside module body");
291 }
292 }
293
294
295 void AsmTyper::VisitReturnStatement(ReturnStatement* stmt) {
296 // Handle module return statement in VisitAsmModule.
297 if (!in_function_) {
298 return;
299 }
300 RECURSE(
301 VisitWithExpectation(stmt->expression(), return_type_,
302 "return expression expected to have return type"));
303 }
304
305
306 void AsmTyper::VisitWithStatement(WithStatement* stmt) {
307 FAIL(stmt, "bad with statement");
308 }
309
310
311 void AsmTyper::VisitSwitchStatement(SwitchStatement* stmt) {
312 if (!in_function_) {
313 FAIL(stmt, "switch statement inside module body");
314 }
315 RECURSE(VisitWithExpectation(stmt->tag(), cache_.kInt32,
316 "switch expression non-integer"));
317 ZoneList<CaseClause*>* clauses = stmt->cases();
318 for (int i = 0; i < clauses->length(); ++i) {
319 CaseClause* clause = clauses->at(i);
320 if (clause->is_default()) continue;
321 Expression* label = clause->label();
322 RECURSE(
323 VisitWithExpectation(label, cache_.kInt32, "case label non-integer"));
324 if (!label->IsLiteral()) FAIL(label, "non-literal case label");
325 Handle<Object> value = label->AsLiteral()->value();
326 int32_t value32;
327 if (!value->ToInt32(&value32)) FAIL(label, "illegal case label value");
328 // TODO(bradnelson): Detect duplicates.
329 ZoneList<Statement*>* stmts = clause->statements();
330 RECURSE(VisitStatements(stmts));
331 }
332 }
333
334
335 void AsmTyper::VisitCaseClause(CaseClause* clause) { UNREACHABLE(); }
336
337
338 void AsmTyper::VisitDoWhileStatement(DoWhileStatement* stmt) {
339 if (!in_function_) {
340 FAIL(stmt, "do statement inside module body");
341 }
342 RECURSE(Visit(stmt->body()));
343 RECURSE(VisitWithExpectation(stmt->cond(), cache_.kInt32,
titzer 2015/08/31 19:16:11 Is this also true? I thought asm allowed naked con
bradn 2015/08/31 19:40:13 At least not in the text of the spec: do stmt:Sta
344 "do condition expected to be integer"));
345 }
346
347
348 void AsmTyper::VisitWhileStatement(WhileStatement* stmt) {
349 if (!in_function_) {
350 FAIL(stmt, "while statement inside module body");
351 }
352 RECURSE(VisitWithExpectation(stmt->cond(), cache_.kInt32,
353 "while condition expected to be integer"));
354 RECURSE(Visit(stmt->body()));
355 }
356
357
358 void AsmTyper::VisitForStatement(ForStatement* stmt) {
359 if (!in_function_) {
360 FAIL(stmt, "for statement inside module body");
361 }
362 if (stmt->init() != NULL) {
363 RECURSE(Visit(stmt->init()));
364 }
365 if (stmt->cond() != NULL) {
366 RECURSE(VisitWithExpectation(stmt->cond(), cache_.kInt32,
titzer 2015/08/31 19:16:11 Same.
bradn 2015/08/31 19:40:13 See above.
367 "for condition expected to be integer"));
368 }
369 if (stmt->next() != NULL) {
370 RECURSE(Visit(stmt->next()));
371 }
372 RECURSE(Visit(stmt->body()));
373 }
374
375
376 void AsmTyper::VisitForInStatement(ForInStatement* stmt) {
377 FAIL(stmt, "for-in statement encountered");
378 }
379
380
381 void AsmTyper::VisitForOfStatement(ForOfStatement* stmt) {
382 FAIL(stmt, "for-of statement encountered");
383 }
384
385
386 void AsmTyper::VisitTryCatchStatement(TryCatchStatement* stmt) {
387 FAIL(stmt, "try statement encountered");
388 }
389
390
391 void AsmTyper::VisitTryFinallyStatement(TryFinallyStatement* stmt) {
392 FAIL(stmt, "try statement encountered");
393 }
394
395
396 void AsmTyper::VisitDebuggerStatement(DebuggerStatement* stmt) {
397 FAIL(stmt, "debugger statement encountered");
398 }
399
400
401 void AsmTyper::VisitFunctionLiteral(FunctionLiteral* expr) {
402 Scope* scope = expr->scope();
403 DCHECK(scope->is_function_scope());
404 if (in_function_) {
405 FAIL(expr, "invalid nested function");
406 }
407
408 if (!expr->bounds().upper->IsFunction()) {
409 FAIL(expr, "invalid function literal");
410 }
411
412 Type::FunctionType* type = expr->bounds().upper->AsFunction();
413 Type* save_return_type = return_type_;
414 return_type_ = type->Result();
415 in_function_ = true;
416 local_variable_type_.Clear();
417 RECURSE(VisitDeclarations(scope->declarations()));
418 RECURSE(VisitStatements(expr->body()));
419 in_function_ = false;
420 return_type_ = save_return_type;
421 IntersectResult(expr, type);
422 }
423
424
425 void AsmTyper::VisitNativeFunctionLiteral(NativeFunctionLiteral* expr) {
426 FAIL(expr, "function info literal encountered");
427 }
428
429
430 void AsmTyper::VisitConditional(Conditional* expr) {
431 RECURSE(VisitWithExpectation(expr->condition(), cache_.kInt32,
432 "condition expected to be integer"));
433 RECURSE(
434 VisitWithExpectation(expr->then_expression(), Type::Number(),
435 "conditional then branch expected to be integer"));
titzer 2015/08/31 19:16:11 You have number in the expectation and then number
bradn 2015/08/31 19:40:13 The comment is wrong, fixed. Also, good point abou
436 Type* then_type = computed_type_;
437 RECURSE(
438 VisitWithExpectation(expr->else_expression(), Type::Number(),
439 "conditional else branch expected to be integer"));
440 Type* else_type = computed_type_;
441 Type* type = Type::Intersect(then_type, else_type, zone());
442 if (!(type->Is(cache_.kInt32) || type->Is(cache_.kFloat64))) {
443 FAIL(expr, "ill-typed conditional");
444 }
445 IntersectResult(expr, type);
446 }
447
448
449 void AsmTyper::VisitVariableProxy(VariableProxy* expr) {
450 Variable* var = expr->var();
451 if (GetType(var) == NULL) {
452 FAIL(expr, "unbound variable");
453 }
454 Type* type = Type::Intersect(GetType(var), expected_type_, zone());
455 if (type->Is(cache_.kInt32)) {
456 type = cache_.kInt32;
457 }
458 SetType(var, type);
459 intish_ = 0;
460 IntersectResult(expr, type);
461 }
462
463
464 void AsmTyper::VisitLiteral(Literal* expr) {
465 intish_ = 0;
466 Handle<Object> value = expr->value();
467 if (value->IsNumber()) {
468 int32_t i;
469 uint32_t u;
470 if (expr->raw_value()->ContainsDot()) {
471 IntersectResult(expr, cache_.kFloat64);
472 } else if (value->ToUint32(&u)) {
473 IntersectResult(expr, cache_.kInt32);
474 } else if (value->ToInt32(&i)) {
475 IntersectResult(expr, cache_.kInt32);
476 } else {
477 FAIL(expr, "illegal number");
478 }
479 } else if (value->IsString()) {
480 IntersectResult(expr, Type::String());
481 } else if (value->IsUndefined()) {
482 IntersectResult(expr, Type::Undefined());
483 } else {
484 FAIL(expr, "illegal literal");
485 }
486 }
487
488
489 void AsmTyper::VisitRegExpLiteral(RegExpLiteral* expr) {
490 FAIL(expr, "regular expression encountered");
491 }
492
493
494 void AsmTyper::VisitObjectLiteral(ObjectLiteral* expr) {
495 if (in_function_) {
496 FAIL(expr, "object literal in function");
497 }
498 // Allowed for asm module's export declaration.
499 ZoneList<ObjectLiteralProperty*>* props = expr->properties();
500 for (int i = 0; i < props->length(); ++i) {
501 ObjectLiteralProperty* prop = props->at(i);
502 RECURSE(VisitWithExpectation(prop->value(), Type::Any(zone()),
503 "object property expected to be a function"));
504 if (!computed_type_->IsFunction()) {
505 FAIL(prop->value(), "non-function in function table");
506 }
507 }
508 IntersectResult(expr, Type::Object(zone()));
509 }
510
511
512 void AsmTyper::VisitArrayLiteral(ArrayLiteral* expr) {
513 if (in_function_) {
514 FAIL(expr, "array literal inside a function");
515 }
516 // Allowed for function tables.
517 ZoneList<Expression*>* values = expr->values();
518 Type* elem_type = Type::None(zone());
519 for (int i = 0; i < values->length(); ++i) {
520 Expression* value = values->at(i);
521 RECURSE(VisitWithExpectation(value, Type::Any(), "UNREACHABLE"));
522 if (!computed_type_->IsFunction()) {
523 FAIL(value, "array component expected to be a function");
524 }
525 elem_type = Type::Union(elem_type, computed_type_, zone());
526 }
527 array_size_ = values->length();
528 IntersectResult(expr, Type::Array(elem_type, zone()));
529 }
530
531
532 void AsmTyper::VisitAssignment(Assignment* expr) {
533 // Handle function tables and everything else in different passes.
534 if (!in_function_) {
535 if (expr->value()->IsArrayLiteral()) {
536 if (!building_function_tables_) {
537 return;
538 }
539 } else {
540 if (building_function_tables_) {
541 return;
542 }
543 }
544 }
545 if (expr->is_compound()) FAIL(expr, "compound assignment encountered");
546 Type* type = expected_type_;
547 RECURSE(VisitWithExpectation(
548 expr->value(), type, "assignment value expected to match surrounding"));
549 if (intish_ != 0) {
550 FAIL(expr, "value still an intish");
551 }
552 RECURSE(VisitWithExpectation(expr->target(), computed_type_,
553 "assignment target expected to match value"));
554 if (intish_ != 0) {
555 FAIL(expr, "value still an intish");
556 }
557 IntersectResult(expr, computed_type_);
558 }
559
560
561 void AsmTyper::VisitYield(Yield* expr) {
562 FAIL(expr, "yield expression encountered");
563 }
564
565
566 void AsmTyper::VisitThrow(Throw* expr) {
567 FAIL(expr, "throw statement encountered");
568 }
569
570
571 int AsmTyper::ElementShiftSize(Type* type) {
572 if (type->Is(cache_.kInt8) || type->Is(cache_.kUint8)) return 0;
573 if (type->Is(cache_.kInt16) || type->Is(cache_.kUint16)) return 1;
574 if (type->Is(cache_.kInt32) || type->Is(cache_.kUint32) ||
575 type->Is(cache_.kFloat32))
576 return 2;
577 if (type->Is(cache_.kFloat64)) return 3;
578 return -1;
579 }
580
581
582 void AsmTyper::VisitHeapAccess(Property* expr) {
583 Type::ArrayType* array_type = computed_type_->AsArray();
584 size_t size = array_size_;
585 Type* type = array_type->AsArray()->Element();
586 if (type->IsFunction()) {
587 BinaryOperation* bin = expr->key()->AsBinaryOperation();
588 if (bin == NULL || bin->op() != Token::BIT_AND) {
589 FAIL(expr->key(), "expected & in call");
590 }
591 RECURSE(VisitWithExpectation(bin->left(), cache_.kInt32,
592 "array index expected to be integer"));
593 Literal* right = bin->right()->AsLiteral();
594 if (right == NULL || right->raw_value()->ContainsDot()) {
595 FAIL(right, "call mask must be integer");
596 }
597 RECURSE(VisitWithExpectation(bin->right(), cache_.kInt32,
598 "call mask expected to be integer"));
599 if (static_cast<size_t>(right->raw_value()->AsNumber()) != size - 1) {
600 FAIL(right, "call mask must match function table");
601 }
602 bin->set_bounds(Bounds(cache_.kInt32));
603 } else {
604 BinaryOperation* bin = expr->key()->AsBinaryOperation();
605 if (bin == NULL || bin->op() != Token::SAR) {
606 FAIL(expr->key(), "expected >> in heap access");
607 }
608 RECURSE(VisitWithExpectation(bin->left(), cache_.kInt32,
609 "array index expected to be integer"));
610 Literal* right = bin->right()->AsLiteral();
611 if (right == NULL || right->raw_value()->ContainsDot()) {
612 FAIL(right, "heap access shift must be integer");
613 }
614 RECURSE(VisitWithExpectation(bin->right(), cache_.kInt32,
615 "array shift expected to be integer"));
616 int n = static_cast<int>(right->raw_value()->AsNumber());
617 int expected_shift = ElementShiftSize(type);
618 if (expected_shift < 0 || n != expected_shift) {
619 FAIL(right, "heap access shift must match element size");
620 }
621 bin->set_bounds(Bounds(cache_.kInt32));
622 }
623 IntersectResult(expr, type);
624 }
625
626
627 void AsmTyper::VisitProperty(Property* expr) {
628 // stdlib.Math.x
629 Property* inner_prop = expr->obj()->AsProperty();
630 if (inner_prop != NULL) {
631 // Get property name.
632 Literal* key = expr->key()->AsLiteral();
633 if (key == NULL || !key->IsPropertyName())
634 FAIL(expr, "invalid type annotation on property 2");
635 Handle<String> name = key->AsPropertyName();
636
637 // Check that inner property name is "Math".
638 Literal* math_key = inner_prop->key()->AsLiteral();
639 if (math_key == NULL || !math_key->IsPropertyName() ||
640 !math_key->AsPropertyName()->IsUtf8EqualTo(CStrVector("Math")))
641 FAIL(expr, "invalid type annotation on stdlib (a1)");
642
643 // Check that object is stdlib.
644 VariableProxy* proxy = inner_prop->obj()->AsVariableProxy();
645 if (proxy == NULL) FAIL(expr, "invalid type annotation on stdlib (a2)");
646 Variable* var = proxy->var();
647 if (var->location() != VariableLocation::PARAMETER || var->index() != 0)
648 FAIL(expr, "invalid type annotation on stdlib (a3)");
649
650 // Look up library type.
651 Type* type = LibType(stdlib_math_types_, name);
652 if (type == NULL) FAIL(expr, "unknown standard function 3 ");
653 SetResult(expr, type);
654 return;
655 }
656
657 // Only recurse at this point so that we avoid needing
658 // stdlib.Math to have a real type.
659 RECURSE(VisitWithExpectation(expr->obj(), Type::Any(),
660 "property holder expected to be object"));
661
662 // For heap view or function table access.
663 if (computed_type_->IsArray()) {
664 VisitHeapAccess(expr);
665 return;
666 }
667
668 // Get property name.
669 Literal* key = expr->key()->AsLiteral();
670 if (key == NULL || !key->IsPropertyName())
671 FAIL(expr, "invalid type annotation on property 3");
672 Handle<String> name = key->AsPropertyName();
673
674 // stdlib.x or foreign.x
675 VariableProxy* proxy = expr->obj()->AsVariableProxy();
676 if (proxy != NULL) {
677 Variable* var = proxy->var();
678 if (var->location() != VariableLocation::PARAMETER) {
679 FAIL(expr, "invalid type annotation on variable");
680 }
681 switch (var->index()) {
682 case 0: {
683 // Object is stdlib, look up library type.
684 Type* type = LibType(stdlib_types_, name);
685 if (type == NULL) {
686 FAIL(expr, "unknown standard function 4");
687 }
688 SetResult(expr, type);
689 return;
690 }
691 case 1:
692 // Object is foreign lib.
693 SetResult(expr, expected_type_);
694 return;
695 default:
696 FAIL(expr, "invalid type annotation on parameter");
697 }
698 }
699
700 FAIL(expr, "invalid property access");
701 }
702
703
704 void AsmTyper::VisitCall(Call* expr) {
705 RECURSE(VisitWithExpectation(expr->expression(), Type::Any(),
706 "callee expected to be any"));
707 if (computed_type_->IsFunction()) {
708 Type::FunctionType* fun_type = computed_type_->AsFunction();
709 ZoneList<Expression*>* args = expr->arguments();
710 if (fun_type->Arity() != args->length()) {
711 FAIL(expr, "call with wrong arity");
712 }
713 for (int i = 0; i < args->length(); ++i) {
714 Expression* arg = args->at(i);
715 RECURSE(VisitWithExpectation(
716 arg, fun_type->Parameter(i),
717 "call argument expected to match callee parameter"));
718 }
719 IntersectResult(expr, fun_type->Result());
720 } else if (computed_type_->Is(Type::Any())) {
721 // For foreign calls.
722 ZoneList<Expression*>* args = expr->arguments();
723 for (int i = 0; i < args->length(); ++i) {
724 Expression* arg = args->at(i);
725 RECURSE(VisitWithExpectation(arg, Type::Any(),
726 "foreign call argument expected to be any"));
727 }
728 IntersectResult(expr, Type::Number());
729 } else {
730 FAIL(expr, "invalid callee");
731 }
732 }
733
734
735 void AsmTyper::VisitCallNew(CallNew* expr) {
736 if (in_function_) {
737 FAIL(expr, "new not allowed in module function");
738 }
739 RECURSE(VisitWithExpectation(expr->expression(), Type::Any(),
740 "expected stdlib function"));
741 if (computed_type_->IsFunction()) {
742 Type::FunctionType* fun_type = computed_type_->AsFunction();
743 ZoneList<Expression*>* args = expr->arguments();
744 if (fun_type->Arity() != args->length())
745 FAIL(expr, "call with wrong arity");
746 for (int i = 0; i < args->length(); ++i) {
747 Expression* arg = args->at(i);
748 RECURSE(VisitWithExpectation(
749 arg, fun_type->Parameter(i),
750 "constructor argument expected to match callee parameter"));
751 }
752 IntersectResult(expr, fun_type->Result());
753 return;
754 }
755
756 FAIL(expr, "ill-typed new operator");
757 }
758
759
760 void AsmTyper::VisitCallRuntime(CallRuntime* expr) {
761 // Allow runtime calls for now.
762 }
763
764
765 void AsmTyper::VisitUnaryOperation(UnaryOperation* expr) {
766 switch (expr->op()) {
767 case Token::NOT: // Used to encode != and !==
768 RECURSE(VisitWithExpectation(expr->expression(), cache_.kInt32,
769 "operand expected to be integer"));
770 IntersectResult(expr, cache_.kInt32);
771 return;
772 case Token::DELETE:
773 FAIL(expr, "delete operator encountered");
774 case Token::VOID:
775 FAIL(expr, "void operator encountered");
776 case Token::TYPEOF:
777 FAIL(expr, "typeof operator encountered");
778 default:
779 UNREACHABLE();
780 }
781 }
782
783
784 void AsmTyper::VisitCountOperation(CountOperation* expr) {
785 FAIL(expr, "increment or decrement operator encountered");
786 }
787
788
789 void AsmTyper::VisitBinaryOperation(BinaryOperation* expr) {
790 switch (expr->op()) {
791 case Token::COMMA: {
792 RECURSE(VisitWithExpectation(expr->left(), Type::Any(),
793 "left comma operand expected to be any"));
794 RECURSE(VisitWithExpectation(expr->right(), Type::Any(),
795 "right comma operand expected to be any"));
796 IntersectResult(expr, computed_type_);
797 return;
798 }
799 case Token::OR:
800 case Token::AND:
801 FAIL(expr, "logical operator encountered");
802 case Token::BIT_OR:
803 case Token::BIT_AND:
804 case Token::BIT_XOR:
805 case Token::SHL:
806 case Token::SHR:
807 case Token::SAR: {
808 // BIT_OR allows Any since it is used as a type coercion.
809 // BIT_XOR allows Number since it is used as a type coercion (encoding ~).
810 Type* expectation =
811 expr->op() == Token::BIT_OR
812 ? Type::Any()
813 : expr->op() == Token::BIT_XOR ? Type::Number() : cache_.kInt32;
814 Type* result =
815 expr->op() == Token::SHR ? Type::Unsigned32() : cache_.kInt32;
816 RECURSE(VisitWithExpectation(expr->left(), expectation,
817 "left bit operand expected to be integer"));
818 int left_intish = intish_;
819 RECURSE(VisitWithExpectation(expr->right(), expectation,
820 "right bit operand expected to be integer"));
821 int right_intish = intish_;
822 if (left_intish > kMaxUncombinedAdditiveSteps) {
823 FAIL(expr, "too many consecutive additive ops");
824 }
825 if (right_intish > kMaxUncombinedAdditiveSteps) {
826 FAIL(expr, "too many consecutive additive ops");
827 }
828 intish_ = 0;
829 IntersectResult(expr, result);
830 return;
831 }
832 case Token::ADD:
833 case Token::SUB:
834 case Token::MUL:
835 case Token::DIV:
836 case Token::MOD: {
837 RECURSE(VisitWithExpectation(
838 expr->left(), Type::Number(),
839 "left arithmetic operand expected to be number"));
840 Type* left_type = computed_type_;
841 int left_intish = intish_;
842 RECURSE(VisitWithExpectation(
843 expr->right(), Type::Number(),
844 "right arithmetic operand expected to be number"));
845 Type* right_type = computed_type_;
846 int right_intish = intish_;
847 Type* type = Type::Union(left_type, right_type, zone());
848 if (type->Is(cache_.kInt32)) {
849 if (expr->op() == Token::MUL) {
850 if (!expr->left()->IsLiteral() && !expr->right()->IsLiteral()) {
851 FAIL(expr, "direct integer multiply forbidden");
852 }
853 intish_ = 0;
854 IntersectResult(expr, cache_.kInt32);
855 return;
856 } else {
857 intish_ = left_intish + right_intish + 1;
858 if (expr->op() == Token::ADD || expr->op() == Token::SUB) {
859 if (intish_ > kMaxUncombinedAdditiveSteps) {
860 FAIL(expr, "too many consecutive additive ops");
861 }
862 } else {
863 if (intish_ > kMaxUncombinedMultiplicativeSteps) {
864 FAIL(expr, "too many consecutive multiplicative ops");
865 }
866 }
867 IntersectResult(expr, cache_.kInt32);
868 return;
869 }
870 } else if (type->Is(Type::Number())) {
871 IntersectResult(expr, cache_.kFloat64);
872 return;
873 } else {
874 FAIL(expr, "ill-typed arithmetic operation");
875 }
876 }
877 default:
878 UNREACHABLE();
879 }
880 }
881
882
883 void AsmTyper::VisitCompareOperation(CompareOperation* expr) {
884 RECURSE(
885 VisitWithExpectation(expr->left(), Type::Number(),
886 "left comparison operand expected to be number"));
887 Type* left_type = computed_type_;
888 RECURSE(
889 VisitWithExpectation(expr->right(), Type::Number(),
890 "right comparison operand expected to be number"));
891 Type* right_type = computed_type_;
892 Type* type = Type::Union(left_type, right_type, zone());
893 expr->set_combined_type(type);
894 if (type->Is(Type::Integral32()) || type->Is(Type::UntaggedFloat64())) {
895 IntersectResult(expr, cache_.kInt32);
896 } else {
897 FAIL(expr, "ill-typed comparison operation");
898 }
899 }
900
901
902 void AsmTyper::VisitThisFunction(ThisFunction* expr) {
903 FAIL(expr, "this function not allowed");
904 }
905
906
907 void AsmTyper::VisitDeclarations(ZoneList<Declaration*>* decls) {
908 for (int i = 0; i < decls->length(); ++i) {
909 Declaration* decl = decls->at(i);
910 RECURSE(Visit(decl));
911 }
912 }
913
914
915 void AsmTyper::VisitImportDeclaration(ImportDeclaration* decl) {
916 FAIL(decl, "import declaration encountered");
917 }
918
919
920 void AsmTyper::VisitExportDeclaration(ExportDeclaration* decl) {
921 FAIL(decl, "export declaration encountered");
922 }
923
924
925 void AsmTyper::VisitClassLiteral(ClassLiteral* expr) {
926 FAIL(expr, "class literal not allowed");
927 }
928
929
930 void AsmTyper::VisitSpread(Spread* expr) { FAIL(expr, "spread not allowed"); }
931
932
933 void AsmTyper::VisitSuperPropertyReference(SuperPropertyReference* expr) {
934 FAIL(expr, "super property reference not allowed");
935 }
936
937
938 void AsmTyper::VisitSuperCallReference(SuperCallReference* expr) {
939 FAIL(expr, "call reference not allowed");
940 }
941
942
943 void AsmTyper::InitializeStdlib() {
944 Type* number_type = Type::Number(zone());
945 Type* double_type = cache_.kFloat64;
946 Type* double_fn1_type = Type::Function(double_type, double_type, zone());
947 Type* double_fn2_type =
948 Type::Function(double_type, double_type, double_type, zone());
949
950 Type* fround_type = Type::Function(cache_.kFloat32, number_type, zone());
951 Type* imul_type =
952 Type::Function(cache_.kInt32, cache_.kInt32, cache_.kInt32, zone());
953 // TODO(bradnelson): currently only approximating the proper intersection type
954 // (which we cannot currently represent).
955 Type* abs_type = Type::Function(number_type, number_type, zone());
956
957 struct Assignment {
958 const char* name;
959 Type* type;
960 };
961
962 const Assignment math[] = {
963 {"PI", double_type}, {"E", double_type},
964 {"LN2", double_type}, {"LN10", double_type},
965 {"LOG2E", double_type}, {"LOG10E", double_type},
966 {"SQRT2", double_type}, {"SQRT1_2", double_type},
967 {"imul", imul_type}, {"abs", abs_type},
968 {"ceil", double_fn1_type}, {"floor", double_fn1_type},
969 {"fround", fround_type}, {"pow", double_fn2_type},
970 {"exp", double_fn1_type}, {"log", double_fn1_type},
971 {"min", double_fn2_type}, {"max", double_fn2_type},
972 {"sqrt", double_fn1_type}, {"cos", double_fn1_type},
973 {"sin", double_fn1_type}, {"tan", double_fn1_type},
974 {"acos", double_fn1_type}, {"asin", double_fn1_type},
975 {"atan", double_fn1_type}, {"atan2", double_fn2_type}};
976 for (unsigned i = 0; i < arraysize(math); ++i) {
977 stdlib_math_types_[math[i].name] = math[i].type;
978 }
979
980 stdlib_types_["Infinity"] = double_type;
981 stdlib_types_["NaN"] = double_type;
982 Type* buffer_type = Type::Any(zone());
983 #define TYPED_ARRAY(TypeName, type_name, TYPE_NAME, ctype, size) \
984 stdlib_types_[#TypeName "Array"] = \
985 Type::Function(cache_.k##TypeName##Array, buffer_type, zone());
986 TYPED_ARRAYS(TYPED_ARRAY)
987 #undef TYPED_ARRAY
988
989 #define TYPED_ARRAY(TypeName, type_name, TYPE_NAME, ctype, size) \
990 stdlib_heap_types_[#TypeName "Array"] = \
991 Type::Function(cache_.k##TypeName##Array, buffer_type, zone());
992 TYPED_ARRAYS(TYPED_ARRAY)
993 #undef TYPED_ARRAY
994 }
995
996
997 Type* AsmTyper::LibType(ObjectTypeMap map, Handle<String> name) {
998 base::SmartArrayPointer<char> aname = name->ToCString();
999 ObjectTypeMap::iterator i = map.find(std::string(aname.get()));
1000 if (i == map.end()) {
1001 return NULL;
1002 }
1003 return i->second;
1004 }
1005
1006
1007 void AsmTyper::SetType(Variable* variable, Type* type) {
1008 ZoneHashMap::Entry* entry;
1009 if (in_function_) {
1010 entry = local_variable_type_.LookupOrInsert(
1011 variable, ComputePointerHash(variable), ZoneAllocationPolicy(zone_));
1012 } else {
1013 entry = global_variable_type_.LookupOrInsert(
1014 variable, ComputePointerHash(variable), ZoneAllocationPolicy(zone_));
1015 }
1016 entry->value = reinterpret_cast<void*>(type);
1017 }
1018
1019
1020 Type* AsmTyper::GetType(Variable* variable) {
1021 i::ZoneHashMap::Entry* entry = NULL;
1022 if (in_function_) {
1023 entry = local_variable_type_.Lookup(variable, ComputePointerHash(variable));
1024 }
1025 if (entry == NULL) {
1026 entry =
1027 global_variable_type_.Lookup(variable, ComputePointerHash(variable));
1028 }
1029 if (entry == NULL) {
1030 return NULL;
1031 } else {
1032 return reinterpret_cast<Type*>(entry->value);
1033 }
1034 }
1035
1036
1037 void AsmTyper::SetResult(Expression* expr, Type* type) {
1038 computed_type_ = type;
1039 expr->set_bounds(Bounds(computed_type_));
1040 }
1041
1042
1043 void AsmTyper::IntersectResult(Expression* expr, Type* type) {
1044 computed_type_ = type;
1045 Type* bounded_type = Type::Intersect(computed_type_, expected_type_, zone());
1046 expr->set_bounds(Bounds(bounded_type));
1047 }
1048
1049
1050 void AsmTyper::VisitWithExpectation(Expression* expr, Type* expected_type,
1051 const char* msg) {
1052 Type* save = expected_type_;
1053 expected_type_ = expected_type;
1054 RECURSE(Visit(expr));
1055 Type* bounded_type = Type::Intersect(computed_type_, expected_type_, zone());
1056 if (bounded_type->Is(Type::None(zone()))) {
1057 #ifdef DEBUG
1058 PrintF("Computed type: ");
1059 computed_type_->Print();
1060 PrintF("Expected type: ");
1061 expected_type_->Print();
1062 #endif
1063 FAIL(expr, msg);
1064 }
1065 expected_type_ = save;
1066 }
1067 }
1068 } // namespace v8::internal
OLDNEW
« no previous file with comments | « src/typing-asm.h ('k') | test/cctest/cctest.gyp » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698