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

Remove nullfunc_t and make INVALID_FUNCTION the same as null. #912

Merged
merged 1 commit into from
Oct 20, 2023
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
11 changes: 11 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -185,3 +185,14 @@ Also as of this release, both the compiler and VM can run on non-x86 platforms
### SourcePawn 1.12

SourcePawn 1.12 is currently in development.

This release contains a number of bug fixes and internal improvements. It also greatly improves
error message readability in the style of modern compilers.

This release contains a number of language changes.
- Function bodies must now be braced. There is no compatibility mode for this change.
- `INVALID_FUNCTION` is now type `null_t`. The old internal type `nullfunc_t` has been removed.
`INVALID_FUNCTION` and `null` are now equivalent, which means code manually checking for `-1`
will not work. There are three functions added to `IPluginContext` to help native code with
this change: `GetNullFunctionValue()`, `IsNullFunctionId()`, and `GetFunctionByIdOrNull()`.
- Warnings around indentation have been removed.
3 changes: 2 additions & 1 deletion compiler/assembler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -924,7 +924,8 @@ Assembler::Assemble(SmxByteBuffer* buffer)
code->header().main = 0;
code->header().code = sizeof(sp_file_code_t);
code->header().features = SmxConsts::kCodeFeatureDirectArrays |
SmxConsts::kCodeFeatureHeapScopes;
SmxConsts::kCodeFeatureHeapScopes |
SmxConsts::kCodeFeatureNullFunctions;
code->setBlob(cg_.code_ptr(), cg_.code_size());

// Set up the data section. Note pre-SourceMod 1.7, the |memsize| was
Expand Down
15 changes: 2 additions & 13 deletions compiler/expressions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -290,20 +290,9 @@ matchobjecttags(Type* formal, Type* actual, int flags)
return FALSE;
}

if (actualtag == types->tag_nullfunc()) {
// All functions are nullable. We use a separate constant for backward
// compatibility; plugins and extensions check -1, not 0.
if (formal->isFunction())
return TRUE;

if (!(flags & MATCHTAG_SILENT))
report(154) << pc_tagname(formaltag);
return FALSE;
}

if (actualtag == types->tag_null()) {
// All objects are nullable.
if (formal->isObject())
if (formal->isFunction() || formal->isObject())
return TRUE;

// Some methodmaps are nullable. The nullable property is inherited
Expand Down Expand Up @@ -427,7 +416,7 @@ matchfunctags(Type* formal, Type* actual)
if (formaltag == types->tag_function() && actual->isFunction())
return TRUE;

if (actualtag == types->tag_nullfunc())
if (actualtag == types->tag_null())
return TRUE;

if (!actual->isFunction())
Expand Down
2 changes: 1 addition & 1 deletion compiler/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -361,7 +361,7 @@ setconstants(void)
{
auto& cc = CompileContext::get();
DefineConstant(cc, cc.atom("EOS"), 0, 0);
DefineConstant(cc, cc.atom("INVALID_FUNCTION"), -1, cc.types()->tag_nullfunc());
DefineConstant(cc, cc.atom("INVALID_FUNCTION"), 0, cc.types()->tag_null());
DefineConstant(cc, cc.atom("cellmax"), INT_MAX, 0);
DefineConstant(cc, cc.atom("cellmin"), INT_MIN, 0);

Expand Down
2 changes: 1 addition & 1 deletion compiler/messages.h
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,7 @@ static const char* errmsg[] = {
/*151*/ "unmatched opening brace ('{') (line %d)\n",
/*152*/ "no setter found for property %s\n",
/*153*/ "Array-based enum structs have been removed. See https://wiki.alliedmods.net/SourcePawn_Transitional_Syntax#Enum_Structs\n",
/*154*/ "cannot assign INVALID_FUNCTION to a non-function type\n",
/*154*/ "unused154\n",
/*155*/ "expected newline, but found '%s'\n",
/*156*/ "invalid 'using' declaration\n",
/*157*/ "'%s' is a reserved keyword\n",
Expand Down
1 change: 0 additions & 1 deletion compiler/types.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,6 @@ TypeDictionary::init()
type_void_ = defineVoid();
type_object_ = defineObject("object");
type_null_ = defineObject("null_t");
type_nullfunc_ = defineObject("nullfunc_t");
}

Type*
Expand Down
3 changes: 0 additions & 3 deletions compiler/types.h
Original file line number Diff line number Diff line change
Expand Up @@ -384,7 +384,6 @@ class TypeDictionary
callback(type);
}

Type* type_nullfunc() const { return type_nullfunc_; }
Type* type_object() const { return type_object_; }
Type* type_null() const { return type_null_; }
Type* type_function() const { return type_function_; }
Expand All @@ -394,7 +393,6 @@ class TypeDictionary
Type* type_bool() const { return type_bool_; }
Type* type_string() const { return type_string_; }

int tag_nullfunc() const { return type_nullfunc_->tagid(); }
int tag_object() const { return type_object_->tagid(); }
int tag_null() const { return type_null_->tagid(); }
int tag_function() const { return type_function_->tagid(); }
Expand All @@ -414,7 +412,6 @@ class TypeDictionary
tr::unordered_map<Atom*, Type*> types_;
tr::unordered_map<int, Type*> tags_;
Type* type_int_ = nullptr;
Type* type_nullfunc_ = nullptr;
Type* type_object_ = nullptr;
Type* type_null_ = nullptr;
Type* type_function_ = nullptr;
Expand Down
3 changes: 3 additions & 0 deletions include/smx/smx-headers.h
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,9 @@ struct SmxConsts {

// This feature adds the HEAP_SAVE and HEAP_RESTORE opcodes.
static const uint32_t kCodeFeatureHeapScopes = (1 << 2);

// This feature indicates that INVALID_FUNCTION is null (0) instead of -1.
static const uint32_t kCodeFeatureNullFunctions = (1 << 3);
};

// These structures are byte-packed.
Expand Down
34 changes: 33 additions & 1 deletion include/sp_vm_api.h
Original file line number Diff line number Diff line change
Expand Up @@ -475,7 +475,7 @@ class IPluginRuntime
virtual IPluginFunction* GetFunctionByName(const char* public_name) = 0;

/**
* @brief Returns a function by its id.
* @brief Deprecated. Use GetFunctionByIdOrNull or GetFunctionByIdOrError instead.
*
* @param func_id Function ID.
* @return A new IPluginFunction pointer, NULL if not found.
Expand Down Expand Up @@ -1198,6 +1198,38 @@ class IPluginContext
* EnterHeapScope.
*/
virtual void LeaveHeapScope() = 0;

/**
* @brief Returns the value of a null function. If kCodeFeatureNullFunction
* is set, this returns 0. If unset, this returns -1, the legacy value.
*/
virtual cell_t GetNullFunctionValue() = 0;

/**
* @brief Returns whether the value is a null function.
*/
virtual bool IsNullFunctionId(funcid_t func) = 0;

/**
* @brief Convert a funcid_t to an IPluginFunction, reporting an error if
* the function is invalid. funcid_t can be INVALID_FUNCTION.
*
* If |func| is non-null and valid, |out| is set to the function and true
* is returned.
*
* If |func| is non-null and invalid, |out| is set to null, false is
* returned, and an error is reported.
*
* If |func| is GetNullFunctionValue(), |out| is set to null and true is
* returned.
*/
virtual bool GetFunctionByIdOrNull(funcid_t func, IPluginFunction** out) = 0;

/**
* @brief Convert a funcid_t to an IPluginFunction, reporting an error if
* the function is invalid or null.
*/
virtual IPluginFunction* GetFunctionByIdOrError(funcid_t func) = 0;
};

class AutoEnterHeapScope
Expand Down
5 changes: 5 additions & 0 deletions tests/api/function-id.sp
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
#include <shell>

public main() {
printnum(execute(INVALID_FUNCTION, 1));
}
41 changes: 35 additions & 6 deletions vm/plugin-context.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1101,14 +1101,43 @@ PluginContext::HeapAlloc2dArray(unsigned int length, unsigned int stride, cell_t
return true;
}

void
PluginContext::EnterHeapScope()
{
void PluginContext::EnterHeapScope() {
enterHeapScope();
}

void
PluginContext::LeaveHeapScope()
{
void PluginContext::LeaveHeapScope() {
leaveHeapScope();
}

cell_t PluginContext::GetNullFunctionValue() {
auto image = runtime()->image();
if (image->DescribeCode().features & SmxConsts::kCodeFeatureNullFunctions) {
return 0;
}
return -1;
}

bool PluginContext::IsNullFunctionId(funcid_t func) {
return func == GetNullFunctionValue();
}

bool PluginContext::GetFunctionByIdOrNull(funcid_t func, IPluginFunction** out) {
if (IsNullFunctionId(func)) {
*out = nullptr;
return true;
}

*out = GetFunctionById(func);
if (!*out) {
ReportError("Invalid function id: 0x%08x", func);
return false;
}
return true;
}

IPluginFunction* PluginContext::GetFunctionByIdOrError(funcid_t func_id) {
if (auto fn = GetFunctionById(func_id))
return fn;
ReportError("Invalid function id: 0x%08x", func_id);
return nullptr;
}
6 changes: 5 additions & 1 deletion vm/plugin-context.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ static const cell_t STACK_MARGIN = 64; // 16 parameters of safety, I guess
class Environment;
class PluginContext;

class PluginContext : public BasePluginContext
class PluginContext final : public BasePluginContext
{
public:
PluginContext(PluginRuntime* pRuntime);
Expand Down Expand Up @@ -61,6 +61,10 @@ class PluginContext : public BasePluginContext
const cell_t* init) override;
void EnterHeapScope() override;
void LeaveHeapScope() override;
cell_t GetNullFunctionValue() override;
bool IsNullFunctionId(funcid_t func) override;
bool GetFunctionByIdOrNull(funcid_t func, IPluginFunction** out) override;
IPluginFunction* GetFunctionByIdOrError(funcid_t func_id) override;
bool Invoke(funcid_t fnid, const cell_t* params, unsigned int num_params, cell_t* result);

size_t HeapSize() const {
Expand Down
20 changes: 11 additions & 9 deletions vm/shell/shell.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -185,22 +185,24 @@ static cell_t DoExecute(IPluginContext* cx, const cell_t* params)
{
int32_t ok = 0;
for (size_t i = 0; i < size_t(params[2]); i++) {
if (IPluginFunction* fn = cx->GetFunctionById(params[1])) {
if (fn->Execute(nullptr) != SP_ERROR_NONE)
continue;
ok++;
}
IPluginFunction* fn;
if (!cx->GetFunctionByIdOrNull(params[1], &fn) || !fn)
return 0;
if (fn->Execute(nullptr) != SP_ERROR_NONE)
continue;
ok++;
}
return ok;
}

static cell_t DoInvoke(IPluginContext* cx, const cell_t* params)
{
for (size_t i = 0; i < size_t(params[2]); i++) {
if (IPluginFunction* fn = cx->GetFunctionById(params[1])) {
if (!fn->Invoke())
return 0;
}
IPluginFunction* fn;
if (!cx->GetFunctionByIdOrNull(params[1], &fn) || !fn)
return 0;
if (!fn->Invoke())
return 0;
}
return 1;
}
Expand Down
3 changes: 2 additions & 1 deletion vm/smx-v1-image.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -280,7 +280,8 @@ SmxV1Image::validateCode()

uint32_t supported_features =
SmxConsts::kCodeFeatureDirectArrays |
SmxConsts::kCodeFeatureHeapScopes;
SmxConsts::kCodeFeatureHeapScopes |
SmxConsts::kCodeFeatureNullFunctions;
if (features & ~supported_features)
return error("unsupported feature set; code is too new");

Expand Down