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 #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) | |
titzer
2015/09/01 14:28:37
Can we make this take a ParseInfo instead, or bett
bradn
2015/09/01 16:28:40
Done.
| |
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, | |
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, | |
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(VisitWithExpectation( | |
434 expr->then_expression(), expected_type_, | |
435 "conditional then branch type mismatch with enclosing expression")); | |
436 Type* then_type = computed_type_; | |
437 RECURSE(VisitWithExpectation( | |
438 expr->else_expression(), expected_type_, | |
439 "conditional else branch type mismatch with enclosing expression")); | |
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 | |
OLD | NEW |