From 4876d97f08ff46bba625a93f80401272396a8796 Mon Sep 17 00:00:00 2001 From: Dave Cunningham Date: Fri, 27 May 2016 11:36:55 -0400 Subject: [PATCH] Added native functions, fix #108 --- core/desugarer.cpp | 3 ++- core/libjsonnet.cpp | 8 +++--- core/libjsonnet_test_snippet.c | 21 ++++++++++++++++ core/vm.cpp | 25 ++++++++++++++++--- include/libjsonnet.h | 8 +++--- test_suite/error.native_not_found.jsonnet | 17 +++++++++++++ .../error.native_not_found.jsonnet.golden | 2 ++ 7 files changed, 73 insertions(+), 11 deletions(-) create mode 100644 test_suite/error.native_not_found.jsonnet create mode 100644 test_suite/error.native_not_found.jsonnet.golden diff --git a/core/desugarer.cpp b/core/desugarer.cpp index 2b78b7f5b..23532128f 100644 --- a/core/desugarer.cpp +++ b/core/desugarer.cpp @@ -31,7 +31,7 @@ struct BuiltinDecl { std::vector params; }; -static unsigned long max_builtin = 24; +static unsigned long max_builtin = 25; BuiltinDecl jsonnet_builtin_decl(unsigned long builtin) { switch (builtin) { @@ -60,6 +60,7 @@ BuiltinDecl jsonnet_builtin_decl(unsigned long builtin) case 22: return {U"modulo", {U"a", U"b"}}; case 23: return {U"extVar", {U"x"}}; case 24: return {U"primitiveEquals", {U"a", U"b"}}; + case 25: return {U"native", {U"name"}}; default: std::cerr << "INTERNAL ERROR: Unrecognized builtin function: " << builtin << std::endl; std::abort(); diff --git a/core/libjsonnet.cpp b/core/libjsonnet.cpp index 9ca2ea5d8..e45df15ff 100644 --- a/core/libjsonnet.cpp +++ b/core/libjsonnet.cpp @@ -51,8 +51,9 @@ static char *from_string(JsonnetVm* vm, const std::string &v) static char *default_import_callback(void *ctx, const char *dir, const char *file, char **found_here_cptr, int *success); -const char *jsonnet_json_extract_string(struct JsonnetJsonValue *v) +const char *jsonnet_json_extract_string(JsonnetVm *vm, const struct JsonnetJsonValue *v) { + (void) vm; if (v->kind != JsonnetJsonValue::STRING) return nullptr; return v->string.c_str(); @@ -60,8 +61,9 @@ const char *jsonnet_json_extract_string(struct JsonnetJsonValue *v) /** Convert the given UTF8 string to a JsonnetJsonValue. */ -JsonnetJsonValue *jsonnet_json_make_string(const char *v) +JsonnetJsonValue *jsonnet_json_make_string(JsonnetVm *vm, const char *v) { + (void) vm; JsonnetJsonValue *r = new JsonnetJsonValue(); r->kind = JsonnetJsonValue::STRING; r->string = v; @@ -235,7 +237,7 @@ void jsonnet_native_callback(struct JsonnetVm *vm, const char *name, JsonnetNati void *ctx, const char * const *params) { std::vector params2; - for (; params != nullptr; params++) + for (; *params != nullptr; params++) params2.push_back(*params); vm->nativeCallbacks[name] = VmNativeCallback {cb, ctx, params2}; } diff --git a/core/libjsonnet_test_snippet.c b/core/libjsonnet_test_snippet.c index 25d06c9d9..81d7d0219 100644 --- a/core/libjsonnet_test_snippet.c +++ b/core/libjsonnet_test_snippet.c @@ -16,19 +16,40 @@ limitations under the License. #include #include +#include #include +struct JsonnetJsonValue *concat(void *ctx, const struct JsonnetJsonValue * const *argv, int *succ) +{ + struct JsonnetVm *vm = (struct JsonnetVm *)ctx; + const char *a = jsonnet_json_extract_string(vm, argv[0]); + const char *b = jsonnet_json_extract_string(vm, argv[1]); + if (a == NULL || b == NULL) { + struct JsonnetJsonValue *r = jsonnet_json_make_string(vm, "Bad params."); + *succ = 0; + return r; + } + char *str = malloc(strlen(a) + strlen(b) + 1); + sprintf(str, "%s%s", a, b); + struct JsonnetJsonValue *r = jsonnet_json_make_string(vm, str); + free(str); + *succ = 1; + return r; +} + int main(int argc, const char **argv) { int error; char *output; struct JsonnetVm *vm; + const char *params[] = {"a", "b", NULL}; if (argc != 2) { fprintf(stderr, "libjsonnet_test_snippet \n"); return EXIT_FAILURE; } vm = jsonnet_make(); + jsonnet_native_callback(vm, "concat", concat, vm, params); output = jsonnet_evaluate_snippet(vm, "snippet", argv[1], &error); if (error) { fprintf(stderr, "%s", output); diff --git a/core/vm.cpp b/core/vm.cpp index d738f9c5d..bc847476e 100644 --- a/core/vm.cpp +++ b/core/vm.cpp @@ -808,6 +808,7 @@ class Interpreter { builtins["modulo"] = &Interpreter::builtinModulo; builtins["extVar"] = &Interpreter::builtinExtVar; builtins["primitiveEquals"] = &Interpreter::builtinPrimitiveEquals; + builtins["native"] = &Interpreter::builtinNative; } /** Clean up the heap, stack, stash, and builtin function ASTs. */ @@ -1243,6 +1244,22 @@ class Interpreter { return nullptr; } + const AST *builtinNative(const LocationRange &loc, const std::vector &args) + { + validateBuiltinArgs(loc, "native", args, {Value::STRING}); + + std::string builtin_name = encode_utf8(static_cast(args[0].v.h)->value); + + VmNativeCallbackMap::const_iterator nit = nativeCallbacks.find(builtin_name); + if (nit == nativeCallbacks.end()) { + throw makeError(loc, "Unrecognized native function name: " + builtin_name); + } + + const VmNativeCallback &cb = nit->second; + scratch = makeNativeBuiltin(builtin_name, cb.params); + return nullptr; + } + String toString(const LocationRange &loc) { @@ -1966,14 +1983,14 @@ class Interpreter { encode_utf8(static_cast(arg.v.h)->value) }); } - std::vector args3; + std::vector args3; for (size_t i = 0; i < args2.size() ; ++i) { args3.push_back(&args2[i]); } if (nit != nativeCallbacks.end()) { const VmNativeCallback &cb = nit->second; int succ; - JsonnetJsonValue *r = cb.cb(cb.ctx, args3.size(), &args3[0], &succ); + JsonnetJsonValue *r = cb.cb(cb.ctx, &args3[0], &succ); if (succ) { // TODO(dcunnin): Support more than just strings. if (r->kind != JsonnetJsonValue::STRING) { @@ -1982,7 +1999,9 @@ class Interpreter { "Native extensions can only return a string."); } std::string rs = r->string; + delete r; scratch = makeString(decode_utf8(rs)); + break; } else { if (r->kind != JsonnetJsonValue::STRING) { delete r; @@ -1997,7 +2016,7 @@ class Interpreter { } throw makeError(ast.location, - "Unrecognized native function name: " + builtin_name); + "Unrecognized builtin name: " + builtin_name); } else { HeapThunk *th = f.thunks[f.elementId++]; if (!th->filled) { diff --git a/include/libjsonnet.h b/include/libjsonnet.h index 3dbccd331..1e8fe596e 100644 --- a/include/libjsonnet.h +++ b/include/libjsonnet.h @@ -74,11 +74,11 @@ struct JsonnetJsonValue; /** If the value is a string, return it as UTF8 otherwise return NULL. */ -const char *jsonnet_json_extract_string(struct JsonnetJsonValue *v); +const char *jsonnet_json_extract_string(struct JsonnetVm *vm, const struct JsonnetJsonValue *v); /** Convert the given UTF8 string to a JsonnetJsonValue. */ -struct JsonnetJsonValue *jsonnet_json_make_string(const char *v); +struct JsonnetJsonValue *jsonnet_json_make_string(struct JsonnetVm *vm, const char *v); /** Callback to provide native extensions to Jsonnet. * @@ -92,8 +92,8 @@ struct JsonnetJsonValue *jsonnet_json_make_string(const char *v); * \param success Set this byref param to 1 to indicate success and 0 for failure. * \returns The content of the imported file, or an error message. */ -typedef struct JsonnetJsonValue *JsonnetNativeCallback(void *ctx, size_t argc, - struct JsonnetJsonValue **argv, +typedef struct JsonnetJsonValue *JsonnetNativeCallback(void *ctx, + const struct JsonnetJsonValue * const *argv, int *success); /** Allocate, resize, or free a buffer. This will abort if the memory cannot be allocated. It will diff --git a/test_suite/error.native_not_found.jsonnet b/test_suite/error.native_not_found.jsonnet new file mode 100644 index 000000000..6e5f38073 --- /dev/null +++ b/test_suite/error.native_not_found.jsonnet @@ -0,0 +1,17 @@ +/* +Copyright 2015 Google Inc. All rights reserved. + +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. +*/ + +std.native("non_existent_native") diff --git a/test_suite/error.native_not_found.jsonnet.golden b/test_suite/error.native_not_found.jsonnet.golden new file mode 100644 index 000000000..303efac4e --- /dev/null +++ b/test_suite/error.native_not_found.jsonnet.golden @@ -0,0 +1,2 @@ +RUNTIME ERROR: Unrecognized native function name: non_existent_native + error.native_not_found.jsonnet:17:1-33