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

Backport AGS.Native.IScriptCompiler to 3.6.2 #2560

Merged
Show file tree
Hide file tree
Changes from all 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
24 changes: 15 additions & 9 deletions Common/script/cc_common.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,21 @@

#include "util/string.h"

#define SCOPT_EXPORTALL 1 // export all functions automatically
#define SCOPT_SHOWWARNINGS 2 // printf warnings to console
#define SCOPT_LINENUMBERS 4 // include line numbers in compiled code
#define SCOPT_AUTOIMPORT 8 // when creating instance, export funcs to other scripts
#define SCOPT_DEBUGRUN 0x10 // write instructions as they are procssed to log file
#define SCOPT_NOIMPORTOVERRIDE 0x20 // do not allow an import to be re-declared
#define SCOPT_LEFTTORIGHT 0x40 // left-to-right operator precedance
#define SCOPT_OLDSTRINGS 0x80 // allow old-style strings
#define SCOPT_UTF8 0x100 // UTF-8 text mode
// FIXME: SCOPT_AUTOIMPORT and SCOPT_DEBUGRUN are runtime only options
// remove them from this list of flags, and use a different way of assigning,
// not using ccSetOption.

#define SCOPT_EXPORTALL 0x0001 // export all functions automatically
// 0x0002 [UNUSED]
#define SCOPT_LINENUMBERS 0x0004 // include line numbers in compiled code
#define SCOPT_AUTOIMPORT 0x0008 // when creating instance, export funcs to other scripts
#define SCOPT_DEBUGRUN 0x0010 // write instructions as they are procssed to log file
// TODO: this flag might have to be removed as it makes inconsistent rules for distinct scripts
#define SCOPT_NOIMPORTOVERRIDE 0x0020 // do not allow an import to be re-declared
#define SCOPT_LEFTTORIGHT 0x0040 // left-to-right operator precedance
#define SCOPT_OLDSTRINGS 0x0080 // allow old-style strings
#define SCOPT_UTF8 0x0100 // UTF-8 text mode
#define SCOPT_HIGHEST SCOPT_UTF8

extern void ccSetOption(int, int);
extern int ccGetOption(int);
Expand Down
4 changes: 1 addition & 3 deletions Compiler/compiler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ void CompilerOptions::PrintToStdout() const {
printf("ScriptCompatLevel: %s\n", ScriptAPI.ScriptCompatLevel.c_str());
printf("Flags: ");
if (Flags.ExportAll) printf("ExportAll; ");
if (Flags.ShowWarnings) printf("ShowWarnings; ");
//if (Flags.ShowWarnings) printf("ShowWarnings; ");
if (Flags.LineNumbers) printf("LineNumbers; ");
if (Flags.AutoImport) printf("AutoImport; ");
if (Flags.DebugRun) printf("DebugRun; ");
Expand Down Expand Up @@ -173,8 +173,6 @@ int Compile(const CompilerOptions& comp_opts)
//-----------------------------------------------------------------------//
ccSetSoftwareVersion(comp_opts.Version.c_str());

ccSetOption(SCOPT_SHOWWARNINGS, comp_opts.Flags.ShowWarnings);

ccSetOption(SCOPT_EXPORTALL, comp_opts.Flags.ExportAll);
ccSetOption(SCOPT_LINENUMBERS, comp_opts.Flags.LineNumbers);
// now deprecated, was used to prevent override imports in the room script
Expand Down
2 changes: 1 addition & 1 deletion Compiler/compiler.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
class CompilerOptions{
struct Flags {
bool ExportAll = true; // export all functions automatically
bool ShowWarnings = true; // printf warnings to console
//bool ShowWarnings = true; // printf warnings to console; [DEPRECATED], impl proper output verbosity instead?
bool LineNumbers = true; // include line numbers in compiled code
bool AutoImport = false; // when creating instance, export funcs to other scripts
bool DebugRun = false; // write instructions as they are processed to log file
Expand Down
3 changes: 2 additions & 1 deletion Compiler/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -192,10 +192,11 @@ ParsedOptions parser_to_compiler_opts(const ParseResult& parseResult)
flag_value = !(flag_val_str == "0");
}

/* [DEPRECATED], impl proper output verbosity instead?
if(flag_name == "showwarnings") {
compilerOptions.Flags.ShowWarnings = flag_value;
continue;
}
} */
if(flag_name == "exportall") {
compilerOptions.Flags.ExportAll = flag_value;
continue;
Expand Down
13 changes: 9 additions & 4 deletions Compiler/script/cs_compiler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,12 @@ const char *ccCurScriptName = "";
std::vector<const char*> defaultheaders;
std::vector<const char*> defaultHeaderNames;

void ccGetExtensions(std::vector<std::string> &exts)
{
// Add extensions here as necessary
return;
}

int ccAddDefaultHeader(const char* nhead, const char *nName)
{
defaultheaders.push_back(nhead);
Expand Down Expand Up @@ -100,10 +106,9 @@ ccScript* ccCompileText(const char *texo, const char *scriptName) {
(sym.get_type(t) != SYM_LOCALVAR)) continue;

if (sym.entries[t].flags & SFLG_IMPORTED) continue;
if (ccGetOption(SCOPT_SHOWWARNINGS)==0) ;
else if ((sym.entries[t].flags & SFLG_ACCESSED)==0) {
printf("warning: variable '%s' is never used\n",sym.get_friendly_name(t).c_str());
}
//if ((sym.entries[t].flags & SFLG_ACCESSED)==0) {
// printf("warning: variable '%s' is never used\n",sym.get_friendly_name(t).c_str());
//}
}

if (ccGetOption(SCOPT_EXPORTALL)) {
Expand Down
3 changes: 3 additions & 0 deletions Compiler/script/cs_compiler.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@
#include "script/cc_script.h" // ccScript

// ********* SCRIPT COMPILATION FUNCTIONS **************
// Get a list of compiler extensions.
extern void ccGetExtensions(std::vector<std::string> &exts);

// add a script that will be compiled as a header into every compilation
// 'name' is the name of the header, used in error reports
// (only the pointer is stored so don't free the memory)
Expand Down
25 changes: 24 additions & 1 deletion Editor/AGS.CScript.Compiler/IScriptCompiler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,30 @@

namespace AGS.CScript.Compiler
{
public interface IScriptCompiler
// TODO: merge this with AGS.Native.IScriptCompiler!
// I wrote a new variant of IScriptCompiler inside AGS.Native lib, because
// this one has certain ties within CScript.Compiler classes.
// The only implementation of IScriptCompiler in here is not completed and
// was never used in AGS Editor (apparently, not sure if possible to test).
//
// Proposal: rework CompileResults, make it contain CompiledScript and
// CompileMessages collection. Merge or substitute CompilerMessage with
// AGS.Types.CompileMessage. But note that CompilerMessage also has
// Error Codes! Need to figure out how to make that shareable without strict
// bind to the Compiler lib.
// Re CompiledScript, see if possible to merge with AGS.Native.CompiledScript,
// which has data stored as native ccScript (?). Need to think this through.
//
// Important: IScriptCompiler implementation does not necessarily has actual
// compiler code inside. It may instead be a wrapper over a code that starts
// **EXTERNAL** compiler, i.e. runs a standalone executable, passing all
// parameters through command line.
// Need to think through how to receive results in that case. Maybe CompileResults
// could contain optionally an object that has compiled data in memory **AND**
// optionally a path to compiled file. And interface user would test which one
// is valid. That's just an idea for now.

public interface IScriptCompiler
{
CompileResults CompileScript(string script);
}
Expand Down
93 changes: 75 additions & 18 deletions Editor/AGS.Editor/AGSEditor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@

namespace AGS.Editor
{
using ScriptCompilerOptions = AGS.Native.ScriptCompilerOptions;

public class AGSEditor
{
public event GetScriptHeaderListHandler GetScriptHeaderList;
Expand Down Expand Up @@ -858,15 +860,53 @@ private void DefineMacrosAccordingToGameSettings(IPreprocessor preprocessor)
}
}

/// <summary>
/// Preprocesses and then compiles the script using the supplied headers.
/// </summary>
public void CompileScript(Script script, List<Script> headers, CompileMessages errors)
private void DefineMacrosFromCompiler(IPreprocessor preprocessor, AGS.Native.IScriptCompiler compiler)
{
var exts = compiler.GetExtensions();
foreach (var ext in exts)
{
preprocessor.DefineMacro("SCRIPT_EXT_" + ext, "1");
}
}

private ScriptCompilerOptions GetScriptCompileOptions(Game game)
{
// Set up compiler options
ScriptCompilerOptions options =
ScriptCompilerOptions.AutoExportFunctions |
ScriptCompilerOptions.LineNumbers;

if (game.Settings.LeftToRightPrecedence)
options = options | ScriptCompilerOptions.LeftToRightPrecendence;
if ((!game.Settings.EnforceNewStrings))
options = options | ScriptCompilerOptions.OldStrings;
if (game.UnicodeMode)
options = options | ScriptCompilerOptions.UTF8;

return options;
}

/// <summary>
/// Preprocesses and then compiles the script prepended with the supplied headers.
/// Warnings and errors are collected in 'messages'.
/// Will _not_ throw whenever compiling results in an error.
/// </summary>
public void CompileScript(AGS.Native.IScriptCompiler compiler, Script script, List<Script> headers, CompileMessages messages)
{
IPreprocessor preprocessor = CompilerFactory.CreatePreprocessor(AGS.Types.Version.AGS_EDITOR_VERSION);
// Clear up previous data, if present
if (script.CompiledData != null)
{
script.CompiledData.Dispose();
script.CompiledData = null;
}

messages = messages ?? new CompileMessages();

IPreprocessor preprocessor = CompilerFactory.CreatePreprocessor(AGS.Types.Version.AGS_EDITOR_VERSION);
DefineMacrosAccordingToGameSettings(preprocessor);
DefineMacrosFromCompiler(preprocessor, compiler);

List<string> preProcessedCode = new List<string>();
List<string> preProcessedCode = new List<string>();
foreach (Script header in headers)
{
preProcessedCode.Add(preprocessor.Preprocess(header.Text, header.FileName));
Expand All @@ -878,19 +918,36 @@ public void CompileScript(Script script, List<Script> headers, CompileMessages e
{
foreach (AGS.CScript.Compiler.Error error in preprocessor.Results)
{
CompileError newError = new CompileError(error.Message, error.ScriptName, error.LineNumber);
if (errors == null)
{
throw newError;
}
errors.Add(newError);
messages.Add(new CompileError(error.Message, error.ScriptName, error.LineNumber));
}
}
else
{
Factory.NativeProxy.CompileScript(script, preProcessedCode.ToArray(), _game);
}
}

// If the preprocessor has found any errors then don't attempt compiling proper
if (messages.HasErrors)
return;
}

script.CompiledData =
compiler.CompileScript(script.FileName, preProcessedCode.ToArray(), GetScriptCompileOptions(_game), messages);
}

/// <summary>
/// Preprocesses and then compiles the script prepended the supplied headers.
/// Retrieves script compiler from AGS.Native. If compiler is not found,
/// reports error and bails out early.
/// Warnings and errors are collected in 'messages'.
/// Will _not_ throw whenever compiling results in an error.
/// </summary>
public void CompileScript(Script script, List<Script> headers, CompileMessages messages)
{
var compiler = Factory.NativeProxy.GetEmbeddedScriptCompilers().FirstOrDefault();
if (compiler == null)
{
messages.Add(new CompileError($"Script compiler is not available. Incomplete AGS Editor installation?"));
return;
}

CompileScript(compiler, script, headers, messages);
}

private Script CompileDialogs(CompileMessages errors, bool rebuildAll)
{
Expand Down
4 changes: 2 additions & 2 deletions Editor/AGS.Editor/NativeProxy.cs
Original file line number Diff line number Diff line change
Expand Up @@ -436,9 +436,9 @@ public string LoadRoomScript(string roomFileName)
return _native.LoadRoomScript(roomFileName);
}

public void CompileScript(Script script, string[] preProcessedData, Game game)
public List<IScriptCompiler> GetEmbeddedScriptCompilers()
{
_native.CompileScript(script, preProcessedData, game);
return _native.GetEmbeddedScriptCompilers();
}

public void CreateDebugMiniEXE(string[] fileList, string exeFileName)
Expand Down
123 changes: 123 additions & 0 deletions Editor/AGS.Native/CompiledScript.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
//=============================================================================
//
// Adventure Game Studio (AGS)
//
// Copyright (C) 1999-2011 Chris Jones and 2011-2024 various contributors
// The full list of copyright holders can be found in the Copyright.txt
// file, which is part of this source code distribution.
//
// The AGS source code is provided under the Artistic License 2.0.
// A copy of this license can be found in the file License.txt and at
// https://opensource.org/license/artistic-2-0/
//
//=============================================================================
#pragma once
#include "script/cc_script.h"
#include "script/cc_internal.h"

namespace AGS
{
namespace Native
{
using namespace System;
using namespace AGS::Types;

public ref class CompiledScript : ICompiledScript
{
private:
PScript* _compiledScript;

void WriteCStr(System::IO::BinaryWriter ^writer, const std::string &str)
{
for (auto c : str)
{
writer->Write((System::Byte)c);
}
writer->Write((System::Byte)0);
}

public:
CompiledScript(PScript script)
{
_compiledScript = new PScript();
*_compiledScript = script;
}

property PScript Data
{
PScript get() { return *_compiledScript; }
void set(PScript newScript) { *_compiledScript = newScript; }
}

~CompiledScript()
{
this->!CompiledScript();
}

!CompiledScript()
{
_compiledScript->reset();
delete _compiledScript;
_compiledScript = NULL;
}

virtual void __clrcall Write(System::IO::FileStream ^ostream, System::String ^scriptFileName)
{
if (*_compiledScript == NULL)
{
throw gcnew AGS::Types::CompileError(gcnew System::String("Script has not been compiled: ") + scriptFileName);
}
System::IO::BinaryWriter ^writer = gcnew System::IO::BinaryWriter(ostream);
for (int i = 0; i < 4; ++i)
{
// the BinaryWriter seems to be treating CHAR as a 4-byte type here?
writer->Write((System::Byte)scfilesig[i]);
}
const ccScript *cs = _compiledScript->get();
writer->Write((unsigned)SCOM_VERSION_CURRENT);
writer->Write((unsigned)cs->globaldata.size());
writer->Write((unsigned)cs->code.size());
writer->Write((unsigned)cs->strings.size());
for (size_t i = 0; i < cs->globaldata.size(); ++i)
{
writer->Write((System::Byte)cs->globaldata[i]);
}
for (size_t i = 0; i < cs->code.size(); ++i)
{
writer->Write((int)cs->code[i]);
}
for (size_t i = 0; i < cs->strings.size(); ++i)
{
writer->Write((System::Byte)cs->strings[i]);
}
writer->Write((unsigned)cs->fixups.size());
for (size_t i = 0; i < cs->fixups.size(); ++i)
{
writer->Write((System::Byte)cs->fixuptypes[i]);
}
for (size_t i = 0; i < cs->fixups.size(); ++i)
{
writer->Write(cs->fixups[i]);
}
writer->Write((unsigned)cs->imports.size());
for (size_t i = 0; i < cs->imports.size(); ++i)
{
WriteCStr(writer, cs->imports[i]);
}
writer->Write((unsigned)cs->exports.size());
for (size_t i = 0; i < cs->exports.size(); ++i)
{
WriteCStr(writer, cs->exports[i]);
writer->Write(cs->export_addr[i]);
}
writer->Write((unsigned)cs->sectionNames.size());
for (size_t i = 0; i < cs->sectionNames.size(); ++i)
{
WriteCStr(writer, cs->sectionNames[i]);
writer->Write(cs->sectionOffsets[i]);
}
writer->Write((unsigned)ENDFILESIG);
}
};
} // namespace Native
} // namespace AGS
Loading