Commit b0711ccc authored by bradnelson's avatar bradnelson Committed by Commit bot

[wasm] Drop old typing-asm and its tests.

BUG= https://bugs.chromium.org/p/v8/issues/detail?id=4203
TEST= cctest/asmjs/test-asm-typer
LOG=N
R=jpp@chromium.org

Review-Url: https://codereview.chromium.org/2146853004
Cr-Commit-Position: refs/heads/master@{#37734}
parent 304572c9
......@@ -794,8 +794,6 @@ v8_source_set("v8_base") {
"src/asmjs/asm-types.h",
"src/asmjs/asm-wasm-builder.cc",
"src/asmjs/asm-wasm-builder.h",
"src/asmjs/typing-asm.cc",
"src/asmjs/typing-asm.h",
"src/assembler.cc",
"src/assembler.h",
"src/assert-scope.cc",
......
// Copyright 2015 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/asmjs/typing-asm.h"
#include <limits>
#include "src/v8.h"
#include "src/ast/ast.h"
#include "src/ast/scopes.h"
#include "src/codegen.h"
#include "src/type-cache.h"
namespace v8 {
namespace internal {
#define FAIL(node, msg) \
do { \
valid_ = false; \
int line = node->position() == kNoSourcePosition \
? -1 \
: script_->GetLineNumber(node->position()); \
base::OS::SNPrintF(error_message_, sizeof(error_message_), \
"asm: line %d: %s\n", line + 1, msg); \
return; \
} while (false)
#define RECURSE(call) \
do { \
DCHECK(!HasStackOverflow()); \
call; \
if (HasStackOverflow()) return; \
if (!valid_) return; \
} while (false)
AsmTyper::AsmTyper(Isolate* isolate, Zone* zone, Script* script,
FunctionLiteral* root)
: zone_(zone),
isolate_(isolate),
script_(script),
root_(root),
valid_(true),
allow_simd_(false),
fixed_signature_(false),
property_info_(nullptr),
intish_(0),
stdlib_types_(zone),
stdlib_heap_types_(zone),
stdlib_math_types_(zone),
#define V(NAME, Name, name, lane_count, lane_type) \
stdlib_simd_##name##_types_(zone),
SIMD128_TYPES(V)
#undef V
global_variable_type_(base::HashMap::PointersMatch,
ZoneHashMap::kDefaultHashMapCapacity,
ZoneAllocationPolicy(zone)),
local_variable_type_(base::HashMap::PointersMatch,
ZoneHashMap::kDefaultHashMapCapacity,
ZoneAllocationPolicy(zone)),
in_function_(false),
building_function_tables_(false),
visiting_exports_(false),
cache_(TypeCache::Get()),
bounds_(zone) {
InitializeAstVisitor(isolate);
InitializeStdlib();
}
bool AsmTyper::Validate() {
VisitAsmModule(root_);
return valid_ && !HasStackOverflow();
}
void AsmTyper::VisitAsmModule(FunctionLiteral* fun) {
Scope* scope = fun->scope();
if (!scope->is_function_scope()) FAIL(fun, "not at function scope");
ExpressionStatement* use_asm = fun->body()->first()->AsExpressionStatement();
if (use_asm == nullptr) FAIL(fun, "missing \"use asm\"");
Literal* use_asm_literal = use_asm->expression()->AsLiteral();
if (use_asm_literal == nullptr) FAIL(fun, "missing \"use asm\"");
if (!use_asm_literal->raw_value()->AsString()->IsOneByteEqualTo("use asm"))
FAIL(fun, "missing \"use asm\"");
// TODO(bradnelson): Generalize this.
if (fixed_signature_ && scope->num_parameters() != 3) {
FAIL(fun,
"only asm modules with (stdlib, foreign, heap) "
"parameters currently supported");
}
// Module parameters.
for (int i = 0; i < scope->num_parameters(); ++i) {
Variable* param = scope->parameter(i);
DCHECK(GetType(param) == nullptr);
SetType(param, Type::None());
}
ZoneList<Declaration*>* decls = scope->declarations();
// Set all globals to type Any.
VariableDeclaration* decl = scope->function();
if (decl != nullptr) SetType(decl->proxy()->var(), Type::None());
RECURSE(VisitDeclarations(scope->declarations()));
// Validate global variables.
RECURSE(VisitStatements(fun->body()));
// Validate function annotations.
for (int i = 0; i < decls->length(); ++i) {
FunctionDeclaration* decl = decls->at(i)->AsFunctionDeclaration();
if (decl != nullptr) {
RECURSE(VisitFunctionAnnotation(decl->fun()));
Variable* var = decl->proxy()->var();
if (property_info_ != nullptr) {
SetVariableInfo(var, property_info_);
property_info_ = nullptr;
}
SetType(var, computed_type_);
DCHECK(GetType(var) != nullptr);
}
}
// Build function tables.
building_function_tables_ = true;
RECURSE(VisitStatements(fun->body()));
building_function_tables_ = false;
// Validate function bodies.
for (int i = 0; i < decls->length(); ++i) {
FunctionDeclaration* decl = decls->at(i)->AsFunctionDeclaration();
if (decl != nullptr) {
RECURSE(VisitWithExpectation(decl->fun(), Type::Any(), "UNREACHABLE"));
if (!computed_type_->IsFunction()) {
FAIL(decl->fun(), "function literal expected to be a function");
}
}
}
// Validate exports.
visiting_exports_ = true;
ReturnStatement* stmt = fun->body()->last()->AsReturnStatement();
if (stmt == nullptr) {
FAIL(fun->body()->last(), "last statement in module is not a return");
}
RECURSE(VisitWithExpectation(stmt->expression(), Type::Object(),
"expected object export"));
}
void AsmTyper::VisitVariableDeclaration(VariableDeclaration* decl) {
Variable* var = decl->proxy()->var();
if (var->location() != VariableLocation::PARAMETER) {
if (GetType(var) == nullptr) {
SetType(var, Type::Any());
} else {
DCHECK(!GetType(var)->IsFunction());
}
}
DCHECK(GetType(var) != nullptr);
intish_ = 0;
}
void AsmTyper::VisitFunctionDeclaration(FunctionDeclaration* decl) {
if (in_function_) {
FAIL(decl, "function declared inside another");
}
// Set function type so global references to functions have some type
// (so they can give a more useful error).
Variable* var = decl->proxy()->var();
if (GetVariableInfo(var)) {
// Detect previously-seen functions.
FAIL(decl->fun(), "function repeated in module");
}
SetType(var, Type::Function());
}
void AsmTyper::VisitFunctionAnnotation(FunctionLiteral* fun) {
// Extract result type.
ZoneList<Statement*>* body = fun->body();
Type* result_type = Type::Undefined();
if (body->length() > 0) {
ReturnStatement* stmt = body->last()->AsReturnStatement();
if (stmt != nullptr) {
Literal* literal = stmt->expression()->AsLiteral();
Type* old_expected = expected_type_;
expected_type_ = Type::Any();
if (literal) {
RECURSE(VisitLiteral(literal, true));
} else {
RECURSE(VisitExpressionAnnotation(stmt->expression(), nullptr, true));
}
expected_type_ = old_expected;
result_type = computed_type_;
}
}
Type* type =
Type::Function(result_type, Type::Any(), fun->parameter_count(), zone());
// Extract parameter types.
bool good = true;
for (int i = 0; i < fun->parameter_count(); ++i) {
good = false;
if (i >= body->length()) break;
ExpressionStatement* stmt = body->at(i)->AsExpressionStatement();
if (stmt == nullptr) break;
Assignment* expr = stmt->expression()->AsAssignment();
if (expr == nullptr || expr->is_compound()) break;
VariableProxy* proxy = expr->target()->AsVariableProxy();
if (proxy == nullptr) break;
Variable* var = proxy->var();
if (var->location() != VariableLocation::PARAMETER || var->index() != i)
break;
RECURSE(VisitExpressionAnnotation(expr->value(), var, false));
if (property_info_ != nullptr) {
SetVariableInfo(var, property_info_);
property_info_ = nullptr;
}
SetType(var, computed_type_);
type->AsFunction()->InitParameter(i, computed_type_);
good = true;
}
if (!good) FAIL(fun, "missing parameter type annotations");
SetResult(fun, type);
}
void AsmTyper::VisitExpressionAnnotation(Expression* expr, Variable* var,
bool is_return) {
// Normal +x or x|0 annotations.
BinaryOperation* bin = expr->AsBinaryOperation();
if (bin != nullptr) {
if (var != nullptr) {
VariableProxy* proxy = bin->left()->AsVariableProxy();
if (proxy == nullptr) {
FAIL(bin->left(), "expected variable for type annotation");
}
if (proxy->var() != var) {
FAIL(proxy, "annotation source doesn't match destination");
}
}
Literal* right = bin->right()->AsLiteral();
if (right != nullptr) {
switch (bin->op()) {
case Token::MUL: // We encode +x as x*1.0
if (right->raw_value()->ContainsDot() &&
right->raw_value()->AsNumber() == 1.0) {
SetResult(expr, cache_.kAsmDouble);
return;
}
break;
case Token::BIT_OR:
if (!right->raw_value()->ContainsDot() &&
right->raw_value()->AsNumber() == 0.0) {
if (is_return) {
SetResult(expr, cache_.kAsmSigned);
} else {
SetResult(expr, cache_.kAsmInt);
}
return;
}
break;
default:
break;
}
}
FAIL(expr, "invalid type annotation on binary op");
}
// Numbers or the undefined literal (for empty returns).
if (expr->IsLiteral()) {
RECURSE(VisitWithExpectation(expr, Type::Any(), "invalid literal"));
return;
}
Call* call = expr->AsCall();
if (call != nullptr) {
VariableProxy* proxy = call->expression()->AsVariableProxy();
if (proxy != nullptr) {
VariableInfo* info = GetVariableInfo(proxy->var());
if (!info ||
(!info->is_check_function && !info->is_constructor_function)) {
if (allow_simd_) {
FAIL(call->expression(),
"only fround/SIMD.checks allowed on expression annotations");
} else {
FAIL(call->expression(),
"only fround allowed on expression annotations");
}
}
Type* type = info->type;
DCHECK(type->IsFunction());
if (info->is_check_function) {
DCHECK(type->AsFunction()->Arity() == 1);
}
if (call->arguments()->length() != type->AsFunction()->Arity()) {
FAIL(call, "invalid argument count calling function");
}
SetResult(expr, type->AsFunction()->Result());
return;
}
}
FAIL(expr, "invalid type annotation");
}
void AsmTyper::VisitStatements(ZoneList<Statement*>* stmts) {
for (int i = 0; i < stmts->length(); ++i) {
Statement* stmt = stmts->at(i);
RECURSE(Visit(stmt));
}
}
void AsmTyper::VisitBlock(Block* stmt) {
RECURSE(VisitStatements(stmt->statements()));
}
void AsmTyper::VisitExpressionStatement(ExpressionStatement* stmt) {
RECURSE(VisitWithExpectation(stmt->expression(), Type::Any(),
"expression statement expected to be any"));
}
void AsmTyper::VisitEmptyStatement(EmptyStatement* stmt) {}
void AsmTyper::VisitSloppyBlockFunctionStatement(
SloppyBlockFunctionStatement* stmt) {
Visit(stmt->statement());
}
void AsmTyper::VisitEmptyParentheses(EmptyParentheses* expr) { UNREACHABLE(); }
void AsmTyper::VisitIfStatement(IfStatement* stmt) {
if (!in_function_) {
FAIL(stmt, "if statement inside module body");
}
RECURSE(VisitWithExpectation(stmt->condition(), cache_.kAsmInt,
"if condition expected to be integer"));
if (intish_ != 0) {
FAIL(stmt, "if condition expected to be signed or unsigned");
}
RECURSE(Visit(stmt->then_statement()));
RECURSE(Visit(stmt->else_statement()));
}
void AsmTyper::VisitContinueStatement(ContinueStatement* stmt) {
if (!in_function_) {
FAIL(stmt, "continue statement inside module body");
}
}
void AsmTyper::VisitBreakStatement(BreakStatement* stmt) {
if (!in_function_) {
FAIL(stmt, "continue statement inside module body");
}
}
void AsmTyper::VisitReturnStatement(ReturnStatement* stmt) {
// Handle module return statement in VisitAsmModule.
if (!in_function_) {
return;
}
Literal* literal = stmt->expression()->AsLiteral();
if (literal) {
VisitLiteral(literal, true);
} else {
RECURSE(
VisitWithExpectation(stmt->expression(), Type::Any(),
"return expression expected to have return type"));
}
if (!computed_type_->Is(return_type_) || !return_type_->Is(computed_type_)) {
FAIL(stmt->expression(), "return type does not match function signature");
}
}
void AsmTyper::VisitWithStatement(WithStatement* stmt) {
FAIL(stmt, "bad with statement");
}
void AsmTyper::VisitSwitchStatement(SwitchStatement* stmt) {
if (!in_function_) {
FAIL(stmt, "switch statement inside module body");
}
RECURSE(VisitWithExpectation(stmt->tag(), cache_.kAsmSigned,
"switch expression non-integer"));
ZoneList<CaseClause*>* clauses = stmt->cases();
ZoneSet<int32_t> cases(zone());
for (int i = 0; i < clauses->length(); ++i) {
CaseClause* clause = clauses->at(i);
if (clause->is_default()) {
if (i != clauses->length() - 1) {
FAIL(clause, "default case out of order");
}
} else {
Expression* label = clause->label();
RECURSE(VisitWithExpectation(label, cache_.kAsmSigned,
"case label non-integer"));
if (!label->IsLiteral()) FAIL(label, "non-literal case label");
Handle<Object> value = label->AsLiteral()->value();
int32_t value32;
if (!value->ToInt32(&value32)) FAIL(label, "illegal case label value");
if (cases.find(value32) != cases.end()) {
FAIL(label, "duplicate case value");
}
cases.insert(value32);
}
// TODO(bradnelson): Detect duplicates.
ZoneList<Statement*>* stmts = clause->statements();
RECURSE(VisitStatements(stmts));
}
if (cases.size() > 0) {
int64_t min_case = *cases.begin();
int64_t max_case = *cases.rbegin();
if (max_case - min_case > std::numeric_limits<int32_t>::max()) {
FAIL(stmt, "case range too large");
}
}
}
void AsmTyper::VisitCaseClause(CaseClause* clause) { UNREACHABLE(); }
void AsmTyper::VisitDoWhileStatement(DoWhileStatement* stmt) {
if (!in_function_) {
FAIL(stmt, "do statement inside module body");
}
RECURSE(Visit(stmt->body()));
RECURSE(VisitWithExpectation(stmt->cond(), cache_.kAsmInt,
"do condition expected to be integer"));
if (intish_ != 0) {
FAIL(stmt, "do condition expected to be signed or unsigned");
}
}
void AsmTyper::VisitWhileStatement(WhileStatement* stmt) {
if (!in_function_) {
FAIL(stmt, "while statement inside module body");
}
RECURSE(VisitWithExpectation(stmt->cond(), cache_.kAsmInt,
"while condition expected to be integer"));
if (intish_ != 0) {
FAIL(stmt, "while condition expected to be signed or unsigned");
}
RECURSE(Visit(stmt->body()));
}
void AsmTyper::VisitForStatement(ForStatement* stmt) {
if (!in_function_) {
FAIL(stmt, "for statement inside module body");
}
if (stmt->init() != nullptr) {
RECURSE(Visit(stmt->init()));
}
if (stmt->cond() != nullptr) {
RECURSE(VisitWithExpectation(stmt->cond(), cache_.kAsmInt,
"for condition expected to be integer"));
}
if (intish_ != 0) {
FAIL(stmt, "for condition expected to be signed or unsigned");
}
if (stmt->next() != nullptr) {
RECURSE(Visit(stmt->next()));
}
RECURSE(Visit(stmt->body()));
}
void AsmTyper::VisitForInStatement(ForInStatement* stmt) {
FAIL(stmt, "for-in statement encountered");
}
void AsmTyper::VisitForOfStatement(ForOfStatement* stmt) {
FAIL(stmt, "for-of statement encountered");
}
void AsmTyper::VisitTryCatchStatement(TryCatchStatement* stmt) {
FAIL(stmt, "try statement encountered");
}
void AsmTyper::VisitTryFinallyStatement(TryFinallyStatement* stmt) {
FAIL(stmt, "try statement encountered");
}
void AsmTyper::VisitDebuggerStatement(DebuggerStatement* stmt) {
FAIL(stmt, "debugger statement encountered");
}
void AsmTyper::VisitFunctionLiteral(FunctionLiteral* expr) {
if (in_function_) {
FAIL(expr, "invalid nested function");
}
Scope* scope = expr->scope();
DCHECK(scope->is_function_scope());
if (!bounds_.get(expr).upper->IsFunction()) {
FAIL(expr, "invalid function literal");
}
Type* type = bounds_.get(expr).upper;
Type* save_return_type = return_type_;
return_type_ = type->AsFunction()->Result();
in_function_ = true;
local_variable_type_.Clear();
RECURSE(VisitDeclarations(scope->declarations()));
RECURSE(VisitStatements(expr->body()));
in_function_ = false;
return_type_ = save_return_type;
RECURSE(IntersectResult(expr, type));
}
void AsmTyper::VisitNativeFunctionLiteral(NativeFunctionLiteral* expr) {
FAIL(expr, "function info literal encountered");
}
void AsmTyper::VisitDoExpression(DoExpression* expr) {
FAIL(expr, "do-expression encountered");
}
void AsmTyper::VisitConditional(Conditional* expr) {
if (!in_function_) {
FAIL(expr, "ternary operator inside module body");
}
RECURSE(VisitWithExpectation(expr->condition(), Type::Number(),
"condition expected to be integer"));
if (!computed_type_->Is(cache_.kAsmInt)) {
FAIL(expr->condition(), "condition must be of type int");
}
RECURSE(VisitWithExpectation(
expr->then_expression(), expected_type_,
"conditional then branch type mismatch with enclosing expression"));
Type* then_type = StorageType(computed_type_);
int then_intish = intish_;
RECURSE(VisitWithExpectation(
expr->else_expression(), expected_type_,
"conditional else branch type mismatch with enclosing expression"));
Type* else_type = StorageType(computed_type_);
int else_intish = intish_;
if (then_intish != 0 || else_intish != 0 ||
!((then_type->Is(cache_.kAsmInt) && else_type->Is(cache_.kAsmInt)) ||
(then_type->Is(cache_.kAsmFloat) && else_type->Is(cache_.kAsmFloat)) ||
(then_type->Is(cache_.kAsmDouble) &&
else_type->Is(cache_.kAsmDouble)))) {
FAIL(expr,
"then and else expressions in ? must have the same type "
"and be int, float, or double");
}
RECURSE(IntersectResult(expr, then_type));
}
void AsmTyper::VisitVariableProxy(VariableProxy* expr) {
Variable* var = expr->var();
VariableInfo* info = GetVariableInfo(var);
if (!in_function_ && !building_function_tables_ && !visiting_exports_) {
if (var->location() != VariableLocation::PARAMETER || var->index() >= 3) {
FAIL(expr, "illegal variable reference in module body");
}
}
if (info == nullptr || info->type == nullptr) {
if (var->mode() == TEMPORARY) {
SetType(var, Type::Any());
info = GetVariableInfo(var);
} else {
FAIL(expr, "unbound variable");
}
}
if (property_info_ != nullptr) {
SetVariableInfo(var, property_info_);
property_info_ = nullptr;
}
Type* type = Type::Intersect(info->type, expected_type_, zone());
if (type->Is(cache_.kAsmInt)) type = cache_.kAsmInt;
intish_ = 0;
RECURSE(IntersectResult(expr, type));
}
void AsmTyper::VisitLiteral(Literal* expr, bool is_return) {
intish_ = 0;
Handle<Object> value = expr->value();
if (value->IsNumber()) {
int32_t i;
uint32_t u;
if (expr->raw_value()->ContainsDot()) {
RECURSE(IntersectResult(expr, cache_.kAsmDouble));
} else if (!is_return && value->ToUint32(&u)) {
if (u <= 0x7fffffff) {
RECURSE(IntersectResult(expr, cache_.kAsmFixnum));
} else {
RECURSE(IntersectResult(expr, cache_.kAsmUnsigned));
}
} else if (value->ToInt32(&i)) {
RECURSE(IntersectResult(expr, cache_.kAsmSigned));
} else {
FAIL(expr, "illegal number");
}
} else if (!is_return && value->IsString()) {
RECURSE(IntersectResult(expr, Type::String()));
} else if (value->IsUndefined(isolate_)) {
RECURSE(IntersectResult(expr, Type::Undefined()));
} else {
FAIL(expr, "illegal literal");
}
}
void AsmTyper::VisitLiteral(Literal* expr) { VisitLiteral(expr, false); }
void AsmTyper::VisitRegExpLiteral(RegExpLiteral* expr) {
FAIL(expr, "regular expression encountered");
}
void AsmTyper::VisitObjectLiteral(ObjectLiteral* expr) {
if (in_function_) {
FAIL(expr, "object literal in function");
}
// Allowed for asm module's export declaration.
ZoneList<ObjectLiteralProperty*>* props = expr->properties();
for (int i = 0; i < props->length(); ++i) {
ObjectLiteralProperty* prop = props->at(i);
RECURSE(VisitWithExpectation(prop->value(), Type::Any(),
"object property expected to be a function"));
if (!computed_type_->IsFunction()) {
FAIL(prop->value(), "non-function in function table");
}
}
RECURSE(IntersectResult(expr, Type::Object()));
}
void AsmTyper::VisitArrayLiteral(ArrayLiteral* expr) {
if (in_function_) {
FAIL(expr, "array literal inside a function");
}
// Allowed for function tables.
ZoneList<Expression*>* values = expr->values();
Type* elem_type = Type::None();
for (int i = 0; i < values->length(); ++i) {
Expression* value = values->at(i);
RECURSE(VisitWithExpectation(value, Type::Any(), "UNREACHABLE"));
if (!computed_type_->IsFunction()) {
FAIL(value, "array component expected to be a function");
}
elem_type = Type::Union(elem_type, computed_type_, zone());
}
array_size_ = values->length();
RECURSE(IntersectResult(expr, Type::Array(elem_type, zone())));
}
void AsmTyper::VisitAssignment(Assignment* expr) {
// Handle function tables and everything else in different passes.
if (!in_function_) {
if (expr->value()->IsArrayLiteral()) {
if (!building_function_tables_) {
return;
}
} else {
if (building_function_tables_) {
return;
}
}
}
if (expr->is_compound()) FAIL(expr, "compound assignment encountered");
Type* type = expected_type_;
RECURSE(VisitWithExpectation(
expr->value(), type, "assignment value expected to match surrounding"));
Type* target_type = StorageType(computed_type_);
if (expr->target()->IsVariableProxy()) {
// Assignment to a local or context variable.
VariableProxy* proxy = expr->target()->AsVariableProxy();
if (intish_ != 0) {
FAIL(expr, "intish or floatish assignment");
}
if (in_function_ && target_type->IsArray()) {
FAIL(expr, "assignment to array variable");
}
expected_type_ = target_type;
Variable* var = proxy->var();
if (!in_function_ && var->IsParameter()) {
FAIL(expr, "assignment to module parameter");
}
VariableInfo* info = GetVariableInfo(var);
if (info == nullptr || info->type == nullptr) {
if (var->mode() == TEMPORARY) {
SetType(var, Type::Any());
info = GetVariableInfo(var);
} else {
FAIL(proxy, "unbound variable");
}
}
if (property_info_ != nullptr) {
SetVariableInfo(var, property_info_);
property_info_ = nullptr;
}
Type* type = Type::Intersect(info->type, expected_type_, zone());
if (type->Is(cache_.kAsmInt)) type = cache_.kAsmInt;
info->type = type;
intish_ = 0;
RECURSE(IntersectResult(proxy, type));
} else if (expr->target()->IsProperty()) {
// Assignment to a property: should be a heap assignment {H[x] = y}.
int32_t value_intish = intish_;
Property* property = expr->target()->AsProperty();
RECURSE(VisitWithExpectation(property->obj(), Type::Any(),
"bad propety object"));
if (!computed_type_->IsArray()) {
FAIL(property->obj(), "array expected");
}
if (value_intish != 0 && computed_type_->Is(cache_.kFloat64Array)) {
FAIL(expr, "floatish assignment to double array");
}
VisitHeapAccess(property, true, target_type);
}
RECURSE(IntersectResult(expr, target_type));
}
void AsmTyper::VisitYield(Yield* expr) {
FAIL(expr, "yield expression encountered");
}
void AsmTyper::VisitThrow(Throw* expr) {
FAIL(expr, "throw statement encountered");
}
int AsmTyper::ElementShiftSize(Type* type) {
if (type->Is(cache_.kAsmSize8)) return 0;
if (type->Is(cache_.kAsmSize16)) return 1;
if (type->Is(cache_.kAsmSize32)) return 2;
if (type->Is(cache_.kAsmSize64)) return 3;
return -1;
}
Type* AsmTyper::StorageType(Type* type) {
if (type->Is(cache_.kAsmInt)) {
return cache_.kAsmInt;
} else {
return type;
}
}
void AsmTyper::VisitHeapAccess(Property* expr, bool assigning,
Type* assignment_type) {
ArrayType* array_type = computed_type_->AsArray();
// size_t size = array_size_;
Type* type = array_type->Element();
if (type->IsFunction()) {
if (assigning) {
FAIL(expr, "assigning to function table is illegal");
}
// TODO(bradnelson): Fix the parser and then un-comment this part
// BinaryOperation* bin = expr->key()->AsBinaryOperation();
// if (bin == nullptr || bin->op() != Token::BIT_AND) {
// FAIL(expr->key(), "expected & in call");
// }
// RECURSE(VisitWithExpectation(bin->left(), cache_.kAsmSigned,
// "array index expected to be integer"));
// Literal* right = bin->right()->AsLiteral();
// if (right == nullptr || right->raw_value()->ContainsDot()) {
// FAIL(right, "call mask must be integer");
// }
// RECURSE(VisitWithExpectation(bin->right(), cache_.kAsmSigned,
// "call mask expected to be integer"));
// if (static_cast<size_t>(right->raw_value()->AsNumber()) != size - 1) {
// FAIL(right, "call mask must match function table");
// }
// bin->set_bounds(Bounds(cache_.kAsmSigned));
RECURSE(
VisitWithExpectation(expr->key(), cache_.kAsmSigned, "must be int"));
RECURSE(IntersectResult(expr, type));
} else {
Literal* literal = expr->key()->AsLiteral();
if (literal) {
RECURSE(VisitWithExpectation(literal, cache_.kAsmUnsigned,
"array index expected to be unsigned"));
} else {
int expected_shift = ElementShiftSize(type);
if (expected_shift == 0) {
RECURSE(Visit(expr->key()));
} else {
BinaryOperation* bin = expr->key()->AsBinaryOperation();
if (bin == nullptr || bin->op() != Token::SAR) {
FAIL(expr->key(), "expected >> in heap access");
}
RECURSE(VisitWithExpectation(bin->left(), cache_.kAsmInt,
"array index expected to be integer"));
Literal* right = bin->right()->AsLiteral();
if (right == nullptr || right->raw_value()->ContainsDot()) {
FAIL(bin->right(), "heap access shift must be integer");
}
RECURSE(VisitWithExpectation(bin->right(), cache_.kAsmFixnum,
"array shift expected to be Fixnum"));
int n = static_cast<int>(right->raw_value()->AsNumber());
if (expected_shift < 0 || n != expected_shift) {
FAIL(right, "heap access shift must match element size");
}
}
bounds_.set(expr->key(), Bounds(cache_.kAsmUnsigned));
}
Type* result_type;
if (type->Is(cache_.kAsmIntArrayElement)) {
result_type = cache_.kAsmIntQ;
intish_ = kMaxUncombinedAdditiveSteps;
} else if (type->Is(cache_.kAsmFloat)) {
if (assigning) {
result_type = cache_.kAsmFloatDoubleQ;
} else {
result_type = cache_.kAsmFloatQ;
}
intish_ = 0;
} else if (type->Is(cache_.kAsmDouble)) {
if (assigning) {
result_type = cache_.kAsmFloatDoubleQ;
if (intish_ != 0) {
FAIL(expr, "Assignment of floatish to Float64Array");
}
} else {
result_type = cache_.kAsmDoubleQ;
}
intish_ = 0;
} else {
UNREACHABLE();
}
if (assigning) {
if (!assignment_type->Is(result_type)) {
FAIL(expr, "illegal type in assignment");
}
} else {
RECURSE(IntersectResult(expr, expected_type_));
RECURSE(IntersectResult(expr, result_type));
}
}
}
bool AsmTyper::IsStdlibObject(Expression* expr) {
VariableProxy* proxy = expr->AsVariableProxy();
if (proxy == nullptr) {
return false;
}
Variable* var = proxy->var();
VariableInfo* info = GetVariableInfo(var);
if (info) {
if (info->standard_member == kStdlib) return true;
}
if (var->location() != VariableLocation::PARAMETER || var->index() != 0) {
return false;
}
info = MakeVariableInfo(var);
info->type = Type::Object();
info->standard_member = kStdlib;
return true;
}
Expression* AsmTyper::GetReceiverOfPropertyAccess(Expression* expr,
const char* name) {
Property* property = expr->AsProperty();
if (property == nullptr) {
return nullptr;
}
Literal* key = property->key()->AsLiteral();
if (key == nullptr || !key->IsPropertyName() ||
!key->AsPropertyName()->IsUtf8EqualTo(CStrVector(name))) {
return nullptr;
}
return property->obj();
}
bool AsmTyper::IsMathObject(Expression* expr) {
Expression* obj = GetReceiverOfPropertyAccess(expr, "Math");
return obj && IsStdlibObject(obj);
}
bool AsmTyper::IsSIMDObject(Expression* expr) {
Expression* obj = GetReceiverOfPropertyAccess(expr, "SIMD");
return obj && IsStdlibObject(obj);
}
bool AsmTyper::IsSIMDTypeObject(Expression* expr, const char* name) {
Expression* obj = GetReceiverOfPropertyAccess(expr, name);
return obj && IsSIMDObject(obj);
}
void AsmTyper::VisitProperty(Property* expr) {
if (IsMathObject(expr->obj())) {
VisitLibraryAccess(&stdlib_math_types_, expr);
return;
}
#define V(NAME, Name, name, lane_count, lane_type) \
if (IsSIMDTypeObject(expr->obj(), #Name)) { \
VisitLibraryAccess(&stdlib_simd_##name##_types_, expr); \
return; \
} \
if (IsSIMDTypeObject(expr, #Name)) { \
VariableInfo* info = stdlib_simd_##name##_constructor_type_; \
SetResult(expr, info->type); \
property_info_ = info; \
return; \
}
SIMD128_TYPES(V)
#undef V
if (IsStdlibObject(expr->obj())) {
VisitLibraryAccess(&stdlib_types_, expr);
return;
}
property_info_ = nullptr;
// Only recurse at this point so that we avoid needing
// stdlib.Math to have a real type.
RECURSE(
VisitWithExpectation(expr->obj(), Type::Any(), "bad property object"));
// For heap view or function table access.
if (computed_type_->IsArray()) {
VisitHeapAccess(expr, false, nullptr);
return;
}
VariableProxy* proxy = expr->obj()->AsVariableProxy();
if (proxy != nullptr) {
Variable* var = proxy->var();
if (var->location() == VariableLocation::PARAMETER && var->index() == 1) {
// foreign.x - Function represent as () -> Any
if (Type::Any()->Is(expected_type_)) {
SetResult(expr, Type::Function(Type::Any(), zone()));
} else {
SetResult(expr, expected_type_);
}
return;
}
}
FAIL(expr, "invalid property access");
}
void AsmTyper::CheckPolymorphicStdlibArguments(
enum StandardMember standard_member, ZoneList<Expression*>* args) {
if (args->length() == 0) {
return;
}
// Handle polymorphic stdlib functions specially.
Expression* arg0 = args->at(0);
Type* arg0_type = bounds_.get(arg0).upper;
switch (standard_member) {
case kMathFround: {
if (!arg0_type->Is(cache_.kAsmFloat) &&
!arg0_type->Is(cache_.kAsmDouble) &&
!arg0_type->Is(cache_.kAsmSigned) &&
!arg0_type->Is(cache_.kAsmUnsigned)) {
FAIL(arg0, "illegal function argument type");
}
break;
}
case kMathCeil:
case kMathFloor:
case kMathSqrt: {
if (!arg0_type->Is(cache_.kAsmFloat) &&
!arg0_type->Is(cache_.kAsmDouble)) {
FAIL(arg0, "illegal function argument type");
}
break;
}
case kMathAbs:
case kMathMin:
case kMathMax: {
if (!arg0_type->Is(cache_.kAsmFloat) &&
!arg0_type->Is(cache_.kAsmDouble) &&
!arg0_type->Is(cache_.kAsmSigned)) {
FAIL(arg0, "illegal function argument type");
}
if (args->length() > 1) {
Type* other = Type::Intersect(bounds_.get(args->at(0)).upper,
bounds_.get(args->at(1)).upper, zone());
if (!other->Is(cache_.kAsmFloat) && !other->Is(cache_.kAsmDouble) &&
!other->Is(cache_.kAsmSigned)) {
FAIL(arg0, "function arguments types don't match");
}
}
break;
}
default: { break; }
}
}
void AsmTyper::VisitCall(Call* expr) {
Type* expected_type = expected_type_;
RECURSE(VisitWithExpectation(expr->expression(), Type::Any(),
"callee expected to be any"));
StandardMember standard_member = kNone;
VariableProxy* proxy = expr->expression()->AsVariableProxy();
if (proxy) {
standard_member = VariableAsStandardMember(proxy->var());
}
if (!in_function_ && (proxy == nullptr || standard_member != kMathFround)) {
FAIL(expr, "calls forbidden outside function bodies");
}
if (proxy == nullptr && !expr->expression()->IsProperty()) {
FAIL(expr, "calls must be to bound variables or function tables");
}
if (computed_type_->IsFunction()) {
FunctionType* fun_type = computed_type_->AsFunction();
Type* result_type = fun_type->Result();
ZoneList<Expression*>* args = expr->arguments();
if (Type::Any()->Is(result_type)) {
// For foreign calls.
for (int i = 0; i < args->length(); ++i) {
Expression* arg = args->at(i);
RECURSE(VisitWithExpectation(
arg, Type::Any(), "foreign call argument expected to be any"));
// Checking for asm extern types explicitly, as the type system
// doesn't correctly check their inheritance relationship.
if (!computed_type_->Is(cache_.kAsmSigned) &&
!computed_type_->Is(cache_.kAsmFixnum) &&
!computed_type_->Is(cache_.kAsmDouble)) {
FAIL(arg,
"foreign call argument expected to be int, double, or fixnum");
}
}
intish_ = 0;
bounds_.set(expr->expression(),
Bounds(Type::Function(Type::Any(), zone())));
RECURSE(IntersectResult(expr, expected_type));
} else {
if (fun_type->Arity() != args->length()) {
FAIL(expr, "call with wrong arity");
}
for (int i = 0; i < args->length(); ++i) {
Expression* arg = args->at(i);
RECURSE(VisitWithExpectation(
arg, fun_type->Parameter(i),
"call argument expected to match callee parameter"));
if (standard_member != kNone && standard_member != kMathFround &&
i == 0) {
result_type = computed_type_;
}
}
RECURSE(CheckPolymorphicStdlibArguments(standard_member, args));
intish_ = 0;
RECURSE(IntersectResult(expr, result_type));
}
} else {
FAIL(expr, "invalid callee");
}
}
void AsmTyper::VisitCallNew(CallNew* expr) {
if (in_function_) {
FAIL(expr, "new not allowed in module function");
}
RECURSE(VisitWithExpectation(expr->expression(), Type::Any(),
"expected stdlib function"));
if (computed_type_->IsFunction()) {
FunctionType* fun_type = computed_type_->AsFunction();
ZoneList<Expression*>* args = expr->arguments();
if (fun_type->Arity() != args->length())
FAIL(expr, "call with wrong arity");
for (int i = 0; i < args->length(); ++i) {
Expression* arg = args->at(i);
RECURSE(VisitWithExpectation(
arg, fun_type->Parameter(i),
"constructor argument expected to match callee parameter"));
}
RECURSE(IntersectResult(expr, fun_type->Result()));
return;
}
FAIL(expr, "ill-typed new operator");
}
void AsmTyper::VisitCallRuntime(CallRuntime* expr) {
FAIL(expr, "runtime call not allowed");
}
void AsmTyper::VisitUnaryOperation(UnaryOperation* expr) {
if (!in_function_) {
FAIL(expr, "unary operator inside module body");
}
switch (expr->op()) {
case Token::NOT: // Used to encode != and !==
RECURSE(VisitWithExpectation(expr->expression(), cache_.kAsmInt,
"operand expected to be integer"));
RECURSE(IntersectResult(expr, cache_.kAsmSigned));
return;
case Token::DELETE:
FAIL(expr, "delete operator encountered");
case Token::VOID:
FAIL(expr, "void operator encountered");
case Token::TYPEOF:
FAIL(expr, "typeof operator encountered");
default:
UNREACHABLE();
}
}
void AsmTyper::VisitCountOperation(CountOperation* expr) {
FAIL(expr, "increment or decrement operator encountered");
}
void AsmTyper::VisitIntegerBitwiseOperator(BinaryOperation* expr,
Type* left_expected,
Type* right_expected,
Type* result_type, bool conversion) {
RECURSE(VisitWithExpectation(expr->left(), Type::Number(),
"left bitwise operand expected to be a number"));
int32_t left_intish = intish_;
Type* left_type = computed_type_;
if (!left_type->Is(left_expected)) {
FAIL(expr->left(), "left bitwise operand expected to be an integer");
}
if (left_intish > kMaxUncombinedAdditiveSteps) {
FAIL(expr->left(), "too many consecutive additive ops");
}
RECURSE(
VisitWithExpectation(expr->right(), Type::Number(),
"right bitwise operand expected to be a number"));
int32_t right_intish = intish_;
Type* right_type = computed_type_;
if (!right_type->Is(right_expected)) {
FAIL(expr->right(), "right bitwise operand expected to be an integer");
}
if (right_intish > kMaxUncombinedAdditiveSteps) {
FAIL(expr->right(), "too many consecutive additive ops");
}
intish_ = 0;
if (left_type->Is(cache_.kAsmFixnum) && right_type->Is(cache_.kAsmInt)) {
left_type = right_type;
}
if (right_type->Is(cache_.kAsmFixnum) && left_type->Is(cache_.kAsmInt)) {
right_type = left_type;
}
if (!conversion) {
if (!left_type->Is(cache_.kAsmIntQ) || !right_type->Is(cache_.kAsmIntQ)) {
FAIL(expr, "ill-typed bitwise operation");
}
}
RECURSE(IntersectResult(expr, result_type));
}
void AsmTyper::VisitBinaryOperation(BinaryOperation* expr) {
if (!in_function_) {
if (expr->op() != Token::BIT_OR && expr->op() != Token::MUL) {
FAIL(expr, "illegal binary operator inside module body");
}
if (!(expr->left()->IsProperty() || expr->left()->IsVariableProxy()) ||
!expr->right()->IsLiteral()) {
FAIL(expr, "illegal computation inside module body");
}
DCHECK(expr->right()->AsLiteral() != nullptr);
const AstValue* right_value = expr->right()->AsLiteral()->raw_value();
if (expr->op() == Token::BIT_OR) {
if (right_value->AsNumber() != 0.0 || right_value->ContainsDot()) {
FAIL(expr, "illegal integer annotation value");
}
}
if (expr->op() == Token::MUL) {
if (right_value->AsNumber() != 1.0 && right_value->ContainsDot()) {
FAIL(expr, "illegal double annotation value");
}
}
}
switch (expr->op()) {
case Token::COMMA: {
RECURSE(VisitWithExpectation(expr->left(), Type::Any(),
"left comma operand expected to be any"));
RECURSE(VisitWithExpectation(expr->right(), Type::Any(),
"right comma operand expected to be any"));
RECURSE(IntersectResult(expr, computed_type_));
return;
}
case Token::OR:
case Token::AND:
FAIL(expr, "illegal logical operator");
case Token::BIT_OR: {
// BIT_OR allows Any since it is used as a type coercion.
RECURSE(VisitIntegerBitwiseOperator(expr, Type::Any(), cache_.kAsmIntQ,
cache_.kAsmSigned, true));
if (expr->left()->IsCall() && expr->op() == Token::BIT_OR &&
Type::Number()->Is(bounds_.get(expr->left()).upper)) {
// Force the return types of foreign functions.
bounds_.set(expr->left(), Bounds(cache_.kAsmSigned));
}
if (in_function_ &&
!bounds_.get(expr->left()).upper->Is(cache_.kAsmIntQ)) {
FAIL(expr->left(), "intish required");
}
return;
}
case Token::BIT_XOR: {
// Handle booleans specially to handle de-sugared !
Literal* left = expr->left()->AsLiteral();
if (left && left->value()->IsBoolean()) {
if (left->ToBooleanIsTrue()) {
bounds_.set(left, Bounds(cache_.kSingletonOne));
RECURSE(VisitWithExpectation(expr->right(), cache_.kAsmIntQ,
"not operator expects an integer"));
RECURSE(IntersectResult(expr, cache_.kAsmSigned));
return;
} else {
FAIL(left, "unexpected false");
}
}
// BIT_XOR allows Any since it is used as a type coercion (via ~~).
RECURSE(VisitIntegerBitwiseOperator(expr, Type::Any(), cache_.kAsmIntQ,
cache_.kAsmSigned, true));
return;
}
case Token::SHR: {
RECURSE(VisitIntegerBitwiseOperator(
expr, cache_.kAsmIntQ, cache_.kAsmIntQ, cache_.kAsmUnsigned, false));
return;
}
case Token::SHL:
case Token::SAR:
case Token::BIT_AND: {
RECURSE(VisitIntegerBitwiseOperator(
expr, cache_.kAsmIntQ, cache_.kAsmIntQ, cache_.kAsmSigned, false));
return;
}
case Token::ADD:
case Token::SUB:
case Token::MUL:
case Token::DIV:
case Token::MOD: {
RECURSE(VisitWithExpectation(
expr->left(), Type::Number(),
"left arithmetic operand expected to be number"));
Type* left_type = computed_type_;
int32_t left_intish = intish_;
RECURSE(VisitWithExpectation(
expr->right(), Type::Number(),
"right arithmetic operand expected to be number"));
Type* right_type = computed_type_;
int32_t right_intish = intish_;
Type* type = Type::Union(left_type, right_type, zone());
if (type->Is(cache_.kAsmInt)) {
if (expr->op() == Token::MUL) {
int32_t i;
Literal* left = expr->left()->AsLiteral();
Literal* right = expr->right()->AsLiteral();
if (left != nullptr && left->value()->IsNumber() &&
left->value()->ToInt32(&i)) {
if (right_intish != 0) {
FAIL(expr, "intish not allowed in multiply");
}
} else if (right != nullptr && right->value()->IsNumber() &&
right->value()->ToInt32(&i)) {
if (left_intish != 0) {
FAIL(expr, "intish not allowed in multiply");
}
} else {
FAIL(expr, "multiply must be by an integer literal");
}
i = abs(i);
if (i >= (1 << 20)) {
FAIL(expr, "multiply must be by value in -2^20 < n < 2^20");
}
intish_ = i;
RECURSE(IntersectResult(expr, cache_.kAsmInt));
return;
} else {
intish_ = left_intish + right_intish + 1;
if (expr->op() == Token::ADD || expr->op() == Token::SUB) {
if (intish_ > kMaxUncombinedAdditiveSteps) {
FAIL(expr, "too many consecutive additive ops");
}
} else {
if (intish_ > kMaxUncombinedMultiplicativeSteps) {
FAIL(expr, "too many consecutive multiplicative ops");
}
}
if (expr->op() == Token::MOD || expr->op() == Token::DIV) {
if (!((left_type->Is(cache_.kAsmSigned) &&
right_type->Is(cache_.kAsmSigned)) ||
(left_type->Is(cache_.kAsmUnsigned) &&
right_type->Is(cache_.kAsmUnsigned)))) {
FAIL(expr,
"left and right side of integer / or % "
"must match and be signed or unsigned");
}
}
RECURSE(IntersectResult(expr, cache_.kAsmInt));
return;
}
} else if (expr->op() == Token::MUL && expr->right()->IsLiteral() &&
right_type->Is(cache_.kAsmDouble) &&
expr->right()->AsLiteral()->raw_value()->ContainsDot() &&
expr->right()->AsLiteral()->raw_value()->AsNumber() == 1.0) {
// For unary +, expressed as x * 1.0
if (expr->left()->IsCall() &&
Type::Number()->Is(bounds_.get(expr->left()).upper)) {
// Force the return types of foreign functions.
bounds_.set(expr->left(), Bounds(cache_.kAsmDouble));
left_type = bounds_.get(expr->left()).upper;
}
if (!(expr->left()->IsProperty() &&
Type::Number()->Is(bounds_.get(expr->left()).upper))) {
if (!left_type->Is(cache_.kAsmSigned) &&
!left_type->Is(cache_.kAsmUnsigned) &&
!left_type->Is(cache_.kAsmFixnum) &&
!left_type->Is(cache_.kAsmFloatQ) &&
!left_type->Is(cache_.kAsmDoubleQ)) {
FAIL(
expr->left(),
"unary + only allowed on signed, unsigned, float?, or double?");
}
}
RECURSE(IntersectResult(expr, cache_.kAsmDouble));
return;
} else if (expr->op() == Token::MUL && left_type->Is(cache_.kAsmDouble) &&
expr->right()->IsLiteral() &&
!expr->right()->AsLiteral()->raw_value()->ContainsDot() &&
expr->right()->AsLiteral()->raw_value()->AsNumber() == -1.0) {
// For unary -, expressed as x * -1
bounds_.set(expr->right(), Bounds(cache_.kAsmDouble));
RECURSE(IntersectResult(expr, cache_.kAsmDouble));
return;
} else if (type->Is(cache_.kAsmFloat) && expr->op() != Token::MOD) {
if (left_intish != 0 || right_intish != 0) {
FAIL(expr, "float operation before required fround");
}
RECURSE(IntersectResult(expr, cache_.kAsmFloat));
intish_ = 1;
return;
} else if (type->Is(cache_.kAsmDouble)) {
RECURSE(IntersectResult(expr, cache_.kAsmDouble));
return;
} else {
FAIL(expr, "ill-typed arithmetic operation");
}
}
default:
UNREACHABLE();
}
}
void AsmTyper::VisitCompareOperation(CompareOperation* expr) {
if (!in_function_) {
FAIL(expr, "comparison inside module body");
}
Token::Value op = expr->op();
if (op != Token::EQ && op != Token::NE && op != Token::LT &&
op != Token::LTE && op != Token::GT && op != Token::GTE) {
FAIL(expr, "illegal comparison operator");
}
RECURSE(
VisitWithExpectation(expr->left(), Type::Number(),
"left comparison operand expected to be number"));
Type* left_type = computed_type_;
int left_intish = intish_;
RECURSE(
VisitWithExpectation(expr->right(), Type::Number(),
"right comparison operand expected to be number"));
Type* right_type = computed_type_;
int right_intish = intish_;
if (left_intish != 0 || right_intish != 0 ||
!((left_type->Is(cache_.kAsmUnsigned) &&
right_type->Is(cache_.kAsmUnsigned)) ||
(left_type->Is(cache_.kAsmSigned) &&
right_type->Is(cache_.kAsmSigned)) ||
(left_type->Is(cache_.kAsmFloat) && right_type->Is(cache_.kAsmFloat)) ||
(left_type->Is(cache_.kAsmDouble) &&
right_type->Is(cache_.kAsmDouble)))) {
FAIL(expr,
"left and right side of comparison must match type "
"and be signed, unsigned, float, or double");
}
RECURSE(IntersectResult(expr, cache_.kAsmSigned));
}
void AsmTyper::VisitThisFunction(ThisFunction* expr) {
FAIL(expr, "this function not allowed");
}
void AsmTyper::VisitDeclarations(ZoneList<Declaration*>* decls) {
for (int i = 0; i < decls->length(); ++i) {
Declaration* decl = decls->at(i);
RECURSE(Visit(decl));
}
}
void AsmTyper::VisitImportDeclaration(ImportDeclaration* decl) {
FAIL(decl, "import declaration encountered");
}
void AsmTyper::VisitClassLiteral(ClassLiteral* expr) {
FAIL(expr, "class literal not allowed");
}
void AsmTyper::VisitSpread(Spread* expr) { FAIL(expr, "spread not allowed"); }
void AsmTyper::VisitSuperPropertyReference(SuperPropertyReference* expr) {
FAIL(expr, "super property reference not allowed");
}
void AsmTyper::VisitSuperCallReference(SuperCallReference* expr) {
FAIL(expr, "call reference not allowed");
}
void AsmTyper::InitializeStdlibSIMD() {
#define V(NAME, Name, name, lane_count, lane_type) \
{ \
Type* type = Type::Function(Type::Name(isolate_, zone()), Type::Any(), \
lane_count, zone()); \
for (int i = 0; i < lane_count; ++i) { \
type->AsFunction()->InitParameter(i, Type::Number()); \
} \
stdlib_simd_##name##_constructor_type_ = new (zone()) VariableInfo(type); \
stdlib_simd_##name##_constructor_type_->is_constructor_function = true; \
}
SIMD128_TYPES(V)
#undef V
}
void AsmTyper::InitializeStdlib() {
if (allow_simd_) {
InitializeStdlibSIMD();
}
Type* number_type = Type::Number();
Type* double_type = cache_.kAsmDouble;
Type* double_fn1_type = Type::Function(double_type, double_type, zone());
Type* double_fn2_type =
Type::Function(double_type, double_type, double_type, zone());
Type* fround_type = Type::Function(cache_.kAsmFloat, number_type, zone());
Type* imul_type =
Type::Function(cache_.kAsmSigned, cache_.kAsmInt, cache_.kAsmInt, zone());
// TODO(bradnelson): currently only approximating the proper intersection type
// (which we cannot currently represent).
Type* number_fn1_type = Type::Function(number_type, number_type, zone());
Type* number_fn2_type =
Type::Function(number_type, number_type, number_type, zone());
struct Assignment {
const char* name;
StandardMember standard_member;
Type* type;
};
const Assignment math[] = {{"PI", kMathPI, double_type},
{"E", kMathE, double_type},
{"LN2", kMathLN2, double_type},
{"LN10", kMathLN10, double_type},
{"LOG2E", kMathLOG2E, double_type},
{"LOG10E", kMathLOG10E, double_type},
{"SQRT2", kMathSQRT2, double_type},
{"SQRT1_2", kMathSQRT1_2, double_type},
{"imul", kMathImul, imul_type},
{"abs", kMathAbs, number_fn1_type},
{"ceil", kMathCeil, number_fn1_type},
{"floor", kMathFloor, number_fn1_type},
{"fround", kMathFround, fround_type},
{"pow", kMathPow, double_fn2_type},
{"exp", kMathExp, double_fn1_type},
{"log", kMathLog, double_fn1_type},
{"min", kMathMin, number_fn2_type},
{"max", kMathMax, number_fn2_type},
{"sqrt", kMathSqrt, number_fn1_type},
{"cos", kMathCos, double_fn1_type},
{"sin", kMathSin, double_fn1_type},
{"tan", kMathTan, double_fn1_type},
{"acos", kMathAcos, double_fn1_type},
{"asin", kMathAsin, double_fn1_type},
{"atan", kMathAtan, double_fn1_type},
{"atan2", kMathAtan2, double_fn2_type}};
for (unsigned i = 0; i < arraysize(math); ++i) {
stdlib_math_types_[math[i].name] = new (zone()) VariableInfo(math[i].type);
stdlib_math_types_[math[i].name]->standard_member = math[i].standard_member;
}
stdlib_math_types_["fround"]->is_check_function = true;
stdlib_types_["Infinity"] = new (zone()) VariableInfo(double_type);
stdlib_types_["Infinity"]->standard_member = kInfinity;
stdlib_types_["NaN"] = new (zone()) VariableInfo(double_type);
stdlib_types_["NaN"]->standard_member = kNaN;
Type* buffer_type = Type::Any();
#define TYPED_ARRAY(TypeName, type_name, TYPE_NAME, ctype, size) \
stdlib_types_[#TypeName "Array"] = new (zone()) VariableInfo( \
Type::Function(cache_.k##TypeName##Array, buffer_type, zone()));
TYPED_ARRAYS(TYPED_ARRAY)
#undef TYPED_ARRAY
#define TYPED_ARRAY(TypeName, type_name, TYPE_NAME, ctype, size) \
stdlib_heap_types_[#TypeName "Array"] = new (zone()) VariableInfo( \
Type::Function(cache_.k##TypeName##Array, buffer_type, zone()));
TYPED_ARRAYS(TYPED_ARRAY)
#undef TYPED_ARRAY
}
void AsmTyper::VisitLibraryAccess(ObjectTypeMap* map, Property* expr) {
Literal* key = expr->key()->AsLiteral();
if (key == nullptr || !key->IsPropertyName())
FAIL(expr, "invalid key used on stdlib member");
Handle<String> name = key->AsPropertyName();
VariableInfo* info = LibType(map, name);
if (info == nullptr || info->type == nullptr)
FAIL(expr, "unknown stdlib function");
SetResult(expr, info->type);
property_info_ = info;
}
AsmTyper::VariableInfo* AsmTyper::LibType(ObjectTypeMap* map,
Handle<String> name) {
base::SmartArrayPointer<char> aname = name->ToCString();
ObjectTypeMap::iterator i = map->find(std::string(aname.get()));
if (i == map->end()) {
return nullptr;
}
return i->second;
}
void AsmTyper::SetType(Variable* variable, Type* type) {
VariableInfo* info = MakeVariableInfo(variable);
info->type = type;
}
Type* AsmTyper::GetType(Variable* variable) {
VariableInfo* info = GetVariableInfo(variable);
if (!info) return nullptr;
return info->type;
}
AsmTyper::VariableInfo* AsmTyper::GetVariableInfo(Variable* variable) {
ZoneHashMap* map =
in_function_ ? &local_variable_type_ : &global_variable_type_;
ZoneHashMap::Entry* entry =
map->Lookup(variable, ComputePointerHash(variable));
if (!entry && in_function_) {
entry =
global_variable_type_.Lookup(variable, ComputePointerHash(variable));
}
return entry ? reinterpret_cast<VariableInfo*>(entry->value) : nullptr;
}
AsmTyper::VariableInfo* AsmTyper::MakeVariableInfo(Variable* variable) {
ZoneHashMap* map =
in_function_ ? &local_variable_type_ : &global_variable_type_;
ZoneHashMap::Entry* entry = map->LookupOrInsert(
variable, ComputePointerHash(variable), ZoneAllocationPolicy(zone()));
if (!entry->value) entry->value = new (zone()) VariableInfo;
return reinterpret_cast<VariableInfo*>(entry->value);
}
void AsmTyper::SetVariableInfo(Variable* variable, const VariableInfo* info) {
VariableInfo* dest = MakeVariableInfo(variable);
dest->type = info->type;
dest->is_check_function = info->is_check_function;
dest->is_constructor_function = info->is_constructor_function;
dest->standard_member = info->standard_member;
}
AsmTyper::StandardMember AsmTyper::VariableAsStandardMember(
Variable* variable) {
VariableInfo* info = GetVariableInfo(variable);
if (!info) return kNone;
return info->standard_member;
}
void AsmTyper::SetResult(Expression* expr, Type* type) {
computed_type_ = type;
bounds_.set(expr, Bounds(computed_type_));
}
void AsmTyper::IntersectResult(Expression* expr, Type* type) {
computed_type_ = type;
Type* bounded_type = Type::Intersect(computed_type_, expected_type_, zone());
if (Type::Representation(bounded_type, zone())->Is(Type::None())) {
#ifdef DEBUG
PrintF("Computed type: ");
computed_type_->Print();
PrintF("Expected type: ");
expected_type_->Print();
#endif
FAIL(expr, "type mismatch");
}
bounds_.set(expr, Bounds(bounded_type));
}
void AsmTyper::VisitWithExpectation(Expression* expr, Type* expected_type,
const char* msg) {
Type* save = expected_type_;
expected_type_ = expected_type;
RECURSE(Visit(expr));
Type* bounded_type = Type::Intersect(computed_type_, expected_type_, zone());
if (Type::Representation(bounded_type, zone())->Is(Type::None())) {
#ifdef DEBUG
PrintF("Computed type: ");
computed_type_->Print();
PrintF("Expected type: ");
expected_type_->Print();
#endif
FAIL(expr, msg);
}
expected_type_ = save;
}
void AsmTyper::VisitRewritableExpression(RewritableExpression* expr) {
RECURSE(Visit(expr->expression()));
}
} // namespace internal
} // namespace v8
// Copyright 2015 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef V8_ASMJS_TYPING_ASM_H_
#define V8_ASMJS_TYPING_ASM_H_
#include "src/allocation.h"
#include "src/ast/ast-type-bounds.h"
#include "src/ast/ast.h"
#include "src/effects.h"
#include "src/type-info.h"
#include "src/types.h"
#include "src/zone.h"
namespace v8 {
namespace internal {
class TypeCache;
class AsmTyper : public AstVisitor {
public:
explicit AsmTyper(Isolate* isolate, Zone* zone, Script* script,
FunctionLiteral* root);
bool Validate();
void set_allow_simd(bool simd) { allow_simd_ = simd; }
void set_fixed_signature(bool fixed) { fixed_signature_ = fixed; }
const char* error_message() { return error_message_; }
const AstTypeBounds* bounds() { return &bounds_; }
enum StandardMember {
kNone = 0,
kStdlib,
kInfinity,
kNaN,
kMathAcos,
kMathAsin,
kMathAtan,
kMathCos,
kMathSin,
kMathTan,
kMathExp,
kMathLog,
kMathCeil,
kMathFloor,
kMathSqrt,
kMathAbs,
kMathMin,
kMathMax,
kMathAtan2,
kMathPow,
kMathImul,
kMathFround,
kMathE,
kMathLN10,
kMathLN2,
kMathLOG2E,
kMathLOG10E,
kMathPI,
kMathSQRT1_2,
kMathSQRT2,
};
StandardMember VariableAsStandardMember(Variable* variable);
DEFINE_AST_VISITOR_SUBCLASS_MEMBERS();
private:
Zone* zone_;
Isolate* isolate_;
Script* script_;
FunctionLiteral* root_;
bool valid_;
bool allow_simd_;
bool fixed_signature_;
struct VariableInfo : public ZoneObject {
Type* type;
bool is_check_function;
bool is_constructor_function;
StandardMember standard_member;
VariableInfo()
: type(NULL),
is_check_function(false),
is_constructor_function(false),
standard_member(kNone) {}
explicit VariableInfo(Type* t)
: type(t),
is_check_function(false),
is_constructor_function(false),
standard_member(kNone) {}
};
// Information for bi-directional typing with a cap on nesting depth.
Type* expected_type_;
Type* computed_type_;
VariableInfo* property_info_;
int32_t intish_; // How many ops we've gone without a x|0.
Type* return_type_; // Return type of last function.
size_t array_size_; // Array size of last ArrayLiteral.
typedef ZoneMap<std::string, VariableInfo*> ObjectTypeMap;
ObjectTypeMap stdlib_types_;
ObjectTypeMap stdlib_heap_types_;
ObjectTypeMap stdlib_math_types_;
#define V(NAME, Name, name, lane_count, lane_type) \
ObjectTypeMap stdlib_simd_##name##_types_; \
VariableInfo* stdlib_simd_##name##_constructor_type_;
SIMD128_TYPES(V)
#undef V
// Map from Variable* to global/local variable Type*.
ZoneHashMap global_variable_type_;
ZoneHashMap local_variable_type_;
bool in_function_; // In module function?
bool building_function_tables_;
bool visiting_exports_;
TypeCache const& cache_;
AstTypeBounds bounds_;
static const int kErrorMessageLimit = 150;
char error_message_[kErrorMessageLimit];
static const int kMaxUncombinedAdditiveSteps = 1 << 20;
static const int kMaxUncombinedMultiplicativeSteps = 1;
void InitializeStdlib();
void InitializeStdlibSIMD();
void VisitDeclarations(ZoneList<Declaration*>* d) override;
void VisitStatements(ZoneList<Statement*>* s) override;
void VisitExpressionAnnotation(Expression* e, Variable* var, bool is_return);
void VisitFunctionAnnotation(FunctionLiteral* f);
void VisitAsmModule(FunctionLiteral* f);
void VisitHeapAccess(Property* expr, bool assigning, Type* assignment_type);
void CheckPolymorphicStdlibArguments(enum StandardMember standard_member,
ZoneList<Expression*>* args);
Expression* GetReceiverOfPropertyAccess(Expression* expr, const char* name);
bool IsMathObject(Expression* expr);
bool IsSIMDObject(Expression* expr);
bool IsSIMDTypeObject(Expression* expr, const char* name);
bool IsStdlibObject(Expression* expr);
void VisitSIMDProperty(Property* expr);
int ElementShiftSize(Type* type);
Type* StorageType(Type* type);
void SetType(Variable* variable, Type* type);
Type* GetType(Variable* variable);
VariableInfo* GetVariableInfo(Variable* variable);
VariableInfo* MakeVariableInfo(Variable* variable);
void SetVariableInfo(Variable* variable, const VariableInfo* info);
VariableInfo* LibType(ObjectTypeMap* map, Handle<String> name);
void VisitLibraryAccess(ObjectTypeMap* map, Property* expr);
void SetResult(Expression* expr, Type* type);
void IntersectResult(Expression* expr, Type* type);
void VisitWithExpectation(Expression* expr, Type* expected_type,
const char* msg);
void VisitLiteral(Literal* expr, bool is_return);
void VisitIntegerBitwiseOperator(BinaryOperation* expr, Type* left_expected,
Type* right_expected, Type* result_type,
bool conversion);
Zone* zone() const { return zone_; }
#define DECLARE_VISIT(type) void Visit##type(type* node) override;
AST_NODE_LIST(DECLARE_VISIT)
#undef DECLARE_VISIT
DISALLOW_COPY_AND_ASSIGN(AsmTyper);
};
} // namespace internal
} // namespace v8
#endif // V8_TYPING_ASM_H_
......@@ -7,7 +7,7 @@
#include <algorithm>
#include "src/asmjs/asm-js.h"
#include "src/asmjs/typing-asm.h"
#include "src/asmjs/asm-typer.h"
#include "src/ast/ast-numbering.h"
#include "src/ast/prettyprinter.h"
#include "src/ast/scopeinfo.h"
......
......@@ -426,8 +426,6 @@
'asmjs/asm-types.h',
'asmjs/asm-wasm-builder.cc',
'asmjs/asm-wasm-builder.h',
'asmjs/typing-asm.cc',
'asmjs/typing-asm.h',
'assembler.cc',
'assembler.h',
'assert-scope.h',
......
......@@ -4,8 +4,8 @@
#include "src/api-natives.h"
#include "src/api.h"
#include "src/asmjs/asm-typer.h"
#include "src/asmjs/asm-wasm-builder.h"
#include "src/asmjs/typing-asm.h"
#include "src/assert-scope.h"
#include "src/ast/ast.h"
#include "src/ast/scopes.h"
......
......@@ -16,5 +16,3 @@ per-file *-s390*=mbrandy@us.ibm.com
per-file *-s390*=michael_dawson@ca.ibm.com
per-file *-x87*=chunyang.dai@intel.com
per-file *-x87*=weiliang.lin@intel.com
per-file expression-type-collector*=aseemgarg@chromium.org
per-file expression-type-collector*=bradnelson@chromium.org
// Copyright 2015 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/v8.h"
#include "src/asmjs/typing-asm.h"
#include "src/ast/ast-expression-visitor.h"
#include "src/ast/ast.h"
#include "src/ast/scopes.h"
#include "src/parsing/parser.h"
#include "src/parsing/rewriter.h"
#include "src/type-cache.h"
#include "test/cctest/cctest.h"
#include "test/cctest/expression-type-collector-macros.h"
#include "test/cctest/expression-type-collector.h"
// Macros for function types.
#define FUNC_FOREIGN_TYPE Bounds(Type::Function(Type::Any(), zone))
#define FUNC_V_TYPE Bounds(Type::Function(Type::Undefined(), zone))
#define FUNC_I_TYPE Bounds(Type::Function(cache.kAsmSigned, zone))
#define FUNC_F_TYPE Bounds(Type::Function(cache.kAsmFloat, zone))
#define FUNC_D_TYPE Bounds(Type::Function(cache.kAsmDouble, zone))
#define FUNC_D2D_TYPE \
Bounds(Type::Function(cache.kAsmDouble, cache.kAsmDouble, zone))
#define FUNC_N2F_TYPE \
Bounds(Type::Function(cache.kAsmFloat, Type::Number(), zone))
#define FUNC_I2I_TYPE \
Bounds(Type::Function(cache.kAsmSigned, cache.kAsmInt, zone))
#define FUNC_II2D_TYPE \
Bounds(Type::Function(cache.kAsmDouble, cache.kAsmInt, cache.kAsmInt, zone))
#define FUNC_II2I_TYPE \
Bounds(Type::Function(cache.kAsmSigned, cache.kAsmInt, cache.kAsmInt, zone))
#define FUNC_DD2D_TYPE \
Bounds(Type::Function(cache.kAsmDouble, cache.kAsmDouble, cache.kAsmDouble, \
zone))
#define FUNC_NN2N_TYPE \
Bounds(Type::Function(Type::Number(), Type::Number(), Type::Number(), zone))
#define FUNC_N2N_TYPE \
Bounds(Type::Function(Type::Number(), Type::Number(), zone))
// Macros for array types.
#define FLOAT64_ARRAY_TYPE Bounds(Type::Array(cache.kAsmDouble, zone))
#define FUNC_I2I_ARRAY_TYPE \
Bounds(Type::Array(Type::Function(cache.kAsmSigned, cache.kAsmInt, zone), \
zone))
using namespace v8::internal;
namespace {
std::string Validate(Zone* zone, const char* source,
ZoneVector<ExpressionTypeEntry>* types) {
i::Isolate* isolate = CcTest::i_isolate();
i::Factory* factory = isolate->factory();
i::Handle<i::String> source_code =
factory->NewStringFromUtf8(i::CStrVector(source)).ToHandleChecked();
i::Handle<i::Script> script = factory->NewScript(source_code);
i::ParseInfo info(zone, script);
i::Parser parser(&info);
info.set_global();
info.set_lazy(false);
info.set_allow_lazy_parsing(false);
info.set_toplevel(true);
CHECK(i::Compiler::ParseAndAnalyze(&info));
FunctionLiteral* root =
info.scope()->declarations()->at(0)->AsFunctionDeclaration()->fun();
AsmTyper typer(isolate, zone, *script, root);
if (typer.Validate()) {
ExpressionTypeCollector(isolate, root, typer.bounds(), types).Run();
return "";
} else {
return typer.error_message();
}
}
} // namespace
TEST(ValidateMinimum) {
const char test_function[] =
"function GeometricMean(stdlib, foreign, buffer) {\n"
" \"use asm\";\n"
"\n"
" var exp = stdlib.Math.exp;\n"
" var log = stdlib.Math.log;\n"
" var values = new stdlib.Float64Array(buffer);\n"
"\n"
" function logSum(start, end) {\n"
" start = start|0;\n"
" end = end|0;\n"
"\n"
" var sum = 0.0, p = 0, q = 0;\n"
"\n"
" // asm.js forces byte addressing of the heap by requiring shifting "
"by 3\n"
" for (p = start << 3, q = end << 3; (p|0) < (q|0); p = (p + 8)|0) {\n"
" sum = sum + +log(values[p>>3]);\n"
" }\n"
"\n"
" return +sum;\n"
" }\n"
"\n"
" function geometricMean(start, end) {\n"
" start = start|0;\n"
" end = end|0;\n"
"\n"
" return +exp(+logSum(start, end) / +((end - start)|0));\n"
" }\n"
"\n"
" return { geometricMean: geometricMean };\n"
"}\n";
v8::V8::Initialize();
HandleAndZoneScope handles;
Zone* zone = handles.main_zone();
ZoneVector<ExpressionTypeEntry> types(zone);
CHECK_EQ("", Validate(zone, test_function, &types));
TypeCache cache;
CHECK_TYPES_BEGIN {
// Module.
CHECK_EXPR(FunctionLiteral, Bounds::Unbounded()) {
// function logSum
CHECK_EXPR(FunctionLiteral, FUNC_II2D_TYPE) {
CHECK_EXPR(Assignment, Bounds(cache.kAsmInt)) {
CHECK_VAR(start, Bounds(cache.kAsmInt));
CHECK_EXPR(BinaryOperation, Bounds(cache.kAsmSigned)) {
CHECK_VAR(start, Bounds(cache.kAsmInt));
CHECK_EXPR(Literal, Bounds(cache.kAsmFixnum));
}
}
CHECK_EXPR(Assignment, Bounds(cache.kAsmInt)) {
CHECK_VAR(end, Bounds(cache.kAsmInt));
CHECK_EXPR(BinaryOperation, Bounds(cache.kAsmSigned)) {
CHECK_VAR(end, Bounds(cache.kAsmInt));
CHECK_EXPR(Literal, Bounds(cache.kAsmFixnum));
}
}
CHECK_EXPR(Assignment, Bounds(cache.kAsmDouble)) {
CHECK_VAR(sum, Bounds(cache.kAsmDouble));
CHECK_EXPR(Literal, Bounds(cache.kAsmDouble));
}
CHECK_EXPR(Assignment, Bounds(cache.kAsmInt)) {
CHECK_VAR(p, Bounds(cache.kAsmInt));
CHECK_EXPR(Literal, Bounds(cache.kAsmFixnum));
}
CHECK_EXPR(Assignment, Bounds(cache.kAsmInt)) {
CHECK_VAR(q, Bounds(cache.kAsmInt));
CHECK_EXPR(Literal, Bounds(cache.kAsmFixnum));
}
// for (p = start << 3, q = end << 3;
CHECK_EXPR(BinaryOperation, Bounds(cache.kAsmInt)) {
CHECK_EXPR(Assignment, Bounds(cache.kAsmInt)) {
CHECK_VAR(p, Bounds(cache.kAsmInt));
CHECK_EXPR(BinaryOperation, Bounds(cache.kAsmSigned)) {
CHECK_VAR(start, Bounds(cache.kAsmInt));
CHECK_EXPR(Literal, Bounds(cache.kAsmFixnum));
}
}
CHECK_EXPR(Assignment, Bounds(cache.kAsmInt)) {
CHECK_VAR(q, Bounds(cache.kAsmInt));
CHECK_EXPR(BinaryOperation, Bounds(cache.kAsmSigned)) {
CHECK_VAR(end, Bounds(cache.kAsmInt));
CHECK_EXPR(Literal, Bounds(cache.kAsmFixnum));
}
}
}
// (p|0) < (q|0);
CHECK_EXPR(CompareOperation, Bounds(cache.kAsmSigned)) {
CHECK_EXPR(BinaryOperation, Bounds(cache.kAsmSigned)) {
CHECK_VAR(p, Bounds(cache.kAsmInt));
CHECK_EXPR(Literal, Bounds(cache.kAsmFixnum));
}
CHECK_EXPR(BinaryOperation, Bounds(cache.kAsmSigned)) {
CHECK_VAR(q, Bounds(cache.kAsmInt));
CHECK_EXPR(Literal, Bounds(cache.kAsmFixnum));
}
}
// p = (p + 8)|0) {
CHECK_EXPR(Assignment, Bounds(cache.kAsmInt)) {
CHECK_VAR(p, Bounds(cache.kAsmInt));
CHECK_EXPR(BinaryOperation, Bounds(cache.kAsmSigned)) {
CHECK_EXPR(BinaryOperation, Bounds(cache.kAsmInt)) {
CHECK_VAR(p, Bounds(cache.kAsmInt));
CHECK_EXPR(Literal, Bounds(cache.kAsmFixnum));
}
CHECK_EXPR(Literal, Bounds(cache.kAsmFixnum));
}
}
// sum = sum + +log(values[p>>3]);
CHECK_EXPR(Assignment, Bounds(cache.kAsmDouble)) {
CHECK_VAR(sum, Bounds(cache.kAsmDouble));
CHECK_EXPR(BinaryOperation, Bounds(cache.kAsmDouble)) {
CHECK_VAR(sum, Bounds(cache.kAsmDouble));
CHECK_EXPR(BinaryOperation, Bounds(cache.kAsmDouble)) {
CHECK_EXPR(Call, Bounds(cache.kAsmDouble)) {
CHECK_VAR(log, FUNC_D2D_TYPE);
CHECK_EXPR(Property, Bounds(cache.kAsmDouble)) {
CHECK_VAR(values, FLOAT64_ARRAY_TYPE);
CHECK_EXPR(BinaryOperation, Bounds(cache.kAsmUnsigned)) {
CHECK_VAR(p, Bounds(cache.kAsmInt));
CHECK_EXPR(Literal, Bounds(cache.kAsmFixnum));
}
}
}
CHECK_EXPR(Literal, Bounds(cache.kAsmDouble));
}
}
}
// return +sum;
CHECK_EXPR(BinaryOperation, Bounds(cache.kAsmDouble)) {
CHECK_VAR(sum, Bounds(cache.kAsmDouble));
CHECK_EXPR(Literal, Bounds(cache.kAsmDouble));
}
}
// function geometricMean
CHECK_EXPR(FunctionLiteral, FUNC_II2D_TYPE) {
CHECK_EXPR(Assignment, Bounds(cache.kAsmInt)) {
CHECK_VAR(start, Bounds(cache.kAsmInt));
CHECK_EXPR(BinaryOperation, Bounds(cache.kAsmSigned)) {
CHECK_VAR(start, Bounds(cache.kAsmInt));
CHECK_EXPR(Literal, Bounds(cache.kAsmFixnum));
}
}
CHECK_EXPR(Assignment, Bounds(cache.kAsmInt)) {
CHECK_VAR(end, Bounds(cache.kAsmInt));
CHECK_EXPR(BinaryOperation, Bounds(cache.kAsmSigned)) {
CHECK_VAR(end, Bounds(cache.kAsmInt));
CHECK_EXPR(Literal, Bounds(cache.kAsmFixnum));
}
}
// return +exp(+logSum(start, end) / +((end - start)|0));
CHECK_EXPR(BinaryOperation, Bounds(cache.kAsmDouble)) {
CHECK_EXPR(Call, Bounds(cache.kAsmDouble)) {
CHECK_VAR(exp, FUNC_D2D_TYPE);
CHECK_EXPR(BinaryOperation, Bounds(cache.kAsmDouble)) {
CHECK_EXPR(BinaryOperation, Bounds(cache.kAsmDouble)) {
CHECK_EXPR(Call, Bounds(cache.kAsmDouble)) {
CHECK_VAR(logSum, FUNC_II2D_TYPE);
CHECK_VAR(start, Bounds(cache.kAsmInt));
CHECK_VAR(end, Bounds(cache.kAsmInt));
}
CHECK_EXPR(Literal, Bounds(cache.kAsmDouble));
}
CHECK_EXPR(BinaryOperation, Bounds(cache.kAsmDouble)) {
CHECK_EXPR(BinaryOperation, Bounds(cache.kAsmSigned)) {
CHECK_EXPR(BinaryOperation, Bounds(cache.kAsmInt)) {
CHECK_VAR(end, Bounds(cache.kAsmInt));
CHECK_VAR(start, Bounds(cache.kAsmInt));
}
CHECK_EXPR(Literal, Bounds(cache.kAsmFixnum));
}
CHECK_EXPR(Literal, Bounds(cache.kAsmDouble));
}
}
}
CHECK_EXPR(Literal, Bounds(cache.kAsmDouble));
}
}
// "use asm";
CHECK_EXPR(Literal, Bounds(Type::String()));
// var exp = stdlib.Math.exp;
CHECK_EXPR(Assignment, FUNC_D2D_TYPE) {
CHECK_VAR(exp, FUNC_D2D_TYPE);
CHECK_EXPR(Property, FUNC_D2D_TYPE) {
CHECK_EXPR(Property, Bounds::Unbounded()) {
CHECK_VAR(stdlib, Bounds::Unbounded());
CHECK_EXPR(Literal, Bounds::Unbounded());
}
CHECK_EXPR(Literal, Bounds::Unbounded());
}
}
// var log = stdlib.Math.log;
CHECK_EXPR(Assignment, FUNC_D2D_TYPE) {
CHECK_VAR(log, FUNC_D2D_TYPE);
CHECK_EXPR(Property, FUNC_D2D_TYPE) {
CHECK_EXPR(Property, Bounds::Unbounded()) {
CHECK_VAR(stdlib, Bounds::Unbounded());
CHECK_EXPR(Literal, Bounds::Unbounded());
}
CHECK_EXPR(Literal, Bounds::Unbounded());
}
}
// var values = new stdlib.Float64Array(buffer);
CHECK_EXPR(Assignment, FLOAT64_ARRAY_TYPE) {
CHECK_VAR(values, FLOAT64_ARRAY_TYPE);
CHECK_EXPR(CallNew, FLOAT64_ARRAY_TYPE) {
CHECK_EXPR(Property, Bounds::Unbounded()) {
CHECK_VAR(stdlib, Bounds::Unbounded());
CHECK_EXPR(Literal, Bounds::Unbounded());
}
CHECK_VAR(buffer, Bounds::Unbounded());
}
}
// return { geometricMean: geometricMean };
CHECK_EXPR(ObjectLiteral, Bounds::Unbounded()) {
CHECK_VAR(geometricMean, FUNC_II2D_TYPE);
}
}
}
CHECK_TYPES_END
}
TEST(MissingUseAsm) {
const char test_function[] =
"function foo() {\n"
" function bar() {}\n"
" return { bar: bar };\n"
"}\n";
v8::V8::Initialize();
HandleAndZoneScope handles;
Zone* zone = handles.main_zone();
ZoneVector<ExpressionTypeEntry> types(zone);
CHECK_EQ("asm: line 1: missing \"use asm\"\n",
Validate(zone, test_function, &types));
}
TEST(WrongUseAsm) {
const char test_function[] =
"function foo() {\n"
" \"use wasm\"\n"
" function bar() {}\n"
" return { bar: bar };\n"
"}\n";
v8::V8::Initialize();
HandleAndZoneScope handles;
Zone* zone = handles.main_zone();
ZoneVector<ExpressionTypeEntry> types(zone);
CHECK_EQ("asm: line 1: missing \"use asm\"\n",
Validate(zone, test_function, &types));
}
TEST(MissingReturnExports) {
const char test_function[] =
"function foo() {\n"
" \"use asm\"\n"
" function bar() {}\n"
"}\n";
v8::V8::Initialize();
HandleAndZoneScope handles;
Zone* zone = handles.main_zone();
ZoneVector<ExpressionTypeEntry> types(zone);
CHECK_EQ("asm: line 2: last statement in module is not a return\n",
Validate(zone, test_function, &types));
}
#define HARNESS_STDLIB() \
"var Infinity = stdlib.Infinity; " \
"var NaN = stdlib.NaN; " \
"var acos = stdlib.Math.acos; " \
"var asin = stdlib.Math.asin; " \
"var atan = stdlib.Math.atan; " \
"var cos = stdlib.Math.cos; " \
"var sin = stdlib.Math.sin; " \
"var tan = stdlib.Math.tan; " \
"var exp = stdlib.Math.exp; " \
"var log = stdlib.Math.log; " \
"var ceil = stdlib.Math.ceil; " \
"var floor = stdlib.Math.floor; " \
"var sqrt = stdlib.Math.sqrt; " \
"var min = stdlib.Math.min; " \
"var max = stdlib.Math.max; " \
"var atan2 = stdlib.Math.atan2; " \
"var pow = stdlib.Math.pow; " \
"var abs = stdlib.Math.abs; " \
"var imul = stdlib.Math.imul; " \
"var fround = stdlib.Math.fround; " \
"var E = stdlib.Math.E; " \
"var LN10 = stdlib.Math.LN10; " \
"var LN2 = stdlib.Math.LN2; " \
"var LOG2E = stdlib.Math.LOG2E; " \
"var LOG10E = stdlib.Math.LOG10E; " \
"var PI = stdlib.Math.PI; " \
"var SQRT1_2 = stdlib.Math.SQRT1_2; " \
"var SQRT2 = stdlib.Math.SQRT2; "
#define HARNESS_HEAP() \
"var u8 = new stdlib.Uint8Array(buffer); " \
"var i8 = new stdlib.Int8Array(buffer); " \
"var u16 = new stdlib.Uint16Array(buffer); " \
"var i16 = new stdlib.Int16Array(buffer); " \
"var u32 = new stdlib.Uint32Array(buffer); " \
"var i32 = new stdlib.Int32Array(buffer); " \
"var f32 = new stdlib.Float32Array(buffer); " \
"var f64 = new stdlib.Float64Array(buffer); "
#define HARNESS_PREAMBLE() \
const char test_function[] = \
"function Module(stdlib, foreign, buffer) { " \
"\"use asm\"; " HARNESS_STDLIB() HARNESS_HEAP()
#define HARNESS_POSTAMBLE() \
"return { foo: foo }; " \
"} ";
#define CHECK_VAR_MATH_SHORTCUT(name, type) \
CHECK_EXPR(Assignment, type) { \
CHECK_VAR(name, type); \
CHECK_EXPR(Property, type) { \
CHECK_EXPR(Property, Bounds::Unbounded()) { \
CHECK_VAR(stdlib, Bounds::Unbounded()); \
CHECK_EXPR(Literal, Bounds::Unbounded()); \
} \
CHECK_EXPR(Literal, Bounds::Unbounded()); \
} \
}
#define CHECK_VAR_SHORTCUT(name, type) \
CHECK_EXPR(Assignment, type) { \
CHECK_VAR(name, type); \
CHECK_EXPR(Property, type) { \
CHECK_VAR(stdlib, Bounds::Unbounded()); \
CHECK_EXPR(Literal, Bounds::Unbounded()); \
} \
}
#define CHECK_VAR_NEW_SHORTCUT(name, type) \
CHECK_EXPR(Assignment, type) { \
CHECK_VAR(name, type); \
CHECK_EXPR(CallNew, type) { \
CHECK_EXPR(Property, Bounds::Unbounded()) { \
CHECK_VAR(stdlib, Bounds::Unbounded()); \
CHECK_EXPR(Literal, Bounds::Unbounded()); \
} \
CHECK_VAR(buffer, Bounds::Unbounded()); \
} \
}
namespace {
void CheckStdlibShortcuts1(Zone* zone, ZoneVector<ExpressionTypeEntry>& types,
size_t& index, int& depth, TypeCache& cache) {
// var exp = stdlib.*;
CHECK_VAR_SHORTCUT(Infinity, Bounds(cache.kAsmDouble));
CHECK_VAR_SHORTCUT(NaN, Bounds(cache.kAsmDouble));
// var x = stdlib.Math.x;
CHECK_VAR_MATH_SHORTCUT(acos, FUNC_D2D_TYPE);
CHECK_VAR_MATH_SHORTCUT(asin, FUNC_D2D_TYPE);
CHECK_VAR_MATH_SHORTCUT(atan, FUNC_D2D_TYPE);
CHECK_VAR_MATH_SHORTCUT(cos, FUNC_D2D_TYPE);
CHECK_VAR_MATH_SHORTCUT(sin, FUNC_D2D_TYPE);
CHECK_VAR_MATH_SHORTCUT(tan, FUNC_D2D_TYPE);
CHECK_VAR_MATH_SHORTCUT(exp, FUNC_D2D_TYPE);
CHECK_VAR_MATH_SHORTCUT(log, FUNC_D2D_TYPE);
CHECK_VAR_MATH_SHORTCUT(ceil, FUNC_N2N_TYPE);
CHECK_VAR_MATH_SHORTCUT(floor, FUNC_N2N_TYPE);
CHECK_VAR_MATH_SHORTCUT(sqrt, FUNC_N2N_TYPE);
CHECK_VAR_MATH_SHORTCUT(min, FUNC_NN2N_TYPE);
CHECK_VAR_MATH_SHORTCUT(max, FUNC_NN2N_TYPE);
CHECK_VAR_MATH_SHORTCUT(atan2, FUNC_DD2D_TYPE);
CHECK_VAR_MATH_SHORTCUT(pow, FUNC_DD2D_TYPE);
CHECK_VAR_MATH_SHORTCUT(abs, FUNC_N2N_TYPE);
CHECK_VAR_MATH_SHORTCUT(imul, FUNC_II2I_TYPE);
CHECK_VAR_MATH_SHORTCUT(fround, FUNC_N2F_TYPE);
}
void CheckStdlibShortcuts2(Zone* zone, ZoneVector<ExpressionTypeEntry>& types,
size_t& index, int& depth, TypeCache& cache) {
// var exp = stdlib.Math.*; (D * 12)
CHECK_VAR_MATH_SHORTCUT(E, Bounds(cache.kAsmDouble));
CHECK_VAR_MATH_SHORTCUT(LN10, Bounds(cache.kAsmDouble));
CHECK_VAR_MATH_SHORTCUT(LN2, Bounds(cache.kAsmDouble));
CHECK_VAR_MATH_SHORTCUT(LOG2E, Bounds(cache.kAsmDouble));
CHECK_VAR_MATH_SHORTCUT(LOG10E, Bounds(cache.kAsmDouble));
CHECK_VAR_MATH_SHORTCUT(PI, Bounds(cache.kAsmDouble));
CHECK_VAR_MATH_SHORTCUT(SQRT1_2, Bounds(cache.kAsmDouble));
CHECK_VAR_MATH_SHORTCUT(SQRT2, Bounds(cache.kAsmDouble));
// var values = new stdlib.*Array(buffer);
CHECK_VAR_NEW_SHORTCUT(u8, Bounds(cache.kUint8Array));
CHECK_VAR_NEW_SHORTCUT(i8, Bounds(cache.kInt8Array));
CHECK_VAR_NEW_SHORTCUT(u16, Bounds(cache.kUint16Array));
CHECK_VAR_NEW_SHORTCUT(i16, Bounds(cache.kInt16Array));
CHECK_VAR_NEW_SHORTCUT(u32, Bounds(cache.kUint32Array));
CHECK_VAR_NEW_SHORTCUT(i32, Bounds(cache.kInt32Array));
CHECK_VAR_NEW_SHORTCUT(f32, Bounds(cache.kFloat32Array));
CHECK_VAR_NEW_SHORTCUT(f64, Bounds(cache.kFloat64Array));
}
} // namespace
#define CHECK_FUNC_TYPES_BEGIN(func) \
HARNESS_PREAMBLE() \
func "\n" HARNESS_POSTAMBLE(); \
\
v8::V8::Initialize(); \
HandleAndZoneScope handles; \
Zone* zone = handles.main_zone(); \
ZoneVector<ExpressionTypeEntry> types(zone); \
CHECK_EQ("", Validate(zone, test_function, &types)); \
TypeCache cache; \
\
CHECK_TYPES_BEGIN { \
/* Module. */ \
CHECK_EXPR(FunctionLiteral, Bounds::Unbounded()) {
#define CHECK_FUNC_TYPES_END_1() \
/* "use asm"; */ \
CHECK_EXPR(Literal, Bounds(Type::String())); \
/* stdlib shortcuts. */ \
CheckStdlibShortcuts1(zone, types, index, depth, cache); \
CheckStdlibShortcuts2(zone, types, index, depth, cache);
#define CHECK_FUNC_TYPES_END_2() \
/* return { foo: foo }; */ \
CHECK_EXPR(ObjectLiteral, Bounds::Unbounded()) { \
CHECK_VAR(foo, FUNC_V_TYPE); \
} \
} \
} \
CHECK_TYPES_END
#define CHECK_FUNC_TYPES_END \
CHECK_FUNC_TYPES_END_1(); \
CHECK_FUNC_TYPES_END_2();
#define CHECK_FUNC_ERROR(func, message) \
HARNESS_PREAMBLE() \
func "\n" HARNESS_POSTAMBLE(); \
\
v8::V8::Initialize(); \
HandleAndZoneScope handles; \
Zone* zone = handles.main_zone(); \
ZoneVector<ExpressionTypeEntry> types(zone); \
CHECK_EQ(message, Validate(zone, test_function, &types));
TEST(BareHarness) {
CHECK_FUNC_TYPES_BEGIN("function foo() {}") {
CHECK_EXPR(FunctionLiteral, FUNC_V_TYPE) {}
}
CHECK_FUNC_TYPES_END
}
TEST(ReturnVoid) {
CHECK_FUNC_TYPES_BEGIN(
"function bar() { return; }\n"
"function foo() { bar(); }") {
CHECK_EXPR(FunctionLiteral, FUNC_V_TYPE) {
// return undefined;
CHECK_EXPR(Literal, Bounds(Type::Undefined()));
}
CHECK_EXPR(FunctionLiteral, FUNC_V_TYPE) {
CHECK_EXPR(Call, Bounds(Type::Undefined())) {
CHECK_VAR(bar, FUNC_V_TYPE);
}
}
}
CHECK_FUNC_TYPES_END
}
TEST(EmptyBody) {
CHECK_FUNC_TYPES_BEGIN(
"function bar() { }\n"
"function foo() { bar(); }") {
CHECK_EXPR(FunctionLiteral, FUNC_V_TYPE);
CHECK_EXPR(FunctionLiteral, FUNC_V_TYPE) {
CHECK_EXPR(Call, Bounds(Type::Undefined())) {
CHECK_VAR(bar, FUNC_V_TYPE);
}
}
}
CHECK_FUNC_TYPES_END
}
TEST(DoesNothing) {
CHECK_FUNC_TYPES_BEGIN(
"function bar() { var x = 1.0; }\n"
"function foo() { bar(); }") {
CHECK_EXPR(FunctionLiteral, FUNC_V_TYPE) {
CHECK_EXPR(Assignment, Bounds(cache.kAsmDouble)) {
CHECK_VAR(x, Bounds(cache.kAsmDouble));
CHECK_EXPR(Literal, Bounds(cache.kAsmDouble));
}
}
CHECK_EXPR(FunctionLiteral, FUNC_V_TYPE) {
CHECK_EXPR(Call, Bounds(Type::Undefined())) {
CHECK_VAR(bar, FUNC_V_TYPE);
}
}
}
CHECK_FUNC_TYPES_END
}
TEST(ReturnInt32Literal) {
CHECK_FUNC_TYPES_BEGIN(
"function bar() { return 1; }\n"
"function foo() { bar(); }") {
CHECK_EXPR(FunctionLiteral, FUNC_I_TYPE) {
// return 1;
CHECK_EXPR(Literal, Bounds(cache.kAsmSigned));
}
CHECK_EXPR(FunctionLiteral, FUNC_V_TYPE) {
CHECK_EXPR(Call, Bounds(cache.kAsmSigned)) {
CHECK_VAR(bar, FUNC_I_TYPE);
}
}
}
CHECK_FUNC_TYPES_END
}
TEST(ReturnFloat64Literal) {
CHECK_FUNC_TYPES_BEGIN(
"function bar() { return 1.0; }\n"
"function foo() { bar(); }") {
CHECK_EXPR(FunctionLiteral, FUNC_D_TYPE) {
// return 1.0;
CHECK_EXPR(Literal, Bounds(cache.kAsmDouble));
}
CHECK_EXPR(FunctionLiteral, FUNC_V_TYPE) {
CHECK_EXPR(Call, Bounds(cache.kAsmDouble)) {
CHECK_VAR(bar, FUNC_D_TYPE);
}
}
}
CHECK_FUNC_TYPES_END
}
TEST(ReturnFloat32Literal) {
CHECK_FUNC_TYPES_BEGIN(
"function bar() { return fround(1.0); }\n"
"function foo() { bar(); }") {
CHECK_EXPR(FunctionLiteral, FUNC_F_TYPE) {
// return fround(1.0);
CHECK_EXPR(Call, Bounds(cache.kAsmFloat)) {
CHECK_VAR(fround, FUNC_N2F_TYPE);
CHECK_EXPR(Literal, Bounds(cache.kAsmDouble));
}
}
CHECK_EXPR(FunctionLiteral, FUNC_V_TYPE) {
CHECK_EXPR(Call, Bounds(cache.kAsmFloat)) { CHECK_VAR(bar, FUNC_F_TYPE); }
}
}
CHECK_FUNC_TYPES_END
}
TEST(ReturnFloat64Var) {
CHECK_FUNC_TYPES_BEGIN(
"function bar() { var x = 1.0; return +x; }\n"
"function foo() { bar(); }") {
CHECK_EXPR(FunctionLiteral, FUNC_D_TYPE) {
// return 1.0;
CHECK_EXPR(Assignment, Bounds(cache.kAsmDouble)) {
CHECK_VAR(x, Bounds(cache.kAsmDouble));
CHECK_EXPR(Literal, Bounds(cache.kAsmDouble));
}
// return 1.0;
CHECK_EXPR(BinaryOperation, Bounds(cache.kAsmDouble)) {
CHECK_VAR(x, Bounds(cache.kAsmDouble));
CHECK_EXPR(Literal, Bounds(cache.kAsmDouble));
}
}
CHECK_EXPR(FunctionLiteral, FUNC_V_TYPE) {
CHECK_EXPR(Call, Bounds(cache.kAsmDouble)) {
CHECK_VAR(bar, FUNC_D_TYPE);
}
}
}
CHECK_FUNC_TYPES_END
}
TEST(Addition2) {
CHECK_FUNC_TYPES_BEGIN(
"function bar() { var x = 1; var y = 2; return (x+y)|0; }\n"
"function foo() { bar(); }") {
CHECK_EXPR(FunctionLiteral, FUNC_I_TYPE) {
CHECK_EXPR(Assignment, Bounds(cache.kAsmInt)) {
CHECK_VAR(x, Bounds(cache.kAsmInt));
CHECK_EXPR(Literal, Bounds(cache.kAsmFixnum));
}
CHECK_EXPR(Assignment, Bounds(cache.kAsmInt)) {
CHECK_VAR(y, Bounds(cache.kAsmInt));
CHECK_EXPR(Literal, Bounds(cache.kAsmFixnum));
}
CHECK_EXPR(BinaryOperation, Bounds(cache.kAsmSigned)) {
CHECK_EXPR(BinaryOperation, Bounds(cache.kAsmInt)) {
CHECK_VAR(x, Bounds(cache.kAsmInt));
CHECK_VAR(y, Bounds(cache.kAsmInt));
}
CHECK_EXPR(Literal, Bounds(cache.kAsmFixnum));
}
}
CHECK_SKIP();
}
CHECK_FUNC_TYPES_END
}
#define TEST_COMPARE_OP(name, op) \
TEST(name) { \
CHECK_FUNC_TYPES_BEGIN("function bar() { return (0 " op \
" 0)|0; }\n" \
"function foo() { bar(); }") { \
CHECK_EXPR(FunctionLiteral, FUNC_I_TYPE) { \
CHECK_EXPR(BinaryOperation, Bounds(cache.kAsmSigned)) { \
CHECK_EXPR(CompareOperation, Bounds(cache.kAsmSigned)) { \
CHECK_EXPR(Literal, Bounds(cache.kAsmFixnum)); \
CHECK_EXPR(Literal, Bounds(cache.kAsmFixnum)); \
} \
CHECK_EXPR(Literal, Bounds(cache.kAsmFixnum)); \
} \
} \
CHECK_SKIP(); \
} \
CHECK_FUNC_TYPES_END \
}
TEST_COMPARE_OP(EqOperator, "==")
TEST_COMPARE_OP(LtOperator, "<")
TEST_COMPARE_OP(LteOperator, "<=")
TEST_COMPARE_OP(GtOperator, ">")
TEST_COMPARE_OP(GteOperator, ">=")
TEST(NeqOperator) {
CHECK_FUNC_TYPES_BEGIN(
"function bar() { return (0 != 0)|0; }\n"
"function foo() { bar(); }") {
CHECK_EXPR(FunctionLiteral, FUNC_I_TYPE) {
CHECK_EXPR(BinaryOperation, Bounds(cache.kAsmSigned)) {
CHECK_EXPR(UnaryOperation, Bounds(cache.kAsmSigned)) {
CHECK_EXPR(CompareOperation, Bounds(cache.kAsmSigned)) {
CHECK_EXPR(Literal, Bounds(cache.kAsmFixnum));
CHECK_EXPR(Literal, Bounds(cache.kAsmFixnum));
}
}
CHECK_EXPR(Literal, Bounds(cache.kAsmFixnum));
}
}
CHECK_SKIP();
}
CHECK_FUNC_TYPES_END
}
TEST(NotOperator) {
CHECK_FUNC_TYPES_BEGIN(
"function bar() { var x = 0; return (!x)|0; }\n"
"function foo() { bar(); }") {
CHECK_EXPR(FunctionLiteral, FUNC_I_TYPE) {
CHECK_EXPR(Assignment, Bounds(cache.kAsmInt)) {
CHECK_VAR(x, Bounds(cache.kAsmInt));
CHECK_EXPR(Literal, Bounds(cache.kAsmFixnum));
}
CHECK_EXPR(BinaryOperation, Bounds(cache.kAsmSigned)) {
CHECK_EXPR(UnaryOperation, Bounds(cache.kAsmSigned)) {
CHECK_VAR(x, Bounds(cache.kAsmInt));
}
CHECK_EXPR(Literal, Bounds(cache.kAsmFixnum));
}
}
CHECK_SKIP();
}
CHECK_FUNC_TYPES_END
}
TEST(InvertOperator) {
CHECK_FUNC_TYPES_BEGIN(
"function bar() { var x = 0; return (~x)|0; }\n"
"function foo() { bar(); }") {
CHECK_EXPR(FunctionLiteral, FUNC_I_TYPE) {
CHECK_EXPR(Assignment, Bounds(cache.kAsmInt)) {
CHECK_VAR(x, Bounds(cache.kAsmInt));
CHECK_EXPR(Literal, Bounds(cache.kAsmFixnum));
}
CHECK_EXPR(BinaryOperation, Bounds(cache.kAsmSigned)) {
CHECK_EXPR(BinaryOperation, Bounds(cache.kAsmSigned)) {
CHECK_VAR(x, Bounds(cache.kAsmInt));
CHECK_EXPR(Literal, Bounds(cache.kAsmSigned));
}
CHECK_EXPR(Literal, Bounds(cache.kAsmFixnum));
}
}
CHECK_SKIP();
}
CHECK_FUNC_TYPES_END
}
TEST(InvertConversion) {
CHECK_FUNC_TYPES_BEGIN(
"function bar() { var x = 0.0; return (~~x)|0; }\n"
"function foo() { bar(); }") {
CHECK_EXPR(FunctionLiteral, FUNC_I_TYPE) {
CHECK_EXPR(Assignment, Bounds(cache.kAsmDouble)) {
CHECK_VAR(x, Bounds(cache.kAsmDouble));
CHECK_EXPR(Literal, Bounds(cache.kAsmDouble));
}
CHECK_EXPR(BinaryOperation, Bounds(cache.kAsmSigned)) {
CHECK_EXPR(BinaryOperation, Bounds(cache.kAsmSigned)) {
CHECK_EXPR(BinaryOperation, Bounds(cache.kAsmSigned)) {
CHECK_VAR(x, Bounds(cache.kAsmDouble));
CHECK_EXPR(Literal, Bounds(cache.kAsmSigned));
}
CHECK_EXPR(Literal, Bounds(cache.kAsmSigned));
}
CHECK_EXPR(Literal, Bounds(cache.kAsmFixnum));
}
}
CHECK_SKIP();
}
CHECK_FUNC_TYPES_END
}
TEST(Ternary) {
CHECK_FUNC_TYPES_BEGIN(
"function bar() { var x = 1; var y = 1; return (x?y:5)|0; }\n"
"function foo() { bar(); }") {
CHECK_EXPR(FunctionLiteral, FUNC_I_TYPE) {
CHECK_EXPR(Assignment, Bounds(cache.kAsmInt)) {
CHECK_VAR(x, Bounds(cache.kAsmInt));
CHECK_EXPR(Literal, Bounds(cache.kAsmFixnum));
}
CHECK_EXPR(Assignment, Bounds(cache.kAsmInt)) {
CHECK_VAR(y, Bounds(cache.kAsmInt));
CHECK_EXPR(Literal, Bounds(cache.kAsmFixnum));
}
CHECK_EXPR(BinaryOperation, Bounds(cache.kAsmSigned)) {
CHECK_EXPR(Conditional, Bounds(cache.kAsmInt)) {
CHECK_VAR(x, Bounds(cache.kAsmInt));
CHECK_VAR(y, Bounds(cache.kAsmInt));
CHECK_EXPR(Literal, Bounds(cache.kAsmFixnum));
}
CHECK_EXPR(Literal, Bounds(cache.kAsmFixnum));
}
}
CHECK_SKIP();
}
CHECK_FUNC_TYPES_END
}
#define TEST_INT_BIN_OP(name, op) \
TEST(name) { \
CHECK_FUNC_TYPES_BEGIN("function bar() { var x = 0; return (x " op \
" 123)|0; }\n" \
"function foo() { bar(); }") { \
CHECK_EXPR(FunctionLiteral, FUNC_I_TYPE) { \
CHECK_EXPR(Assignment, Bounds(cache.kAsmInt)) { \
CHECK_VAR(x, Bounds(cache.kAsmInt)); \
CHECK_EXPR(Literal, Bounds(cache.kAsmFixnum)); \
} \
CHECK_EXPR(BinaryOperation, Bounds(cache.kAsmSigned)) { \
CHECK_EXPR(BinaryOperation, Bounds(cache.kAsmSigned)) { \
CHECK_VAR(x, Bounds(cache.kAsmInt)); \
CHECK_EXPR(Literal, Bounds(cache.kAsmFixnum)); \
} \
CHECK_EXPR(Literal, Bounds(cache.kAsmFixnum)); \
} \
} \
CHECK_SKIP(); \
} \
CHECK_FUNC_TYPES_END \
}
TEST_INT_BIN_OP(AndOperator, "&")
TEST_INT_BIN_OP(OrOperator, "|")
TEST_INT_BIN_OP(XorOperator, "^")
TEST(SignedCompare) {
CHECK_FUNC_TYPES_BEGIN(
"function bar() { var x = 1; var y = 1; return ((x|0) < (y|0))|0; }\n"
"function foo() { bar(); }") {
CHECK_EXPR(FunctionLiteral, FUNC_I_TYPE) {
CHECK_EXPR(Assignment, Bounds(cache.kAsmInt)) {
CHECK_VAR(x, Bounds(cache.kAsmInt));
CHECK_EXPR(Literal, Bounds(cache.kAsmFixnum));
}
CHECK_EXPR(Assignment, Bounds(cache.kAsmInt)) {
CHECK_VAR(y, Bounds(cache.kAsmInt));
CHECK_EXPR(Literal, Bounds(cache.kAsmFixnum));
}
CHECK_EXPR(BinaryOperation, Bounds(cache.kAsmSigned)) {
CHECK_EXPR(CompareOperation, Bounds(cache.kAsmSigned)) {
CHECK_EXPR(BinaryOperation, Bounds(cache.kAsmSigned)) {
CHECK_VAR(x, Bounds(cache.kAsmInt));
CHECK_EXPR(Literal, Bounds(cache.kAsmFixnum));
}
CHECK_EXPR(BinaryOperation, Bounds(cache.kAsmSigned)) {
CHECK_VAR(y, Bounds(cache.kAsmInt));
CHECK_EXPR(Literal, Bounds(cache.kAsmFixnum));
}
}
CHECK_EXPR(Literal, Bounds(cache.kAsmFixnum));
}
}
CHECK_SKIP();
}
CHECK_FUNC_TYPES_END
}
TEST(SignedCompareConst) {
CHECK_FUNC_TYPES_BEGIN(
"function bar() { var x = 1; var y = 1; return ((x|0) < (1<<31))|0; }\n"
"function foo() { bar(); }") {
CHECK_EXPR(FunctionLiteral, FUNC_I_TYPE) {
CHECK_EXPR(Assignment, Bounds(cache.kAsmInt)) {
CHECK_VAR(x, Bounds(cache.kAsmInt));
CHECK_EXPR(Literal, Bounds(cache.kAsmFixnum));
}
CHECK_EXPR(Assignment, Bounds(cache.kAsmInt)) {
CHECK_VAR(y, Bounds(cache.kAsmInt));
CHECK_EXPR(Literal, Bounds(cache.kAsmFixnum));
}
CHECK_EXPR(BinaryOperation, Bounds(cache.kAsmSigned)) {
CHECK_EXPR(CompareOperation, Bounds(cache.kAsmSigned)) {
CHECK_EXPR(BinaryOperation, Bounds(cache.kAsmSigned)) {
CHECK_VAR(x, Bounds(cache.kAsmInt));
CHECK_EXPR(Literal, Bounds(cache.kAsmFixnum));
}
CHECK_EXPR(Literal, Bounds(cache.kAsmSigned));
}
CHECK_EXPR(Literal, Bounds(cache.kAsmFixnum));
}
}
CHECK_SKIP();
}
CHECK_FUNC_TYPES_END
}
TEST(UnsignedCompare) {
CHECK_FUNC_TYPES_BEGIN(
"function bar() { var x = 1; var y = 1; return ((x>>>0) < (y>>>0))|0; }\n"
"function foo() { bar(); }") {
CHECK_EXPR(FunctionLiteral, FUNC_I_TYPE) {
CHECK_EXPR(Assignment, Bounds(cache.kAsmInt)) {
CHECK_VAR(x, Bounds(cache.kAsmInt));
CHECK_EXPR(Literal, Bounds(cache.kAsmFixnum));
}
CHECK_EXPR(Assignment, Bounds(cache.kAsmInt)) {
CHECK_VAR(y, Bounds(cache.kAsmInt));
CHECK_EXPR(Literal, Bounds(cache.kAsmFixnum));
}
CHECK_EXPR(BinaryOperation, Bounds(cache.kAsmSigned)) {
CHECK_EXPR(CompareOperation, Bounds(cache.kAsmSigned)) {
CHECK_EXPR(BinaryOperation, Bounds(cache.kAsmUnsigned)) {
CHECK_VAR(x, Bounds(cache.kAsmInt));
CHECK_EXPR(Literal, Bounds(cache.kAsmFixnum));
}
CHECK_EXPR(BinaryOperation, Bounds(cache.kAsmUnsigned)) {
CHECK_VAR(y, Bounds(cache.kAsmInt));
CHECK_EXPR(Literal, Bounds(cache.kAsmFixnum));
}
}
CHECK_EXPR(Literal, Bounds(cache.kAsmFixnum));
}
}
CHECK_SKIP();
}
CHECK_FUNC_TYPES_END
}
TEST(UnsignedCompareConst0) {
CHECK_FUNC_TYPES_BEGIN(
"function bar() { var x = 1; var y = 1; return ((x>>>0) < (0>>>0))|0; }\n"
"function foo() { bar(); }") {
CHECK_EXPR(FunctionLiteral, FUNC_I_TYPE) {
CHECK_EXPR(Assignment, Bounds(cache.kAsmInt)) {
CHECK_VAR(x, Bounds(cache.kAsmInt));
CHECK_EXPR(Literal, Bounds(cache.kAsmFixnum));
}
CHECK_EXPR(Assignment, Bounds(cache.kAsmInt)) {
CHECK_VAR(y, Bounds(cache.kAsmInt));
CHECK_EXPR(Literal, Bounds(cache.kAsmFixnum));
}
CHECK_EXPR(BinaryOperation, Bounds(cache.kAsmSigned)) {
CHECK_EXPR(CompareOperation, Bounds(cache.kAsmSigned)) {
CHECK_EXPR(BinaryOperation, Bounds(cache.kAsmUnsigned)) {
CHECK_VAR(x, Bounds(cache.kAsmInt));
CHECK_EXPR(Literal, Bounds(cache.kAsmFixnum));
}
CHECK_EXPR(Literal, Bounds(cache.kAsmFixnum));
}
CHECK_EXPR(Literal, Bounds(cache.kAsmFixnum));
}
}
CHECK_SKIP();
}
CHECK_FUNC_TYPES_END
}
TEST(UnsignedCompareConst1) {
CHECK_FUNC_TYPES_BEGIN(
"function bar() { var x = 1; var y = 1; return ((x>>>0) < "
"(0xffffffff>>>0))|0; }\n"
"function foo() { bar(); }") {
CHECK_EXPR(FunctionLiteral, FUNC_I_TYPE) {
CHECK_EXPR(Assignment, Bounds(cache.kAsmInt)) {
CHECK_VAR(x, Bounds(cache.kAsmInt));
CHECK_EXPR(Literal, Bounds(cache.kAsmFixnum));
}
CHECK_EXPR(Assignment, Bounds(cache.kAsmInt)) {
CHECK_VAR(y, Bounds(cache.kAsmInt));
CHECK_EXPR(Literal, Bounds(cache.kAsmFixnum));
}
CHECK_EXPR(BinaryOperation, Bounds(cache.kAsmSigned)) {
CHECK_EXPR(CompareOperation, Bounds(cache.kAsmSigned)) {
CHECK_EXPR(BinaryOperation, Bounds(cache.kAsmUnsigned)) {
CHECK_VAR(x, Bounds(cache.kAsmInt));
CHECK_EXPR(Literal, Bounds(cache.kAsmFixnum));
}
CHECK_EXPR(Literal, Bounds(cache.kAsmUnsigned));
}
CHECK_EXPR(Literal, Bounds(cache.kAsmFixnum));
}
}
CHECK_SKIP();
}
CHECK_FUNC_TYPES_END
}
TEST(UnsignedDivide) {
CHECK_FUNC_TYPES_BEGIN(
"function bar() { var x = 1; var y = 1; return ((x>>>0) / (y>>>0))|0; }\n"
"function foo() { bar(); }") {
CHECK_EXPR(FunctionLiteral, FUNC_I_TYPE) {
CHECK_EXPR(Assignment, Bounds(cache.kAsmInt)) {
CHECK_VAR(x, Bounds(cache.kAsmInt));
CHECK_EXPR(Literal, Bounds(cache.kAsmFixnum));
}
CHECK_EXPR(Assignment, Bounds(cache.kAsmInt)) {
CHECK_VAR(y, Bounds(cache.kAsmInt));
CHECK_EXPR(Literal, Bounds(cache.kAsmFixnum));
}
CHECK_EXPR(BinaryOperation, Bounds(cache.kAsmSigned)) {
CHECK_EXPR(BinaryOperation, Bounds(Type::None(), Type::Any())) {
CHECK_EXPR(BinaryOperation, Bounds(cache.kAsmUnsigned)) {
CHECK_VAR(x, Bounds(cache.kAsmInt));
CHECK_EXPR(Literal, Bounds(cache.kAsmFixnum));
}
CHECK_EXPR(BinaryOperation, Bounds(cache.kAsmUnsigned)) {
CHECK_VAR(y, Bounds(cache.kAsmInt));
CHECK_EXPR(Literal, Bounds(cache.kAsmFixnum));
}
}
CHECK_EXPR(Literal, Bounds(cache.kAsmFixnum));
}
}
CHECK_SKIP();
}
CHECK_FUNC_TYPES_END
}
TEST(UnsignedFromFloat64) {
CHECK_FUNC_ERROR(
"function bar() { var x = 1.0; return (x>>>0)|0; }\n"
"function foo() { bar(); }",
"asm: line 1: left bitwise operand expected to be an integer\n");
}
TEST(AndFloat64) {
CHECK_FUNC_ERROR(
"function bar() { var x = 1.0; return (x&0)|0; }\n"
"function foo() { bar(); }",
"asm: line 1: left bitwise operand expected to be an integer\n");
}
TEST(TypeMismatchAddInt32Float64) {
CHECK_FUNC_ERROR(
"function bar() { var x = 1.0; var y = 0; return (x + y)|0; }\n"
"function foo() { bar(); }",
"asm: line 1: ill-typed arithmetic operation\n");
}
TEST(TypeMismatchSubInt32Float64) {
CHECK_FUNC_ERROR(
"function bar() { var x = 1.0; var y = 0; return (x - y)|0; }\n"
"function foo() { bar(); }",
"asm: line 1: ill-typed arithmetic operation\n");
}
TEST(TypeMismatchDivInt32Float64) {
CHECK_FUNC_ERROR(
"function bar() { var x = 1.0; var y = 0; return (x / y)|0; }\n"
"function foo() { bar(); }",
"asm: line 1: ill-typed arithmetic operation\n");
}
TEST(TypeMismatchModInt32Float64) {
CHECK_FUNC_ERROR(
"function bar() { var x = 1.0; var y = 0; return (x % y)|0; }\n"
"function foo() { bar(); }",
"asm: line 1: ill-typed arithmetic operation\n");
}
TEST(ModFloat32) {
CHECK_FUNC_ERROR(
"function bar() { var x = fround(1.0); return (x % x)|0; }\n"
"function foo() { bar(); }",
"asm: line 1: ill-typed arithmetic operation\n");
}
TEST(TernaryMismatchInt32Float64) {
CHECK_FUNC_ERROR(
"function bar() { var x = 1; var y = 0.0; return (1 ? x : y)|0; }\n"
"function foo() { bar(); }",
"asm: line 1: then and else expressions in ? must have the same type "
"and be int, float, or double\n");
}
TEST(TernaryMismatchIntish) {
CHECK_FUNC_ERROR(
"function bar() { var x = 1; var y = 0; return (1 ? x + x : y)|0; }\n"
"function foo() { bar(); }",
"asm: line 1: then and else expressions in ? must have the same type "
"and be int, float, or double\n");
}
TEST(TernaryMismatchInt32Float32) {
CHECK_FUNC_ERROR(
"function bar() { var x = 1; var y = 2.0; return (x?fround(y):x)|0; }\n"
"function foo() { bar(); }",
"asm: line 1: then and else expressions in ? must have the same type "
"and be int, float, or double\n");
}
TEST(TernaryBadCondition) {
CHECK_FUNC_ERROR(
"function bar() { var x = 1; var y = 2.0; return (y?x:1)|0; }\n"
"function foo() { bar(); }",
"asm: line 1: condition must be of type int\n");
}
TEST(BadIntishMultiply) {
CHECK_FUNC_ERROR(
"function bar() { var x = 1; return ((x + x) * 4) | 0; }\n"
"function foo() { bar(); }",
"asm: line 1: intish not allowed in multiply\n");
}
TEST(IntToFloat32) {
CHECK_FUNC_ERROR(
"function bar() { var x = 1; return fround(x); }\n"
"function foo() { bar(); }",
"asm: line 1: illegal function argument type\n");
}
TEST(Int32ToFloat32) {
CHECK_FUNC_TYPES_BEGIN(
"function bar() { var x = 1; return fround(x|0); }\n"
"function foo() { bar(); }") {
CHECK_EXPR(FunctionLiteral, FUNC_F_TYPE) {
CHECK_EXPR(Assignment, Bounds(cache.kAsmInt)) {
CHECK_VAR(x, Bounds(cache.kAsmInt));
CHECK_EXPR(Literal, Bounds(cache.kAsmFixnum));
}
CHECK_EXPR(Call, Bounds(cache.kAsmFloat)) {
CHECK_VAR(fround, FUNC_N2F_TYPE);
CHECK_EXPR(BinaryOperation, Bounds(cache.kAsmSigned)) {
CHECK_VAR(x, Bounds(cache.kAsmInt));
CHECK_EXPR(Literal, Bounds(cache.kAsmFixnum));
}
}
}
CHECK_SKIP();
}
CHECK_FUNC_TYPES_END
}
TEST(Uint32ToFloat32) {
CHECK_FUNC_TYPES_BEGIN(
"function bar() { var x = 1; return fround(x>>>0); }\n"
"function foo() { bar(); }") {
CHECK_EXPR(FunctionLiteral, FUNC_F_TYPE) {
CHECK_EXPR(Assignment, Bounds(cache.kAsmInt)) {
CHECK_VAR(x, Bounds(cache.kAsmInt));
CHECK_EXPR(Literal, Bounds(cache.kAsmFixnum));
}
CHECK_EXPR(Call, Bounds(cache.kAsmFloat)) {
CHECK_VAR(fround, FUNC_N2F_TYPE);
CHECK_EXPR(BinaryOperation, Bounds(cache.kAsmUnsigned)) {
CHECK_VAR(x, Bounds(cache.kAsmInt));
CHECK_EXPR(Literal, Bounds(cache.kAsmFixnum));
}
}
}
CHECK_SKIP();
}
CHECK_FUNC_TYPES_END
}
TEST(Float64ToFloat32) {
CHECK_FUNC_TYPES_BEGIN(
"function bar() { var x = 1.0; return fround(x); }\n"
"function foo() { bar(); }") {
CHECK_EXPR(FunctionLiteral, FUNC_F_TYPE) {
CHECK_EXPR(Assignment, Bounds(cache.kAsmDouble)) {
CHECK_VAR(x, Bounds(cache.kAsmDouble));
CHECK_EXPR(Literal, Bounds(cache.kAsmDouble));
}
CHECK_EXPR(Call, Bounds(cache.kAsmFloat)) {
CHECK_VAR(fround, FUNC_N2F_TYPE);
CHECK_VAR(x, Bounds(cache.kAsmDouble));
}
}
CHECK_SKIP();
}
CHECK_FUNC_TYPES_END
}
TEST(Int32ToFloat32ToInt32) {
CHECK_FUNC_TYPES_BEGIN(
"function bar() { var x = 1; return ~~fround(x|0) | 0; }\n"
"function foo() { bar(); }") {
CHECK_EXPR(FunctionLiteral, FUNC_I_TYPE) {
CHECK_EXPR(Assignment, Bounds(cache.kAsmInt)) {
CHECK_VAR(x, Bounds(cache.kAsmInt));
CHECK_EXPR(Literal, Bounds(cache.kAsmFixnum));
}
CHECK_EXPR(BinaryOperation, Bounds(cache.kAsmSigned)) {
CHECK_EXPR(BinaryOperation, Bounds(cache.kAsmSigned)) {
CHECK_EXPR(BinaryOperation, Bounds(cache.kAsmSigned)) {
CHECK_EXPR(Call, Bounds(cache.kAsmFloat)) {
CHECK_VAR(fround, FUNC_N2F_TYPE);
CHECK_EXPR(BinaryOperation, Bounds(cache.kAsmSigned)) {
CHECK_VAR(x, Bounds(cache.kAsmInt));
CHECK_EXPR(Literal, Bounds(cache.kAsmFixnum));
}
}
CHECK_EXPR(Literal, Bounds(cache.kAsmSigned));
}
CHECK_EXPR(Literal, Bounds(cache.kAsmSigned));
}
CHECK_EXPR(Literal, Bounds(cache.kAsmFixnum));
}
}
CHECK_SKIP();
}
CHECK_FUNC_TYPES_END
}
TEST(Addition4) {
CHECK_FUNC_TYPES_BEGIN(
"function bar() { var x = 1; var y = 2; return (x+y+x+y)|0; }\n"
"function foo() { bar(); }") {
CHECK_EXPR(FunctionLiteral, FUNC_I_TYPE) {
CHECK_EXPR(Assignment, Bounds(cache.kAsmInt)) {
CHECK_VAR(x, Bounds(cache.kAsmInt));
CHECK_EXPR(Literal, Bounds(cache.kAsmFixnum));
}
CHECK_EXPR(Assignment, Bounds(cache.kAsmInt)) {
CHECK_VAR(y, Bounds(cache.kAsmInt));
CHECK_EXPR(Literal, Bounds(cache.kAsmFixnum));
}
CHECK_EXPR(BinaryOperation, Bounds(cache.kAsmSigned)) {
CHECK_EXPR(BinaryOperation, Bounds(cache.kAsmInt)) {
CHECK_EXPR(BinaryOperation, Bounds(cache.kAsmInt)) {
CHECK_EXPR(BinaryOperation, Bounds(cache.kAsmInt)) {
CHECK_VAR(x, Bounds(cache.kAsmInt));
CHECK_VAR(y, Bounds(cache.kAsmInt));
}
CHECK_VAR(x, Bounds(cache.kAsmInt));
}
CHECK_VAR(y, Bounds(cache.kAsmInt));
}
CHECK_EXPR(Literal, Bounds(cache.kAsmFixnum));
}
}
CHECK_SKIP();
}
CHECK_FUNC_TYPES_END
}
TEST(Multiplication2) {
CHECK_FUNC_ERROR(
"function bar() { var x = 1; var y = 2; return (x*y)|0; }\n"
"function foo() { bar(); }",
"asm: line 1: multiply must be by an integer literal\n");
}
TEST(Division4) {
CHECK_FUNC_ERROR(
"function bar() { var x = 1; var y = 2; return (x/y/x/y)|0; }\n"
"function foo() { bar(); }",
"asm: line 1: left and right side of integer / or % "
"must match and be signed or unsigned\n");
}
TEST(ModInt) {
CHECK_FUNC_ERROR(
"function bar() { var x = 1; var y = 2; return (x%y)|0; }\n"
"function foo() { bar(); }",
"asm: line 1: left and right side of integer / or % "
"must match and be signed or unsigned\n");
}
TEST(DivInt) {
CHECK_FUNC_ERROR(
"function bar() { var x = 1; var y = 2; return (x/y)|0; }\n"
"function foo() { bar(); }",
"asm: line 1: left and right side of integer / or % "
"must match and be signed or unsigned\n");
}
TEST(ModIntMismatch) {
CHECK_FUNC_ERROR(
"function bar() { var x = 1; var y = 2; return ((x|0)%(y>>>0))|0; }\n"
"function foo() { bar(); }",
"asm: line 1: left and right side of integer / or % "
"must match and be signed or unsigned\n");
}
TEST(DivIntMismatch) {
CHECK_FUNC_ERROR(
"function bar() { var x = 1; var y = 2; return ((x|0)/(y>>>0))|0; }\n"
"function foo() { bar(); }",
"asm: line 1: left and right side of integer / or % "
"must match and be signed or unsigned\n");
}
TEST(CompareToStringLeft) {
CHECK_FUNC_ERROR(
"function bar() { var x = 1; return ('hi' > x)|0; }\n"
"function foo() { bar(); }",
"asm: line 1: left and right side of comparison must match type "
"and be signed, unsigned, float, or double\n");
}
TEST(CompareToStringRight) {
CHECK_FUNC_ERROR(
"function bar() { var x = 1; return (x < 'hi')|0; }\n"
"function foo() { bar(); }",
"asm: line 1: left and right side of comparison must match type "
"and be signed, unsigned, float, or double\n");
}
TEST(CompareMismatchInt32Float64) {
CHECK_FUNC_ERROR(
"function bar() { var x = 1; var y = 2.0; return (x < y)|0; }\n"
"function foo() { bar(); }",
"asm: line 1: left and right side of comparison must match type "
"and be signed, unsigned, float, or double\n");
}
TEST(CompareMismatchInt32Uint32) {
CHECK_FUNC_ERROR(
"function bar() { var x = 1; var y = 2; return ((x|0) < (y>>>0))|0; }\n"
"function foo() { bar(); }",
"asm: line 1: left and right side of comparison must match type "
"and be signed, unsigned, float, or double\n");
}
TEST(CompareMismatchInt32Float32) {
CHECK_FUNC_ERROR(
"function bar() { var x = 1; var y = 2.0; return (x < fround(y))|0; }\n"
"function foo() { bar(); }",
"asm: line 1: left and right side of comparison must match type "
"and be signed, unsigned, float, or double\n");
}
TEST(FunctionRepeated) {
CHECK_FUNC_ERROR(
"function foo() { return 0; }\n"
"function foo() { return 0; }",
"asm: line 2: function repeated in module\n");
}
TEST(Float64ToInt32) {
CHECK_FUNC_TYPES_BEGIN(
"function bar() { var x = 1; var y = 0.0; x = ~~y; }\n"
"function foo() { bar(); }") {
CHECK_EXPR(FunctionLiteral, FUNC_V_TYPE) {
CHECK_EXPR(Assignment, Bounds(cache.kAsmInt)) {
CHECK_VAR(x, Bounds(cache.kAsmInt));
CHECK_EXPR(Literal, Bounds(cache.kAsmFixnum));
}
CHECK_EXPR(Assignment, Bounds(cache.kAsmDouble)) {
CHECK_VAR(y, Bounds(cache.kAsmDouble));
CHECK_EXPR(Literal, Bounds(cache.kAsmDouble));
}
CHECK_EXPR(Assignment, Bounds(cache.kAsmInt)) {
CHECK_VAR(x, Bounds(cache.kAsmInt));
CHECK_EXPR(BinaryOperation, Bounds(cache.kAsmSigned)) {
CHECK_EXPR(BinaryOperation, Bounds(cache.kAsmSigned)) {
CHECK_VAR(y, Bounds(cache.kAsmDouble));
CHECK_EXPR(Literal, Bounds(cache.kAsmSigned));
}
CHECK_EXPR(Literal, Bounds(cache.kAsmSigned));
}
}
}
CHECK_SKIP();
}
CHECK_FUNC_TYPES_END
}
TEST(Load1) {
CHECK_FUNC_TYPES_BEGIN(
"function bar() { var x = 1; var y = i8[x>>0]|0; }\n"
"function foo() { bar(); }") {
CHECK_EXPR(FunctionLiteral, FUNC_V_TYPE) {
CHECK_EXPR(Assignment, Bounds(cache.kAsmInt)) {
CHECK_VAR(x, Bounds(cache.kAsmInt));
CHECK_EXPR(Literal, Bounds(cache.kAsmFixnum));
}
CHECK_EXPR(Assignment, Bounds(cache.kAsmInt)) {
CHECK_VAR(y, Bounds(cache.kAsmInt));
CHECK_EXPR(BinaryOperation, Bounds(cache.kAsmSigned)) {
CHECK_EXPR(Property, Bounds(cache.kAsmInt)) {
CHECK_VAR(i8, Bounds(cache.kInt8Array));
CHECK_EXPR(BinaryOperation, Bounds(cache.kAsmUnsigned)) {
CHECK_VAR(x, Bounds(cache.kAsmInt));
CHECK_EXPR(Literal, Bounds(cache.kAsmFixnum));
}
}
CHECK_EXPR(Literal, Bounds(cache.kAsmFixnum));
}
}
}
CHECK_SKIP();
}
CHECK_FUNC_TYPES_END
}
TEST(LoadDouble) {
CHECK_FUNC_TYPES_BEGIN(
"function bar() { var x = 1; var y = 0.0; y = +f64[x>>3]; }\n"
"function foo() { bar(); }") {
CHECK_EXPR(FunctionLiteral, FUNC_V_TYPE) {
CHECK_EXPR(Assignment, Bounds(cache.kAsmInt)) {
CHECK_VAR(x, Bounds(cache.kAsmInt));
CHECK_EXPR(Literal, Bounds(cache.kAsmFixnum));
}
CHECK_EXPR(Assignment, Bounds(cache.kAsmDouble)) {
CHECK_VAR(y, Bounds(cache.kAsmDouble));
CHECK_EXPR(Literal, Bounds(cache.kAsmDouble));
}
CHECK_EXPR(Assignment, Bounds(cache.kAsmDouble)) {
CHECK_VAR(y, Bounds(cache.kAsmDouble));
CHECK_EXPR(BinaryOperation, Bounds(cache.kAsmDouble)) {
CHECK_EXPR(Property, Bounds(cache.kAsmDouble)) {
CHECK_VAR(f64, Bounds(cache.kFloat64Array));
CHECK_EXPR(BinaryOperation, Bounds(cache.kAsmUnsigned)) {
CHECK_VAR(x, Bounds(cache.kAsmInt));
CHECK_EXPR(Literal, Bounds(cache.kAsmFixnum));
}
}
CHECK_EXPR(Literal, Bounds(cache.kAsmDouble));
}
}
}
CHECK_SKIP();
}
CHECK_FUNC_TYPES_END
}
TEST(Store1) {
CHECK_FUNC_TYPES_BEGIN(
"function bar() { var x = 1; i8[x>>0] = 0; }\n"
"function foo() { bar(); }") {
CHECK_EXPR(FunctionLiteral, FUNC_V_TYPE) {
CHECK_EXPR(Assignment, Bounds(cache.kAsmInt)) {
CHECK_VAR(x, Bounds(cache.kAsmInt));
CHECK_EXPR(Literal, Bounds(cache.kAsmFixnum));
}
CHECK_EXPR(Assignment, Bounds(cache.kAsmInt)) {
CHECK_EXPR(Property, Bounds::Unbounded()) {
CHECK_VAR(i8, Bounds(cache.kInt8Array));
CHECK_EXPR(BinaryOperation, Bounds(cache.kAsmUnsigned)) {
CHECK_VAR(x, Bounds(cache.kAsmInt));
CHECK_EXPR(Literal, Bounds(cache.kAsmFixnum));
}
}
CHECK_EXPR(Literal, Bounds(cache.kAsmFixnum));
}
}
CHECK_SKIP();
}
CHECK_FUNC_TYPES_END
}
TEST(StoreFloat) {
CHECK_FUNC_TYPES_BEGIN(
"function bar() { var x = fround(1.0); "
"f32[0] = fround(x + fround(1.0)); }\n"
"function foo() { bar(); }") {
CHECK_EXPR(FunctionLiteral, FUNC_V_TYPE) {
CHECK_EXPR(Assignment, Bounds(cache.kAsmFloat)) {
CHECK_VAR(x, Bounds(cache.kAsmFloat));
CHECK_EXPR(Call, Bounds(cache.kAsmFloat)) {
CHECK_VAR(fround, FUNC_N2F_TYPE);
CHECK_EXPR(Literal, Bounds(cache.kAsmDouble));
}
}
CHECK_EXPR(Assignment, Bounds(cache.kAsmFloat)) {
CHECK_EXPR(Property, Bounds::Unbounded()) {
CHECK_VAR(f32, Bounds(cache.kFloat32Array));
CHECK_EXPR(Literal, Bounds(cache.kAsmFixnum));
}
CHECK_EXPR(Call, Bounds(cache.kAsmFloat)) {
CHECK_VAR(fround, FUNC_N2F_TYPE);
CHECK_EXPR(BinaryOperation, Bounds(cache.kAsmFloat)) {
CHECK_VAR(x, Bounds(cache.kAsmFloat));
CHECK_EXPR(Call, Bounds(cache.kAsmFloat)) {
CHECK_VAR(fround, FUNC_N2F_TYPE);
CHECK_EXPR(Literal, Bounds(cache.kAsmDouble));
}
}
}
}
}
CHECK_SKIP();
}
CHECK_FUNC_TYPES_END
}
TEST(StoreIntish) {
CHECK_FUNC_TYPES_BEGIN(
"function bar() { var x = 1; var y = 1; i32[0] = x + y; }\n"
"function foo() { bar(); }") {
CHECK_EXPR(FunctionLiteral, FUNC_V_TYPE) {
CHECK_EXPR(Assignment, Bounds(cache.kAsmInt)) {
CHECK_VAR(x, Bounds(cache.kAsmInt));
CHECK_EXPR(Literal, Bounds(cache.kAsmFixnum));
}
CHECK_EXPR(Assignment, Bounds(cache.kAsmInt)) {
CHECK_VAR(y, Bounds(cache.kAsmInt));
CHECK_EXPR(Literal, Bounds(cache.kAsmFixnum));
}
CHECK_EXPR(Assignment, Bounds(cache.kAsmInt)) {
CHECK_EXPR(Property, Bounds::Unbounded()) {
CHECK_VAR(i32, Bounds(cache.kInt32Array));
CHECK_EXPR(Literal, Bounds(cache.kAsmFixnum));
}
CHECK_EXPR(BinaryOperation, Bounds(cache.kAsmInt)) {
CHECK_VAR(x, Bounds(cache.kAsmInt));
CHECK_VAR(y, Bounds(cache.kAsmInt));
}
}
}
CHECK_SKIP();
}
CHECK_FUNC_TYPES_END
}
TEST(StoreFloatish) {
CHECK_FUNC_TYPES_BEGIN(
"function bar() { "
"var x = fround(1.0); "
"var y = fround(1.0); f32[0] = x + y; }\n"
"function foo() { bar(); }") {
CHECK_EXPR(FunctionLiteral, FUNC_V_TYPE) {
CHECK_EXPR(Assignment, Bounds(cache.kAsmFloat)) {
CHECK_VAR(x, Bounds(cache.kAsmFloat));
CHECK_EXPR(Call, Bounds(cache.kAsmFloat)) {
CHECK_VAR(fround, FUNC_N2F_TYPE);
CHECK_EXPR(Literal, Bounds(cache.kAsmDouble));
}
}
CHECK_EXPR(Assignment, Bounds(cache.kAsmFloat)) {
CHECK_VAR(y, Bounds(cache.kAsmFloat));
CHECK_EXPR(Call, Bounds(cache.kAsmFloat)) {
CHECK_VAR(fround, FUNC_N2F_TYPE);
CHECK_EXPR(Literal, Bounds(cache.kAsmDouble));
}
}
CHECK_EXPR(Assignment, Bounds(cache.kAsmFloat)) {
CHECK_EXPR(Property, Bounds::Unbounded()) {
CHECK_VAR(f32, Bounds(cache.kFloat32Array));
CHECK_EXPR(Literal, Bounds(cache.kAsmFixnum));
}
CHECK_EXPR(BinaryOperation, Bounds(cache.kAsmFloat)) {
CHECK_VAR(x, Bounds(cache.kAsmFloat));
CHECK_VAR(y, Bounds(cache.kAsmFloat));
}
}
}
CHECK_SKIP();
}
CHECK_FUNC_TYPES_END
}
TEST(Load1Constant) {
CHECK_FUNC_TYPES_BEGIN(
"function bar() { var x = 1; var y = i8[5]|0; }\n"
"function foo() { bar(); }") {
CHECK_EXPR(FunctionLiteral, FUNC_V_TYPE) {
CHECK_EXPR(Assignment, Bounds(cache.kAsmInt)) {
CHECK_VAR(x, Bounds(cache.kAsmInt));
CHECK_EXPR(Literal, Bounds(cache.kAsmFixnum));
}
CHECK_EXPR(Assignment, Bounds(cache.kAsmInt)) {
CHECK_VAR(y, Bounds(cache.kAsmInt));
CHECK_EXPR(BinaryOperation, Bounds(cache.kAsmSigned)) {
CHECK_EXPR(Property, Bounds(cache.kAsmInt)) {
CHECK_VAR(i8, Bounds(cache.kInt8Array));
CHECK_EXPR(Literal, Bounds(cache.kAsmFixnum));
}
CHECK_EXPR(Literal, Bounds(cache.kAsmFixnum));
}
}
}
CHECK_SKIP();
}
CHECK_FUNC_TYPES_END
}
TEST(FunctionTables) {
CHECK_FUNC_TYPES_BEGIN(
"function func1(x) { x = x | 0; return (x * 5) | 0; }\n"
"function func2(x) { x = x | 0; return (x * 25) | 0; }\n"
"var table1 = [func1, func2];\n"
"function bar(x, y) { x = x | 0; y = y | 0;\n"
" return table1[x & 1](y)|0; }\n"
"function foo() { bar(1, 2); }") {
CHECK_EXPR(FunctionLiteral, FUNC_I2I_TYPE) {
CHECK_EXPR(Assignment, Bounds(cache.kAsmInt)) {
CHECK_VAR(x, Bounds(cache.kAsmInt));
CHECK_EXPR(BinaryOperation, Bounds(cache.kAsmSigned)) {
CHECK_VAR(x, Bounds(cache.kAsmInt));
CHECK_EXPR(Literal, Bounds(cache.kAsmFixnum));
}
}
CHECK_EXPR(BinaryOperation, Bounds(cache.kAsmSigned)) {
CHECK_EXPR(BinaryOperation, Bounds(cache.kAsmInt)) {
CHECK_VAR(x, Bounds(cache.kAsmInt));
CHECK_EXPR(Literal, Bounds(cache.kAsmFixnum));
}
CHECK_EXPR(Literal, Bounds(cache.kAsmFixnum));
}
}
CHECK_EXPR(FunctionLiteral, FUNC_I2I_TYPE) {
CHECK_EXPR(Assignment, Bounds(cache.kAsmInt)) {
CHECK_VAR(x, Bounds(cache.kAsmInt));
CHECK_EXPR(BinaryOperation, Bounds(cache.kAsmSigned)) {
CHECK_VAR(x, Bounds(cache.kAsmInt));
CHECK_EXPR(Literal, Bounds(cache.kAsmFixnum));
}
}
CHECK_EXPR(BinaryOperation, Bounds(cache.kAsmSigned)) {
CHECK_EXPR(BinaryOperation, Bounds(cache.kAsmInt)) {
CHECK_VAR(x, Bounds(cache.kAsmInt));
CHECK_EXPR(Literal, Bounds(cache.kAsmFixnum));
}
CHECK_EXPR(Literal, Bounds(cache.kAsmFixnum));
}
}
CHECK_EXPR(FunctionLiteral, FUNC_II2I_TYPE) {
CHECK_EXPR(Assignment, Bounds(cache.kAsmInt)) {
CHECK_VAR(x, Bounds(cache.kAsmInt));
CHECK_EXPR(BinaryOperation, Bounds(cache.kAsmSigned)) {
CHECK_VAR(x, Bounds(cache.kAsmInt));
CHECK_EXPR(Literal, Bounds(cache.kAsmFixnum));
}
}
CHECK_EXPR(Assignment, Bounds(cache.kAsmInt)) {
CHECK_VAR(y, Bounds(cache.kAsmInt));
CHECK_EXPR(BinaryOperation, Bounds(cache.kAsmSigned)) {
CHECK_VAR(y, Bounds(cache.kAsmInt));
CHECK_EXPR(Literal, Bounds(cache.kAsmFixnum));
}
}
CHECK_EXPR(BinaryOperation, Bounds(cache.kAsmSigned)) {
CHECK_EXPR(Call, Bounds(cache.kAsmSigned)) {
CHECK_EXPR(Property, FUNC_I2I_TYPE) {
CHECK_VAR(table1, FUNC_I2I_ARRAY_TYPE);
CHECK_EXPR(BinaryOperation, Bounds(cache.kAsmSigned)) {
// TODO(bradnelson): revert this
// CHECK_VAR(x, Bounds(cache.kAsmSigned));
CHECK_VAR(x, Bounds(cache.kAsmInt));
CHECK_EXPR(Literal, Bounds(cache.kAsmFixnum));
}
}
CHECK_VAR(y, Bounds(cache.kAsmInt));
}
CHECK_EXPR(Literal, Bounds(cache.kAsmFixnum));
}
}
CHECK_SKIP();
}
CHECK_FUNC_TYPES_END_1();
CHECK_EXPR(Assignment, FUNC_I2I_ARRAY_TYPE) {
CHECK_VAR(table1, FUNC_I2I_ARRAY_TYPE);
CHECK_EXPR(ArrayLiteral, FUNC_I2I_ARRAY_TYPE) {
CHECK_VAR(func1, FUNC_I2I_TYPE);
CHECK_VAR(func2, FUNC_I2I_TYPE);
}
}
CHECK_FUNC_TYPES_END_2();
}
TEST(BadFunctionTable) {
CHECK_FUNC_ERROR(
"function func1(x) { x = x | 0; return (x * 5) | 0; }\n"
"var table1 = [func1, 1];\n"
"function bar(x, y) { x = x | 0; y = y | 0;\n"
" return table1[x & 1](y)|0; }\n"
"function foo() { bar(1, 2); }",
"asm: line 2: array component expected to be a function\n");
}
TEST(MissingParameterTypes) {
CHECK_FUNC_ERROR(
"function bar(x) { var y = 1; }\n"
"function foo() { bar(2); }",
"asm: line 1: missing parameter type annotations\n");
}
TEST(InvalidTypeAnnotationBinaryOpDiv) {
CHECK_FUNC_ERROR(
"function bar(x) { x = x / 4; }\n"
"function foo() { bar(2); }",
"asm: line 1: invalid type annotation on binary op\n");
}
TEST(InvalidTypeAnnotationBinaryOpMul) {
CHECK_FUNC_ERROR(
"function bar(x) { x = x * 4.0; }\n"
"function foo() { bar(2); }",
"asm: line 1: invalid type annotation on binary op\n");
}
TEST(InvalidArgumentCount) {
CHECK_FUNC_ERROR(
"function bar(x) { return fround(4, 5); }\n"
"function foo() { bar(); }",
"asm: line 1: invalid argument count calling function\n");
}
TEST(InvalidTypeAnnotationArity) {
CHECK_FUNC_ERROR(
"function bar(x) { x = max(x); }\n"
"function foo() { bar(3); }",
"asm: line 1: only fround allowed on expression annotations\n");
}
TEST(InvalidTypeAnnotationOnlyFround) {
CHECK_FUNC_ERROR(
"function bar(x) { x = sin(x); }\n"
"function foo() { bar(3); }",
"asm: line 1: only fround allowed on expression annotations\n");
}
TEST(InvalidTypeAnnotation) {
CHECK_FUNC_ERROR(
"function bar(x) { x = (x+x)(x); }\n"
"function foo() { bar(3); }",
"asm: line 1: invalid type annotation\n");
}
TEST(WithStatement) {
CHECK_FUNC_ERROR(
"function bar() { var x = 0; with (x) { x = x + 1; } }\n"
"function foo() { bar(); }",
"asm: line 1: bad with statement\n");
}
TEST(NestedFunction) {
CHECK_FUNC_ERROR(
"function bar() { function x() { return 1; } }\n"
"function foo() { bar(); }",
"asm: line 1: function declared inside another\n");
}
TEST(UnboundVariable) {
CHECK_FUNC_ERROR(
"function bar() { var x = y; }\n"
"function foo() { bar(); }",
"asm: line 1: unbound variable\n");
}
TEST(EqStrict) {
CHECK_FUNC_ERROR(
"function bar() { return (0 === 0)|0; }\n"
"function foo() { bar(); }",
"asm: line 1: illegal comparison operator\n");
}
TEST(NeStrict) {
CHECK_FUNC_ERROR(
"function bar() { return (0 !== 0)|0; }\n"
"function foo() { bar(); }",
"asm: line 1: illegal comparison operator\n");
}
TEST(InstanceOf) {
CHECK_FUNC_ERROR(
"function bar() { return (0 instanceof 0)|0; }\n"
"function foo() { bar(); }",
"asm: line 1: illegal comparison operator\n");
}
TEST(InOperator) {
CHECK_FUNC_ERROR(
"function bar() { return (0 in 0)|0; }\n"
"function foo() { bar(); }",
"asm: line 1: illegal comparison operator\n");
}
TEST(LogicalAndOperator) {
CHECK_FUNC_ERROR(
"function bar() { return (0 && 0)|0; }\n"
"function foo() { bar(); }",
"asm: line 1: illegal logical operator\n");
}
TEST(LogicalOrOperator) {
CHECK_FUNC_ERROR(
"function bar() { return (0 || 0)|0; }\n"
"function foo() { bar(); }",
"asm: line 1: illegal logical operator\n");
}
TEST(BitOrDouble) {
CHECK_FUNC_ERROR(
"function bar() { var x = 1.0; return x | 0; }\n"
"function foo() { bar(); }",
"asm: line 1: intish required\n");
}
TEST(BadLiteral) {
CHECK_FUNC_ERROR(
"function bar() { return true | 0; }\n"
"function foo() { bar(); }",
"asm: line 1: illegal literal\n");
}
TEST(MismatchedReturnTypeLiteral) {
CHECK_FUNC_ERROR(
"function bar() { if(1) { return 1; } return 1.0; }\n"
"function foo() { bar(); }",
"asm: line 1: return type does not match function signature\n");
}
TEST(MismatchedReturnTypeExpression) {
CHECK_FUNC_ERROR(
"function bar() {\n"
" var x = 1; var y = 1.0; if(1) { return x; } return +y; }\n"
"function foo() { bar(); }",
"asm: line 2: return type does not match function signature\n");
}
TEST(AssignToFloatishToF64) {
CHECK_FUNC_ERROR(
"function bar() { var v = fround(1.0); f64[0] = v + fround(1.0); }\n"
"function foo() { bar(); }",
"asm: line 1: floatish assignment to double array\n");
}
TEST(ForeignFunction) {
CHECK_FUNC_TYPES_BEGIN(
"var baz = foreign.baz;\n"
"function bar() { return baz(1, 2)|0; }\n"
"function foo() { bar(); }") {
CHECK_EXPR(FunctionLiteral, FUNC_I_TYPE) {
CHECK_EXPR(BinaryOperation, Bounds(cache.kAsmSigned)) {
CHECK_EXPR(Call, Bounds(cache.kAsmSigned)) {
CHECK_VAR(baz, FUNC_FOREIGN_TYPE);
CHECK_EXPR(Literal, Bounds(cache.kAsmFixnum));
CHECK_EXPR(Literal, Bounds(cache.kAsmFixnum));
}
CHECK_EXPR(Literal, Bounds(cache.kAsmFixnum));
}
}
CHECK_EXPR(FunctionLiteral, FUNC_V_TYPE) {
CHECK_EXPR(Call, Bounds(cache.kAsmSigned)) {
CHECK_VAR(bar, FUNC_I_TYPE);
}
}
}
CHECK_FUNC_TYPES_END_1()
CHECK_EXPR(Assignment, Bounds(FUNC_FOREIGN_TYPE)) {
CHECK_VAR(baz, Bounds(FUNC_FOREIGN_TYPE));
CHECK_EXPR(Property, Bounds(FUNC_FOREIGN_TYPE)) {
CHECK_VAR(foreign, Bounds::Unbounded());
CHECK_EXPR(Literal, Bounds::Unbounded());
}
}
CHECK_FUNC_TYPES_END_2()
}
TEST(ByteArray) {
// Forbidden by asm.js spec, present in embenchen.
CHECK_FUNC_TYPES_BEGIN(
"function bar() { var x = 0; i8[x] = 2; }\n"
"function foo() { bar(); }") {
CHECK_EXPR(FunctionLiteral, FUNC_V_TYPE) {
CHECK_EXPR(Assignment, Bounds(cache.kAsmInt)) {
CHECK_VAR(x, Bounds(cache.kAsmInt));
CHECK_EXPR(Literal, Bounds(cache.kAsmFixnum));
}
CHECK_EXPR(Assignment, Bounds(cache.kAsmInt)) {
CHECK_EXPR(Property, Bounds::Unbounded()) {
CHECK_VAR(i8, Bounds(cache.kInt8Array));
CHECK_VAR(x, Bounds(cache.kAsmUnsigned));
}
CHECK_EXPR(Literal, Bounds(cache.kAsmFixnum));
}
}
CHECK_SKIP();
}
CHECK_FUNC_TYPES_END
}
TEST(BadExports) {
HARNESS_PREAMBLE()
"function foo() {};\n"
"return {foo: foo, bar: 1};"
"}\n";
v8::V8::Initialize();
HandleAndZoneScope handles;
Zone* zone = handles.main_zone();
ZoneVector<ExpressionTypeEntry> types(zone);
CHECK_EQ("asm: line 2: non-function in function table\n",
Validate(zone, test_function, &types));
}
TEST(NestedHeapAssignment) {
CHECK_FUNC_ERROR(
"function bar() { var x = 0; i16[x = 1] = 2; }\n"
"function foo() { bar(); }",
"asm: line 1: expected >> in heap access\n");
}
TEST(BadOperatorHeapAssignment) {
CHECK_FUNC_ERROR(
"function bar() { var x = 0; i16[x & 1] = 2; }\n"
"function foo() { bar(); }",
"asm: line 1: expected >> in heap access\n");
}
TEST(BadArrayAssignment) {
CHECK_FUNC_ERROR(
"function bar() { i8[0] = 0.0; }\n"
"function foo() { bar(); }",
"asm: line 1: illegal type in assignment\n");
}
TEST(BadStandardFunctionCallOutside) {
CHECK_FUNC_ERROR(
"var s0 = sin(0);\n"
"function bar() { }\n"
"function foo() { bar(); }",
"asm: line 1: illegal variable reference in module body\n");
}
TEST(BadFunctionCallOutside) {
CHECK_FUNC_ERROR(
"function bar() { return 0.0; }\n"
"var s0 = bar(0);\n"
"function foo() { bar(); }",
"asm: line 2: illegal variable reference in module body\n");
}
TEST(UnaryPlusOnIntForbidden) {
CHECK_FUNC_ERROR(
"function bar() { var x = 1; return +x; }\n"
"function foo() { bar(); }",
"asm: line 1: "
"unary + only allowed on signed, unsigned, float?, or double?\n");
}
TEST(MultiplyNon1ConvertForbidden) {
CHECK_FUNC_ERROR(
"function bar() { var x = 0.0; return x * 2.0; }\n"
"function foo() { bar(); }",
"asm: line 1: invalid type annotation on binary op\n");
}
TEST(NestedVariableAssignment) {
CHECK_FUNC_TYPES_BEGIN(
"function bar() { var x = 0; x = x = 4; }\n"
"function foo() { bar(); }") {
CHECK_EXPR(FunctionLiteral, FUNC_V_TYPE) {
CHECK_EXPR(Assignment, Bounds(cache.kAsmInt)) {
CHECK_VAR(x, Bounds(cache.kAsmInt));
CHECK_EXPR(Literal, Bounds(cache.kAsmFixnum));
}
CHECK_EXPR(Assignment, Bounds(cache.kAsmInt)) {
CHECK_VAR(x, Bounds(cache.kAsmInt));
CHECK_EXPR(Assignment, Bounds(cache.kAsmInt)) {
CHECK_VAR(x, Bounds(cache.kAsmInt));
CHECK_EXPR(Literal, Bounds(cache.kAsmFixnum));
}
}
}
CHECK_SKIP();
}
CHECK_FUNC_TYPES_END
}
TEST(NestedAssignmentInHeap) {
CHECK_FUNC_TYPES_BEGIN(
"function bar() { var x = 0; i8[(x = 1) >> 0] = 2; }\n"
"function foo() { bar(); }") {
CHECK_EXPR(FunctionLiteral, FUNC_V_TYPE) {
CHECK_EXPR(Assignment, Bounds(cache.kAsmInt)) {
CHECK_VAR(x, Bounds(cache.kAsmInt));
CHECK_EXPR(Literal, Bounds(cache.kAsmFixnum));
}
CHECK_EXPR(Assignment, Bounds(cache.kAsmInt)) {
CHECK_EXPR(Property, Bounds::Unbounded()) {
CHECK_VAR(i8, Bounds(cache.kInt8Array));
CHECK_EXPR(BinaryOperation, Bounds(cache.kAsmUnsigned)) {
CHECK_EXPR(Assignment, Bounds(cache.kAsmInt)) {
CHECK_VAR(x, Bounds(cache.kAsmInt));
CHECK_EXPR(Literal, Bounds(cache.kAsmFixnum));
}
CHECK_EXPR(Literal, Bounds(cache.kAsmFixnum));
}
}
CHECK_EXPR(Literal, Bounds(cache.kAsmFixnum));
}
}
CHECK_SKIP();
}
CHECK_FUNC_TYPES_END
}
TEST(NegativeDouble) {
CHECK_FUNC_TYPES_BEGIN(
"function bar() { var x = -123.2; }\n"
"function foo() { bar(); }") {
CHECK_EXPR(FunctionLiteral, FUNC_V_TYPE) {
CHECK_EXPR(Assignment, Bounds(cache.kAsmDouble)) {
CHECK_VAR(x, Bounds(cache.kAsmDouble));
CHECK_EXPR(Literal, Bounds(cache.kAsmDouble));
}
}
CHECK_SKIP();
}
CHECK_FUNC_TYPES_END
}
TEST(NegativeInteger) {
CHECK_FUNC_TYPES_BEGIN(
"function bar() { var x = -123; }\n"
"function foo() { bar(); }") {
CHECK_EXPR(FunctionLiteral, FUNC_V_TYPE) {
CHECK_EXPR(Assignment, Bounds(cache.kAsmInt)) {
CHECK_VAR(x, Bounds(cache.kAsmInt));
CHECK_EXPR(Literal, Bounds(cache.kAsmSigned));
}
}
CHECK_SKIP();
}
CHECK_FUNC_TYPES_END
}
TEST(AbsFunction) {
CHECK_FUNC_TYPES_BEGIN(
"function bar() { var x = -123.0; x = abs(x); }\n"
"function foo() { bar(); }") {
CHECK_EXPR(FunctionLiteral, FUNC_V_TYPE) {
CHECK_EXPR(Assignment, Bounds(cache.kAsmDouble)) {
CHECK_VAR(x, Bounds(cache.kAsmDouble));
CHECK_EXPR(Literal, Bounds(cache.kAsmDouble));
}
CHECK_EXPR(Assignment, Bounds(cache.kAsmDouble)) {
CHECK_VAR(x, Bounds(cache.kAsmDouble));
CHECK_EXPR(Call, Bounds(cache.kAsmDouble)) {
CHECK_VAR(abs, FUNC_N2N_TYPE);
CHECK_VAR(x, Bounds(cache.kAsmDouble));
}
}
}
CHECK_SKIP();
}
CHECK_FUNC_TYPES_END
}
TEST(CeilFloat) {
CHECK_FUNC_TYPES_BEGIN(
"function bar() { var x = fround(3.1); x = ceil(x); }\n"
"function foo() { bar(); }") {
CHECK_EXPR(FunctionLiteral, FUNC_V_TYPE) {
CHECK_EXPR(Assignment, Bounds(cache.kAsmFloat)) {
CHECK_VAR(x, Bounds(cache.kAsmFloat));
CHECK_EXPR(Call, Bounds(cache.kAsmFloat)) {
CHECK_VAR(fround, FUNC_N2F_TYPE);
CHECK_EXPR(Literal, Bounds(cache.kAsmDouble));
}
}
CHECK_EXPR(Assignment, Bounds(cache.kAsmFloat)) {
CHECK_VAR(x, Bounds(cache.kAsmFloat));
CHECK_EXPR(Call, Bounds(cache.kAsmFloat)) {
CHECK_VAR(ceil, FUNC_N2N_TYPE);
CHECK_VAR(x, Bounds(cache.kAsmFloat));
}
}
}
CHECK_SKIP();
}
CHECK_FUNC_TYPES_END
}
TEST(FloatReturnAsDouble) {
CHECK_FUNC_TYPES_BEGIN(
"function bar() { var x = fround(3.1); return +fround(x); }\n"
"function foo() { bar(); }") {
CHECK_EXPR(FunctionLiteral, FUNC_D_TYPE) {
CHECK_EXPR(Assignment, Bounds(cache.kAsmFloat)) {
CHECK_VAR(x, Bounds(cache.kAsmFloat));
CHECK_EXPR(Call, Bounds(cache.kAsmFloat)) {
CHECK_VAR(fround, FUNC_N2F_TYPE);
CHECK_EXPR(Literal, Bounds(cache.kAsmDouble));
}
}
CHECK_EXPR(BinaryOperation, Bounds(cache.kAsmDouble)) {
CHECK_EXPR(Call, Bounds(cache.kAsmFloat)) {
CHECK_VAR(fround, FUNC_N2F_TYPE);
CHECK_VAR(x, Bounds(cache.kAsmFloat));
}
CHECK_EXPR(Literal, Bounds(cache.kAsmDouble));
}
}
CHECK_SKIP();
}
CHECK_FUNC_TYPES_END
}
TEST(TypeConsistency) {
v8::V8::Initialize();
TypeCache cache;
// Check the consistency of each of the main Asm.js types.
CHECK(cache.kAsmFixnum->Is(cache.kAsmFixnum));
CHECK(cache.kAsmFixnum->Is(cache.kAsmSigned));
CHECK(cache.kAsmFixnum->Is(cache.kAsmUnsigned));
CHECK(cache.kAsmFixnum->Is(cache.kAsmInt));
CHECK(!cache.kAsmFixnum->Is(cache.kAsmFloat));
CHECK(!cache.kAsmFixnum->Is(cache.kAsmDouble));
CHECK(cache.kAsmSigned->Is(cache.kAsmSigned));
CHECK(cache.kAsmSigned->Is(cache.kAsmInt));
CHECK(!cache.kAsmSigned->Is(cache.kAsmFixnum));
CHECK(!cache.kAsmSigned->Is(cache.kAsmUnsigned));
CHECK(!cache.kAsmSigned->Is(cache.kAsmFloat));
CHECK(!cache.kAsmSigned->Is(cache.kAsmDouble));
CHECK(cache.kAsmUnsigned->Is(cache.kAsmUnsigned));
CHECK(cache.kAsmUnsigned->Is(cache.kAsmInt));
CHECK(!cache.kAsmUnsigned->Is(cache.kAsmSigned));
CHECK(!cache.kAsmUnsigned->Is(cache.kAsmFixnum));
CHECK(!cache.kAsmUnsigned->Is(cache.kAsmFloat));
CHECK(!cache.kAsmUnsigned->Is(cache.kAsmDouble));
CHECK(cache.kAsmInt->Is(cache.kAsmInt));
CHECK(!cache.kAsmInt->Is(cache.kAsmUnsigned));
CHECK(!cache.kAsmInt->Is(cache.kAsmSigned));
CHECK(!cache.kAsmInt->Is(cache.kAsmFixnum));
CHECK(!cache.kAsmInt->Is(cache.kAsmFloat));
CHECK(!cache.kAsmInt->Is(cache.kAsmDouble));
CHECK(cache.kAsmFloat->Is(cache.kAsmFloat));
CHECK(!cache.kAsmFloat->Is(cache.kAsmInt));
CHECK(!cache.kAsmFloat->Is(cache.kAsmUnsigned));
CHECK(!cache.kAsmFloat->Is(cache.kAsmSigned));
CHECK(!cache.kAsmFloat->Is(cache.kAsmFixnum));
CHECK(!cache.kAsmFloat->Is(cache.kAsmDouble));
CHECK(cache.kAsmDouble->Is(cache.kAsmDouble));
CHECK(!cache.kAsmDouble->Is(cache.kAsmInt));
CHECK(!cache.kAsmDouble->Is(cache.kAsmUnsigned));
CHECK(!cache.kAsmDouble->Is(cache.kAsmSigned));
CHECK(!cache.kAsmDouble->Is(cache.kAsmFixnum));
CHECK(!cache.kAsmDouble->Is(cache.kAsmFloat));
}
TEST(SwitchTest) {
CHECK_FUNC_TYPES_BEGIN(
"function switcher(x) {\n"
" x = x|0;\n"
" switch (x|0) {\n"
" case 1: return 23;\n"
" case 2: return 43;\n"
" default: return 66;\n"
" }\n"
" return 0;\n"
"}\n"
"function foo() { switcher(1); }") {
CHECK_EXPR(FunctionLiteral, FUNC_I2I_TYPE) {
CHECK_EXPR(Assignment, Bounds(cache.kAsmInt)) {
CHECK_VAR(x, Bounds(cache.kAsmInt));
CHECK_EXPR(BinaryOperation, Bounds(cache.kAsmSigned)) {
CHECK_VAR(x, Bounds(cache.kAsmInt));
CHECK_EXPR(Literal, Bounds(cache.kAsmFixnum));
}
}
CHECK_EXPR(Assignment, Bounds(cache.kAsmInt)) {
CHECK_VAR(.switch_tag, Bounds(cache.kAsmInt));
CHECK_EXPR(BinaryOperation, Bounds(cache.kAsmSigned)) {
CHECK_VAR(x, Bounds(cache.kAsmInt));
CHECK_EXPR(Literal, Bounds(cache.kAsmFixnum));
}
}
CHECK_EXPR(Literal, Bounds(Type::Undefined()));
CHECK_VAR(.switch_tag, Bounds(cache.kAsmSigned));
// case 1: return 23;
CHECK_EXPR(Literal, Bounds(cache.kAsmFixnum));
CHECK_EXPR(Literal, Bounds(cache.kAsmSigned));
// case 2: return 43;
CHECK_EXPR(Literal, Bounds(cache.kAsmFixnum));
CHECK_EXPR(Literal, Bounds(cache.kAsmSigned));
// default: return 66;
CHECK_EXPR(Literal, Bounds(cache.kAsmSigned));
// return 0;
CHECK_EXPR(Literal, Bounds(cache.kAsmSigned));
}
CHECK_SKIP();
}
CHECK_FUNC_TYPES_END
}
TEST(BadSwitchRange) {
CHECK_FUNC_ERROR(
"function bar() { switch (1) { case -1: case 0x7fffffff: } }\n"
"function foo() { bar(); }",
"asm: line 1: case range too large\n");
}
TEST(DuplicateSwitchCase) {
CHECK_FUNC_ERROR(
"function bar() { switch (1) { case 0: case 0: } }\n"
"function foo() { bar(); }",
"asm: line 1: duplicate case value\n");
}
TEST(BadSwitchOrder) {
CHECK_FUNC_ERROR(
"function bar() { switch (1) { default: case 0: } }\n"
"function foo() { bar(); }",
"asm: line 1: default case out of order\n");
}
TEST(BadForeignCall) {
const char test_function[] =
"function TestModule(stdlib, foreign, buffer) {\n"
" \"use asm\";\n"
" var ffunc = foreign.foo;\n"
" function test1() { var x = 0; ffunc(x); }\n"
" return { testFunc1: test1 };\n"
"}\n";
v8::V8::Initialize();
HandleAndZoneScope handles;
Zone* zone = handles.main_zone();
ZoneVector<ExpressionTypeEntry> types(zone);
CHECK_EQ(
"asm: line 4: foreign call argument expected to be int, double, or "
"fixnum\n",
Validate(zone, test_function, &types));
}
TEST(BadImports) {
const char test_function[] =
"function TestModule(stdlib, foreign, buffer) {\n"
" \"use asm\";\n"
" var fint = (foreign.bar | 0) | 0;\n"
" function test1() {}\n"
" return { testFunc1: test1 };\n"
"}\n";
v8::V8::Initialize();
HandleAndZoneScope handles;
Zone* zone = handles.main_zone();
ZoneVector<ExpressionTypeEntry> types(zone);
CHECK_EQ("asm: line 3: illegal computation inside module body\n",
Validate(zone, test_function, &types));
}
TEST(BadVariableReference) {
const char test_function[] =
"function TestModule(stdlib, foreign, buffer) {\n"
" \"use asm\";\n"
" var x = 0;\n"
" var y = x;\n"
" function test1() {}\n"
" return { testFunc1: test1 };\n"
"}\n";
v8::V8::Initialize();
HandleAndZoneScope handles;
Zone* zone = handles.main_zone();
ZoneVector<ExpressionTypeEntry> types(zone);
CHECK_EQ("asm: line 4: illegal variable reference in module body\n",
Validate(zone, test_function, &types));
}
TEST(BadForeignVariableReferenceValueOr) {
const char test_function[] =
"function TestModule(stdlib, foreign, buffer) {\n"
" \"use asm\";\n"
" var fint = foreign.bar | 1;\n"
"}\n";
v8::V8::Initialize();
HandleAndZoneScope handles;
Zone* zone = handles.main_zone();
ZoneVector<ExpressionTypeEntry> types(zone);
CHECK_EQ("asm: line 3: illegal integer annotation value\n",
Validate(zone, test_function, &types));
}
TEST(BadForeignVariableReferenceValueOrDot) {
const char test_function[] =
"function TestModule(stdlib, foreign, buffer) {\n"
" \"use asm\";\n"
" var fint = foreign.bar | 1.0;\n"
"}\n";
v8::V8::Initialize();
HandleAndZoneScope handles;
Zone* zone = handles.main_zone();
ZoneVector<ExpressionTypeEntry> types(zone);
CHECK_EQ("asm: line 3: illegal integer annotation value\n",
Validate(zone, test_function, &types));
}
TEST(BadForeignVariableReferenceValueMul) {
const char test_function[] =
"function TestModule(stdlib, foreign, buffer) {\n"
" \"use asm\";\n"
" var fint = foreign.bar * 2.0;\n"
"}\n";
v8::V8::Initialize();
HandleAndZoneScope handles;
Zone* zone = handles.main_zone();
ZoneVector<ExpressionTypeEntry> types(zone);
CHECK_EQ("asm: line 3: illegal double annotation value\n",
Validate(zone, test_function, &types));
}
TEST(BadForeignVariableReferenceValueMulNoDot) {
const char test_function[] =
"function TestModule(stdlib, foreign, buffer) {\n"
" \"use asm\";\n"
" var fint = foreign.bar * 1;\n"
"}\n";
v8::V8::Initialize();
HandleAndZoneScope handles;
Zone* zone = handles.main_zone();
ZoneVector<ExpressionTypeEntry> types(zone);
CHECK_EQ("asm: line 3: ill-typed arithmetic operation\n",
Validate(zone, test_function, &types));
}
TEST(Imports) {
const char test_function[] =
"function TestModule(stdlib, foreign, buffer) {\n"
" \"use asm\";\n"
" var ffunc = foreign.foo;\n"
" var fint = foreign.bar | 0;\n"
" var fdouble = +foreign.baz;\n"
" function test1() { return ffunc(fint|0, fdouble) | 0; }\n"
" function test2() { return +ffunc(fdouble, fint|0); }\n"
" return { testFunc1: test1, testFunc2: test2 };\n"
"}\n";
v8::V8::Initialize();
HandleAndZoneScope handles;
Zone* zone = handles.main_zone();
ZoneVector<ExpressionTypeEntry> types(zone);
CHECK_EQ("", Validate(zone, test_function, &types));
TypeCache cache;
CHECK_TYPES_BEGIN {
// Module.
CHECK_EXPR(FunctionLiteral, Bounds::Unbounded()) {
// function test1
CHECK_EXPR(FunctionLiteral, FUNC_I_TYPE) {
CHECK_EXPR(BinaryOperation, Bounds(cache.kAsmSigned)) {
CHECK_EXPR(Call, Bounds(cache.kAsmSigned)) {
CHECK_VAR(ffunc, FUNC_FOREIGN_TYPE);
CHECK_EXPR(BinaryOperation, Bounds(cache.kAsmSigned)) {
CHECK_VAR(fint, Bounds(cache.kAsmInt));
CHECK_EXPR(Literal, Bounds(cache.kAsmFixnum));
}
CHECK_VAR(fdouble, Bounds(cache.kAsmDouble));
}
CHECK_EXPR(Literal, Bounds(cache.kAsmFixnum));
}
}
// function test2
CHECK_EXPR(FunctionLiteral, FUNC_D_TYPE) {
CHECK_EXPR(BinaryOperation, Bounds(cache.kAsmDouble)) {
CHECK_EXPR(Call, Bounds(cache.kAsmDouble)) {
CHECK_VAR(ffunc, FUNC_FOREIGN_TYPE);
CHECK_VAR(fdouble, Bounds(cache.kAsmDouble));
CHECK_EXPR(BinaryOperation, Bounds(cache.kAsmSigned)) {
CHECK_VAR(fint, Bounds(cache.kAsmInt));
CHECK_EXPR(Literal, Bounds(cache.kAsmFixnum));
}
}
CHECK_EXPR(Literal, Bounds(cache.kAsmDouble));
}
}
// "use asm";
CHECK_EXPR(Literal, Bounds(Type::String()));
// var func = foreign.foo;
CHECK_EXPR(Assignment, Bounds(FUNC_FOREIGN_TYPE)) {
CHECK_VAR(ffunc, Bounds(FUNC_FOREIGN_TYPE));
CHECK_EXPR(Property, Bounds(FUNC_FOREIGN_TYPE)) {
CHECK_VAR(foreign, Bounds::Unbounded());
CHECK_EXPR(Literal, Bounds::Unbounded());
}
}
// var fint = foreign.bar | 0;
CHECK_EXPR(Assignment, Bounds(cache.kAsmInt)) {
CHECK_VAR(fint, Bounds(cache.kAsmInt));
CHECK_EXPR(BinaryOperation, Bounds(cache.kAsmSigned)) {
CHECK_EXPR(Property, Bounds(Type::Number())) {
CHECK_VAR(foreign, Bounds::Unbounded());
CHECK_EXPR(Literal, Bounds::Unbounded());
}
CHECK_EXPR(Literal, Bounds(cache.kAsmFixnum));
}
}
// var fdouble = +foreign.baz;
CHECK_EXPR(Assignment, Bounds(cache.kAsmDouble)) {
CHECK_VAR(fdouble, Bounds(cache.kAsmDouble));
CHECK_EXPR(BinaryOperation, Bounds(cache.kAsmDouble)) {
CHECK_EXPR(Property, Bounds(Type::Number())) {
CHECK_VAR(foreign, Bounds::Unbounded());
CHECK_EXPR(Literal, Bounds::Unbounded());
}
CHECK_EXPR(Literal, Bounds(cache.kAsmDouble));
}
}
// return { testFunc1: test1, testFunc2: test2 };
CHECK_EXPR(ObjectLiteral, Bounds::Unbounded()) {
CHECK_VAR(test1, FUNC_I_TYPE);
CHECK_VAR(test2, FUNC_D_TYPE);
}
}
}
CHECK_TYPES_END
}
TEST(StoreFloatFromDouble) {
CHECK_FUNC_TYPES_BEGIN(
"function bar() { f32[0] = 0.0; }\n"
"function foo() { bar(); }") {
CHECK_EXPR(FunctionLiteral, FUNC_V_TYPE) {
CHECK_EXPR(Assignment, Bounds(cache.kAsmDouble)) {
CHECK_EXPR(Property, Bounds::Unbounded()) {
CHECK_VAR(f32, Bounds(cache.kFloat32Array));
CHECK_EXPR(Literal, Bounds(cache.kAsmFixnum));
}
CHECK_EXPR(Literal, Bounds(cache.kAsmDouble));
}
}
CHECK_SKIP();
}
CHECK_FUNC_TYPES_END
}
TEST(NegateDouble) {
CHECK_FUNC_TYPES_BEGIN(
"function bar() { var x = 0.0; x = -x; }\n"
"function foo() { bar(); }") {
CHECK_EXPR(FunctionLiteral, FUNC_V_TYPE) {
CHECK_EXPR(Assignment, Bounds(cache.kAsmDouble)) {
CHECK_VAR(x, Bounds(cache.kAsmDouble));
CHECK_EXPR(Literal, Bounds(cache.kAsmDouble));
}
CHECK_EXPR(Assignment, Bounds(cache.kAsmDouble)) {
CHECK_VAR(x, Bounds(cache.kAsmDouble));
CHECK_EXPR(BinaryOperation, Bounds(cache.kAsmDouble)) {
CHECK_VAR(x, Bounds(cache.kAsmDouble));
CHECK_EXPR(Literal, Bounds(cache.kAsmDouble));
}
}
}
CHECK_SKIP();
}
CHECK_FUNC_TYPES_END
}
......@@ -33,7 +33,6 @@
'generated_file': '<(SHARED_INTERMEDIATE_DIR)/resources.cc',
'cctest_sources': [ ### gcmole(all) ###
'asmjs/test-asm-typer.cc',
'asmjs/test-typing-asm.cc',
'compiler/c-signature.h',
'compiler/codegen-tester.cc',
'compiler/codegen-tester.h',
......@@ -78,8 +77,6 @@
'compiler/test-run-wasm-machops.cc',
'compiler/test-simplified-lowering.cc',
'cctest.cc',
'expression-type-collector.cc',
'expression-type-collector.h',
'interpreter/interpreter-tester.cc',
'interpreter/source-position-matcher.cc',
'interpreter/source-position-matcher.h',
......@@ -116,7 +113,6 @@
'test-api-fast-accessor-builder.cc',
'test-array-list.cc',
'test-ast.cc',
'test-ast-expression-visitor.cc',
'test-atomicops.cc',
'test-bignum.cc',
'test-bignum-dtoa.cc',
......
// Copyright 2015 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/v8.h"
#include "test/cctest/expression-type-collector.h"
#include "src/ast/ast-type-bounds.h"
#include "src/ast/ast.h"
#include "src/ast/scopes.h"
#include "src/codegen.h"
namespace v8 {
namespace internal {
namespace {
struct {
AstNode::NodeType type;
const char* name;
} NodeTypeNameList[] = {
#define DECLARE_VISIT(type) \
{ AstNode::k##type, #type } \
,
AST_NODE_LIST(DECLARE_VISIT)
#undef DECLARE_VISIT
};
} // namespace
ExpressionTypeCollector::ExpressionTypeCollector(
Isolate* isolate, FunctionLiteral* root, const AstTypeBounds* bounds,
ZoneVector<ExpressionTypeEntry>* dst)
: AstExpressionVisitor(isolate, root), bounds_(bounds), result_(dst) {}
void ExpressionTypeCollector::Run() {
result_->clear();
AstExpressionVisitor::Run();
}
void ExpressionTypeCollector::VisitExpression(Expression* expression) {
ExpressionTypeEntry e;
e.depth = depth();
VariableProxy* proxy = expression->AsVariableProxy();
if (proxy) {
e.name = proxy->raw_name();
}
e.bounds = bounds_->get(expression);
AstNode::NodeType type = expression->node_type();
e.kind = "unknown";
for (size_t i = 0; i < arraysize(NodeTypeNameList); ++i) {
if (NodeTypeNameList[i].type == type) {
e.kind = NodeTypeNameList[i].name;
break;
}
}
result_->push_back(e);
}
} // namespace internal
} // namespace v8
// Copyright 2015 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef V8_EXPRESSION_TYPE_COLLECTOR_H_
#define V8_EXPRESSION_TYPE_COLLECTOR_H_
#include "src/ast/ast-expression-visitor.h"
namespace v8 {
namespace internal {
class AstTypeBounds;
// A Visitor over an AST that collects a human readable string summarizing
// structure and types. Used for testing of the typing information attached
// to the expression nodes of an AST.
struct ExpressionTypeEntry {
int depth;
const char* kind;
const AstRawString* name;
Bounds bounds;
};
class ExpressionTypeCollector : public AstExpressionVisitor {
public:
ExpressionTypeCollector(Isolate* isolate, FunctionLiteral* root,
const AstTypeBounds* bounds,
ZoneVector<ExpressionTypeEntry>* dst);
void Run();
protected:
void VisitExpression(Expression* expression);
private:
const AstTypeBounds* bounds_;
ZoneVector<ExpressionTypeEntry>* result_;
};
} // namespace internal
} // namespace v8
#endif // V8_EXPRESSION_TYPE_COLLECTOR_H_
// Copyright 2015 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <stdlib.h>
#include <map>
#include "src/v8.h"
#include "src/ast/ast.h"
#include "src/ast/ast-expression-visitor.h"
#include "src/ast/scopes.h"
#include "src/parsing/parser.h"
#include "src/parsing/rewriter.h"
#include "test/cctest/cctest.h"
#include "test/cctest/expression-type-collector.h"
#include "test/cctest/expression-type-collector-macros.h"
using namespace v8::internal;
namespace {
class NodeTypeCounter : public AstExpressionVisitor {
public:
typedef std::map<AstNode::NodeType, int> Counters;
NodeTypeCounter(Isolate* isolate, Expression* expr, Counters* counts)
: AstExpressionVisitor(isolate, expr), counts_(counts) {}
protected:
void VisitExpression(Expression* expr) override {
(*counts_)[expr->node_type()]++;
}
private:
Counters* counts_;
};
} // namespace
TEST(VisitExpression) {
const char test_function[] =
"function GeometricMean(stdlib, foreign, buffer) {\n"
" \"use asm\";\n"
"\n"
" var exp = stdlib.Math.exp;\n"
" var log = stdlib.Math.log;\n"
" var values = new stdlib.Float64Array(buffer);\n"
"\n"
" function logSum(start, end) {\n"
" start = start|0;\n"
" end = end|0;\n"
"\n"
" var sum = 0.0, p = 0, q = 0;\n"
"\n"
" // asm.js forces byte addressing of the heap by requiring shifting "
"by 3\n"
" for (p = start << 3, q = end << 3; (p|0) < (q|0); p = (p + 8)|0) {\n"
" sum = sum + +log(values[p>>3]);\n"
" }\n"
"\n"
" return +sum;\n"
" }\n"
"\n"
" function geometricMean(start, end) {\n"
" start = start|0;\n"
" end = end|0;\n"
"\n"
" return +exp(+logSum(start, end) / +((end - start)|0));\n"
" }\n"
"\n"
" return { geometricMean: geometricMean };\n"
"}\n";
// Parse + compile test_function, and extract the AST node for it.
v8::V8::Initialize();
HandleAndZoneScope handles;
i::Isolate* isolate = CcTest::i_isolate();
i::Handle<i::String> source_code =
isolate->factory()
->NewStringFromUtf8(i::CStrVector(test_function))
.ToHandleChecked();
i::Handle<i::Script> script = isolate->factory()->NewScript(source_code);
i::ParseInfo info(handles.main_zone(), script);
i::Parser parser(&info);
info.set_global();
info.set_lazy(false);
info.set_allow_lazy_parsing(false);
info.set_toplevel(true);
CHECK(i::Compiler::ParseAndAnalyze(&info));
Expression* test_function_expr =
info.scope()->declarations()->at(0)->AsFunctionDeclaration()->fun();
// Run NodeTypeCounter and sanity check counts for 3 expression types,
// and for overall # of types found.
NodeTypeCounter::Counters counts;
NodeTypeCounter(isolate, test_function_expr, &counts).Run();
CHECK_EQ(21, counts[AstNode::kBinaryOperation]);
CHECK_EQ(26, counts[AstNode::kLiteral]);
CHECK_EQ(3, counts[AstNode::kFunctionLiteral]);
CHECK_EQ(10, counts.size());
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment