JDevlieghere created this revision.
JDevlieghere added reviewers: labath, mib, l.frisken, jingham.
JDevlieghere requested review of this revision.

Make it possible to run the `script` command with a different language than 
currently selected.

  $ ./bin/lldb -l python
  (lldb) script -l lua
  >>> io.stdout:write("Hello, Wolrd!\n")
  Hello, Wolrd!

Because of the ability to pass anything after the `script` command, it is 
implemented as a `CommandObjectRaw`. In a first attempt I tried to change this 
to a `CommandObjectParsed` in order to have the existing infrastructure parse 
the language argument and have the script contents as the first argument. That 
didn't work out, first because it unconditionally removed quotes from within 
the script expression and second because it attempts to expand script commands 
between backticks. Given how easy it is to parse the language argument, I think 
it's warranted to keep the `CommandObjectRaw` and just do the parsing manually.


https://reviews.llvm.org/D86996

Files:
  lldb/source/Commands/CommandObjectScript.cpp
  lldb/test/Shell/ScriptInterpreter/Lua/lua-python.test
  lldb/test/Shell/ScriptInterpreter/Lua/lua.test
  lldb/test/Shell/ScriptInterpreter/None/none.test
  lldb/test/Shell/ScriptInterpreter/Python/python.test

Index: lldb/test/Shell/ScriptInterpreter/Python/python.test
===================================================================
--- /dev/null
+++ lldb/test/Shell/ScriptInterpreter/Python/python.test
@@ -0,0 +1,9 @@
+# REQUIRES: python
+# RUN: %lldb --script-language python -o 'script print("{}".format(1000+100+10+1))' 2>&1 | FileCheck %s
+# RUN: %lldb -o 'script -l python print("{}".format(1000+100+10+1))' 2>&1 | FileCheck %s
+# RUN: %lldb -o 'script --language python print("{}".format(1000+100+10+1))' 2>&1 | FileCheck %s
+# CHECK: 1111
+
+# RUN: %lldb -o 'script --language invalid print("{}".format(1000+100+10+1))' 2>&1 | FileCheck %s --check-prefix INVALID
+# INVALID: SyntaxError
+# INVALID-NOT: 1111
Index: lldb/test/Shell/ScriptInterpreter/None/none.test
===================================================================
--- lldb/test/Shell/ScriptInterpreter/None/none.test
+++ lldb/test/Shell/ScriptInterpreter/None/none.test
@@ -1,2 +1,5 @@
 # RUN: %lldb --script-language none -o 'script' 2>&1 | FileCheck %s
 # CHECK: error: the script-lang setting is set to none - scripting not available
+
+# RUN: %lldb -o 'script -l none foo' 2>&1 | FileCheck %s --check-prefix ERROR
+# ERROR: error: the script-lang setting is set to none - scripting not available
Index: lldb/test/Shell/ScriptInterpreter/Lua/lua.test
===================================================================
--- lldb/test/Shell/ScriptInterpreter/Lua/lua.test
+++ lldb/test/Shell/ScriptInterpreter/Lua/lua.test
@@ -1,3 +1,5 @@
 # REQUIRES: lua
-# RUN: %lldb --script-language lua -o 'script print(1000+100+10+1)' 2>&1 | FileCheck %s
+# RUN: %lldb --script-language lua -o 'script io.stdout:write(1000+100+10+1, "\n")' 2>&1 | FileCheck %s
+# RUN: %lldb -o 'script -l lua io.stdout:write(1000+100+10+1, "\n")' 2>&1 | FileCheck %s
+# RUN: %lldb -o 'script --language lua io.stdout:write(1000+100+10+1, "\n")' 2>&1 | FileCheck %s
 # CHECK: 1111
Index: lldb/test/Shell/ScriptInterpreter/Lua/lua-python.test
===================================================================
--- /dev/null
+++ lldb/test/Shell/ScriptInterpreter/Lua/lua-python.test
@@ -0,0 +1,17 @@
+# REQUIRES: lua
+# REQUIRES: python
+# UNSUPPORTED: lldb-repro
+
+# RUN: mkdir -p %t
+# RUN: cd %t
+# RUN: echo "int main() { return 0; }" | %clang_host -x c - -o a.out
+# RUN: cat %s | %lldb 2>&1 | FileCheck %s
+script -l lua
+target = lldb.debugger:CreateTarget("a.out")
+print("target is valid:", tostring(target:IsValid()))
+lldb.debugger:SetSelectedTarget(target)
+quit
+# CHECK: target is valid: true
+script -l python
+print("selected target: {}".format(lldb.debugger.GetSelectedTarget()))
+# CHECK: selected target: a.out
Index: lldb/source/Commands/CommandObjectScript.cpp
===================================================================
--- lldb/source/Commands/CommandObjectScript.cpp
+++ lldb/source/Commands/CommandObjectScript.cpp
@@ -25,21 +25,63 @@
           interpreter, "script",
           "Invoke the script interpreter with provided code and display any "
           "results.  Start the interactive interpreter if no code is supplied.",
-          "script [<script-code>]") {}
+          "script [-l <script-language>] [<script-code>]") {}
 
 CommandObjectScript::~CommandObjectScript() {}
 
+struct CommandParsed {
+  CommandParsed(llvm::StringRef command)
+      : language(llvm::None), command(command) {}
+  CommandParsed(llvm::Optional<ScriptLanguage> language,
+                llvm::StringRef command)
+      : language(language), command(command) {}
+  llvm::Optional<ScriptLanguage> language;
+  llvm::StringRef command;
+};
+
+/// Manually parse the -l <script-language> or --language <script-language>
+/// argument from the command. This is necessary because CommandObjectScript is
+/// an instance of CommandObjectRaw. We cannot use CommandObjectParsed because
+/// it removes quotes and replace embedded script commands between backticks.
+static CommandParsed ParseLanguageArgument(llvm::StringRef command) {
+  llvm::StringRef head;
+  llvm::StringRef tail = command;
+
+  std::tie(head, tail) = tail.split(' ');
+  if (head != "-l" && head != "--language")
+    return CommandParsed(command);
+
+  std::tie(head, tail) = tail.split(' ');
+  llvm::Optional<ScriptLanguage> language =
+      llvm::StringSwitch<llvm::Optional<ScriptLanguage>>(head.lower())
+          .Case("lua", eScriptLanguageLua)
+          .Case("none", eScriptLanguageNone)
+          .Case("python", eScriptLanguagePython)
+          .Default(llvm::None);
+
+  if (language)
+    return CommandParsed(language, tail.ltrim());
+
+  return CommandParsed(command);
+}
+
 bool CommandObjectScript::DoExecute(llvm::StringRef command,
                                     CommandReturnObject &result) {
-  if (m_interpreter.GetDebugger().GetScriptLanguage() ==
-      lldb::eScriptLanguageNone) {
+  CommandParsed parsed = ParseLanguageArgument(command);
+
+  ScriptLanguage language =
+      parsed.language ? *parsed.language
+                      : m_interpreter.GetDebugger().GetScriptLanguage();
+
+  if (language == eScriptLanguageNone) {
     result.AppendError(
         "the script-lang setting is set to none - scripting not available");
     result.SetStatus(eReturnStatusFailed);
     return false;
   }
 
-  ScriptInterpreter *script_interpreter = GetDebugger().GetScriptInterpreter();
+  ScriptInterpreter *script_interpreter =
+      GetDebugger().GetScriptInterpreter(true, language);
 
   if (script_interpreter == nullptr) {
     result.AppendError("no script interpreter");
@@ -51,14 +93,14 @@
   // up to date with it.
   DataVisualization::ForceUpdate();
 
-  if (command.empty()) {
+  if (parsed.command.empty()) {
     script_interpreter->ExecuteInterpreterLoop();
     result.SetStatus(eReturnStatusSuccessFinishNoResult);
     return result.Succeeded();
   }
 
   // We can do better when reporting the status of one-liner script execution.
-  if (script_interpreter->ExecuteOneLine(command, &result))
+  if (script_interpreter->ExecuteOneLine(parsed.command, &result))
     result.SetStatus(eReturnStatusSuccessFinishNoResult);
   else
     result.SetStatus(eReturnStatusFailed);
_______________________________________________
lldb-commits mailing list
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits

Reply via email to