Browse Source

Fix bugs on 64bit archs.
Добавил преобразование объектов в строку через глобальный метод ToString.
Добавил документацию.
Дополнил тесты.
Убрал баг при поиске локальных переменных.
Убрал табуляции.

Fix work with General calling convention.
Add ability to save debug info in compiled sript's bytecode.
Add option `-nostrip` for Scriptompiler.

Aleksandr Orefkov 3 years ago
parent
commit
8836e82f2b

+ 3 - 1
Docs/Reference.dox

@@ -731,6 +731,7 @@ The placeholders in a format string are generally defined as {[NumberOrName][%fo
 If a number is specified for insertion, then it indicates the number among the passed parameters.
 If a name is given, then a variable with the given name is searched in the order: local variables, class members (if the method is called inside a class method), global variables.
 You can also use the name "this" inside class methods. If neither number nor name is specified, the next parameter in order is taken.
+Searching by local variable names only works in scripts built from source code or from bytecode with debug information preserved. To save debugging information, run the ScriptCompiler with the -nostrip option.
 If the passed paramter or founded variable is an object, it is converted to a string as follows - the object method is searched for "String ToString()const", then "String opImplConv()const", then "String toString()const"
 and finally the global method "String ToString(const ObjectTypeName&in)".
 If one of the methods is found, then it is called and its result is used. If no functions are found, the "[object ObjectTypeName ObjectAddress]" is returned.
@@ -3648,12 +3649,13 @@ Compiles AngelScript file(s) to binary bytecode for faster loading. Can also dum
 Usage:
 
 \verbatim
-ScriptCompiler <input file> [resource path for includes]
+ScriptCompiler <input file> [resource path for includes] [-nostrip]
 ScriptCompiler -dumpapi <Doxygen output file> [C header output file]
 
 \endverbatim
 
 The output files are saved with the extension .asc (compiled AngelScript.) binary files are not automatically loaded instead of the text format (.as) script files, instead resource requests and resource references in objects need to point to the compiled files. In a final build of an application it may be convenient to simply replace the text format script files with the compiled scripts.
+By default, debugging information about local variable names is removed from the bytecode. If you use string formatting with local variable names in placeholders, you need to add the -nostrip option when you run the compiler.
 
 The script API dump mode can be used to replace the 'ScriptAPI.dox' file in the 'Docs' directory. If the output file name is not provided then the script API would be dumped to standard output (console) instead.
 

+ 17 - 7
Source/Tools/ScriptCompiler/ScriptCompiler.cpp

@@ -24,7 +24,7 @@
 
 using namespace Urho3D;
 
-void CompileScript(Context* context, const String& fileName);
+void CompileScript(Context* context, const String& fileName, bool stripDebugSymbols);
 
 int main(int argc, char** argv)
 {
@@ -39,7 +39,7 @@ int main(int argc, char** argv)
     String outputFile;
 
     if (arguments.Size() < 1)
-        ErrorExit("Usage: ScriptCompiler <input file> [resource path for includes]\n"
+        ErrorExit("Usage: ScriptCompiler <input file> [resource path for includes] [-nostrip]\n"
                   "       ScriptCompiler -dumpapi <source tree> <Doxygen output file> [C header output file]");
     else
     {
@@ -96,20 +96,30 @@ int main(int argc, char** argv)
 
         auto* cache = context->GetSubsystem<ResourceCache>();
 
+        bool stripDebugSymbols = true;
+        for (int idx = arguments.Size() - 1; idx > 0; idx--)
+        {
+            if (arguments[idx] == "-nostrip")
+            {
+                stripDebugSymbols = false;
+                break;
+            }
+        }
+
         // Add resource path to be able to resolve includes
-        if (arguments.Size() > 1)
+        if (arguments.Size() > 1 && arguments[1] != "-nostrip")
             cache->AddResourceDir(arguments[1]);
         else
             cache->AddResourceDir(cache->GetPreferredResourceDir(path));
 
         if (!file.StartsWith("*"))
-            CompileScript(context, outputFile);
+            CompileScript(context, outputFile, stripDebugSymbols);
         else
         {
             Vector<String> scriptFiles;
             context->GetSubsystem<FileSystem>()->ScanDir(scriptFiles, path, file + extension, SCAN_FILES, false);
             for (unsigned i = 0; i < scriptFiles.Size(); ++i)
-                CompileScript(context, path + scriptFiles[i]);
+                CompileScript(context, path + scriptFiles[i], stripDebugSymbols);
         }
     }
     else
@@ -134,7 +144,7 @@ int main(int argc, char** argv)
     return EXIT_SUCCESS;
 }
 
-void CompileScript(Context* context, const String& fileName)
+void CompileScript(Context* context, const String& fileName, bool stripDebugSymbols)
 {
     PrintLine("Compiling script file " + fileName);
 
@@ -152,5 +162,5 @@ void CompileScript(Context* context, const String& fileName)
     if (!outFile.IsOpen())
         ErrorExit("Failed to open output file " + fileName);
 
-    script.SaveByteCode(outFile);
+    script.SaveByteCode(outFile, stripDebugSymbols);
 }

+ 73 - 25
Source/Urho3D/AngelScript/Manual_Container.cpp

@@ -460,7 +460,30 @@ static String format_argsA(const String& pattern, void* a1, int i1, void* a2, in
     return formatString(pattern, 10, ua);
 }
 
-
+#ifdef AS_MAX_PORTABILITY
+static void String_Format_gen(asIScriptGeneric* gen)
+{
+    int argsCount = gen->GetArgCount();
+    Vector<as_unkn_arg> args(argsCount);
+    for (int i = 0; i < argsCount; i++)
+    {
+        args[i].typeId = gen->GetArgTypeId(i);
+        args[i].val = gen->GetArgAddress(i);
+    }
+    *static_cast<String*>(gen->GetAddressOfReturnLocation()) = formatString(*static_cast<String*>(gen->GetObject()), argsCount, args.Buffer());
+}
+static void Format_String_gen(asIScriptGeneric* gen)
+{
+    int argsCount = gen->GetArgCount();
+    Vector<as_unkn_arg> args(argsCount - 1);
+    for (int i = 1; i < argsCount; i++)
+    {
+        args[i - 1].typeId = gen->GetArgTypeId(i);
+        args[i - 1].val = gen->GetArgAddress(i);
+    }
+    *static_cast<String*>(gen->GetAddressOfReturnLocation()) = formatString(*static_cast<String*>(gen->GetArgAddress(0)), argsCount - 1, args.Buffer());
+}
+#endif
 
 // This function is called after ASRegisterGenerated()
 void ASRegisterManualLast_Container(asIScriptEngine* engine)
@@ -484,30 +507,55 @@ void ASRegisterManualLast_Container(asIScriptEngine* engine)
     engine->RegisterObjectMethod("String", "String& opAssign(bool)", AS_FUNCTION_OBJLAST(StringAssignBool), AS_CALL_CDECL_OBJLAST);
     engine->RegisterObjectMethod("String", "String opAdd(bool) const", AS_FUNCTION_OBJLAST(StringAddBool), AS_CALL_CDECL_OBJLAST);
     engine->RegisterObjectMethod("String", "String opAdd_r(bool) const", AS_FUNCTION_OBJLAST(StringAddBoolReverse), AS_CALL_CDECL_OBJLAST);
-
-    engine->RegisterObjectMethod("String", "String f()const", asFUNCTION(format_args0), asCALL_CDECL_OBJFIRST);
-    engine->RegisterObjectMethod("String", "String f(?&in a0)const", asFUNCTION(format_args1), asCALL_CDECL_OBJFIRST);
-    engine->RegisterObjectMethod("String", "String f(?&in a0,?&in a1)const", asFUNCTION(format_args2), asCALL_CDECL_OBJFIRST);
-    engine->RegisterObjectMethod("String", "String f(?&in a0,?&in a1,?&in a2)const", asFUNCTION(format_args3), asCALL_CDECL_OBJFIRST);
-    engine->RegisterObjectMethod("String", "String f(?&in a0,?&in a1,?&in a2,?&in a3)const", asFUNCTION(format_args4), asCALL_CDECL_OBJFIRST);
-    engine->RegisterObjectMethod("String", "String f(?&in a0,?&in a1,?&in a2,?&in a3,?&in a4)const", asFUNCTION(format_args5), asCALL_CDECL_OBJFIRST);
-    engine->RegisterObjectMethod("String", "String f(?&in a0,?&in a1,?&in a2,?&in a3,?&in a4,?&in a5)const",asFUNCTION(format_args6), asCALL_CDECL_OBJFIRST);
-    engine->RegisterObjectMethod("String", "String f(?&in a0,?&in a1,?&in a2,?&in a3,?&in a4,?&in a5,?&in a6)const", asFUNCTION(format_args7), asCALL_CDECL_OBJFIRST);
-    engine->RegisterObjectMethod("String", "String f(?&in a0,?&in a1,?&in a2,?&in a3,?&in a4,?&in a5,?&in a6,?&in a7)const", asFUNCTION(format_args8), asCALL_CDECL_OBJFIRST);
-    engine->RegisterObjectMethod("String", "String f(?&in a0,?&in a1,?&in a2,?&in a3,?&in a4,?&in a5,?&in a6,?&in a7,?&in a8)const", asFUNCTION(format_args9), asCALL_CDECL_OBJFIRST);
-    engine->RegisterObjectMethod("String", "String f(?&in a0,?&in a1,?&in a2,?&in a3,?&in a4,?&in a5,?&in a6,?&in a7,?&in a8,?&in a9)const",asFUNCTION(format_argsA), asCALL_CDECL_OBJFIRST);
-
-    engine->RegisterGlobalFunction("String format(const String&in pattern)", asFUNCTION(format_args0), asCALL_CDECL);
-    engine->RegisterGlobalFunction("String format(const String&in pattern,?&in a0)", asFUNCTION(format_args1), asCALL_CDECL);
-    engine->RegisterGlobalFunction("String format(const String&in pattern,?&in a0,?&in a1)", asFUNCTION(format_args2), asCALL_CDECL);
-    engine->RegisterGlobalFunction("String format(const String&in pattern,?&in a0,?&in a1,?&in a2)", asFUNCTION(format_args3), asCALL_CDECL);
-    engine->RegisterGlobalFunction("String format(const String&in pattern,?&in a0,?&in a1,?&in a2,?&in a3)", asFUNCTION(format_args4), asCALL_CDECL);
-    engine->RegisterGlobalFunction("String format(const String&in pattern,?&in a0,?&in a1,?&in a2,?&in a3,?&in a4)", asFUNCTION(format_args5), asCALL_CDECL);
-    engine->RegisterGlobalFunction("String format(const String&in pattern,?&in a0,?&in a1,?&in a2,?&in a3,?&in a4,?&in a5)", asFUNCTION(format_args6), asCALL_CDECL);
-    engine->RegisterGlobalFunction("String format(const String&in pattern,?&in a0,?&in a1,?&in a2,?&in a3,?&in a4,?&in a5,?&in a6)", asFUNCTION(format_args7), asCALL_CDECL);
-    engine->RegisterGlobalFunction("String format(const String&in pattern,?&in a0,?&in a1,?&in a2,?&in a3,?&in a4,?&in a5,?&in a6,?&in a7)", asFUNCTION(format_args8), asCALL_CDECL);
-    engine->RegisterGlobalFunction("String format(const String&in pattern,?&in a0,?&in a1,?&in a2,?&in a3,?&in a4,?&in a5,?&in a6,?&in a7,?&in a8)", asFUNCTION(format_args9), asCALL_CDECL);
-    engine->RegisterGlobalFunction("String format(const String&in pattern,?&in a0,?&in a1,?&in a2,?&in a3,?&in a4,?&in a5,?&in a6,?&in a7,?&in a8,?&in a9)", asFUNCTION(format_argsA), asCALL_CDECL);
+#ifndef AS_MAX_PORTABILITY
+    engine->RegisterObjectMethod("String", "String f()const", AS_FUNCTION_OBJFIRST(format_args0), AS_CALL_CDECL_OBJFIRST);
+    engine->RegisterObjectMethod("String", "String f(?&in a0)const", AS_FUNCTION_OBJFIRST(format_args1), AS_CALL_CDECL_OBJFIRST);
+    engine->RegisterObjectMethod("String", "String f(?&in a0,?&in a1)const", AS_FUNCTION_OBJFIRST(format_args2), AS_CALL_CDECL_OBJFIRST);
+    engine->RegisterObjectMethod("String", "String f(?&in a0,?&in a1,?&in a2)const", AS_FUNCTION_OBJFIRST(format_args3), AS_CALL_CDECL_OBJFIRST);
+    engine->RegisterObjectMethod("String", "String f(?&in a0,?&in a1,?&in a2,?&in a3)const", AS_FUNCTION_OBJFIRST(format_args4), AS_CALL_CDECL_OBJFIRST);
+    engine->RegisterObjectMethod("String", "String f(?&in a0,?&in a1,?&in a2,?&in a3,?&in a4)const", AS_FUNCTION_OBJFIRST(format_args5), AS_CALL_CDECL_OBJFIRST);
+    engine->RegisterObjectMethod("String", "String f(?&in a0,?&in a1,?&in a2,?&in a3,?&in a4,?&in a5)const",AS_FUNCTION_OBJFIRST(format_args6), AS_CALL_CDECL_OBJFIRST);
+    engine->RegisterObjectMethod("String", "String f(?&in a0,?&in a1,?&in a2,?&in a3,?&in a4,?&in a5,?&in a6)const", AS_FUNCTION_OBJFIRST(format_args7), AS_CALL_CDECL_OBJFIRST);
+    engine->RegisterObjectMethod("String", "String f(?&in a0,?&in a1,?&in a2,?&in a3,?&in a4,?&in a5,?&in a6,?&in a7)const", AS_FUNCTION_OBJFIRST(format_args8), AS_CALL_CDECL_OBJFIRST);
+    engine->RegisterObjectMethod("String", "String f(?&in a0,?&in a1,?&in a2,?&in a3,?&in a4,?&in a5,?&in a6,?&in a7,?&in a8)const", AS_FUNCTION_OBJFIRST(format_args9), AS_CALL_CDECL_OBJFIRST);
+    engine->RegisterObjectMethod("String", "String f(?&in a0,?&in a1,?&in a2,?&in a3,?&in a4,?&in a5,?&in a6,?&in a7,?&in a8,?&in a9)const",AS_FUNCTION_OBJFIRST(format_argsA), AS_CALL_CDECL_OBJFIRST);
+
+    engine->RegisterGlobalFunction("String format(const String&in pattern)", AS_FUNCTION(format_args0), AS_CALL_CDECL);
+    engine->RegisterGlobalFunction("String format(const String&in pattern,?&in a0)", AS_FUNCTION(format_args1), AS_CALL_CDECL);
+    engine->RegisterGlobalFunction("String format(const String&in pattern,?&in a0,?&in a1)", AS_FUNCTION(format_args2), AS_CALL_CDECL);
+    engine->RegisterGlobalFunction("String format(const String&in pattern,?&in a0,?&in a1,?&in a2)", AS_FUNCTION(format_args3), AS_CALL_CDECL);
+    engine->RegisterGlobalFunction("String format(const String&in pattern,?&in a0,?&in a1,?&in a2,?&in a3)", AS_FUNCTION(format_args4), AS_CALL_CDECL);
+    engine->RegisterGlobalFunction("String format(const String&in pattern,?&in a0,?&in a1,?&in a2,?&in a3,?&in a4)", AS_FUNCTION(format_args5), AS_CALL_CDECL);
+    engine->RegisterGlobalFunction("String format(const String&in pattern,?&in a0,?&in a1,?&in a2,?&in a3,?&in a4,?&in a5)", AS_FUNCTION(format_args6), AS_CALL_CDECL);
+    engine->RegisterGlobalFunction("String format(const String&in pattern,?&in a0,?&in a1,?&in a2,?&in a3,?&in a4,?&in a5,?&in a6)", AS_FUNCTION(format_args7), AS_CALL_CDECL);
+    engine->RegisterGlobalFunction("String format(const String&in pattern,?&in a0,?&in a1,?&in a2,?&in a3,?&in a4,?&in a5,?&in a6,?&in a7)", AS_FUNCTION(format_args8), AS_CALL_CDECL);
+    engine->RegisterGlobalFunction("String format(const String&in pattern,?&in a0,?&in a1,?&in a2,?&in a3,?&in a4,?&in a5,?&in a6,?&in a7,?&in a8)", AS_FUNCTION(format_args9), AS_CALL_CDECL);
+    engine->RegisterGlobalFunction("String format(const String&in pattern,?&in a0,?&in a1,?&in a2,?&in a3,?&in a4,?&in a5,?&in a6,?&in a7,?&in a8,?&in a9)", AS_FUNCTION(format_argsA), AS_CALL_CDECL);
+#else
+    engine->RegisterObjectMethod("String", "String f()const", asFUNCTION(String_Format_gen), asCALL_GENERIC);
+    engine->RegisterObjectMethod("String", "String f(?&in a0)const", asFUNCTION(String_Format_gen), asCALL_GENERIC);
+    engine->RegisterObjectMethod("String", "String f(?&in a0,?&in a1)const", asFUNCTION(String_Format_gen), asCALL_GENERIC);
+    engine->RegisterObjectMethod("String", "String f(?&in a0,?&in a1,?&in a2)const", asFUNCTION(String_Format_gen), asCALL_GENERIC);
+    engine->RegisterObjectMethod("String", "String f(?&in a0,?&in a1,?&in a2,?&in a3)const", asFUNCTION(String_Format_gen), asCALL_GENERIC);
+    engine->RegisterObjectMethod("String", "String f(?&in a0,?&in a1,?&in a2,?&in a3,?&in a4)const", asFUNCTION(String_Format_gen), asCALL_GENERIC);
+    engine->RegisterObjectMethod("String", "String f(?&in a0,?&in a1,?&in a2,?&in a3,?&in a4,?&in a5)const", asFUNCTION(String_Format_gen), asCALL_GENERIC);
+    engine->RegisterObjectMethod("String", "String f(?&in a0,?&in a1,?&in a2,?&in a3,?&in a4,?&in a5,?&in a6)const", asFUNCTION(String_Format_gen), asCALL_GENERIC);
+    engine->RegisterObjectMethod("String", "String f(?&in a0,?&in a1,?&in a2,?&in a3,?&in a4,?&in a5,?&in a6,?&in a7)const", asFUNCTION(String_Format_gen), asCALL_GENERIC);
+    engine->RegisterObjectMethod("String", "String f(?&in a0,?&in a1,?&in a2,?&in a3,?&in a4,?&in a5,?&in a6,?&in a7,?&in a8)const", asFUNCTION(String_Format_gen), asCALL_GENERIC);
+    engine->RegisterObjectMethod("String", "String f(?&in a0,?&in a1,?&in a2,?&in a3,?&in a4,?&in a5,?&in a6,?&in a7,?&in a8,?&in a9)const", asFUNCTION(String_Format_gen), asCALL_GENERIC);
+
+    engine->RegisterGlobalFunction("String format(const String&in pattern)", asFUNCTION(Format_String_gen), asCALL_GENERIC);
+    engine->RegisterGlobalFunction("String format(const String&in pattern,?&in a0)", asFUNCTION(Format_String_gen), asCALL_GENERIC);
+    engine->RegisterGlobalFunction("String format(const String&in pattern,?&in a0,?&in a1)", asFUNCTION(Format_String_gen), asCALL_GENERIC);
+    engine->RegisterGlobalFunction("String format(const String&in pattern,?&in a0,?&in a1,?&in a2)", asFUNCTION(Format_String_gen), asCALL_GENERIC);
+    engine->RegisterGlobalFunction("String format(const String&in pattern,?&in a0,?&in a1,?&in a2,?&in a3)", asFUNCTION(Format_String_gen), asCALL_GENERIC);
+    engine->RegisterGlobalFunction("String format(const String&in pattern,?&in a0,?&in a1,?&in a2,?&in a3,?&in a4)", asFUNCTION(Format_String_gen), asCALL_GENERIC);
+    engine->RegisterGlobalFunction("String format(const String&in pattern,?&in a0,?&in a1,?&in a2,?&in a3,?&in a4,?&in a5)", asFUNCTION(Format_String_gen), asCALL_GENERIC);
+    engine->RegisterGlobalFunction("String format(const String&in pattern,?&in a0,?&in a1,?&in a2,?&in a3,?&in a4,?&in a5,?&in a6)", asFUNCTION(Format_String_gen), asCALL_GENERIC);
+    engine->RegisterGlobalFunction("String format(const String&in pattern,?&in a0,?&in a1,?&in a2,?&in a3,?&in a4,?&in a5,?&in a6,?&in a7)", asFUNCTION(Format_String_gen), asCALL_GENERIC);
+    engine->RegisterGlobalFunction("String format(const String&in pattern,?&in a0,?&in a1,?&in a2,?&in a3,?&in a4,?&in a5,?&in a6,?&in a7,?&in a8)", asFUNCTION(Format_String_gen), asCALL_GENERIC);
+    engine->RegisterGlobalFunction("String format(const String&in pattern,?&in a0,?&in a1,?&in a2,?&in a3,?&in a4,?&in a5,?&in a6,?&in a7,?&in a8,?&in a9)", asFUNCTION(Format_String_gen), asCALL_GENERIC);
+#endif
 }
 
 }

+ 2 - 2
Source/Urho3D/AngelScript/ScriptFile.cpp

@@ -577,13 +577,13 @@ asIScriptObject* ScriptFile::CreateObject(const String& className, bool useInter
     return obj;
 }
 
-bool ScriptFile::SaveByteCode(Serializer& dest)
+bool ScriptFile::SaveByteCode(Serializer& dest, bool stripDebugInfo)
 {
     if (compiled_)
     {
         dest.WriteFileID("ASBC");
         ByteCodeSerializer serializer = ByteCodeSerializer(dest);
-        return scriptModule_->SaveByteCode(&serializer, true) >= 0;
+        return scriptModule_->SaveByteCode(&serializer, stripDebugInfo) >= 0;
     }
     else
         return false;

+ 1 - 1
Source/Urho3D/AngelScript/ScriptFile.h

@@ -82,7 +82,7 @@ public:
     /// Create a script object. Optionally search for the first class in the module that implements the specified interface.
     asIScriptObject* CreateObject(const String& className, bool useInterface = false);
     /// Save the script bytecode. Return true if successful.
-    bool SaveByteCode(Serializer& dest);
+    bool SaveByteCode(Serializer& dest, bool stripDebugInfo = true);
 
     /// Return script module.
     asIScriptModule* GetScriptModule() const { return scriptModule_; }