Chromium Code Reviews| Index: test/cctest/test-asm-validator.cc |
| diff --git a/test/cctest/test-asm-validator.cc b/test/cctest/test-asm-validator.cc |
| index 39d490e7a931761924ac310a62635d51145383b2..8d5a505f65cc9dc52046b8f9da45948da794ad8e 100644 |
| --- a/test/cctest/test-asm-validator.cc |
| +++ b/test/cctest/test-asm-validator.cc |
| @@ -2,18 +2,26 @@ |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| +#include <cstring> |
| +#include <functional> |
| +#include <iostream> |
| +#include <memory> |
| + |
| #include "src/v8.h" |
| -#include "src/ast/ast.h" |
| #include "src/ast/ast-expression-visitor.h" |
| +#include "src/ast/ast-value-factory.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 "src/typing-asm.h" |
| +#include "src/wasm/asm-typer.h" |
| +#include "src/wasm/asm-types.h" |
| #include "test/cctest/cctest.h" |
| -#include "test/cctest/expression-type-collector.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)) |
| @@ -46,6 +54,7 @@ |
| zone)) |
| using namespace v8::internal; |
| +namespace iw = i::wasm; |
| namespace { |
| @@ -2513,3 +2522,557 @@ TEST(NegateDouble) { |
| } |
| CHECK_FUNC_TYPES_END |
| } |
| + |
|
bradnelson
2016/06/24 00:19:03
Maybe put this in test/wasm/test-asm-validator ?
T
John
2016/06/24 17:47:58
I left a TODO, for now. I'll move the file before
bradn
2016/06/24 18:57:03
Ok. Yeah better for review.
|
| +// ----------------------------------------------------------------------------- |
| +// asm.js typer v2. |
| + |
| +namespace v8 { |
| +namespace internal { |
| +namespace wasm { |
| + |
| +enum ValidationType { |
| + ValidateModule, |
| + ValidateGlobals, |
| + ValidateFunctionTables, |
| + ValidateExport, |
| +}; |
| + |
| +class AsmTyperHarnessBuilder { |
| + public: |
| + AsmTyperHarnessBuilder(const char* source, ValidationType type) |
| + : source_(source), |
| + validation_type_(type), |
| + handles_(), |
| + zone_(handles_.main_zone()), |
| + isolate_(CcTest::i_isolate()), |
| + ast_value_factory_(zone_, isolate_->heap()->HashSeed()), |
| + factory_(isolate_->factory()), |
| + source_code_( |
| + factory_->NewStringFromUtf8(CStrVector(source)).ToHandleChecked()), |
| + script_(factory_->NewScript(source_code_)) { |
| + ParseInfo info(zone_, script_); |
| + Parser parser(&info); |
| + info.set_global(); |
| + info.set_lazy(false); |
| + info.set_allow_lazy_parsing(false); |
| + info.set_toplevel(true); |
| + info.set_ast_value_factory(&ast_value_factory_); |
| + info.set_ast_value_factory_owned(false); |
| + |
| + if (!Compiler::ParseAndAnalyze(&info)) { |
| + std::cerr << "Failed to parse:\n" << source_ << "\n"; |
| + CHECK(false); |
| + } |
| + |
| + outer_scope_ = info.script_scope(); |
| + module_ = |
| + info.scope()->declarations()->at(0)->AsFunctionDeclaration()->fun(); |
| + typer_.reset(new AsmTyper(isolate_, zone_, *script_, module_)); |
| + } |
| + |
| + struct VariableName { |
| + VariableName(const char* name, VariableMode mode) |
| + : name_(name), mode_(mode) {} |
| + VariableName(const VariableName&) = default; |
| + VariableName& operator=(const VariableName&) = default; |
| + |
| + const char* name_; |
| + const VariableMode mode_; |
| + }; |
| + |
| + AsmTyperHarnessBuilder* WithLocal(VariableName, AsmType*) { return this; } |
| + |
| + AsmTyperHarnessBuilder* WithGlobal(VariableName var_name, AsmType* type) { |
| + auto* var = DeclareVariable(var_name); |
| + auto* var_info = new (zone_) AsmTyper::VariableInfo(type); |
| + var_info->set_mutability(AsmTyper::VariableInfo::kMutableGlobal); |
| + CHECK(typer_->AddGlobal(var, var_info)); |
| + return this; |
| + } |
| + |
| + AsmTyperHarnessBuilder* WithGlobal( |
| + VariableName var_name, std::function<AsmType*(Zone*)> type_creator) { |
| + return WithGlobal(var_name, type_creator(zone_)); |
| + } |
| + |
| + AsmTyperHarnessBuilder* WithUndefinedGlobal( |
| + VariableName var_name, std::function<AsmType*(Zone*)> type_creator) { |
| + auto* type = type_creator(zone_); |
| + CHECK(type->AsFunctionType() != nullptr || |
| + type->AsFunctionTableType() != nullptr); |
| + WithGlobal(var_name, type); |
| + auto* var_info = typer_->Lookup(DeclareVariable(var_name)); |
| + CHECK(var_info); |
| + var_info->FirstForwardUseIs(nullptr); |
| + return this; |
| + } |
| + |
| + AsmTyperHarnessBuilder* WithImport(VariableName var_name, |
| + AsmTyper::StandardMember standard_member) { |
| + auto* var = DeclareVariable(var_name); |
| + AsmTyper::VariableInfo* var_info = nullptr; |
| + auto* stdlib_map = &typer_->stdlib_math_types_; |
| + switch (standard_member) { |
| + case AsmTyper::kHeap: |
| + case AsmTyper::kStdlib: |
| + case AsmTyper::kModule: |
| + case AsmTyper::kNone: |
| + CHECK(false); |
| + case AsmTyper::kFFI: |
| + stdlib_map = nullptr; |
| + var_info = new (zone_) AsmTyper::VariableInfo(AsmType::FFIType(zone_)); |
| + var_info->set_mutability(AsmTyper::VariableInfo::kImmutableGlobal); |
| + break; |
| + case AsmTyper::kInfinity: |
| + case AsmTyper::kNaN: |
| + stdlib_map = &typer_->stdlib_types_; |
| + default: |
| + break; |
| + } |
| + |
| + if (var_info == nullptr) { |
| + for (auto iter : *stdlib_map) { |
| + if (iter.second->standard_member() == standard_member) { |
| + var_info = iter.second; |
| + break; |
| + } |
| + } |
| + |
| + CHECK(var_info != nullptr); |
| + var_info = var_info->Clone(zone_); |
| + } |
| + |
| + CHECK(typer_->AddGlobal(var, var_info)); |
| + return this; |
| + } |
| + |
| + AsmTyperHarnessBuilder* WithStdlib(VariableName var_name) { |
| + auto* var = DeclareVariable(var_name); |
| + auto* var_info = new (zone_) AsmTyper::VariableInfo(); |
| + var_info->set_mutability(AsmTyper::VariableInfo::kImmutableGlobal); |
| + var_info->set_standard_member(AsmTyper::kStdlib); |
| + CHECK(typer_->AddGlobal(var, var_info)); |
| + return this; |
| + } |
| + |
| + AsmTyperHarnessBuilder* WithHeap(VariableName var_name) { |
| + auto* var = DeclareVariable(var_name); |
| + auto* var_info = new (zone_) AsmTyper::VariableInfo(); |
| + var_info->set_mutability(AsmTyper::VariableInfo::kImmutableGlobal); |
| + var_info->set_standard_member(AsmTyper::kHeap); |
| + CHECK(typer_->AddGlobal(var, var_info)); |
| + return this; |
| + } |
| + |
| + AsmTyperHarnessBuilder* WithFFI(VariableName var_name) { |
| + auto* var = DeclareVariable(var_name); |
| + auto* var_info = |
| + new (zone_) AsmTyper::VariableInfo(AsmType::FFIType(zone_)); |
| + var_info->set_mutability(AsmTyper::VariableInfo::kImmutableGlobal); |
| + var_info->set_standard_member(AsmTyper::kFFI); |
| + CHECK(typer_->AddGlobal(var, var_info)); |
| + return this; |
| + } |
| + |
| + bool Succeeds() { |
| + CHECK(validation_type_ == ValidateModule || |
| + validation_type_ == ValidateGlobals || |
| + validation_type_ == ValidateFunctionTables || |
| + validation_type_ == ValidateExport); |
| + if (typer_->Validate()) { |
| + return true; |
| + } |
| + |
| + std::cerr << "Asm validation failed: " << typer_->error_message() << "\n"; |
| + return false; |
| + } |
| + |
| + bool FailsWithMessage(const char* error_message) { |
| + CHECK(validation_type_ == ValidateModule || |
| + validation_type_ == ValidateGlobals || |
| + validation_type_ == ValidateFunctionTables || |
| + validation_type_ == ValidateExport); |
| + |
| + if (typer_->Validate()) { |
| + std::cerr << "Asm validation succeeded\n"; |
| + return false; |
| + } |
| + |
| + if (std::strstr(typer_->error_message(), error_message) == nullptr) { |
| + std::cerr << "Asm validation failed with the wrong error message:\n" |
| + "Expected to contain '" |
| + << error_message << "'\n" |
| + " Actually is '" |
| + << typer_->error_message() << "'\n"; |
| + return false; |
| + } |
| + |
| + return true; |
| + } |
| + |
| + private: |
| + Variable* DeclareVariable(VariableName var_name) { |
| + auto* name_ast_string = ast_value_factory_.GetOneByteString(var_name.name_); |
| + return var_name.mode_ == DYNAMIC_GLOBAL |
| + ? outer_scope_->DeclareDynamicGlobal(name_ast_string) |
| + : module_->scope()->DeclareLocal(name_ast_string, VAR, |
| + kCreatedInitialized, |
| + Variable::NORMAL); |
| + } |
| + |
| + std::string source_; |
| + ValidationType validation_type_; |
| + HandleAndZoneScope handles_; |
| + Zone* zone_; |
| + Isolate* isolate_; |
| + AstValueFactory ast_value_factory_; |
| + Factory* factory_; |
| + Handle<String> source_code_; |
| + Handle<Script> script_; |
| + |
| + Scope* outer_scope_; |
| + FunctionLiteral* module_; |
| + std::unique_ptr<AsmTyper> typer_; |
| +}; |
| + |
| +} // namespace wasm |
| +} // namespace internal |
| +} // namespace v8 |
| + |
| +namespace { |
| + |
| +struct ValidationInput { |
| + ValidationInput(const std::string& source, iw::ValidationType type) |
| + : source_(source), type_(type) {} |
| + |
| + const std::string source_; |
| + const iw::ValidationType type_; |
| +}; |
| + |
| +std::unique_ptr<iw::AsmTyperHarnessBuilder> ValidationOf( |
| + ValidationInput input) { |
| + return std::unique_ptr<iw::AsmTyperHarnessBuilder>( |
| + new iw::AsmTyperHarnessBuilder(input.source_.c_str(), input.type_)); |
| +} |
| + |
| +ValidationInput Module(const char* source) { |
| + return ValidationInput(source, iw::ValidateModule); |
| +} |
| + |
| +std::string WrapInFunction(const char* source, bool needs_use_asm) { |
| + if (needs_use_asm) { |
| + return std::string( |
| + "function foo() {\n" |
| + " 'use asm';\n" |
| + " ") + |
| + source + |
| + "\n" |
| + "}"; |
| + } |
| + |
| + return std::string( |
| + "function foo() {\n" |
| + " ") + |
| + source + |
| + "\n" |
| + "}"; |
| +} |
| + |
| +ValidationInput Globals(const char* source) { |
| + static const bool kNeedsUseAsm = true; |
| + return ValidationInput(WrapInFunction(source, kNeedsUseAsm), |
| + iw::ValidateGlobals); |
| +} |
| + |
| +ValidationInput FunctionTables(const char* source) { |
| + static const bool kNeedsUseAsm = true; |
| + return ValidationInput(WrapInFunction(source, kNeedsUseAsm), |
| + iw::ValidateFunctionTables); |
| +} |
| + |
| +ValidationInput Export(const char* source) { |
| + static const bool kNeedsUseAsm = true; |
| + return ValidationInput(WrapInFunction(source, kNeedsUseAsm), |
| + iw::ValidateExport); |
| +} |
| + |
| +iw::AsmTyperHarnessBuilder::VariableName Var(const char* name) { |
| + return iw::AsmTyperHarnessBuilder::VariableName(name, VAR); |
| +} |
| + |
| +iw::AsmTyperHarnessBuilder::VariableName DynamicGlobal(const char* name) { |
| + return iw::AsmTyperHarnessBuilder::VariableName(name, DYNAMIC_GLOBAL); |
| +} |
| + |
| +TEST(MissingUseAsmDirective) { |
| + v8::V8::Initialize(); |
| + |
| + // We can't test the empty input ("") because the AsmTyperHarnessBuilder will |
| + // CHECK if there's no function in the top-level scope. |
| + const char* kTests[] = {"function module(){}", |
| + "function module(){ use_asm; }", |
| + "function module(){ \"use asm \"; }", |
| + "function module(){ \" use asm \"; }", |
| + "function module(){ \"use Asm\"; }"}; |
| + |
| + for (size_t ii = 0; ii < arraysize(kTests); ++ii) { |
| + const char* module = kTests[ii]; |
| + if (!ValidationOf(Module(module)) |
| + ->FailsWithMessage("missing \"use asm\"")) { |
| + std::cerr << "Test:\n" << module; |
| + CHECK(false); |
| + } |
| + } |
| +} |
| + |
| +TEST(InvalidModuleSignature) { |
| + v8::V8::Initialize(); |
| + |
| + const struct { |
| + const char* module; |
| + const char* error_message; |
| + } kTests[] = { |
| + {"function eval(){ \"use asm\"; }", |
| + "Invalid ASM.js identifier in module name"}, |
| + {"function arguments(){ \"use asm\"; }", |
| + "Invalid ASM.js identifier in module name"}, |
| + {"function module(eval){ \"use asm\"; }", |
| + "Invalid ASM.js identifier in module parameter"}, |
| + {"function module(arguments){ \"use asm\"; }", |
| + "Invalid ASM.js identifier in module parameter"}, |
| + {"function module(stdlib, eval){ \"use asm\"; }", |
| + "Invalid ASM.js identifier in module parameter"}, |
| + {"function module(stdlib, arguments){ \"use asm\"; }", |
| + "Invalid ASM.js identifier in module parameter"}, |
| + {"function module(stdlib, foreign, eval){ \"use asm\"; }", |
| + "Invalid ASM.js identifier in module parameter"}, |
| + {"function module(stdlib, foreign, arguments){ \"use asm\"; }", |
| + "Invalid ASM.js identifier in module parameter"}, |
| + {"function module(stdlib, foreign, heap, eval){ \"use asm\"; }", |
| + "ASM.js modules may not have more than three parameters"}, |
| + {"function module(stdlib, foreign, heap, arguments){ \"use asm\"; }", |
| + "ASM.js modules may not have more than three parameters"}, |
| + {"function module(module){ \"use asm\"; }", |
| + "Redeclared identifier in module parameter"}, |
| + {"function module(stdlib, module){ \"use asm\"; }", |
| + "Redeclared identifier in module parameter"}, |
| + {"function module(stdlib, stdlib){ \"use asm\"; }", |
| + "Redeclared identifier in module parameter"}, |
| + {"function module(stdlib, foreign, module){ \"use asm\"; }", |
| + "Redeclared identifier in module parameter"}, |
| + {"function module(stdlib, foreign, stdlib){ \"use asm\"; }", |
| + "Redeclared identifier in module parameter"}, |
| + {"function module(stdlib, foreign, foreign){ \"use asm\"; }", |
| + "Redeclared identifier in module parameter"}, |
| + }; |
| + |
| + for (size_t ii = 0; ii < arraysize(kTests); ++ii) { |
| + const auto* test = kTests + ii; |
| + if (!ValidationOf(Module(test->module)) |
| + ->FailsWithMessage(test->error_message)) { |
| + std::cerr << "Test:\n" << test->module; |
| + CHECK(false); |
| + } |
| + } |
| +} |
| + |
| +TEST(ErrorsInGlobalVariableDefinition) { |
| + const struct { |
| + const char* decl; |
| + const char* error_message; |
| + } kTests[] = { |
| + {"var v;", "Global variable missing initializer"}, |
| + {"var v = uninitialized;", "Invalid global variable initializer"}, |
| + {"var v = 'use asm';", "type annotation - forbidden literal"}, |
| + {"var v = 4294967296;", " - forbidden literal"}, |
| + {"var v = not_fround;", "Invalid global variable initializer"}, |
| + {"var v = not_fround(1);", "expected call fround(literal)"}, |
| + {"var v = __fround__(1.0);", "expected call fround(literal)"}, |
| + {"var v = fround(1.0, 1.0);", "expected call fround(literal)"}, |
| + {"var v = fround(not_fround);", "literal argument for call to fround"}, |
| + {"var v = fround(1);", "literal argument to be a floating point"}, |
| + {"var v = stdlib.nan", "Invalid import"}, |
| + {"var v = stdlib.Math.nan", "Invalid import"}, |
| + {"var v = stdlib.Mathh.E", "Invalid import"}, |
| + {"var v = stdlib.Math", "Invalid import"}, |
| + {"var v = Stdlib.Math.E", "Invalid import"}, |
| + {"var v = stdlib.Math.E[0]", "Invalid import"}, |
| + {"var v = stdlibb.NaN", "Invalid import"}, |
| + {"var v = ffi.NaN[0]", "Invalid import"}, |
| + {"var v = heap.NaN[0]", "Invalid import"}, |
| + {"var v = ffi.foo * 2.0;", "unrecognized annotation"}, |
| + {"var v = ffi.foo|1;", "unrecognized annotation"}, |
| + {"var v = ffi()|0;", "must import member"}, |
| + {"var v = +ffi();", "must import member"}, |
| + {"var v = ffi().a|0;", "object lookup failed"}, |
| + {"var v = +ffi().a;", "object lookup failed"}, |
| + {"var v = sstdlib.a|0;", "object lookup failed"}, |
| + {"var v = +sstdlib.a;", "object lookup failed"}, |
| + {"var v = stdlib.NaN|0;", "object is not the ffi"}, |
| + {"var v = +stdlib.NaN;", "object is not the ffi"}, |
| + {"var v = new f()", "Invalid type after new"}, |
| + {"var v = new stdli.Uint8Array(heap)", "Unknown stdlib member in heap"}, |
| + {"var v = new stdlib.dd(heap)", "Unknown stdlib member in heap"}, |
| + {"var v = new stdlib.Math.fround(heap)", "Type is not a heap view type"}, |
| + {"var v = new stdlib.Uint8Array(a, b)", "Invalid number of arguments"}, |
| + {"var v = new stdlib.Uint8Array(heap())", "should be the module's heap"}, |
| + {"var v = new stdlib.Uint8Array(heap_)", "instead of heap parameter"}, |
| + {"var v = new stdlib.Uint8Array(ffi)", "should be the module's heap"}, |
| + {"var eval = 0;", "in global variable"}, |
| + {"var eval = 0.0;", "in global variable"}, |
| + {"var eval = fround(0.0);", "in global variable"}, |
| + {"var eval = +ffi.a;", "in global variable"}, |
| + {"var eval = ffi.a|0;", "in global variable"}, |
| + {"var eval = ffi.a;", "in global variable"}, |
| + {"var eval = new stdlib.Uint8Array(heap);", "in global variable"}, |
| + {"var arguments = 0;", "in global variable"}, |
| + {"var arguments = 0.0;", "in global variable"}, |
| + {"var arguments = fround(0.0);", "in global variable"}, |
| + {"var arguments = +ffi.a;", "in global variable"}, |
| + {"var arguments = ffi.a|0;", "in global variable"}, |
| + {"var arguments = ffi.a;", "in global variable"}, |
| + {"var arguments = new stdlib.Uint8Array(heap);", "in global variable"}, |
| + {"var a = 0, a = 0.0;", "Redefined global variable"}, |
| + {"var a = 0; var a = 0;", "Redefined global variable"}, |
| + {"var a = 0, b = 0; var a = 0;", "Redefined global variable"}, |
| + {"var a = 0, b = 0; var b = 0, a = 0.0;", "Redefined global variable"}, |
| + }; |
| + |
| + for (size_t ii = 0; ii < arraysize(kTests); ++ii) { |
| + const auto* test = kTests + ii; |
| + if (!ValidationOf(Globals(test->decl)) |
| + ->WithStdlib(DynamicGlobal("stdlib")) |
| + ->WithFFI(DynamicGlobal("ffi")) |
| + ->WithHeap(DynamicGlobal("heap")) |
| + ->WithGlobal(DynamicGlobal("not_fround"), iw::AsmType::Int()) |
| + ->WithImport(DynamicGlobal("fround"), iw::AsmTyper::kMathFround) |
| + ->FailsWithMessage(test->error_message)) { |
| + std::cerr << "Test:\n" << test->decl; |
| + CHECK(false); |
| + } |
| + } |
| +} |
| + |
| +TEST(ErrorsInFunctionTableDefinition) { |
| + const struct { |
| + const char* tables; |
| + const char* error_message; |
| + } kTests[] = { |
| + {"var a = [a, a, a];", "Invalid length for function pointer table"}, |
| + {"var a = [d2s0()];", "must be a function name"}, |
| + {"var a = [d2s44];", "Undefined identifier in function pointer"}, |
| + {"var a = [fround];", "not be a member of the standard library"}, |
| + {"var a = [imul];", "not be a member of the standard library"}, |
| + {"var a = [ffi_import];", "must be an ASM.js function"}, |
| + {"var a = [dI];", "must be an ASM.js function"}, |
| + {"var a = [d2s0, d2s1, d2s0, f2s0];", "mismatch in function pointer"}, |
| + {"var eval = [d2s0, d2s1];", "ASM.js identifier in function table name"}, |
| + {"var arguments = [d2s0, d2s1];", "ASM.js identifier in function table"}, |
| + {"var foo = [d2s0, d2s1];", "Identifier redefined in function table"}, |
| + {"var I = [d2s0, d2s1];", "Identifier redefined in function table name"}, |
| + {"var d2s = [d2f0, d2f1];", "redeclared as function pointer table"}, |
| + {"var d2s_t = [d2s0];", "Function table size mismatch"}, |
| + {"var d2s_t = [d2f0, d2sf];", "initializer does not match previous"}, |
| + }; |
| + |
| + auto d2s = [](Zone* zone) -> iw::AsmType* { |
| + auto* ret = iw::AsmType::Function(zone, iw::AsmType::Signed()); |
| + ret->AsFunctionType()->AddArgument(iw::AsmType::Double()); |
| + return ret; |
| + }; |
| + |
| + auto d2s_tbl = [](Zone* zone) -> iw::AsmType* { |
| + auto* d2s = iw::AsmType::Function(zone, iw::AsmType::Signed()); |
| + d2s->AsFunctionType()->AddArgument(iw::AsmType::Double()); |
| + |
| + auto* ret = iw::AsmType::FunctionTableType(zone, 2, d2s); |
| + return ret; |
| + }; |
| + |
| + auto f2s = [](Zone* zone) -> iw::AsmType* { |
| + auto* ret = iw::AsmType::Function(zone, iw::AsmType::Signed()); |
| + ret->AsFunctionType()->AddArgument(iw::AsmType::Float()); |
| + return ret; |
| + }; |
| + |
| + auto d2f = [](Zone* zone) -> iw::AsmType* { |
| + auto* ret = iw::AsmType::Function(zone, iw::AsmType::Float()); |
| + ret->AsFunctionType()->AddArgument(iw::AsmType::Double()); |
| + return ret; |
| + }; |
| + |
| + for (size_t ii = 0; ii < arraysize(kTests); ++ii) { |
| + const auto* test = kTests + ii; |
| + if (!ValidationOf(FunctionTables(test->tables)) |
| + ->WithImport(DynamicGlobal("ffi_import"), iw::AsmTyper::kFFI) |
| + ->WithImport(DynamicGlobal("imul"), iw::AsmTyper::kMathImul) |
| + ->WithImport(DynamicGlobal("E"), iw::AsmTyper::kMathE) |
| + ->WithImport(DynamicGlobal("fround"), iw::AsmTyper::kMathFround) |
| + ->WithImport(DynamicGlobal("floor"), iw::AsmTyper::kMathFround) |
| + ->WithGlobal(DynamicGlobal("d2s0"), d2s) |
| + ->WithGlobal(DynamicGlobal("d2s1"), d2s) |
| + ->WithGlobal(DynamicGlobal("f2s0"), f2s) |
| + ->WithGlobal(DynamicGlobal("f2s1"), f2s) |
| + ->WithGlobal(DynamicGlobal("d2f0"), d2f) |
| + ->WithGlobal(DynamicGlobal("d2f1"), d2f) |
| + ->WithGlobal(DynamicGlobal("dI"), iw::AsmType::Int()) |
| + ->WithGlobal(Var("I"), iw::AsmType::Int()) |
| + ->WithUndefinedGlobal(Var("d2s"), d2s) |
| + ->WithUndefinedGlobal(Var("d2s_t"), d2s_tbl) |
| + ->FailsWithMessage(test->error_message)) { |
| + std::cerr << "Test:\n" << test->tables; |
| + CHECK(false); |
| + } |
| + } |
| +} |
| + |
| +TEST(ErrorsInModuleExport) { |
|
bradnelson
2016/06/24 00:19:03
These tests are great John, definitely a good appr
John
2016/06/24 17:47:58
Acknowledged.
|
| + const struct { |
| + const char* module_export; |
| + const char* error_message; |
| + } kTests[] = { |
| + {"", "Missing ASM.js module export"}, |
| + {"return;", "Unrecognized expression in ASM.js module export expression"}, |
| + {"return d2s_tbl;", "is not an ASM.js function"}, |
| + {"return min;", "is not an ASM.js function"}, |
| + {"return I;", "is not an ASM.js function"}, |
| + {"return {\'a\': d2s_tbl}", "export property is not an ASM.js"}, |
| + {"return {\'a\': min}", "export cannot contain standard library"}, |
| + {"return {\'a\': f()}", "must be an ASM.js function name"}, |
| + {"return {\'a\': f}", "in ASM.js module export property"}, |
| + {"function v() { a(); } return {b: d2s}", "Missing definition for forw"}, |
| + {"return {b: d2s, \'a\': d2s_tbl}", "export property is not an ASM.js"}, |
| + {"return {b: d2s, \'a\': min}", "export cannot contain standard library"}, |
| + {"return {b: d2s, \'a\': f()}", "must be an ASM.js function name"}, |
| + {"return {b: d2s, \'a\': f}", "in ASM.js module export property"}, |
| + }; |
| + |
| + auto d2s_tbl = [](Zone* zone) -> iw::AsmType* { |
| + auto* d2s = iw::AsmType::Function(zone, iw::AsmType::Signed()); |
| + d2s->AsFunctionType()->AddArgument(iw::AsmType::Double()); |
| + |
| + auto* ret = iw::AsmType::FunctionTableType(zone, 2, d2s); |
| + return ret; |
| + }; |
| + |
| + auto d2s = [](Zone* zone) -> iw::AsmType* { |
| + auto* ret = iw::AsmType::Function(zone, iw::AsmType::Signed()); |
| + ret->AsFunctionType()->AddArgument(iw::AsmType::Double()); |
| + return ret; |
| + }; |
| + |
| + for (size_t ii = 0; ii < arraysize(kTests); ++ii) { |
| + const auto* test = kTests + ii; |
| + if (!ValidationOf(Export(test->module_export)) |
| + ->WithGlobal(DynamicGlobal("d2s_tbl"), d2s_tbl) |
| + ->WithGlobal(DynamicGlobal("d2s"), d2s) |
| + ->WithImport(DynamicGlobal("min"), iw::AsmTyper::kMathMin) |
| + ->WithGlobal(DynamicGlobal("I"), iw::AsmType::Int()) |
| + ->FailsWithMessage(test->error_message)) { |
| + std::cerr << "Test:\n" << test->module_export; |
| + CHECK(false); |
| + } |
| + } |
| +} |
| + |
| +} // namespace |