ObjFW  Artifact [8df38d6d4f]

Artifact 8df38d6d4f05b0d93dd40290f08a56da868c3d0bc1b81f504c06b0b46658b8cd:

  • File generators/library/GlueGenerator.m — part of check-in [8da0192f8a] at 2025-04-30 11:47:48 on branch amiga-library — Merge trunk and adjust to changes (user: js size: 5971) [more...]

/*
 * Copyright (c) 2008-2025 Jonathan Schleifer <js@nil.im>
 *
 * All rights reserved.
 *
 * This program is free software: you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License version 3.0 only,
 * as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
 * version 3.0 for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * version 3.0 along with this program. If not, see
 * <https://www.gnu.org/licenses/>.
 */

#include "config.h"

#import "OFArray.h"
#import "OFXMLAttribute.h"

#import "GlueGenerator.h"

#import "OFInvalidFormatException.h"
#import "OFUnsupportedVersionException.h"

#import "copyright.h"

@implementation GlueGenerator
- (instancetype)initWithLibrary: (OFXMLElement *)library
			 header: (OFStream *)header
		 implementation: (OFStream *)impl
	  morphOSImplementation: (OFStream *)morphOSImpl
{
	self = [super init];

	@try {
		OFXMLAttribute *version;

		if (![library.name isEqual: @"amiga-library"] ||
		    library.namespace != nil)
			@throw [OFInvalidFormatException exception];

		if ((version = [library attributeForName: @"version"]) == nil)
			@throw [OFInvalidFormatException exception];

		if (![version.stringValue isEqual: @"1.0"])
			@throw [OFUnsupportedVersionException
			    exceptionWithVersion: version.stringValue];

		_library = [library retain];
		_header = [header retain];
		_impl = [impl retain];
		_morphOSImpl = [morphOSImpl retain];
	} @catch (id e) {
		[self release];
		@throw e;
	}

	return self;
}

- (void)dealloc
{
	[_library release];
	[_header release];
	[_impl release];
	[_morphOSImpl release];

	[super dealloc];
}

- (void)generate
{
	size_t includes = 0;

	[_header writeString: COPYRIGHT];
	[_impl writeString: COPYRIGHT];
	[_morphOSImpl writeString: COPYRIGHT];

	[_header writeString:
	    @"/* This file is automatically generated from amiga-library.xml */"
	    @"\n\n"];

	[_impl writeString:
	    @"/* This file is automatically generated from amiga-library.xml */"
	    @"\n\n"
	    @"#include \"config.h\"\n"
	    @"\n"
	    @"#import \"amiga-glue.h\"\n"
	    @"\n"];

	[_morphOSImpl writeString:
	    @"/* This file is automatically generated from amiga-library.xml */"
	    @"\n\n"
	    @"#include \"config.h\"\n"
	    @"\n"
	    @".section .text\n"];

	for (OFXMLElement *include in [_library elementsForName: @"include"]) {
		[_header writeFormat: @"#import \"%@\"\n",
				      include.stringValue];
		includes++;
	}

	if (includes > 0)
		[_header writeString: @"\n"];

	[_header writeString:
	    @"#ifdef OF_AMIGAOS_M68K\n"
	    @"# define PPC_PARAMS(...) (void)\n"
	    @"# define M68K_ARG(type, name, reg)\t\t\\\n"
	    @"\tregister type reg##name __asm__(#reg);\t\\\n"
	    @"\ttype name = reg##name;\n"
	    @"#else\n"
	    @"# define PPC_PARAMS(...) (__VA_ARGS__)\n"
	    @"# define M68K_ARG(...)\n"
	    @"#endif\n"
	    @"\n"];

	for (OFXMLElement *function in
	    [_library elementsForName: @"function"]) {
		OFString *name =
		    [function attributeForName: @"name"].stringValue;
		OFString *returnType =
		    [function attributeForName: @"return-type"].stringValue;
		OFString *condition =
		    [function attributeForName: @"condition"].stringValue;
		OFArray OF_GENERIC(OFXMLElement *) *arguments =
		    [function elementsForName: @"argument"];
		size_t argumentIndex;

		[_impl writeString: @"\n"];
		[_morphOSImpl writeString: @"\n"];

		if (condition != nil) {
			[_header writeFormat: @"#if %@\n", condition];
			[_impl writeFormat: @"#if %@\n", condition];
			[_morphOSImpl writeFormat: @"#if %@\n", condition];
		}

		if (returnType == nil)
			returnType = @"void";

		[_header writeFormat:
		    @"extern %@%@glue_%@",
		    returnType,
		    (![returnType hasSuffix: @"*"] ? @" " : @""),
		    name];

		[_impl writeFormat: @"%@ __saveds\n"
				    @"glue_%@",
				    returnType, name];

		if (arguments.count > 0)
			[_header writeString: @" PPC_PARAMS("];
		else
			[_header writeString: @"(void"];

		[_impl writeString: @"(void)\n"];

		argumentIndex = 0;
		for (OFXMLElement *argument in arguments) {
			OFString *argName =
			    [argument attributeForName: @"name"].stringValue;
			OFString *argType =
			    [argument attributeForName: @"type"].stringValue;

			if (argumentIndex++ > 0)
				[_header writeString: @", "];

			[_header writeString: argType];
			if (![argType hasSuffix: @"*"])
				[_header writeString: @" "];
			[_header writeString: argName];
		}

		[_header writeString: @");\n"];
		[_impl writeString: @"{\n"];

		for (OFXMLElement *argument in arguments) {
			OFString *argName =
			    [argument attributeForName: @"name"].stringValue;
			OFString *argType =
			    [argument attributeForName: @"type"].stringValue;
			OFString *m68kReg = [argument
			    attributeForName: @"m68k-reg"].stringValue;

			[_impl writeFormat: @"\tM68K_ARG(%@, %@, %@)\n",
					    argType, argName, m68kReg];
		}

		if (arguments.count > 0)
			[_impl writeString: @"\n"];

		if (![returnType isEqual: @"void"])
			[_impl writeString: @"\treturn "];
		else
			[_impl writeString: @"\t"];

		[_impl writeFormat: @"%@(", name];

		argumentIndex = 0;
		for (OFXMLElement *argument in arguments) {
			OFString *argName =
			    [argument attributeForName: @"name"].stringValue;

			if (argumentIndex++ > 0)
				[_impl writeString: @", "];

			[_impl writeString: argName];
		}

		[_impl writeString: @");\n"
				    @"}\n"];

		[_morphOSImpl writeFormat: @".globl glue_%@\n"
					   @"glue_%@:\n"
					   @"	lwz	%%r13, 44(%%r12)\n"
					   @"	b	%@\n",
					   name, name, name];

		if (condition != nil) {
			[_header writeString: @"#endif\n"];
			[_impl writeString: @"#endif\n"];
			[_morphOSImpl writeString: @"#endif\n"];
		}
	}
}
@end