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