Skip to content

Commit

Permalink
[WIP] Create better and more performang step begin/end macros
Browse files Browse the repository at this point in the history
Signed-off-by: Juan Cruz Viotti <[email protected]>
  • Loading branch information
jviotti committed Sep 3, 2024
1 parent 2917e0c commit 6e7ccc2
Showing 1 changed file with 76 additions and 42 deletions.
118 changes: 76 additions & 42 deletions src/jsonschema/compile_evaluate.cc
Original file line number Diff line number Diff line change
Expand Up @@ -234,9 +234,16 @@ class EvaluationContext {
this->property_as_instance = (type == TargetType::Key);
}

auto resolve_target(const JSON &instance) -> const JSON & {
auto
resolve_target(const JSON &instance,
const Pointer &relative_instance_location) -> const JSON & {
using namespace sourcemeta::jsontoolkit;
if (this->property_as_instance) [[unlikely]] {
if (!relative_instance_location.empty()) {
assert(relative_instance_location.back().is_property());
return this->value(relative_instance_location.back().to_property());
}

assert(!this->instance_location().empty());
assert(this->instance_location().back().is_property());
return this->value(this->instance_location().back().to_property());
Expand All @@ -247,7 +254,13 @@ class EvaluationContext {
// Can we be smarter? Maybe we keep a reference to the current
// instance location in this class that we manipulate through
// .push() and .pop()
return get(instance, this->instance_location());
return get(get(instance, this->instance_location()),
relative_instance_location);
}

auto resolve_target(const JSON &instance) -> const JSON & {
return this->resolve_target(instance,
sourcemeta::jsontoolkit::empty_pointer);
}

auto mark(const std::size_t id, const Template &children) -> void {
Expand Down Expand Up @@ -312,13 +325,13 @@ auto evaluate_step(
using namespace sourcemeta::jsontoolkit;
bool result{false};

// TODO: Get rid of these legacy macros in favor of the new begin/end ones
#define CALLBACK_PRE(current_step, current_instance_location) \
if (current_step.report && callback.has_value()) { \
callback.value()(SchemaCompilerEvaluationType::Pre, true, step, \
context.evaluate_path(), current_instance_location, \
context.value(nullptr)); \
}

#define CALLBACK_POST(title, current_step) \
if (current_step.report && callback.has_value()) { \
callback.value()(SchemaCompilerEvaluationType::Post, result, step, \
Expand All @@ -339,39 +352,64 @@ auto evaluate_step(
context.evaluate_path(), current_instance_location, \
annotation_result.first); \
}

#define EVALUATE_IMPLICIT_PRECONDITION(title, current_step, precondition) \
if (!(precondition)) { \
context.pop(current_step); \
SOURCEMETA_TRACE_END(trace_id, title); \
return true; \
}

if (std::holds_alternative<SchemaCompilerAssertionFail>(step)) {
SOURCEMETA_TRACE_START(trace_id, "SchemaCompilerAssertionFail");
const auto &assertion{std::get<SchemaCompilerAssertionFail>(step)};
context.push(assertion);
CALLBACK_PRE(assertion, context.instance_location());
CALLBACK_POST("SchemaCompilerAssertionFail", assertion);
} else if (std::holds_alternative<SchemaCompilerAssertionDefines>(step)) {
SOURCEMETA_TRACE_START(trace_id, "SchemaCompilerAssertionDefines");
const auto &assertion{std::get<SchemaCompilerAssertionDefines>(step)};
context.push(assertion);
const auto &target{context.resolve_target(instance)};
EVALUATE_IMPLICIT_PRECONDITION("SchemaCompilerAssertionDefines", assertion,
target.is_object());
CALLBACK_PRE(assertion, context.instance_location());
#define STRINGIFY(x) #x
#define IS_STEP(step_type) std::holds_alternative<step_type>(step)

#define EVALUATE_BEGIN(step_category, step_type, precondition) \
SOURCEMETA_TRACE_START(trace_id, STRINGIFY(step_type)); \
const auto &step_category{std::get<step_type>(step)}; \
const auto &target{context.resolve_target( \
instance, step_category.relative_instance_location)}; \
if (!(precondition)) { \
SOURCEMETA_TRACE_END(trace_id, STRINGIFY(step_type)); \
return true; \
} \
context.push(step_category); \
if (step_category.report && callback.has_value()) { \
callback.value()(SchemaCompilerEvaluationType::Pre, true, step, \
context.evaluate_path(), context.instance_location(), \
context.value(nullptr)); \
}

#define EVALUATE_BEGIN_NO_PRECONDITION(step_category, step_type) \
SOURCEMETA_TRACE_START(trace_id, STRINGIFY(step_type)); \
const auto &step_category{std::get<step_type>(step)}; \
context.push(step_category); \
if (step_category.report && callback.has_value()) { \
callback.value()(SchemaCompilerEvaluationType::Pre, true, step, \
context.evaluate_path(), context.instance_location(), \
context.value(nullptr)); \
}

#define EVALUATE_END(step_category, step_type) \
if (step_category.report && callback.has_value()) { \
callback.value()(SchemaCompilerEvaluationType::Post, result, step, \
context.evaluate_path(), context.instance_location(), \
context.value(nullptr)); \
} \
context.pop(step_category); \
SOURCEMETA_TRACE_END(trace_id, STRINGIFY(step_type)); \
return result;

if (IS_STEP(SchemaCompilerAssertionFail)) {
EVALUATE_BEGIN_NO_PRECONDITION(assertion, SchemaCompilerAssertionFail);
EVALUATE_END(assertion, SchemaCompilerAssertionFail);
} else if (IS_STEP(SchemaCompilerAssertionDefines)) {
EVALUATE_BEGIN(assertion, SchemaCompilerAssertionDefines,
target.is_object());
result = target.defines(assertion.value);
CALLBACK_POST("SchemaCompilerAssertionDefines", assertion);
} else if (std::holds_alternative<SchemaCompilerAssertionDefinesAll>(step)) {
SOURCEMETA_TRACE_START(trace_id, "SchemaCompilerAssertionDefinesAll");
const auto &assertion{std::get<SchemaCompilerAssertionDefinesAll>(step)};
context.push(assertion);
const auto &target{context.resolve_target(instance)};
EVALUATE_IMPLICIT_PRECONDITION("SchemaCompilerAssertionDefinesAll",
assertion, target.is_object());
EVALUATE_END(assertion, SchemaCompilerAssertionDefines);
} else if (IS_STEP(SchemaCompilerAssertionDefinesAll)) {
EVALUATE_BEGIN(assertion, SchemaCompilerAssertionDefinesAll,
target.is_object());

CALLBACK_PRE(assertion, context.instance_location());
// Otherwise we are we even emitting this instruction?
assert(assertion.value.size() > 1);
result = true;
Expand All @@ -382,22 +420,12 @@ auto evaluate_step(
}
}

CALLBACK_POST("SchemaCompilerAssertionDefinesAll", assertion);
} else if (std::holds_alternative<
SchemaCompilerAssertionPropertyDependencies>(step)) {
SOURCEMETA_TRACE_START(trace_id,
"SchemaCompilerAssertionPropertyDependencies");
const auto &assertion{
std::get<SchemaCompilerAssertionPropertyDependencies>(step)};
context.push(assertion);
const auto &target{context.resolve_target(instance)};
EVALUATE_IMPLICIT_PRECONDITION(
"SchemaCompilerAssertionPropertyDependencies", assertion,
target.is_object());
CALLBACK_PRE(assertion, context.instance_location());
EVALUATE_END(assertion, SchemaCompilerAssertionDefinesAll);
} else if (IS_STEP(SchemaCompilerAssertionPropertyDependencies)) {
EVALUATE_BEGIN(assertion, SchemaCompilerAssertionPropertyDependencies,
target.is_object());
// Otherwise we are we even emitting this instruction?
assert(!assertion.value.empty());

result = true;
for (const auto &[property, dependencies] : assertion.value) {
if (!target.defines(property)) {
Expand All @@ -415,7 +443,7 @@ auto evaluate_step(
}

evaluate_assertion_property_dependencies_end:
CALLBACK_POST("SchemaCompilerAssertionPropertyDependencies", assertion);
EVALUATE_END(assertion, SchemaCompilerAssertionPropertyDependencies);
} else if (std::holds_alternative<SchemaCompilerAssertionType>(step)) {
SOURCEMETA_TRACE_START(trace_id, "SchemaCompilerAssertionType");
const auto &assertion{std::get<SchemaCompilerAssertionType>(step)};
Expand Down Expand Up @@ -1398,6 +1426,12 @@ auto evaluate_step(
#undef CALLBACK_POST
#undef CALLBACK_ANNOTATION
#undef EVALUATE_IMPLICIT_PRECONDITION

#undef STRINGIFY
#undef IS_STEP
#undef EVALUATE_BEGIN
#undef EVALUATE_BEGIN_NO_PRECONDITION
#undef EVALUATE_END
// We should never get here
assert(false);
return result;
Expand Down

0 comments on commit 6e7ccc2

Please sign in to comment.