Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature: Web REPL using Emscripten #138

Merged
merged 16 commits into from
Nov 10, 2021
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@
^default.prof*
^fuzz-*
^luau$
/.vs
37 changes: 37 additions & 0 deletions CLI/Repl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,41 @@ static void completeRepl(lua_State* L, const char* editBuffer, std::vector<std::
completeIndexer(L, editBuffer, start, completions);
}

#ifdef LUAU_WEB_REPL
MathematicalDessert marked this conversation as resolved.
Show resolved Hide resolved
extern "C"
{
// Luau errors are exceptions (see luaD_throw) which cannot be directly
MathematicalDessert marked this conversation as resolved.
Show resolved Hide resolved
// handled by emscripten. However we can recieve the pointer in JS and
// pass it through to this method to get the string content of the
// exception.
const char* getExceptionFromPtr(int ptr)
{
return reinterpret_cast<std::exception*>(ptr)->what();
}

void executeScript(const char* source)
MathematicalDessert marked this conversation as resolved.
Show resolved Hide resolved
{
std::unique_ptr<lua_State, void (*)(lua_State*)> globalState(luaL_newstate(), lua_close);
lua_State* L = globalState.get();

setupState(L);

luaL_sandboxthread(L);

linenoise::SetCompletionCallback([L](const char* editBuffer, std::vector<std::string>& completions) {
MathematicalDessert marked this conversation as resolved.
Show resolved Hide resolved
completeRepl(L, editBuffer, completions);
});

std::string error = runCode(L, source);

if (error.length())
{
fprintf(stdout, "%s\n", error.c_str());
}
}
}
#endif

static void runRepl()
{
std::unique_ptr<lua_State, void (*)(lua_State*)> globalState(luaL_newstate(), lua_close);
Expand Down Expand Up @@ -424,6 +459,7 @@ static int assertionHandler(const char* expr, const char* file, int line)
return 1;
}

#ifndef LUAU_WEB_REPL
int main(int argc, char** argv)
{
Luau::assertHandler() = assertionHandler;
Expand Down Expand Up @@ -515,3 +551,4 @@ int main(int argc, char** argv)
return failed;
}
}
#endif
53 changes: 42 additions & 11 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,28 +6,58 @@ endif()

cmake_minimum_required(VERSION 3.0)
project(Luau LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 17)
MathematicalDessert marked this conversation as resolved.
Show resolved Hide resolved

option(LUAU_BUILD_CLI "Build CLI" ON)
option(LUAU_BUILD_WEB_REPL "Build Web CLI" ON)
option(LUAU_BUILD_TESTS "Build tests" ON)

add_library(Luau.Ast STATIC)
add_library(Luau.Compiler STATIC)
add_library(Luau.Analysis STATIC)
add_library(Luau.VM STATIC)

if(LUAU_BUILD_CLI)
add_executable(Luau.Repl.CLI)
add_executable(Luau.Analyze.CLI)
if(LUAU_BUILD_WEB_REPL AND EMSCRIPTEN)
MathematicalDessert marked this conversation as resolved.
Show resolved Hide resolved
# this is a hack, disable cli and tests since we're compiling using emscripten
set(LUAU_BUILD_CLI OFF)
set(LUAU_BUILD_TESTS OFF)

# This also adds target `name` on Linux/macOS and `name.exe` on Windows
set_target_properties(Luau.Repl.CLI PROPERTIES OUTPUT_NAME luau)
set_target_properties(Luau.Analyze.CLI PROPERTIES OUTPUT_NAME luau-analyze)
endif()
add_definitions(-DLUAU_WEB_REPL)
MathematicalDessert marked this conversation as resolved.
Show resolved Hide resolved

# luau web repl
add_executable(Luau.Repl.Web)

if(LUAU_BUILD_TESTS)
add_executable(Luau.UnitTest)
add_executable(Luau.Conformance)
# configuration below
target_compile_options(Luau.Repl.Web PRIVATE ${LUAU_OPTIONS})

target_include_directories(Luau.Repl.Web PRIVATE extern)
target_link_libraries(Luau.Repl.Web PRIVATE Luau.Compiler Luau.VM)

if(UNIX)
target_link_libraries(Luau.Repl.Web PRIVATE pthread)
endif()

# declare exported functions to emscripten
target_link_options(Luau.Repl.Web PRIVATE -sEXPORTED_FUNCTIONS=['_getExceptionFromPtr','_executeScript'] -sEXPORTED_RUNTIME_METHODS=['ccall','cwrap'] -sNO_DISABLE_EXCEPTION_CATCHING)

# custom output directory for webm + js file
set_target_properties(Luau.Repl.Web PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/docs/assets/luau)
else()
if(LUAU_BUILD_CLI)
add_executable(Luau.Repl.CLI)
add_executable(Luau.Analyze.CLI)

# This also adds target `name` on Linux/macOS and `name.exe` on Windows
set_target_properties(Luau.Repl.CLI PROPERTIES OUTPUT_NAME luau)
set_target_properties(Luau.Analyze.CLI PROPERTIES OUTPUT_NAME luau-analyze)
endif()

if(LUAU_BUILD_TESTS)
add_executable(Luau.UnitTest)
add_executable(Luau.Conformance)
endif()
endif()

include(Sources.cmake)

target_compile_features(Luau.Ast PUBLIC cxx_std_17)
Expand All @@ -54,7 +84,8 @@ else()
list(APPEND LUAU_OPTIONS -Wall) # All warnings
list(APPEND LUAU_OPTIONS -Werror) # Warnings are errors

if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
# temporary hot fix (removes need for commenting out line above when compiling with Clang)
MathematicalDessert marked this conversation as resolved.
Show resolved Hide resolved
if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU" OR CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
list(APPEND LUAU_OPTIONS -Wno-unused) # GCC considers variables declared/checked in if() as unused
endif()
endif()
Expand Down
10 changes: 10 additions & 0 deletions Sources.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,16 @@ if(TARGET Luau.Repl.CLI)
CLI/Repl.cpp)
endif()

if (TARGET Luau.Repl.Web)
# Luau.Repl.Web Sources
target_sources(Luau.Repl.Web PRIVATE
CLI/FileUtils.h
CLI/FileUtils.cpp
CLI/Profiler.h
CLI/Profiler.cpp
CLI/Repl.cpp)
endif()

if(TARGET Luau.Analyze.CLI)
# Luau.Analyze.CLI Sources
target_sources(Luau.Analyze.CLI PRIVATE
Expand Down
2 changes: 2 additions & 0 deletions docs/_data/navigation.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,5 @@ pages:
url: /profile
- title: Library
url: /library
- title: Demo
url: /demo
53 changes: 53 additions & 0 deletions docs/_includes/repl.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
<form>
<div>
<label>Script:</label>
<br>
<textarea rows="10" cols="70" id="script">print("Hello World!")</textarea>
<br><br>
<button onclick="clearInput(); return false;">
Clear Input
</button>
<button onclick="executeScript(); return false;">
Run
</button>
</div>
<br><br>
<div>
<label>Output:</label>
<br>
<textarea readonly rows="10" cols="70" id="output"></textarea>
<br><br>
<button onclick="clearOutput(); return false;">
Clear Output
</button>
</div>
</form>

<script>
function output(text) {
document.getElementById("output").value += new Date().toLocaleTimeString() + ": " + text + "\n";
}

var Module = {
'print': function (msg) { output(msg) },
'printErr': function (err) { } // potentially unused?
};

function clearInput() {
document.getElementById("script").value = "";
}

function clearOutput() {
document.getElementById("output").value = "";
}

function executeScript() {
try {
Module.ccall('executeScript', null, ['string'], [document.getElementById("script").value]);
}
catch (e) {
output(Module.ccall('getExceptionFromPtr', 'string', ['number'], [e]).replace('stdin', '[ERROR]'));
}
}
</script>
<script async src="assets/luau/Luau.Repl.Web.js"></script>
MathematicalDessert marked this conversation as resolved.
Show resolved Hide resolved
7 changes: 7 additions & 0 deletions docs/_pages/demo.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
permalink: /demo
title: Demo
toc: true
MathematicalDessert marked this conversation as resolved.
Show resolved Hide resolved
---

{% include repl.html %}