diff --git a/core/desugarer.cpp b/core/desugarer.cpp
index 70b40888b..64ac1cafb 100644
--- a/core/desugarer.cpp
+++ b/core/desugarer.cpp
@@ -34,7 +34,7 @@ struct BuiltinDecl {
     std::vector<UString> params;
 };
 
-static unsigned long max_builtin = 37;
+static unsigned long max_builtin = 38;
 BuiltinDecl jsonnet_builtin_decl(unsigned long builtin)
 {
     switch (builtin) {
@@ -76,6 +76,7 @@ BuiltinDecl jsonnet_builtin_decl(unsigned long builtin)
         case 35: return {U"parseJson", {U"str"}};
         case 36: return {U"encodeUTF8", {U"str"}};
         case 37: return {U"decodeUTF8", {U"arr"}};
+        case 38: return {U"importstrDyn", {U"str"}};
         default:
             std::cerr << "INTERNAL ERROR: Unrecognized builtin function: " << builtin << std::endl;
             std::abort();
diff --git a/core/vm.cpp b/core/vm.cpp
index 539d4e4ac..f4f34375e 100644
--- a/core/vm.cpp
+++ b/core/vm.cpp
@@ -734,7 +734,7 @@ class Interpreter {
      */
     HeapThunk *import(const LocationRange &loc, const LiteralString *file)
     {
-        ImportCacheValue *input = importString(loc, file);
+        ImportCacheValue *input = importLiteralString(loc, file);
         if (input->thunk == nullptr) {
             Tokens tokens = jsonnet_lex(input->foundHere, input->content.c_str());
             AST *expr = jsonnet_parse(alloc, tokens);
@@ -756,11 +756,14 @@ class Interpreter {
      * \param file Path to the filename.
      * \param found_here If non-null, used to store the actual path of the file
      */
-    ImportCacheValue *importString(const LocationRange &loc, const LiteralString *file)
+    ImportCacheValue *importLiteralString(const LocationRange &loc, const LiteralString *file)
     {
-        std::string dir = dir_name(loc.file);
+        return importString(loc, file->value);
+    }
 
-        const UString &path = file->value;
+    ImportCacheValue *importString(const LocationRange &loc, const UString &path)
+    {
+        std::string dir = dir_name(loc.file);
 
         std::pair<std::string, UString> key(dir, path);
         ImportCacheValue *cached_value = cachedImports[key];
@@ -881,6 +884,7 @@ class Interpreter {
         builtins["parseJson"] = &Interpreter::builtinParseJson;
         builtins["encodeUTF8"] = &Interpreter::builtinEncodeUTF8;
         builtins["decodeUTF8"] = &Interpreter::builtinDecodeUTF8;
+        builtins["importstrDyn"] = &Interpreter::builtinImportstrDyn;
     }
 
     /** Clean up the heap, stack, stash, and builtin function ASTs. */
@@ -1191,6 +1195,15 @@ class Interpreter {
         return nullptr;
     }
 
+    const AST *builtinImportstrDyn(const LocationRange &loc, const std::vector<Value> &args)
+    {
+        validateBuiltinArgs(loc, "importstrDyn", args, {Value::STRING});
+        const auto *str = static_cast<const HeapString *>(args[0].v.h);
+        const ImportCacheValue *importedValue = importString(loc, str->value);
+        scratch = makeString(decode_utf8(importedValue->content));
+        return nullptr;
+    }
+
     const AST *builtinExp(const LocationRange &loc, const std::vector<Value> &args)
     {
         validateBuiltinArgs(loc, "exp", args, {Value::NUMBER});
@@ -1921,7 +1934,7 @@ class Interpreter {
 
             case AST_IMPORTSTR: {
                 const auto &ast = *static_cast<const Importstr *>(ast_);
-                const ImportCacheValue *value = importString(ast.location, ast.file);
+                const ImportCacheValue *value = importLiteralString(ast.location, ast.file);
                 scratch = makeString(decode_utf8(value->content));
             } break;