| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437 |
- //
- // Urho3D Engine
- // Copyright (c) 2008-2011 Lasse Öörni
- //
- // Permission is hereby granted, free of charge, to any person obtaining a copy
- // of this software and associated documentation files (the "Software"), to deal
- // in the Software without restriction, including without limitation the rights
- // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- // copies of the Software, and to permit persons to whom the Software is
- // furnished to do so, subject to the following conditions:
- //
- // The above copyright notice and this permission notice shall be included in
- // all copies or substantial portions of the Software.
- //
- // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- // THE SOFTWARE.
- //
- #include "Precompiled.h"
- #include "Addons.h"
- #include "Context.h"
- #include "Log.h"
- #include "Profiler.h"
- #include "Scene.h"
- #include "Script.h"
- #include "ScriptFile.h"
- #include "ScriptInstance.h"
- #include "StringUtils.h"
- #include <angelscript.h>
- #include "DebugNew.h"
- /// Script object property info for script API dump
- struct PropertyInfo
- {
- PropertyInfo() :
- read_(false),
- write_(false),
- indexed_(false)
- {
- }
-
- String name_;
- String type_;
- bool read_;
- bool write_;
- bool indexed_;
- };
- void ExtractPropertyInfo(const String& functionName, const String& declaration, Vector<PropertyInfo>& propertyInfos)
- {
- String propertyName = functionName.Substring(4);
- PropertyInfo* info = 0;
- for (unsigned k = 0; k < propertyInfos.Size(); ++k)
- {
- if (propertyInfos[k].name_ == propertyName)
- info = &propertyInfos[k];
- }
- if (!info)
- {
- propertyInfos.Resize(propertyInfos.Size() + 1);
- info = &propertyInfos[propertyInfos.Size() - 1];
- info->name_ = propertyName;
- }
- if (functionName.Find("get_") != String::NPOS)
- {
- info->read_ = true;
- // Extract type from the return value
- Vector<String> parts = declaration.Split(' ');
- if (parts.Size())
- {
- if (parts[0] != "const")
- info->type_ = parts[0];
- else if (parts.Size() > 1)
- info->type_ = parts[1];
- }
- // If get method has parameters, it is indexed
- if (declaration.Find("()") == String::NPOS)
- {
- info->indexed_ = true;
- info->type_ += "[]";
- }
- }
- if (functionName.Find("set_") != String::NPOS)
- info->write_ = true;
- }
- OBJECTTYPESTATIC(Script);
- Script::Script(Context* context) :
- Object(context),
- scriptEngine_(0),
- immediateContext_(0),
- scriptNestingLevel_(0)
- {
- scriptEngine_ = asCreateScriptEngine(ANGELSCRIPT_VERSION);
- if (!scriptEngine_)
- {
- LOGERROR("Could not create AngelScript engine");
- return;
- }
-
- scriptEngine_->SetUserData(this);
- scriptEngine_->SetEngineProperty(asEP_USE_CHARACTER_LITERALS, true);
- scriptEngine_->SetEngineProperty(asEP_ALLOW_UNSAFE_REFERENCES, true);
- scriptEngine_->SetEngineProperty(asEP_ALLOW_IMPLICIT_HANDLE_TYPES, true);
- scriptEngine_->SetEngineProperty(asEP_BUILD_WITHOUT_LINE_CUES, true);
- scriptEngine_->SetMessageCallback(asMETHOD(Script, MessageCallback), this, asCALL_THISCALL);
-
- // Create the context for immediate execution
- immediateContext_ = scriptEngine_->CreateContext();
- immediateContext_->SetExceptionCallback(asMETHOD(Script, ExceptionCallback), this, asCALL_THISCALL);
-
- // Create the function/method contexts
- for (unsigned i = 0 ; i < MAX_SCRIPT_NESTING_LEVEL; ++i)
- {
- scriptFileContexts_.Push(scriptEngine_->CreateContext());
- scriptFileContexts_[i]->SetExceptionCallback(asMETHOD(Script, ExceptionCallback), this, asCALL_THISCALL);
- }
-
- // Register the Array & String types
- RegisterArray(scriptEngine_);
- RegisterString(scriptEngine_);
- }
- Script::~Script()
- {
- if (immediateContext_)
- {
- immediateContext_->Release();
- immediateContext_ = 0;
- }
- for (unsigned i = 0 ; i < MAX_SCRIPT_NESTING_LEVEL; ++i)
- {
- if (scriptFileContexts_[i])
- scriptFileContexts_[i]->Release();
- }
- scriptFileContexts_.Clear();
-
- if (scriptEngine_)
- {
- scriptEngine_->Release();
- scriptEngine_ = 0;
- }
- }
- bool Script::Execute(const String& line)
- {
- // Note: compiling code each time is slow. Not to be used for performance-critical or repeating activity
- PROFILE(ExecuteImmediate);
-
- String wrappedLine = "void f(){\n" + line + ";\n}";
-
- // If no immediate mode script file set, create a dummy module for compiling the line
- asIScriptModule* module = 0;
- if (defaultScriptFile_)
- module = defaultScriptFile_->GetScriptModule();
- if (!module)
- module = scriptEngine_->GetModule("ExecuteImmediate", asGM_CREATE_IF_NOT_EXISTS);
- if (!module)
- return false;
-
- asIScriptFunction *function = 0;
- if (module->CompileFunction("", wrappedLine.CString(), -1, 0, &function) < 0)
- return false;
-
- if (immediateContext_->Prepare(function->GetId()) < 0)
- {
- function->Release();
- return false;
- }
-
- bool success = false;
-
- success = immediateContext_->Execute() >= 0;
- immediateContext_->Unprepare();
- function->Release();
-
- return success;
- }
- void Script::GarbageCollect(bool fullCycle)
- {
- PROFILE(GarbageCollect);
-
- if (fullCycle)
- scriptEngine_->GarbageCollect(asGC_FULL_CYCLE);
- else
- scriptEngine_->GarbageCollect(asGC_ONE_STEP);
- }
- void Script::SetDefaultScriptFile(ScriptFile* file)
- {
- defaultScriptFile_ = file;
- }
- void Script::SetDefaultScene(Scene* scene)
- {
- defaultScene_ = scene;
- }
- void Script::SetLogMode(ScriptLogMode mode)
- {
- logMode_ = mode;
- }
- void Script::ClearLogMessages()
- {
- logMessages_.Clear();
- }
- void Script::DumpAPI()
- {
- LOGRAW("Urho3D script API:\n");
-
- Vector<PropertyInfo> globalPropertyInfos;
- Vector<String> globalFunctions;
-
- unsigned functions = scriptEngine_->GetGlobalFunctionCount();
- for (unsigned i = 0; i < functions; ++i)
- {
- unsigned id = scriptEngine_->GetGlobalFunctionIdByIndex(i);
- asIScriptFunction* function = scriptEngine_->GetFunctionDescriptorById(id);
- String functionName(function->GetName());
- String declaration(function->GetDeclaration());
-
- if ((functionName.Find("set_") != String::NPOS) || (functionName.Find("get_") != String::NPOS))
- ExtractPropertyInfo(functionName, declaration, globalPropertyInfos);
- else
- globalFunctions.Push(declaration);
- }
-
- LOGRAW("\nGlobal functions:\n");
-
- for (unsigned i = 0; i < globalFunctions.Size(); ++i)
- OutputAPIRow(globalFunctions[i]);
-
- LOGRAW("\nGlobal properties:\n");
-
- for (unsigned i = 0; i < globalPropertyInfos.Size(); ++i)
- {
- // For now, skip write-only properties
- if (!globalPropertyInfos[i].read_)
- continue;
-
- OutputAPIRow(globalPropertyInfos[i].type_ + " " + globalPropertyInfos[i].name_, true);
- }
-
- LOGRAW("\nGlobal constants:\n");
-
- unsigned properties = scriptEngine_->GetGlobalPropertyCount();
- for (unsigned i = 0; i < properties; ++i)
- {
- const char* propertyName;
- const char* propertyDeclaration;
- int typeId;
- scriptEngine_->GetGlobalPropertyByIndex(i, &propertyName, &typeId);
- propertyDeclaration = scriptEngine_->GetTypeDeclaration(typeId);
-
- String type(propertyDeclaration);
- OutputAPIRow(type + " " + String(propertyName), true);
- }
-
- LOGRAW("\nClasses:\n");
-
- unsigned types = scriptEngine_->GetObjectTypeCount();
- for (unsigned i = 0; i < types; ++i)
- {
- asIObjectType* type = scriptEngine_->GetObjectTypeByIndex(i);
- if (type)
- {
- String typeName(type->GetName());
- Vector<String> methodDeclarations;
- Vector<PropertyInfo> propertyInfos;
-
- LOGRAW("\n" + typeName + "\n");
-
- unsigned methods = type->GetMethodCount();
- for (unsigned j = 0; j < methods; ++j)
- {
- asIScriptFunction* method = type->GetMethodDescriptorByIndex(j);
- String methodName(method->GetName());
- String declaration(method->GetDeclaration());
- if ((methodName.Find("get_") == String::NPOS) && (methodName.Find("set_") == String::NPOS))
- {
- // Sanitate the method name. For now, skip the operators
- if (declaration.Find("::op") == String::NPOS)
- {
- String prefix(typeName + "::");
- methodDeclarations.Push(declaration.Replace(prefix, ""));
- }
- }
- else
- ExtractPropertyInfo(methodName, declaration, propertyInfos);
- }
-
- // Assume that the same property is never both an accessor property, and a direct one
- unsigned properties = type->GetPropertyCount();
- for (unsigned j = 0; j < properties; ++j)
- {
- const char* propertyName;
- const char* propertyDeclaration;
- int typeId;
-
- type->GetProperty(j, &propertyName, &typeId);
- propertyDeclaration = scriptEngine_->GetTypeDeclaration(typeId);
-
- PropertyInfo newInfo;
- newInfo.name_ = String(propertyName);
- newInfo.type_ = String(propertyDeclaration);
- newInfo.read_ = newInfo.write_ = true;
- propertyInfos.Push(newInfo);
- }
-
- if (!methodDeclarations.Empty())
- {
- LOGRAW("\nMethods:\n");
- for (unsigned j = 0; j < methodDeclarations.Size(); ++j)
- OutputAPIRow(methodDeclarations[j]);
- }
-
- if (!propertyInfos.Empty())
- {
- LOGRAW("\nProperties:\n");
- for (unsigned j = 0; j < propertyInfos.Size(); ++j)
- {
- // For now, skip write-only properties
- if (!propertyInfos[j].read_)
- continue;
-
- String readOnly;
- if (!propertyInfos[j].write_)
- readOnly = " (readonly)";
-
- OutputAPIRow(propertyInfos[j].type_ + " " + propertyInfos[j].name_ + readOnly);
- }
- }
-
- LOGRAW("\n");
- }
- }
- }
- void Script::MessageCallback(const asSMessageInfo* msg)
- {
- String message = String(msg->section) + " (" + msg->row + "," + msg->col + ") " +
- String(msg->message);
-
- if (logMode_ == LOGMODE_IMMEDIATE)
- {
- switch (msg->type)
- {
- case asMSGTYPE_ERROR:
- LOGERROR(message);
- break;
-
- case asMSGTYPE_WARNING:
- LOGWARNING(message);
- break;
-
- default:
- LOGINFO(message);
- break;
- }
- }
- else
- {
- // Ignore info messages in retained mode
- if ((msg->type == asMSGTYPE_ERROR) || (msg->type == asMSGTYPE_WARNING))
- logMessages_ += message + "\n";
- }
- }
- void Script::ExceptionCallback(asIScriptContext* context)
- {
- int funcId = context->GetExceptionFunction();
- const asIScriptFunction *function = scriptEngine_->GetFunctionDescriptorById(funcId);
- String message = "Exception '" + String(context->GetExceptionString()) + "' in '" +
- String(function->GetDeclaration()) + "'";
-
- asSMessageInfo msg;
- msg.row = context->GetExceptionLineNumber(&msg.col, &msg.section);
- msg.type = asMSGTYPE_ERROR;
- msg.message = message.CString();
-
- MessageCallback(&msg);
- }
- asIScriptContext* Script::GetScriptFileContext() const
- {
- return scriptNestingLevel_ < scriptFileContexts_.Size() ? scriptFileContexts_[scriptNestingLevel_] : 0;
- }
- ScriptFile* Script::GetDefaultScriptFile() const
- {
- return defaultScriptFile_;
- }
- Scene* Script::GetDefaultScene() const
- {
- return defaultScene_;
- }
- asIObjectType* Script::GetObjectType(const char* declaration)
- {
- Map<const char*, asIObjectType*>::ConstIterator i = objectTypes_.Find(declaration);
- if (i != objectTypes_.End())
- return i->second_;
-
- asIObjectType* type = scriptEngine_->GetObjectTypeById(scriptEngine_->GetTypeIdByDecl(declaration));
- objectTypes_[declaration] = type;
- return type;
- }
- void Script::OutputAPIRow(const String& row, bool removeReference)
- {
- String out = row;
- out.ReplaceInPlace("double", "float");
- out.ReplaceInPlace("&in", "&");
- out.ReplaceInPlace("&out", "&");
- if (removeReference)
- out.ReplaceInPlace("&", "");
-
- LOGRAW(out + "\n");
- }
- void RegisterScriptLibrary(Context* context)
- {
- ScriptFile::RegisterObject(context);
- ScriptInstance::RegisterObject(context);
- }
|