/*
* Copyright 2016 WebAssembly Community Group participants
*
* Licensed under the Apache License , Version 2 . 0 ( the " License " ) ;
* you may not use this file except in compliance with the License .
* You may obtain a copy of the License at
*
* http : //www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing , software
* distributed under the License is distributed on an " AS IS " BASIS ,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND , either express or implied .
* See the License for the specific language governing permissions and
* limitations under the License .
*/
#include "wabt/binary-writer-spec.h"
#include <cassert>
#include <cinttypes>
#include <string_view>
#include "wabt/config.h"
#include "wabt/binary-writer.h"
#include "wabt/binary.h"
#include "wabt/cast.h"
#include "wabt/filenames.h"
#include "wabt/ir.h"
#include "wabt/literal.h"
#include "wabt/stream.h"
namespace wabt {
namespace {
class BinaryWriterSpec {
public :
BinaryWriterSpec(Stream* json_stream,
WriteBinarySpecStreamFactory module_stream_factory,
std::string_view source_filename,
std::string_view module_filename_noext,
const WriteBinaryOptions& options);
Result WriteScript(const Script& script);
private :
std::string GetModuleFilename(const char* extension);
void WriteString(const char* s);
void WriteKey(const char* key);
void WriteSeparator();
void WriteEscapedString(std::string_view);
void WriteCommandType(const Command& command);
void WriteLocation(const Location& loc);
void WriteVar(const Var& var);
void WriteTypeObject(Type type);
void WriteF32(uint32_t, ExpectedNan);
void WriteF64(uint64_t, ExpectedNan);
void WriteRefBits(uintptr_t ref_bits);
void WriteConst(const Const & const_);
void WriteConstVector(const ConstVector& consts);
void WriteAction(const Action& action);
void WriteActionResultType(const Action& action);
void WriteModule(std::string_view filename, const Module& module);
void WriteScriptModule(std::string_view filename,
const ScriptModule& script_module);
void WriteInvalidModule(const ScriptModule& module, std::string_view text);
void WriteCommands();
const Script* script_ = nullptr;
Stream* json_stream_ = nullptr;
WriteBinarySpecStreamFactory module_stream_factory_;
std::string source_filename_;
std::string module_filename_noext_;
const WriteBinaryOptions& options_;
Result result_ = Result::Ok;
size_t num_modules_ = 0 ;
};
BinaryWriterSpec::BinaryWriterSpec(
Stream* json_stream,
WriteBinarySpecStreamFactory module_stream_factory,
std::string_view source_filename,
std::string_view module_filename_noext,
const WriteBinaryOptions& options)
: json_stream_(json_stream),
module_stream_factory_(module_stream_factory),
source_filename_(source_filename),
module_filename_noext_(module_filename_noext),
options_(options) {}
std::string BinaryWriterSpec::GetModuleFilename(const char* extension) {
std::string result = module_filename_noext_;
result += '.' ;
result += std::to_string(num_modules_);
result += extension;
ConvertBackslashToSlash(&result);
return result;
}
void BinaryWriterSpec::WriteString(const char* s) {
json_stream_->Writef("\" %s\"" , s);
}
void BinaryWriterSpec::WriteKey(const char* key) {
json_stream_->Writef("\" %s\": " , key);
}
void BinaryWriterSpec::WriteSeparator() {
json_stream_->Writef(", " );
}
void BinaryWriterSpec::WriteEscapedString(std::string_view s) {
json_stream_->WriteChar('"' );
for (size_t i = 0 ; i < s.length(); ++i) {
uint8_t c = s[i];
if (c < 0 x20 || c == '\\' || c == '"' ) {
json_stream_->Writef("\\u%04x" , c);
} else {
json_stream_->WriteChar(c);
}
}
json_stream_->WriteChar('"' );
}
void BinaryWriterSpec::WriteCommandType(const Command& command) {
static const char* s_command_names[] = {
"module" ,
"module" ,
"action" ,
"register" ,
"assert_malformed" ,
"assert_invalid" ,
"assert_unlinkable" ,
"assert_uninstantiable" ,
"assert_return" ,
"assert_trap" ,
"assert_exhaustion" ,
"assert_exception" ,
};
WABT_STATIC_ASSERT(WABT_ARRAY_SIZE(s_command_names) == kCommandTypeCount);
WriteKey("type" );
assert(s_command_names[static_cast <size_t>(command.type)]);
WriteString(s_command_names[static_cast <size_t>(command.type)]);
}
void BinaryWriterSpec::WriteLocation(const Location& loc) {
WriteKey("line" );
json_stream_->Writef("%d" , loc.line);
}
void BinaryWriterSpec::WriteVar(const Var& var) {
if (var.is_index()) {
json_stream_->Writef("\" %" PRIindex " \"" , var.index());
} else {
WriteEscapedString(var.name());
}
}
void BinaryWriterSpec::WriteTypeObject(Type type) {
json_stream_->Writef("{" );
WriteKey("type" );
WriteString(type.GetName().c_str());
json_stream_->Writef("}" );
}
void BinaryWriterSpec::WriteF32(uint32_t f32_bits, ExpectedNan expected) {
switch (expected) {
case ExpectedNan::None:
json_stream_->Writef("\" %u\"" , f32_bits);
break ;
case ExpectedNan::Arithmetic:
WriteString("nan:arithmetic" );
break ;
case ExpectedNan::Canonical:
WriteString("nan:canonical" );
break ;
}
}
void BinaryWriterSpec::WriteF64(uint64_t f64_bits, ExpectedNan expected) {
switch (expected) {
case ExpectedNan::None:
json_stream_->Writef("\" %" PRIu64 " \"" , f64_bits);
break ;
case ExpectedNan::Arithmetic:
WriteString("nan:arithmetic" );
break ;
case ExpectedNan::Canonical:
WriteString("nan:canonical" );
break ;
}
}
void BinaryWriterSpec::WriteRefBits(uintptr_t ref_bits) {
if (ref_bits == Const ::kRefNullBits) {
json_stream_->Writef("\" null\"" );
} else {
json_stream_->Writef("\" %" PRIuPTR " \"" , ref_bits);
}
}
void BinaryWriterSpec::WriteConst(const Const & const_) {
json_stream_->Writef("{" );
WriteKey("type" );
/* Always write the values as strings, even though they may be representable
* as JSON numbers. This way the formatting is consistent. */
switch (const_.type()) {
case Type::I32:
WriteString("i32" );
WriteSeparator();
WriteKey("value" );
json_stream_->Writef("\" %u\"" , const_.u32());
break ;
case Type::I64:
WriteString("i64" );
WriteSeparator();
WriteKey("value" );
json_stream_->Writef("\" %" PRIu64 " \"" , const_.u64());
break ;
case Type::F32:
WriteString("f32" );
WriteSeparator();
WriteKey("value" );
WriteF32(const_.f32_bits(), const_.expected_nan());
break ;
case Type::F64:
WriteString("f64" );
WriteSeparator();
WriteKey("value" );
WriteF64(const_.f64_bits(), const_.expected_nan());
break ;
case Type::FuncRef: {
WriteString("funcref" );
WriteSeparator();
WriteKey("value" );
WriteRefBits(const_.ref_bits());
break ;
}
case Type::ExternRef: {
WriteString("externref" );
WriteSeparator();
WriteKey("value" );
WriteRefBits(const_.ref_bits());
break ;
}
case Type::ExnRef: {
WriteString("exnref" );
WriteSeparator();
WriteKey("value" );
WriteRefBits(const_.ref_bits());
break ;
}
case Type::V128: {
WriteString("v128" );
WriteSeparator();
WriteKey("lane_type" );
WriteString(const_.lane_type().GetName().c_str());
WriteSeparator();
WriteKey("value" );
json_stream_->Writef("[" );
for (int lane = 0 ; lane < const_.lane_count(); ++lane) {
switch (const_.lane_type()) {
case Type::I8:
json_stream_->Writef("\" %u\"" , const_.v128_lane<uint8_t>(lane));
break ;
case Type::I16:
json_stream_->Writef("\" %u\"" , const_.v128_lane<uint16_t>(lane));
break ;
case Type::I32:
json_stream_->Writef("\" %u\"" , const_.v128_lane<uint32_t>(lane));
break ;
case Type::I64:
json_stream_->Writef("\" %" PRIu64 " \"" ,
const_.v128_lane<uint64_t>(lane));
break ;
case Type::F32:
WriteF32(const_.v128_lane<uint32_t>(lane),
const_.expected_nan(lane));
break ;
case Type::F64:
WriteF64(const_.v128_lane<uint64_t>(lane),
const_.expected_nan(lane));
break ;
default :
WABT_UNREACHABLE;
}
if (lane != const_.lane_count() - 1 ) {
WriteSeparator();
}
}
json_stream_->Writef("]" );
break ;
}
default :
WABT_UNREACHABLE;
}
json_stream_->Writef("}" );
}
void BinaryWriterSpec::WriteConstVector(const ConstVector& consts) {
json_stream_->Writef("[" );
for (size_t i = 0 ; i < consts.size(); ++i) {
const Const & const_ = consts[i];
WriteConst(const_);
if (i != consts.size() - 1 ) {
WriteSeparator();
}
}
json_stream_->Writef("]" );
}
void BinaryWriterSpec::WriteAction(const Action& action) {
WriteKey("action" );
json_stream_->Writef("{" );
WriteKey("type" );
if (action.type() == ActionType::Invoke) {
WriteString("invoke" );
} else {
assert(action.type() == ActionType::Get);
WriteString("get" );
}
WriteSeparator();
if (action.module_var.is_name()) {
WriteKey("module" );
WriteVar(action.module_var);
WriteSeparator();
}
if (action.type() == ActionType::Invoke) {
WriteKey("field" );
WriteEscapedString(action.name);
WriteSeparator();
WriteKey("args" );
WriteConstVector(cast<InvokeAction>(&action)->args);
} else {
WriteKey("field" );
WriteEscapedString(action.name);
}
json_stream_->Writef("}" );
}
void BinaryWriterSpec::WriteActionResultType(const Action& action) {
const Module* module = script_->GetModule(action.module_var);
const Export* export_;
json_stream_->Writef("[" );
switch (action.type()) {
case ActionType::Invoke: {
export_ = module->GetExport(action.name);
assert(export_->kind == ExternalKind::Func);
const Func* func = module->GetFunc(export_->var);
Index num_results = func->GetNumResults();
for (Index i = 0 ; i < num_results; ++i)
WriteTypeObject(func->GetResultType(i));
break ;
}
case ActionType::Get: {
export_ = module->GetExport(action.name);
assert(export_->kind == ExternalKind::Global);
const Global* global = module->GetGlobal(export_->var);
WriteTypeObject(global->type);
break ;
}
}
json_stream_->Writef("]" );
}
void BinaryWriterSpec::WriteModule(std::string_view filename,
const Module& module) {
result_ |=
WriteBinaryModule(module_stream_factory_(filename), &module, options_);
}
void BinaryWriterSpec::WriteScriptModule(std::string_view filename,
const ScriptModule& script_module) {
switch (script_module.type()) {
case ScriptModuleType::Text:
WriteModule(filename, cast<TextScriptModule>(&script_module)->module);
break ;
case ScriptModuleType::Binary:
module_stream_factory_(filename)->WriteData(
cast<BinaryScriptModule>(&script_module)->data, "" );
break ;
case ScriptModuleType::Quoted:
module_stream_factory_(filename)->WriteData(
cast<QuotedScriptModule>(&script_module)->data, "" );
break ;
}
}
void BinaryWriterSpec::WriteInvalidModule(const ScriptModule& module,
std::string_view text) {
const char* extension = "" ;
const char* module_type = "" ;
switch (module.type()) {
case ScriptModuleType::Text:
extension = kWasmExtension;
module_type = "binary" ;
break ;
case ScriptModuleType::Binary:
extension = kWasmExtension;
module_type = "binary" ;
break ;
case ScriptModuleType::Quoted:
extension = kWatExtension;
module_type = "text" ;
break ;
}
WriteLocation(module.location());
WriteSeparator();
std::string filename = GetModuleFilename(extension);
WriteKey("filename" );
WriteEscapedString(GetBasename(filename));
WriteSeparator();
WriteKey("text" );
WriteEscapedString(text);
WriteSeparator();
WriteKey("module_type" );
WriteString(module_type);
WriteScriptModule(filename, module);
}
void BinaryWriterSpec::WriteCommands() {
json_stream_->Writef("{\" source_filename\": " );
WriteEscapedString(source_filename_);
json_stream_->Writef(",\n \" commands\": [\n" );
Index last_module_index = kInvalidIndex;
for (size_t i = 0 ; i < script_->commands.size(); ++i) {
const Command* command = script_->commands[i].get();
if (i != 0 ) {
WriteSeparator();
json_stream_->Writef("\n" );
}
json_stream_->Writef(" {" );
WriteCommandType(*command);
WriteSeparator();
switch (command->type) {
case CommandType::Module: {
const Module& module = cast<ModuleCommand>(command)->module;
std::string filename = GetModuleFilename(kWasmExtension);
WriteLocation(module.loc);
WriteSeparator();
if (!module.name.empty()) {
WriteKey("name" );
WriteEscapedString(module.name);
WriteSeparator();
}
WriteKey("filename" );
WriteEscapedString(GetBasename(filename));
WriteModule(filename, module);
num_modules_++;
last_module_index = i;
break ;
}
case CommandType::ScriptModule: {
auto * script_module_command = cast<ScriptModuleCommand>(command);
const auto & module = script_module_command->module;
std::string filename = GetModuleFilename(kWasmExtension);
WriteLocation(module.loc);
WriteSeparator();
if (!module.name.empty()) {
WriteKey("name" );
WriteEscapedString(module.name);
WriteSeparator();
}
WriteKey("filename" );
WriteEscapedString(GetBasename(filename));
WriteScriptModule(filename, *script_module_command->script_module);
num_modules_++;
last_module_index = i;
break ;
}
case CommandType::Action: {
const Action& action = *cast<ActionCommand>(command)->action;
WriteLocation(action.loc);
WriteSeparator();
WriteAction(action);
WriteSeparator();
WriteKey("expected" );
WriteActionResultType(action);
break ;
}
case CommandType::Register : {
auto * register_command = cast<RegisterCommand>(command);
const Var& var = register_command->var;
WriteLocation(var.loc);
WriteSeparator();
if (var.is_name()) {
WriteKey("name" );
WriteVar(var);
WriteSeparator();
} else {
/* If we're not registering by name, then we should only be
* registering the last module. */
WABT_USE(last_module_index);
assert(var.index() == last_module_index);
}
WriteKey("as" );
WriteEscapedString(register_command->module_name);
break ;
}
case CommandType::AssertMalformed: {
auto * assert_malformed_command = cast<AssertMalformedCommand>(command);
WriteInvalidModule(*assert_malformed_command->module,
assert_malformed_command->text);
num_modules_++;
break ;
}
case CommandType::AssertInvalid: {
auto * assert_invalid_command = cast<AssertInvalidCommand>(command);
WriteInvalidModule(*assert_invalid_command->module,
assert_invalid_command->text);
num_modules_++;
break ;
}
case CommandType::AssertUnlinkable: {
auto * assert_unlinkable_command =
cast<AssertUnlinkableCommand>(command);
WriteInvalidModule(*assert_unlinkable_command->module,
assert_unlinkable_command->text);
num_modules_++;
break ;
}
case CommandType::AssertUninstantiable: {
auto * assert_uninstantiable_command =
cast<AssertUninstantiableCommand>(command);
WriteInvalidModule(*assert_uninstantiable_command->module,
assert_uninstantiable_command->text);
num_modules_++;
break ;
}
case CommandType::AssertReturn: {
auto * assert_return_command = cast<AssertReturnCommand>(command);
WriteLocation(assert_return_command->action->loc);
WriteSeparator();
WriteAction(*assert_return_command->action);
WriteSeparator();
const Expectation* expectation = assert_return_command->expected.get();
switch (expectation->type()) {
case ExpectationType::Values:
WriteKey("expected" );
break ;
case ExpectationType::Either:
WriteKey("either" );
break ;
}
WriteConstVector(expectation->expected);
break ;
}
case CommandType::AssertTrap: {
auto * assert_trap_command = cast<AssertTrapCommand>(command);
WriteLocation(assert_trap_command->action->loc);
WriteSeparator();
WriteAction(*assert_trap_command->action);
WriteSeparator();
WriteKey("text" );
WriteEscapedString(assert_trap_command->text);
WriteSeparator();
WriteKey("expected" );
WriteActionResultType(*assert_trap_command->action);
break ;
}
case CommandType::AssertExhaustion: {
auto * assert_exhaustion_command =
cast<AssertExhaustionCommand>(command);
WriteLocation(assert_exhaustion_command->action->loc);
WriteSeparator();
WriteAction(*assert_exhaustion_command->action);
WriteSeparator();
WriteKey("text" );
WriteEscapedString(assert_exhaustion_command->text);
WriteSeparator();
WriteKey("expected" );
WriteActionResultType(*assert_exhaustion_command->action);
break ;
}
case CommandType::AssertException: {
auto * assert_exception_command = cast<AssertExceptionCommand>(command);
WriteLocation(assert_exception_command->action->loc);
WriteSeparator();
WriteAction(*assert_exception_command->action);
WriteSeparator();
WriteKey("expected" );
WriteActionResultType(*assert_exception_command->action);
break ;
}
}
json_stream_->Writef("}" );
}
json_stream_->Writef("]}\n" );
}
Result BinaryWriterSpec::WriteScript(const Script& script) {
script_ = &script;
WriteCommands();
return result_;
}
} // end anonymous namespace
Result WriteBinarySpecScript(Stream* json_stream,
WriteBinarySpecStreamFactory module_stream_factory,
Script* script,
std::string_view source_filename,
std::string_view module_filename_noext,
const WriteBinaryOptions& options) {
BinaryWriterSpec binary_writer_spec(json_stream, module_stream_factory,
source_filename, module_filename_noext,
options);
return binary_writer_spec.WriteScript(*script);
}
Result WriteBinarySpecScript(
Stream* json_stream,
Script* script,
std::string_view source_filename,
std::string_view module_filename_noext,
const WriteBinaryOptions& options,
std::vector<FilenameMemoryStreamPair>* out_module_streams,
Stream* log_stream) {
WriteBinarySpecStreamFactory module_stream_factory =
[&](std::string_view filename) {
out_module_streams->emplace_back(
filename, std::make_unique<MemoryStream>(log_stream));
return out_module_streams->back().stream.get();
};
BinaryWriterSpec binary_writer_spec(json_stream, module_stream_factory,
source_filename, module_filename_noext,
options);
return binary_writer_spec.WriteScript(*script);
}
} // namespace wabt
Messung V0.5 in Prozent C=100 H=100 G=100
¤ Dauer der Verarbeitung: 0.16 Sekunden
¤
*© Formatika GbR, Deutschland