// Copyright (c) 2015-2016 The Khronos Group Inc. // // 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 "source/diagnostic.h" #include <cassert> #include <cstring> #include <iostream> #include <sstream> #include <utility> #include "source/table.h" // Diagnostic API spv_diagnostic spvDiagnosticCreate(const spv_position position, const char* message) { spv_diagnostic diagnostic = new spv_diagnostic_t; if (!diagnostic) return nullptr; size_t length = strlen(message) + 1; diagnostic->error = new char[length]; if (!diagnostic->error) { delete diagnostic; return nullptr; } diagnostic->position = *position; diagnostic->isTextSource = false; memset(diagnostic->error, 0, length); strncpy(diagnostic->error, message, length); return diagnostic; } void spvDiagnosticDestroy(spv_diagnostic diagnostic) { if (!diagnostic) return; delete[] diagnostic->error; delete diagnostic; } spv_result_t spvDiagnosticPrint(const spv_diagnostic diagnostic) { if (!diagnostic) return SPV_ERROR_INVALID_DIAGNOSTIC; if (diagnostic->isTextSource) { // NOTE: This is a text position // NOTE: add 1 to the line as editors start at line 1, we are counting new // line characters to start at line 0 std::cerr << "error: " << diagnostic->position.line + 1 << ": " << diagnostic->position.column + 1 << ": " << diagnostic->error << "\n"; return SPV_SUCCESS; } // NOTE: Assume this is a binary position std::cerr << "error: "; if (diagnostic->position.index > 0) std::cerr << diagnostic->position.index << ": "; std::cerr << diagnostic->error << "\n"; return SPV_SUCCESS; } namespace spvtools { DiagnosticStream::DiagnosticStream(DiagnosticStream&& other) : stream_(), position_(other.position_), consumer_(other.consumer_), disassembled_instruction_(std::move(other.disassembled_instruction_)), error_(other.error_) { // Prevent the other object from emitting output during destruction. other.error_ = SPV_FAILED_MATCH; // Some platforms are missing support for std::ostringstream functionality, // including: move constructor, swap method. Either would have been a // better choice than copying the string. stream_ << other.stream_.str(); } DiagnosticStream::~DiagnosticStream() { if (error_ != SPV_FAILED_MATCH && consumer_ != nullptr) { auto level = SPV_MSG_ERROR; switch (error_) { case SPV_SUCCESS: case SPV_REQUESTED_TERMINATION: // Essentially success. level = SPV_MSG_INFO; break; case SPV_WARNING: level = SPV_MSG_WARNING; break; case SPV_UNSUPPORTED: case SPV_ERROR_INTERNAL: case SPV_ERROR_INVALID_TABLE: level = SPV_MSG_INTERNAL_ERROR; break; case SPV_ERROR_OUT_OF_MEMORY: level = SPV_MSG_FATAL; break; default: break; } if (disassembled_instruction_.size() > 0) stream_ << std::endl << " " << disassembled_instruction_ << std::endl; consumer_(level, "input", position_, stream_.str().c_str()); } } void UseDiagnosticAsMessageConsumer(spv_context context, spv_diagnostic* diagnostic) { assert(diagnostic && *diagnostic == nullptr); auto create_diagnostic = [diagnostic](spv_message_level_t, const char*, const spv_position_t& position, const char* message) { auto p = position; spvDiagnosticDestroy(*diagnostic); // Avoid memory leak. *diagnostic = spvDiagnosticCreate(&p, message); }; SetContextMessageConsumer(context, std::move(create_diagnostic)); } std::string spvResultToString(spv_result_t res) { std::string out; switch (res) { case SPV_SUCCESS: out = "SPV_SUCCESS"; break; case SPV_UNSUPPORTED: out = "SPV_UNSUPPORTED"; break; case SPV_END_OF_STREAM: out = "SPV_END_OF_STREAM"; break; case SPV_WARNING: out = "SPV_WARNING"; break; case SPV_FAILED_MATCH: out = "SPV_FAILED_MATCH"; break; case SPV_REQUESTED_TERMINATION: out = "SPV_REQUESTED_TERMINATION"; break; case SPV_ERROR_INTERNAL: out = "SPV_ERROR_INTERNAL"; break; case SPV_ERROR_OUT_OF_MEMORY: out = "SPV_ERROR_OUT_OF_MEMORY"; break; case SPV_ERROR_INVALID_POINTER: out = "SPV_ERROR_INVALID_POINTER"; break; case SPV_ERROR_INVALID_BINARY: out = "SPV_ERROR_INVALID_BINARY"; break; case SPV_ERROR_INVALID_TEXT: out = "SPV_ERROR_INVALID_TEXT"; break; case SPV_ERROR_INVALID_TABLE: out = "SPV_ERROR_INVALID_TABLE"; break; case SPV_ERROR_INVALID_VALUE: out = "SPV_ERROR_INVALID_VALUE"; break; case SPV_ERROR_INVALID_DIAGNOSTIC: out = "SPV_ERROR_INVALID_DIAGNOSTIC"; break; case SPV_ERROR_INVALID_LOOKUP: out = "SPV_ERROR_INVALID_LOOKUP"; break; case SPV_ERROR_INVALID_ID: out = "SPV_ERROR_INVALID_ID"; break; case SPV_ERROR_INVALID_CFG: out = "SPV_ERROR_INVALID_CFG"; break; case SPV_ERROR_INVALID_LAYOUT: out = "SPV_ERROR_INVALID_LAYOUT"; break; default: out = "Unknown Error"; } return out; } } // namespace spvtools