QBJS Quirks Cheat Sheet

I’m adding this to share my thoughts, especially the quirks that QBJS 0.10.0 has at the time of writing.

Constants

All consts in QBJS should not have sigil suffixes:

const maxHealth = 100
const vgaWidth = 320

' Valid in QB64 but not in QBJS
const maxHealth% = 100
const vgaWidth& = 320

The interpreter will throw an error if you add a suffix. An example error message is something like this:

ERROR : 0 : Missing initializer in const declaration

This is due to how dynamic typing is implemented in its backend language (JS)

Variables

This of QBJS as a QB64-to-JS transpiler with the full power of JS

This makes dynamic typing possible, which is similar to Lua

Known Issue

Some variables with the system prefix may cause the transpiler to break

For example, with the name systemFont being used as a variable:

dim as TBMFont systemFont

This will cause the transpiler to sometimes produce an incomplete object string in JS, but this only happens occasionally

If this happens, just reorder your variable declarations — there may be occasional ordering-dependent bugs in the QBJS-to-JS transpiler

Image Handling

Image handles start at 1000 instead of a negative value

With the same example code

Option _Explicit

Dim As Long imgGasolineMaid
imgGasolineMaid = _LoadImage("IMG\gasoline_maid_256px.png")

Print "image handle:", imgGasolineMaid

_FreeImage imgGasolineMaid

This is the comparison:
QB64: image handle: -10
QBJS: image handle: 1000

Non-existent image

Loading a non-existent image is also possible, which is dangerous:

Option _Explicit

Dim As Long imgInvalid
imgInvalid = _loadImage("invalid_image.jpg")
Print "image handle:", imgInvalid

Output (QBJS):

image handle: 1000

This can be prevented with the _FileExists statement

Option _Explicit

Dim As Long imgInvalid
If Not _FileExists("invalid_image.jpg") Then
  Print "Invalid image doesn't exist!"
Else
  imgInvalid = _LoadImage("invalid_image.jpg")
  Print "image handle:", imgInvalid
End If

Output:

Invalid image doesn't exist!

Precompiler

$Let doesn’t work with QBJS

$If precompiler only works with either:

  • $If WEB then
  • $If Javascript then

Use $If WEB to use the functionalities that only exist in QBJS

Option _Explicit

$if WEB then
import console from "lib\web\console.bas"
console.log "Hello QBJS!"

$else
print "Hello QB64!"
$endif

Use $If Javascript block to execute any JS code in it:

Option _Explicit

Dim As Long a, b, c
$if javascript then
  a = 1
  b = 2
  c = a + b
$endif
print c

Output:

3

This makes it possible to use fetch API and BigInt along with QB64

Console / Debugging

$Debug and any Log statements (_LogError, _LogWarn, _LogTrace, and so on) don’t work in QBJS

Instead, use the Console import

Option _Explicit

$if web then
import console from "lib\web\console.bas"
console.log "Hello QBJS!"
console.warn "This is a warning"
console.error "This is an error log"
$endif

This makes it possible to use console in QBJS, but since everything is async under the hood, using console.log in Javascript context will throw an error

Function Return Value

Since QBJS uses JavaScript as its backend, it can return an object, or even a UDT

Option _Explicit

Type TCat
  name As String
  colour As String
End Type

Dim cat As TCat
cat = newCat("Honey", "orange")
Print cat.name, cat.colour

' implementation
Function newCat(name$, colour$)
  Dim c As TCat
  c.name = name$
  c.colour = colour$
  newCat = c
End Function

This makes it similar to VBScript but with full browser support

Module System

QBJS is more like the OOP in JS, which makes it also similar to Lua

MAIN.BAS

import console from "lib\web\console.bas"
import YourModule from "modules\hi.bas"

YourModule.sayHi
print YourModule.add(2, 3)

MODULES\HI.BAS

option _explicit

export sayHi, add

sub sayHi
  print "Hi from another module!"
end sub

function add(a, b)
  add = a + b
end function

JavaScript Interop

(TBA)

Sound Handling

(TBA)

MapTriangle

It’s not possible to use _MapTriangle statement in QBJS, so drawing a rotated image + scaled image is still a dream at the moment of writing

ByRef or ByVal?

QBJS always uses pass-by-value instead of pass-by-reference like what QB64 uses

Therefore, loadImage in QBJS returns a value instead of assigning to the target image handle

Boolean value

The values true & false can be used immediately from JS

The AND, OR, and NOT operators in QBJS use JS bitwise operators under the hood (Ref: https://qb64phoenix.com/forum/showthread.php?tid=3376)

import console from "lib\web\console.bas"

console.log "What is TRUE? " + i32str(1 > 0)
console.log "What is FALSE? " + i32str(1 < 0)

true is 1, false is 0

This makes casting to strings can be easier instead of using the usual STR$

console.log "What is TRUE? " + (1 > 0)

Not operator in QBJS

The transpiler uses the ~ operator under the hood, which basically flips all the bits in the 64-bit integer. This makes it harder to track a boolean value & an actual integer

So, instead of using the usual

If Not isImageSet(imgHandle) Then Exit Sub

It’s better to use:

If isImageSet(imgHandle) = false Then Exit Sub

But in my case of returning an integer (supposedly), I’ll use an integer return value instead of a boolean. Something like this:

Function isImageSet% (imgHandle As Long)
  isImageSet = -(imgHandle >= 1000)
End Function

true is 1 in JS, while false is the same: 0

About _PutImage

Compared to QB64, it uses destX + destW - 1 and also destY + destH - 1 because of how the decrement is handled automatically under the hood

So this is the acceptable version in QBJS:

Sub sprRegion (imgHandle As Long, srcX%, srcY%, srcW%, srcH%, destX%, destY%)
  _PutImage (destX%, destY%)-(destX% + srcW%, destY% + srcH%), imgHandle, , (srcX%, srcY%)-(srcX% + srcW%, srcY% + srcH%)
End Sub

About copying a UDT

UDT’s (User-defined Types) in QBJS are basically just a JS object

It doesn’t support copying as if it’s a record in Pascal, since the object reference remains the same

I found this problem when calling loadBMFont, but the output is just ~~~~~~, while it should be something like "Hello QBJS!" but with PICO-8 font

So, instead of declaring tempGlyph at the beginning like what I usually do in Pascal, something like this:

sub loadBMFont ' procedure signature
  dim as TBMFontGlyph tempGlyph
  ' the rest of the implementation
end sub

It’s better to declare the variable inside the block it’s supposed to be

elseif startsWith(txtLine, "char") and not startsWith(txtLine, "chars") then
  dim as TBMFontGlyph tempGlyph
  ' the rest of the implementation
end if

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *