<commit_before>---------------------------------------------------------------------
-- SF Instance class.
-- Contains the compiled SF script and essential data. Essentially
-- the execution context.
---------------------------------------------------------------------

SF.Instance = {}
SF.Instance.__index = SF.Instance

--- Instance fields
-- @name Instance
-- @class table
-- @field env Environment table for the script
-- @field data Data that libraries can store.
-- @field ppdata Preprocessor data
-- @field ops Currently used ops.
-- @field hooks Registered hooks
-- @field scripts The compiled script functions.
-- @field initialized True if initialized, nil if not.
-- @field error True if instance is errored and should not be executed
-- @field mainfile The main file
-- @field player The "owner" of the instance

--- Internal function - do not call.
-- Runs a function while incrementing the instance ops coutner.
-- This does no setup work and shouldn't be called by client code
-- @param func The function to run
-- @param ... Arguments to func
-- @return True if ok
-- @return A table of values that the hook returned
function SF.Instance:runWithOps ( func, ... )

	local args = { ... }
	local traceback

	local function xpcall_callback ( err )
		if type( err ) == "table" then
			if err.message then
				local line= err.line
				local file = err.file

				err = ( file and ( file .. ":" ) or "" ) .. ( line and ( line .. ": " ) or "" ) .. err.message
			end
		end
		err = tostring( err )
		traceback = debug.traceback( err, 2 )
		return err
	end

	local oldSysTime = SysTime()

	local function cpuCheck ()
		self.cpuTime.current = SysTime() - oldSysTime

		local ind = self.cpuTime.bufferI
		self.cpuTime.buffer[ ind ] = ( self.cpuTime.buffer[ ind ] or 0 ) + self.cpuTime.current

		if self.cpuTime:getBufferAverage() > SF.cpuQuota:GetFloat() then
			debug.sethook( nil )
			SF.throw( "CPU Quota exceeded.", 0, true )
		end
	end

	local wrapperfunc = function ()
		local ret = { func( unpack( args ) ) }
		cpuCheck()
		return ret
	end

	debug.sethook( cpuCheck, "", 500 )
	local ok, rt = xpcall( wrapperfunc, xpcall_callback )
	debug.sethook( nil )

	if ok then
		return true, rt
	else
		return false, rt, traceback
	end
end

--- Internal function - Do not call. Prepares the script to be executed.
-- This is done automatically by Initialize and runScriptHook.
function SF.Instance:prepare ( hook, name )
	assert( self.initialized, "Instance not initialized!" )
	assert( not self.error, "Instance is errored!" )
	assert( SF.instance == nil )
	
	self:runLibraryHook( "prepare", hook, name )
	SF.instance = self
end<commit_msg>cpuTime global sethook for recursive runWithOps<commit_after>---------------------------------------------------------------------
-- SF Instance class.
-- Contains the compiled SF script and essential data. Essentially
-- the execution context.
---------------------------------------------------------------------

SF.Instance = {}
SF.Instance.__index = SF.Instance

SF.Instance.sethook = {}
SF.Instance.sethook.counter = 0
SF.Instance.sethook.set = function ( func, mask, count )
	if SF.Instance.sethook.counter == 0 then
		debug.sethook( func, mask, count )
	end
end

SF.Instance.sethook.clear = function ()
	if SF.Instance.sethook.counter == 0 then
		debug.sethook( nil )
	end
end
-- Prevent further writing of indexes to this table.
setmetatable( SF.Instance.sethook, { __newindex = function() return end } )

--- Instance fields
-- @name Instance
-- @class table
-- @field env Environment table for the script
-- @field data Data that libraries can store.
-- @field ppdata Preprocessor data
-- @field ops Currently used ops.
-- @field hooks Registered hooks
-- @field scripts The compiled script functions.
-- @field initialized True if initialized, nil if not.
-- @field error True if instance is errored and should not be executed
-- @field mainfile The main file
-- @field player The "owner" of the instance

--- Internal function - do not call.
-- Runs a function while incrementing the instance ops coutner.
-- This does no setup work and shouldn't be called by client code
-- @param func The function to run
-- @param ... Arguments to func
-- @return True if ok
-- @return A table of values that the hook returned
function SF.Instance:runWithOps ( func, ... )

	local args = { ... }
	local traceback

	local function xpcall_callback ( err )
		if type( err ) == "table" then
			if err.message then
				local line= err.line
				local file = err.file

				err = ( file and ( file .. ":" ) or "" ) .. ( line and ( line .. ": " ) or "" ) .. err.message
			end
		end
		err = tostring( err )
		traceback = debug.traceback( err, 2 )
		return err
	end

	local oldSysTime = SysTime()

	local function cpuCheck ()
		self.cpuTime.current = SysTime() - oldSysTime

		local ind = self.cpuTime.bufferI
		self.cpuTime.buffer[ ind ] = ( self.cpuTime.buffer[ ind ] or 0 ) + self.cpuTime.current

		if self.cpuTime:getBufferAverage() > SF.cpuQuota:GetFloat() then
			debug.sethook( nil )
			SF.throw( "CPU Quota exceeded.", 0, true )
		end
	end

	local wrapperfunc = function ()
		local ret = { func( unpack( args ) ) }
		cpuCheck()
		return ret
	end

	SF.Instance.sethook.set( cpuCheck, "", 500 )
	SF.Instance.sethook.counter = SF.Instance.sethook.counter + 1

	local ok, rt = xpcall( wrapperfunc, xpcall_callback )

	SF.Instance.sethook.counter = SF.Instance.sethook.counter - 1
	SF.Instance.sethook.clear()

	if ok then
		return true, rt
	else
		return false, rt, traceback
	end
end

--- Internal function - Do not call. Prepares the script to be executed.
-- This is done automatically by Initialize and runScriptHook.
function SF.Instance:prepare ( hook, name )
	assert( self.initialized, "Instance not initialized!" )
	assert( not self.error, "Instance is errored!" )
	assert( SF.instance == nil )
	
	self:runLibraryHook( "prepare", hook, name )
	SF.instance = self
end
{'fraction_non_alphanumeric': 0.15223274695534506, 'fraction_numerical': 0.002875507442489851, 'mean_word_length': 2.663568773234201, 'pattern_counts': {'":': 4, '<': 3, '<?xml version=': 0, '>': 5, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4118868', 'n_tokens_mistral': 1811, 'n_tokens_neox': 1703, 'n_words': 649}