Skip to content

Instantly share code, notes, and snippets.

@tmpvar
Last active October 9, 2024 04:35
Show Gist options
  • Save tmpvar/961320f3ea71c36f907ab644dd49cfeb to your computer and use it in GitHub Desktop.
Save tmpvar/961320f3ea71c36f907ab644dd49cfeb to your computer and use it in GitHub Desktop.
angelscript compiled with emscripten

this is meant to live in the samples/wasm directory of angelscript

to build/test

requirements on NixOS

  • python3
  • cmake
  • emscripten
  • nodejs
emcmake cmake -B build -S .
make
node ../bin/angelscript-wasm.js
cmake_minimum_required(VERSION 2.6)
project(angelscript-wasm)
add_executable(
angelscript-wasm
${CMAKE_CURRENT_SOURCE_DIR}/../../add_on/scriptstdstring/scriptstdstring.cpp
${CMAKE_CURRENT_SOURCE_DIR}/main.cpp
)
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/../../angelscript/projects/cmake angelscript)
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../../angelscript/include)
target_link_libraries(angelscript-wasm ${ANGELSCRIPT_LIBRARY_NAME})
set_target_properties(angelscript-wasm PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}/bin)
#include <iostream> // cout
#include <assert.h> // assert()
#include <string.h> // strstr()
#include <angelscript.h>
#include "../../add_on/scriptstdstring/scriptstdstring.h"
using namespace std;
void MessageCallback(const asSMessageInfo *msg, void *param)
{
const char *type = "ERR ";
if( msg->type == asMSGTYPE_WARNING )
type = "WARN";
else if( msg->type == asMSGTYPE_INFORMATION )
type = "INFO";
printf("%s (%d, %d) : %s : %s\n", msg->section, msg->row, msg->col, type, msg->message);
}
int CompileScript(asIScriptEngine *engine, string &script) {
// Add the script sections that will be compiled into executable code.
// If we want to combine more than one file into the same script, then
// we can call AddScriptSection() several times for the same module and
// the script engine will treat them all as if they were one. The script
// section name, will allow us to localize any errors in the script code.
asIScriptModule *mod = engine->GetModule(0, asGM_ALWAYS_CREATE);
int r = mod->AddScriptSection("script", &script[0], script.size());
if( r < 0 )
{
cout << "AddScriptSection() failed" << endl;
return -1;
}
// Compile the script. If there are any compiler messages they will
// be written to the message stream that we set right after creating the
// script engine. If there are no errors, and no warnings, nothing will
// be written to the stream.
r = mod->Build();
if( r < 0 )
{
cout << "Build() failed" << endl;
return -1;
}
// The engine doesn't keep a copy of the script sections after Build() has
// returned. So if the script needs to be recompiled, then all the script
// sections must be added again.
// If we want to have several scripts executing at different times but
// that have no direct relation with each other, then we can compile them
// into separate script modules. Each module use their own namespace and
// scope, so function names, and global variables will not conflict with
// each other.
return 0;
}
// Function implementation with generic script interface
void PrintString_Generic(asIScriptGeneric *gen)
{
string *str = (string*)gen->GetArgAddress(0);
cout << *str;
}
void ConfigureEngine(asIScriptEngine *engine)
{
RegisterStdString(engine);
int r = engine->RegisterGlobalFunction("void Print(string &in)", asFUNCTION(PrintString_Generic), asCALL_GENERIC); assert( r >= 0 );
}
int RunApplication()
{
int r;
// Create the script engine
asIScriptEngine *engine = asCreateScriptEngine();
if( engine == 0 )
{
cout << "Failed to create script engine." << endl;
return -1;
}
// The script compiler will write any compiler messages to the callback.
engine->SetMessageCallback(asFUNCTION(MessageCallback), 0, asCALL_CDECL);
// Configure the script engine with all the functions,
// and variables that the script should be able to use.
ConfigureEngine(engine);
string script = "float calc(float a, float b) {"
" Print(\"Received: \" + a + \", \" + b + \" \\n\");"
" return a * b;"
"}";
// Compile the script code
r = CompileScript(engine, script);
if( r < 0 )
{
engine->Release();
return -1;
}
// Create a context that will execute the script.
asIScriptContext *ctx = engine->CreateContext();
if( ctx == 0 )
{
cout << "Failed to create the context." << endl;
engine->Release();
return -1;
}
// Find the function for the function we want to execute.
asIScriptFunction *func = engine->GetModule(0)->GetFunctionByDecl("float calc(float, float)");
if( func == 0 )
{
cout << "The function 'float calc(float, float)' was not found." << endl;
ctx->Release();
engine->Release();
return -1;
}
// Prepare the script context with the function we wish to execute. Prepare()
// must be called on the context before each new script function that will be
// executed. Note, that if you intend to execute the same function several
// times, it might be a good idea to store the function returned by
// GetFunctionByDecl(), so that this relatively slow call can be skipped.
r = ctx->Prepare(func);
if( r < 0 )
{
cout << "Failed to prepare the context." << endl;
ctx->Release();
engine->Release();
return -1;
}
// Now we need to pass the parameters to the script function.
ctx->SetArgFloat(0, 3.14159265359f);
ctx->SetArgFloat(1, 2.71828182846f);
// Execute the function
cout << "Executing the script." << endl;
cout << "---" << endl;
r = ctx->Execute();
cout << "---" << endl;
if( r != asEXECUTION_FINISHED )
{
// The execution didn't finish as we had planned. Determine why.
if( r == asEXECUTION_ABORTED )
cout << "The script was aborted before it could finish. Probably it timed out." << endl;
else if( r == asEXECUTION_EXCEPTION )
{
cout << "The script ended with an exception." << endl;
// Write some information about the script exception
asIScriptFunction *func = ctx->GetExceptionFunction();
cout << "func: " << func->GetDeclaration() << endl;
cout << "modl: " << func->GetModuleName() << endl;
cout << "sect: " << func->GetScriptSectionName() << endl;
cout << "line: " << ctx->GetExceptionLineNumber() << endl;
cout << "desc: " << ctx->GetExceptionString() << endl;
}
else
cout << "The script ended for some unforeseen reason (" << r << ")." << endl;
}
else
{
// Retrieve the return value from the context
float returnValue = ctx->GetReturnFloat();
cout << "The script function returned: " << returnValue << endl;
}
// We must release the contexts when no longer using them
ctx->Release();
// Shut down the engine
engine->ShutDownAndRelease();
return 0;
}
int main(int argc, char **argv)
{
RunApplication();
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment