hBasic > hManual > System

hBasic Manual
System changes

Interrupt Scope Fixes Fix scope during an interrupt
Fix for volkeys at start of run Fix volkey status at start of run (ack:aFox)
Faster BYTE.WRITE.BUFFER
Bundle Expressions Accept bundle.key expressions ( ** On Hold   --- awaiting bug fix --- ** )
Array Pointers Allow array pointers for assignment
Array Functions Functions returning an array
TEXT/BYTE.OPEN
change behaviour on failure
SAF Infrastructure
Internal support for File.SAF commands
Shortcut Support StandAlone APKs can recieve shortcut data
DEVICE fix
Fix crash on Android-10+
Sensors fix
Fix for Sensors with less than 3 parameters ack:dkal, aFox

Interrupt Scope Fixes

By 'scope' we mean the list/s that Basic! searches through when trying to find a variable.
There are mainly two types of scopes which a basic program is running in.

1. The main scope i.e
the main program or
the main block of an interrupt e.g OnBackKey
or
2. A local scope i.e
inside a function

An interrupt could happen in the middle of a function.
In 01.91,  Basic would detect this and change the scope (from local) to the 'global' list which starts at the main program list. If the variable is not found, it would keep searching through the list of the first function that was called, then the next function, and so on until it reached the end of the last called function (the function that was interrupted).
This normally works fine if the variable exists in the main list. Many uses of Interrupts rely on finding a variable in a convenient 'global' area such as the main list.

There are two problems with this design;
a) If the variable does not exist in the main list. Then it might (un-intentionally) be found in one of the other lists. This can lead to errors which could be difficult to trace.
and
b) If a function is called inside an interrupt's main block,

e.g
OnBackKey:
myfunc ()
Back.resume

basic still thinks it's inside an interrupt (which it is) and uses the 'global' scope. This means any local variable inside the function would be searched starting from 'main'. Only if the variable does not exist in main or any other preceeding function call, would the correct list be finally searched.

In 01.91, Functions called by an interrupt block were getting wrong scope.
This is clearly undesirable, and is a side-effect of an interrupt handling system not intended to call functions.

The following fixes were done.

Fixed scope during an interrupt.

a) Variable searches inside the main-block or an interrupt-main-block will search only the main list and nothing but the main list.

else
b) Variable searches inside a function-block (no matter when they are called) will only search the function's local variable list.

All that is needed is a flag to indicate which scope (a or b) the code is running in. This flag is implemented in this fix and is updated for each interrupt or function call and restored upon interrupt resumes and function exits.

Fix scope for new variables created during an interrupt

Make sure new variables in main-block and interrupt-main-block are always created in main scope.
Make sure new variables in function-block are always created in local scope.

This is a similar fix using the new scope flag for newly created variables.



Exceptions

The only exception to these fixes is code run inside an ONERROR block.
ONERROR is treated differently than other interrupts and it's behaviour (regarding scope) has been left alone. That is, it's scope is the scope that was interrupted. e,g If an error occurred in a function, then the scope of the ONERROR block is that of the function. The rationale behind being that it is sometimes useful to return to the error point with a GOTO, in which a flag or variable (in the same scope) is neccessary to transfer meaning.

Fix for volkeys at start of run

When VOLKEYS.OFF is run in the editor, the next program that is run does not reset a system flag, so volume keys stay blocked.

Fix was to reset that flag at start of a run.

Faster BYTE.WRITE.BUFFER

BYTE.WRITE.BUFFER <file_table_nexp>, <buffer_sexp>

(Basic 01.91 writes this output one low byte at a time, each time ignoring the high byte ( each character is two bytes).) which is rather slow.

This enhancement converts the string to a byte array and writes the whole array at once.

The conversion (from a string to a byte array)  assumes this;
from UTF-16 (i.e a Basic/Java string)
to ISO-8859-1 (by default -intentionally to save lower byte after translation).
It is essentially a two-byte to one-byte conversion.

If there's anything in the high byte of the source (i.e >255) then a question mark glyph (3F) is used to mark unknown data because it can't find anything to map to.


Bundle Expressions

** Bundle Expressions have been removed due to a bug. Below is for reference only **

The interpreter will now accept bundle expressions of the forms;

Syntax

<nvar>.< key> simple key
or

<nvar>."<key>" quoted key

e.g
mybundle.key1
mybundle."Key 1"

The first form has no quotes surrounding the key and conforms to rules simiar to variable names;
Will be converted to lowercase,
Cannot have spaces
Must begin with a letter, but can have numbers.
The only allowable special characters are : "_"  or "@" or '#"

The second form has quotes and the key can be any string similar to bundle.put,
Is case sensitive,
Is fully compatible with bundle.put

Expression Rules

  - A bundle.key will evaluate to a value similar to: bundle.get
        e.g
            bundle.create mybundle
            bundle.put mybundle, "key1", 99            

            bundle.get mybundle, "key1", n    % old way
            n = mybundle.key1        % new way

    - A bundle.key can also be the destination of an assignment
        e.g
            bundle.create mybundle
            
            bundle.put mybundle, "key1", 98   % old way
            mybundle.key1 = 99      % new way

    - A bundle.key value can either be a number or a string
        e.g
            bundle.create mybundle
            mybundle.key1 = 99
            mybundle.key2 = "red"

            n = mybundle.key1        % value at key1 must exist and is a number
            s$= mybundle.key2        % value at key2 must exist and is a string

      If the index or key does not exist, you will get an error.

    - A bundle.key assignment can either be a number or a string
        and can also change the value type.

            bundle.create mybundle

            mybundle.key1 = n        % value is a number
            mybundle.key1 = "wow"        % value is no longer a number

    - A bundle.key will auto-create the key if the key is not found.

            bundle.create mybundle

            mybundle.newkey3 = "first value"


    - A bundle.key will NOT auto-create a new bundle if the index is not found.
        The bundle (i.e it's index) MUST exist either by creating the bundle with
        bundle.create or bundle.put. Otherwise an error occurs.
        (note: this is different to the normal bundle.put behaviour)

        e.g

            bundle.create mybundle        % mandatory

            mybundle.key2 = "red"        % assignment auto-creates key2

            print mybundle.key2        % red
            print your_bundle.key2        % ERROR


    - A numeric bundle.key can be used with pre/post inc/dec operators similar to
        variables.
        e.g
            bundle.create mybundle

            mybundle.key1 = 99
            n = mybundle.key1++        % n=99
            a = mybundle.key1        % a=100

        These operators will update the bundle value pointed to by bundle.key,
        in which the value must be numeric.

    - A bundle.key can have embedded keys i.e bundle indexes inside bundle keys (bundles within bundles). This is of the form

bundle.key1.key2.key3..

bundle is an <nvar> and it's value is the bundle index of key1.
key1's value is the bundle index of key2.
key2's value is the bundle index of key3.

key3's value will be the value of the whole expression and any update will be to the value at key3.
Only the last key (key3 in this case) can be auto-created (if it doesn't exist).

Note that all keys must be literal, they cannot be expressions.

        e.g
      bundle.create location
        location.area = "Westminster"
        location.city = "London"


        bundle.create contact
        contact.name = "John"
        contact.phone = 55556666
        contact.assigned = location

        bundle.create work
        work.sales = contact
        !work.dog = humpty

        ? work.sales.name;
        ? " will cover ";
        ? work.sales.assigned.area        % embedded keys

        ! John will cover Westminster


Known quirks
If both the index and key are bad, you might get 2 errors reported.


Array Pointers

Assignments

e.g  a[] = b[]    or    LET a[] = b[]
e.g  a$[] = b$[]

a[] = b[]

Makes an exact copy of b[] to a[] including the size and dimensions
Background references, i.e passed by referenced variables, are preserved for a[].

   
hBasic uses the term  "array pointer" (e.g a[] )as representing the whole array. An array pointer has no indices within the brackets. It is a reference to an array as opposed to just one element.

(This syntax is inline with traditional Basic 01.91 which already uses the syntax for
some commands and functions.)
   

Evaluation (place holder for future development).

Evaluation of a[] always returns 0        ( might be changed in the future )
Evaluation of a$[] always returns ""        ( might be changed in the future )
Evaluation of non-existent a[] leads to an error.

e.g PRINT a[]$, b[]
will print ",  0.0"






Array Functions

Functions can now return an Array (pointer)

        e.g

        fn.def func_a$(n)[]        % define an Array Function

            dim b$[3]
            b$[2] = "nice"

            if n=1 then fn.rtn        % returns a useless default array
            if n=2 then fn.rtn b$[]   % return specified array

        fn.end                        % returns default array

        ! fn.def func_a$()            % (error) Function previously defined at:
       
        a$[] = func_a$(99)[]          % Array function assignment (deep copy)

        ! print func_a$(99)[]         % Error> Array functions cannot be evaluated :
        print a$[2]               % only normal arrays can be evaluated with an index

  


TEXT/BYTE.OPEN change on failure

Before, if TEXT.OPEN or BYTE.OPEN fails to open a file, an unusable filetable entry was still created.
This no longer happens for v2.80+, there is no new filetable entry upon failure.


Shortcut Support

If the app is compiled as a StandAlone APK (i.e without editor), then a shortcut (on the Android home screen) can launch the app and send a string of data.

Normally this is only possible for a non-standalone (standard) APK, and the data string is limited to <something>.bas. For a Standalone APK, the shortcut is normally ignored, moreover, some apk makers might also remove the shortcut-dialog code

This feature will entertain any shortcut data string being sent by a shortcut launch and put the string into an undocumented function called COMMAND$(). For this to work, the APK maker must not remove the  shortcut dialog code (e.g hSuite will support this).

Shortcuts to standard APKs (non-standalone) will not be affected and have no effect to COMMAND$().

Note that COMMAND$() is also used for a file-share data string (e.g from a file-manager) , the difference between a shortcut and a file-share is that a file-share will prepend a relative path to a file-name. Shortcut data strings are not prepended with anything, the data string is what the user typed in and is clean. The string can be any text and can have spaces, e.g "Dark Theme".


DEVICE Fix

Ack: dkal - forum question.
DEVICE command would crash on devices with Android-10+ because Google decided not to allow access to 'Non-resettable device identifiers' which can potentially be tracked or used
for ads. So they are banned for normal apps.

The two keys which did this was 'DeviceID' and 'SIM SN

They both require READ_PRIVILEGED_PHONE_STATE or hasCarrierPrivileges, of which
normal apps/users are unlikely to get.

To fix the crash ->

If the device is < Android Q
DeviceID is supposed to return the IMEI or MEID.
SIM SN is supposed to return the card serial number.

If the device is >= Android Q,
DeviceID will return the Android_ID.
(ANDROID_ID is per device (not card) and is generated on device set-up.
It is not 100% reliable and could change on a factory reset or after an OTA update + app re-install).

SIM SN will return "Not available".
Sensors Fix

Ack: dkal,daveacre - forum question.
SENSORS command would crash on devices with Android-10+ if (internal) parameters were less than 3.
The fix code is from aFox from a github issue.