compiler/builtins/*.ts. Built-ins are core APIs like btoa, String.prototype.trim, array methods, and more.
Porffor Types
Porffor has usual JS types plus some internal types for optimization.ByteString
Most important internal type. Regular strings in Porffor are UTF-16 encoded (2 bytes per character). ByteStrings are optimized for ASCII/LATIN-1 characters, using only 1 byte per character. Benefits:- Halves memory usage for ASCII strings
- Faster operations
- Many built-ins need to be written twice (for
stringandbytestring)
i32
Use this only for pointers. It’s the Wasm valtypei32 (not explicitly signed or unsigned - that’s instruction-dependent).
Naming Conventions
Built-in functions use underscores instead of dots:__Type_prototype_method for prototype methods.
Function Signature Requirements
The _this Parameter
Since this doesn’t exist in Porffor yet, use a _this parameter:
Arrow Functions Required
Always use arrow functions for built-ins:No Return Type Annotation for Prototype Methods
Porffor-Specific TypeScript Notes
Explicit Type Annotations Required
Object Literals Need Variables
Due to precompile allocator constraints:Use Non-Strict Equality
Generally use== and != instead of === and !==:
Truthy Checks
if (...) uses a fast but non-spec-compliant truthy check. For spec-compliant behavior:
Scope Limitations
- Cannot use other functions in the file unless exported
- Cannot use variables outside the current function
- Keep everything self-contained
Working with Pointers
Pointers are essential when working with objects (arrays, strings, etc.).Get a Pointer
variable as a number instead of the object itself.
Store a Character in ByteString
Store a Character in String
Load a Character from ByteString
Load a Character from String
Set Object Length
The last two arguments in store/load functions (alignment and byte offset) are required for the Wasm instruction but you don’t need to change them.
Complete Example: ByteString.prototype.toUpperCase
Here’s a real example from Porffor:1. Function Definition
- Uses
__ByteString_prototype_toUpperCasenaming - Takes
_thisparameter (the string to convert) - Uses arrow function
- No return type annotation
2. Setup Output Variable
- Get the length (will be same as input)
- Create empty output string
- Set its length in advance (Wasm intrinsics won’t update it automatically)
3. Get Pointers
i= pointer to input stringj= pointer to output string- Both are
i32(numbers representing memory locations)
4. Loop Setup
- Calculate end pointer (start + length)
- Loop until we reach the end
- This iterates through every character
5. Read Character
- Load character code at current pointer
- Increment pointer for next iteration
- Uses
load8_ubecause it’s a ByteString (1 byte per char)
6. Convert to Uppercase
- If character code is between 97 (
a) and 122 (z) - Subtract 32 to convert to uppercase
- Example: 97 (
a) - 32 = 65 (A)
7. Write Character
- Store the (possibly converted) character to output
- Increment output pointer
- Uses
store8for ByteString
Porffor.wasm Inline Assembly
For advanced operations, you can write inline WebAssembly:local name type- Define local variablesreturns type type- Set return types (be careful - most functions needf64 i32);;- Comments in Wasm${variable}- Reference JS variables${variable+1}- Get the type of a variablei32.from,i32.to- Convert between valtype and i32 (Porffor custom instructions)
Best Practices
- Avoid heavy object/string/array code - Use more primitive variables when possible (better for memory and performance)
- Don’t worry about formatting - Focus on correct code; formatting can be cleaned up later
-
Look at existing built-ins - The
compiler/builtins/files are full of examples -
Test frequently - Use
./porf precompileand test after each change - Ask for help - Join the Discord if you get stuck!
Resources
- MDN - High-level descriptions and spec links
- WebAssembly Opcodes - Wasm instruction reference
- Porffor Discord - Ask questions
Next Steps
- Learn about Testing your built-ins
- Follow Commit Style conventions
- Review Project Structure