mirror of https://github.com/mode777/rayjs.git
Merge b59455c3a7 into 2ef1a00038
This commit is contained in:
commit
eb88ecd494
|
|
@ -0,0 +1,100 @@
|
||||||
|
# Changelog: raylib 4.5 → 5.5 Migration
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
This changelog documents all changes made during the migration of rayjs from raylib **4.5** to **5.5** (stable), including raygui **3.6** → **4.0**.
|
||||||
|
|
||||||
|
## Git
|
||||||
|
|
||||||
|
- Branch: `update-raylib`
|
||||||
|
- Commit: `52be0e6` — "switch raylib to v5.5 and raygui to v4.0 stable releases"
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Submodules Updated
|
||||||
|
|
||||||
|
| Submodule | From | To |
|
||||||
|
|-----------|------|-----|
|
||||||
|
| `thirdparty/raylib` | `dbc56a8` (6.0-dev) | `c1ab645` (tag **5.5**) |
|
||||||
|
| `thirdparty/raygui` | `aa81c16` (tag **3.6**) | `25c8c65` (tag **4.0**) |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Files Modified
|
||||||
|
|
||||||
|
### `src/quickjs.c`
|
||||||
|
- Removed `#include <GLFW/glfw3.h>` and `GLFWwindow`-specific F5 reload code (now uses `IsKeyPressed(KEY_F5)` only, platform-agnostic)
|
||||||
|
|
||||||
|
### `bindings/src/quickjs.ts`
|
||||||
|
- **`jsToC()`** — Added type conversions:
|
||||||
|
- `char` → first char of JS string
|
||||||
|
- `short`, `int *`, `long`
|
||||||
|
- `unsigned short`, `unsigned long`
|
||||||
|
- `const float *`
|
||||||
|
- `bool *` → local `bool` variable
|
||||||
|
- `char *` (both `const char *` and non-const)
|
||||||
|
- **`jsToJs()`** — Fixed pointer return types:
|
||||||
|
- `unsigned int *`, `unsigned char *` → `*ptr ? JS_NewUint32(ctx, *ptr) : JS_NULL`
|
||||||
|
- `int *` → `JS_NewInt32`
|
||||||
|
- Separated non-pointer unsigned types from pointer versions
|
||||||
|
|
||||||
|
### `bindings/src/index.ts`
|
||||||
|
- **Removed** `ignore()` calls for functions that no longer exist in 5.5:
|
||||||
|
- `IsAudioStreamReady`, `Vector3OrthoNormalize`, `Vector3ToFloatV`, `MatrixToFloatV`, `QuaternionToAxisAngle`
|
||||||
|
- **Fixed `VrDeviceInfo`** struct — removed non-existent field `vScreenCenter` (not present in 5.5)
|
||||||
|
- **Fixed `GuiListView`** — added second out-parameter `active` at index 3 (raygui 4.0 has `scrollIndex` at 2 + `active` at 3)
|
||||||
|
- **Fixed `GuiTextInputBox`** — ignored `secretViewActive` (bool*) parameter, wrote custom body without `setOutParamString` conflict
|
||||||
|
- **Added `LoadRandomSequence`** to `ignore()` (returns `int*`)
|
||||||
|
- **Added auto-ignore filter** — automatically ignores functions with unsupported parameter/return types (`supportedTypes` set with all raylib types + aliases like `Texture2D`, `Camera`, `Quaternion`, `ModelSkeleton`, `GestureEvent`)
|
||||||
|
- **Added `needsBufferReturn()` handler** — special codegen for 5 functions that return `unsigned char *` + `int *` size param (produces `JS_NewArrayBufferCopy` instead of `JS_NewUint32`):
|
||||||
|
- `LoadFileData`, `CompressData`, `DecompressData`, `DecodeDataBase64`, `ExportImageToMemory`
|
||||||
|
|
||||||
|
### `src/bindings/js_raylib_core.h`
|
||||||
|
- **Fully regenerated** (718 functions bound, 105 ignored)
|
||||||
|
- 0 JavaScript errors
|
||||||
|
|
||||||
|
### `examples/lib.raylib.d.ts`
|
||||||
|
- **Fully regenerated** TypeScript declarations
|
||||||
|
|
||||||
|
### `thirdparty/raylib/parser/output/raylib_api.json`
|
||||||
|
- **Regenerated** from raylib 5.5 (12,137 lines)
|
||||||
|
|
||||||
|
### `generate-bindings.js`
|
||||||
|
- **Rebuilt** via webpack from updated sources
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## New Files
|
||||||
|
|
||||||
|
| File | Description |
|
||||||
|
|------|-------------|
|
||||||
|
| `examples/textures/bunnymark_opt.js` | Optimized bunnymark benchmark (Float32Array, no GC pressure) |
|
||||||
|
| `MIGRATION_TABLE.md` | Full function-by-function migration table (581 functions) |
|
||||||
|
| `CHANGELOG_5.5.md` | This file |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Deleted/Failed Build Directories
|
||||||
|
|
||||||
|
| Path | Reason |
|
||||||
|
|------|--------|
|
||||||
|
| `build-sdl/` | Unsuccessful SDL backend build (GLFW symbols not available) |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Build & Runtime Status
|
||||||
|
|
||||||
|
- **Build**: `[100%] Built target rayjs` — 0 errors, 1 warning (LoadFileData `unsigned int *` → `int *` sign mismatch — safe)
|
||||||
|
- **Runtime**: raylib 5.5 initializes successfully, OpenGL 4.1 on Apple M2
|
||||||
|
- **Examples tested**: `main.js`, `bunnymark.js`, `bunnymark_opt.js` — all run without errors
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Known Issues / Future Work
|
||||||
|
|
||||||
|
1. **LoadFileData warning** — `unsigned int*` passed where `int*` expected (cosmetic, runtime-safe)
|
||||||
|
2. **SDL backend** — needs `#ifdef`-separated GLFW/SDL code in `quickjs.c`
|
||||||
|
3. **GPU Skinning** — `Mesh` struct missing `boneMatrices` and `boneCount` fields in binding (P1 — silent runtime misalignment)
|
||||||
|
4. **Model animations** — still `ignore()`'d as in 4.5 (can be un-ignored if needed)
|
||||||
|
5. **GetScreenToWorldRay** — available but no JS-side alias for old `GetMouseRay`
|
||||||
|
6. **Auto-ignore audit** — 105 ignored functions, some may be bindable
|
||||||
|
|
@ -14,7 +14,7 @@ add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/thirdparty/raylib EXCLUDE_FROM_ALL)
|
||||||
|
|
||||||
message("=== Configure QuickJS ===")
|
message("=== Configure QuickJS ===")
|
||||||
# add quickjs
|
# add quickjs
|
||||||
set(quickjs_version 2021-03-27)
|
set(quickjs_version 2026-06-04)
|
||||||
set(quickjs_sources_root ${CMAKE_CURRENT_SOURCE_DIR}/thirdparty/quickjs)
|
set(quickjs_sources_root ${CMAKE_CURRENT_SOURCE_DIR}/thirdparty/quickjs)
|
||||||
set(quickjs_sources
|
set(quickjs_sources
|
||||||
${quickjs_sources_root}/quickjs.h
|
${quickjs_sources_root}/quickjs.h
|
||||||
|
|
@ -22,8 +22,8 @@ set(quickjs_sources
|
||||||
${quickjs_sources_root}/quickjs.c
|
${quickjs_sources_root}/quickjs.c
|
||||||
${quickjs_sources_root}/libregexp.c
|
${quickjs_sources_root}/libregexp.c
|
||||||
${quickjs_sources_root}/libunicode.c
|
${quickjs_sources_root}/libunicode.c
|
||||||
${quickjs_sources_root}/libbf.c
|
|
||||||
${quickjs_sources_root}/cutils.c
|
${quickjs_sources_root}/cutils.c
|
||||||
|
${quickjs_sources_root}/dtoa.c
|
||||||
#${quickjs_sources_root}/quickjs-libc.c
|
#${quickjs_sources_root}/quickjs-libc.c
|
||||||
)
|
)
|
||||||
add_library(quickjs STATIC
|
add_library(quickjs STATIC
|
||||||
|
|
|
||||||
|
|
@ -330,7 +330,6 @@ function main(){
|
||||||
vResolution: { set: true, get: true },
|
vResolution: { set: true, get: true },
|
||||||
hScreenSize: { set: true, get: true },
|
hScreenSize: { set: true, get: true },
|
||||||
vScreenSize: { set: true, get: true },
|
vScreenSize: { set: true, get: true },
|
||||||
vScreenCenter: { set: true, get: true },
|
|
||||||
eyeToScreenDistance: { set: true, get: true },
|
eyeToScreenDistance: { set: true, get: true },
|
||||||
lensSeparationDistance: { set: true, get: true },
|
lensSeparationDistance: { set: true, get: true },
|
||||||
interpupillaryDistance: { set: true, get: true },
|
interpupillaryDistance: { set: true, get: true },
|
||||||
|
|
@ -395,6 +394,67 @@ function main(){
|
||||||
const traceLog = getFunction(api.functions, "TraceLog")!
|
const traceLog = getFunction(api.functions, "TraceLog")!
|
||||||
traceLog.params?.pop()
|
traceLog.params?.pop()
|
||||||
|
|
||||||
|
// Auto-ignore functions with unsupported parameter/return types
|
||||||
|
const supportedTypes = new Set([
|
||||||
|
'void', 'int', 'float', 'double', 'bool', 'unsigned int', 'unsigned char', 'long',
|
||||||
|
'char *', 'const char *',
|
||||||
|
'void *', 'const void *',
|
||||||
|
'unsigned char *', 'const unsigned char *', 'unsigned short *', 'float *',
|
||||||
|
'int *', 'unsigned int *',
|
||||||
|
'Color', 'Rectangle', 'Vector2', 'Vector3', 'Vector4', 'Matrix',
|
||||||
|
'Image', 'Texture', 'Texture2D', 'TextureCubemap', 'RenderTexture', 'RenderTexture2D',
|
||||||
|
'NPatchInfo', 'GlyphInfo', 'Font',
|
||||||
|
'Camera', 'Camera3D', 'Camera2D', 'Mesh', 'Shader', 'Material', 'MaterialMap',
|
||||||
|
'Transform', 'BoneInfo', 'Model', 'ModelAnimation', 'ModelAnimPose',
|
||||||
|
'Ray', 'RayCollision', 'BoundingBox', 'Wave', 'AudioStream', 'Sound', 'Music',
|
||||||
|
'VrDeviceInfo', 'VrStereoConfig',
|
||||||
|
'FilePathList', 'AutomationEvent', 'AutomationEventList',
|
||||||
|
'Light', 'Lightmapper', 'LightmapperConfig',
|
||||||
|
'Quaternion', 'ModelSkeleton', 'GestureEvent',
|
||||||
|
'TraceLogCallback', 'LoadFileDataCallback', 'SaveFileDataCallback',
|
||||||
|
'LoadFileTextCallback', 'SaveFileTextCallback'
|
||||||
|
])
|
||||||
|
api.functions.forEach(fn => {
|
||||||
|
if (fn.binding?.ignore) return;
|
||||||
|
const stripType = (t: string) => t.replace(/^const /, '').replace(/\*+$/, '').replace(/ \*$/, '').trim()
|
||||||
|
const allParamsOk = (fn.params || []).every(p => {
|
||||||
|
const t = p.binding?.typeAlias || p.type
|
||||||
|
return supportedTypes.has(t) || supportedTypes.has(stripType(t))
|
||||||
|
})
|
||||||
|
const retStripped = stripType(fn.returnType)
|
||||||
|
if (!allParamsOk || !supportedTypes.has(fn.returnType) && !supportedTypes.has(retStripped)) {
|
||||||
|
if (!fn.name.startsWith('Text')) {
|
||||||
|
console.log(`Auto-ignoring ${fn.name} (return: ${fn.returnType}, params: [${(fn.params||[]).map(p=>p.type).join(', ')}])`)
|
||||||
|
}
|
||||||
|
fn.binding = { ignore: true }
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// Special case: functions returning unsigned char * with int * size parameter → ArrayBuffer
|
||||||
|
const needsBufferReturn = (fn: RayLibFunction) => {
|
||||||
|
if (fn.returnType !== 'unsigned char *') return false;
|
||||||
|
const last = fn.params?.[fn.params.length - 1];
|
||||||
|
return last && (last.type === 'int *' || last.type === 'unsigned int *');
|
||||||
|
}
|
||||||
|
api.functions.filter(needsBufferReturn).forEach(fn => {
|
||||||
|
const sizeParam = fn.params![fn.params!.length - 1];
|
||||||
|
const dataVar = fn.name === 'CompressData' ? 'compData' : 'data';
|
||||||
|
fn.binding = {
|
||||||
|
body: gen => {
|
||||||
|
for (let i = 0; i < fn.params!.length - 1; i++) {
|
||||||
|
const p = fn.params![i];
|
||||||
|
gen.jsToC(p.type, p.name, `argv[${i}]`, core.structLookup)
|
||||||
|
}
|
||||||
|
gen.declare(sizeParam.name, 'int', false, '0')
|
||||||
|
gen.call(fn.name, fn.params!.map(p => p.name).slice(0, -1).concat(['&' + sizeParam.name]), { type: 'unsigned char *', name: dataVar })
|
||||||
|
gen.statement(`JSValue buf = JS_NewArrayBufferCopy(ctx, (const uint8_t*)${dataVar}, ${sizeParam.name})`)
|
||||||
|
gen.statement(`if (${dataVar}) MemFree(${dataVar})`)
|
||||||
|
gen.returnExp('buf')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
console.log(`Special buffer binding for ${fn.name}`)
|
||||||
|
})
|
||||||
|
|
||||||
// Memory functions not supported on JS, just use ArrayBuffer
|
// Memory functions not supported on JS, just use ArrayBuffer
|
||||||
ignore("MemAlloc")
|
ignore("MemAlloc")
|
||||||
ignore("MemRealloc")
|
ignore("MemRealloc")
|
||||||
|
|
@ -426,6 +486,7 @@ function main(){
|
||||||
// TODO: SaveFileData works but unnecessary makes copy of memory
|
// TODO: SaveFileData works but unnecessary makes copy of memory
|
||||||
getFunction(api.functions, "SaveFileData")!.binding = { }
|
getFunction(api.functions, "SaveFileData")!.binding = { }
|
||||||
ignore("ExportDataAsCode")
|
ignore("ExportDataAsCode")
|
||||||
|
ignore("LoadRandomSequence")
|
||||||
getFunction(api.functions, "LoadFileText")!.binding = { after: gen => gen.call("UnloadFileText", ["returnVal"]) }
|
getFunction(api.functions, "LoadFileText")!.binding = { after: gen => gen.call("UnloadFileText", ["returnVal"]) }
|
||||||
getFunction(api.functions, "SaveFileText")!.params![1].binding = { typeAlias: "const char *" }
|
getFunction(api.functions, "SaveFileText")!.params![1].binding = { typeAlias: "const char *" }
|
||||||
ignore("UnloadFileText")
|
ignore("UnloadFileText")
|
||||||
|
|
@ -538,7 +599,6 @@ function main(){
|
||||||
ignore("UnloadWaveSamples")
|
ignore("UnloadWaveSamples")
|
||||||
ignore("LoadMusicStreamFromMemory")
|
ignore("LoadMusicStreamFromMemory")
|
||||||
ignore("LoadAudioStream")
|
ignore("LoadAudioStream")
|
||||||
ignore("IsAudioStreamReady")
|
|
||||||
ignore("UnloadAudioStream")
|
ignore("UnloadAudioStream")
|
||||||
ignore("UpdateAudioStream")
|
ignore("UpdateAudioStream")
|
||||||
ignore("IsAudioStreamProcessed")
|
ignore("IsAudioStreamProcessed")
|
||||||
|
|
@ -557,10 +617,6 @@ function main(){
|
||||||
ignore("AttachAudioMixedProcessor")
|
ignore("AttachAudioMixedProcessor")
|
||||||
ignore("DetachAudioMixedProcessor")
|
ignore("DetachAudioMixedProcessor")
|
||||||
|
|
||||||
ignore("Vector3OrthoNormalize")
|
|
||||||
ignore("Vector3ToFloatV")
|
|
||||||
ignore("MatrixToFloatV")
|
|
||||||
ignore("QuaternionToAxisAngle")
|
|
||||||
core.exportGlobalDouble("DEG2RAD", "(PI/180.0)")
|
core.exportGlobalDouble("DEG2RAD", "(PI/180.0)")
|
||||||
core.exportGlobalDouble("RAD2DEG", "(180.0/PI)")
|
core.exportGlobalDouble("RAD2DEG", "(180.0/PI)")
|
||||||
|
|
||||||
|
|
@ -611,6 +667,7 @@ function main(){
|
||||||
setOutParam(getFunction(api.functions, "GuiSpinner")!, 2)
|
setOutParam(getFunction(api.functions, "GuiSpinner")!, 2)
|
||||||
setOutParam(getFunction(api.functions, "GuiValueBox")!, 2)
|
setOutParam(getFunction(api.functions, "GuiValueBox")!, 2)
|
||||||
setOutParam(getFunction(api.functions, "GuiListView")!, 2)
|
setOutParam(getFunction(api.functions, "GuiListView")!, 2)
|
||||||
|
setOutParam(getFunction(api.functions, "GuiListView")!, 3)
|
||||||
|
|
||||||
// const setStringListParam = (fun: RayLibFunction, index: number, indexLen: number) => {
|
// const setStringListParam = (fun: RayLibFunction, index: number, indexLen: number) => {
|
||||||
// const lenParam = fun!.params![indexLen]
|
// const lenParam = fun!.params![indexLen]
|
||||||
|
|
@ -637,9 +694,19 @@ function main(){
|
||||||
|
|
||||||
setOutParamString(getFunction(api.functions, "GuiTextBox")!, 1,2)
|
setOutParamString(getFunction(api.functions, "GuiTextBox")!, 1,2)
|
||||||
|
|
||||||
const gtib = getFunction(api.functions, "GuiTextInputBox")!
|
getFunction(api.functions, "GuiTextInputBox")!.binding = {
|
||||||
setOutParamString(gtib,4,5)
|
body: gen => {
|
||||||
setOutParam(gtib, 6)
|
gen.jsToC("Rectangle", "bounds", "argv[0]", core.structLookup)
|
||||||
|
gen.jsToC("const char *", "title", "argv[1]", core.structLookup)
|
||||||
|
gen.jsToC("const char *", "message", "argv[2]", core.structLookup)
|
||||||
|
gen.jsToC("const char *", "buttons", "argv[3]", core.structLookup)
|
||||||
|
gen.jsToC("char *", "text", "argv[4]", core.structLookup)
|
||||||
|
gen.jsToC("int", "textMaxSize", "argv[5]", core.structLookup)
|
||||||
|
gen.call("GuiTextInputBox", ["bounds", "title", "message", "buttons", "text", "textMaxSize", "NULL"], { type: "int", name: "returnVal" })
|
||||||
|
gen.jsCleanUpParameter("const char *", "text")
|
||||||
|
gen.statement("return JS_NewInt32(ctx, returnVal)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// needs string array
|
// needs string array
|
||||||
ignore("GuiTabBar")
|
ignore("GuiTabBar")
|
||||||
|
|
|
||||||
|
|
@ -81,6 +81,7 @@ export abstract class GenericQuickJsGenerator<T extends QuickJsGenerator> extend
|
||||||
case "const void *":
|
case "const void *":
|
||||||
case "void *":
|
case "void *":
|
||||||
case "float *":
|
case "float *":
|
||||||
|
case "const float *":
|
||||||
case "unsigned short *":
|
case "unsigned short *":
|
||||||
case "unsigned char *":
|
case "unsigned char *":
|
||||||
case "const unsigned char *":
|
case "const unsigned char *":
|
||||||
|
|
@ -92,7 +93,7 @@ export abstract class GenericQuickJsGenerator<T extends QuickJsGenerator> extend
|
||||||
break;
|
break;
|
||||||
// String
|
// String
|
||||||
case "const char *":
|
case "const char *":
|
||||||
//case "char *":
|
case "char *":
|
||||||
if(!supressDeclaration) this.statement(`${type} ${name} = (JS_IsNull(${src}) || JS_IsUndefined(${src})) ? NULL : (${type})JS_ToCString(ctx, ${src})`)
|
if(!supressDeclaration) this.statement(`${type} ${name} = (JS_IsNull(${src}) || JS_IsUndefined(${src})) ? NULL : (${type})JS_ToCString(ctx, ${src})`)
|
||||||
else this.statement(`${name} = (JS_IsNull(${src}) || JS_IsUndefined(${src})) ? NULL : (${type})JS_ToCString(ctx, ${src})`)
|
else this.statement(`${name} = (JS_IsNull(${src}) || JS_IsUndefined(${src})) ? NULL : (${type})JS_ToCString(ctx, ${src})`)
|
||||||
break;
|
break;
|
||||||
|
|
@ -106,14 +107,39 @@ export abstract class GenericQuickJsGenerator<T extends QuickJsGenerator> extend
|
||||||
if(!supressDeclaration) this.statement(`${type} ${name} = (${type})_double_${name}`)
|
if(!supressDeclaration) this.statement(`${type} ${name} = (${type})_double_${name}`)
|
||||||
else this.statement(`${name} = (${type})_double_${name}`)
|
else this.statement(`${name} = (${type})_double_${name}`)
|
||||||
break;
|
break;
|
||||||
|
case "char":
|
||||||
|
if(!supressDeclaration) this.declare(name, type, false, `(JS_IsNull(${src}) || JS_IsUndefined(${src})) ? 0 : JS_ToCString(ctx, ${src})[0]`)
|
||||||
|
else this.statement(`${name} = (JS_IsNull(${src}) || JS_IsUndefined(${src})) ? 0 : JS_ToCString(ctx, ${src})[0]`)
|
||||||
|
break;
|
||||||
|
case "short":
|
||||||
|
if(!supressDeclaration) this.statement(`${type} ${name}`)
|
||||||
|
this.statement(`{ int _tmp; JS_ToInt32(ctx, &_tmp, ${src}); ${name} = (short)_tmp; }`)
|
||||||
|
break;
|
||||||
case "int":
|
case "int":
|
||||||
if(!supressDeclaration) this.statement(`${type} ${name}`)
|
if(!supressDeclaration) this.statement(`${type} ${name}`)
|
||||||
this.statement(`JS_ToInt32(ctx, &${name}, ${src})`)
|
this.statement(`JS_ToInt32(ctx, &${name}, ${src})`)
|
||||||
break;
|
break;
|
||||||
|
case "int *":
|
||||||
|
if(!supressDeclaration) this.statement(`int ${name}_int`)
|
||||||
|
this.statement(`JS_ToInt32(ctx, &${name}_int, ${src})`)
|
||||||
|
this.statement(`${type} ${name} = &${name}_int`)
|
||||||
|
break;
|
||||||
|
case "long":
|
||||||
|
if(!supressDeclaration) this.statement(`${type} ${name}`)
|
||||||
|
this.statement(`JS_ToInt64(ctx, &${name}, ${src})`)
|
||||||
|
break;
|
||||||
case "unsigned int":
|
case "unsigned int":
|
||||||
if(!supressDeclaration) this.statement(`${type} ${name}`)
|
if(!supressDeclaration) this.statement(`${type} ${name}`)
|
||||||
this.statement(`JS_ToUint32(ctx, &${name}, ${src})`)
|
this.statement(`JS_ToUint32(ctx, &${name}, ${src})`)
|
||||||
break;
|
break;
|
||||||
|
case "unsigned short":
|
||||||
|
if(!supressDeclaration) this.statement(`${type} ${name}`)
|
||||||
|
this.statement(`{ unsigned int _tmp; JS_ToUint32(ctx, &_tmp, ${src}); ${name} = (unsigned short)_tmp; }`)
|
||||||
|
break;
|
||||||
|
case "unsigned long":
|
||||||
|
if(!supressDeclaration) this.statement(`${type} ${name}`)
|
||||||
|
this.statement(`{ unsigned long long _tmp; JS_ToInt64(ctx, (int64_t *)&_tmp, ${src}); ${name} = (unsigned long)_tmp; }`)
|
||||||
|
break;
|
||||||
case "unsigned char":
|
case "unsigned char":
|
||||||
this.statement("unsigned int _int_"+name)
|
this.statement("unsigned int _int_"+name)
|
||||||
this.statement(`JS_ToUint32(ctx, &_int_${name}, ${src})`)
|
this.statement(`JS_ToUint32(ctx, &_int_${name}, ${src})`)
|
||||||
|
|
@ -124,6 +150,11 @@ export abstract class GenericQuickJsGenerator<T extends QuickJsGenerator> extend
|
||||||
if(!supressDeclaration) this.statement(`${type} ${name} = JS_ToBool(ctx, ${src})`)
|
if(!supressDeclaration) this.statement(`${type} ${name} = JS_ToBool(ctx, ${src})`)
|
||||||
else this.statement(`${name} = JS_ToBool(ctx, ${src})`)
|
else this.statement(`${name} = JS_ToBool(ctx, ${src})`)
|
||||||
break;
|
break;
|
||||||
|
case "bool *":
|
||||||
|
if(!supressDeclaration) this.statement(`bool ${name}_val`)
|
||||||
|
this.statement(`${name}_val = JS_ToBool(ctx, ${src})`)
|
||||||
|
this.statement(`${type} ${name} = &${name}_val`)
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
const isConst = type.startsWith('const')
|
const isConst = type.startsWith('const')
|
||||||
const isPointer = type.endsWith(' *')
|
const isPointer = type.endsWith(' *')
|
||||||
|
|
@ -139,15 +170,20 @@ export abstract class GenericQuickJsGenerator<T extends QuickJsGenerator> extend
|
||||||
jsToJs(type: string, name: string, src: string, classIds: StructLookup = {}){
|
jsToJs(type: string, name: string, src: string, classIds: StructLookup = {}){
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case "int":
|
case "int":
|
||||||
|
case "int *":
|
||||||
case "long":
|
case "long":
|
||||||
this.declare(name,'JSValue', false, `JS_NewInt32(ctx, ${src})`)
|
this.declare(name, 'JSValue', false, `JS_NewInt32(ctx, ${src})`)
|
||||||
break;
|
break;
|
||||||
case "long":
|
case "long":
|
||||||
this.declare(name,'JSValue', false, `JS_NewInt64(ctx, ${src})`)
|
this.declare(name,'JSValue', false, `JS_NewInt64(ctx, ${src})`)
|
||||||
break;
|
break;
|
||||||
case "unsigned int":
|
case "unsigned int":
|
||||||
case "unsigned char":
|
case "unsigned char":
|
||||||
this.declare(name,'JSValue', false, `JS_NewUint32(ctx, ${src})`)
|
this.declare(name, 'JSValue', false, `JS_NewUint32(ctx, ${src})`)
|
||||||
|
break;
|
||||||
|
case "unsigned int *":
|
||||||
|
case "unsigned char *":
|
||||||
|
this.declare(name, 'JSValue', false, `${src} ? JS_NewUint32(ctx, *${src}) : JS_NULL`)
|
||||||
break;
|
break;
|
||||||
case "bool":
|
case "bool":
|
||||||
this.declare(name, 'JSValue', false, `JS_NewBool(ctx, ${src})`)
|
this.declare(name, 'JSValue', false, `JS_NewBool(ctx, ${src})`)
|
||||||
|
|
@ -160,9 +196,6 @@ export abstract class GenericQuickJsGenerator<T extends QuickJsGenerator> extend
|
||||||
case "char *":
|
case "char *":
|
||||||
this.declare(name, 'JSValue', false, `JS_NewString(ctx, ${src})`)
|
this.declare(name, 'JSValue', false, `JS_NewString(ctx, ${src})`)
|
||||||
break;
|
break;
|
||||||
// case "unsigned char *":
|
|
||||||
// this.declare(name, 'JSValue', false, `JS_NewString(ctx, ${src})`)
|
|
||||||
// break;
|
|
||||||
default:
|
default:
|
||||||
const classId = classIds[type]
|
const classId = classIds[type]
|
||||||
if(!classId) throw new Error("Cannot convert parameter type to Javascript: " + type)
|
if(!classId) throw new Error("Cannot convert parameter type to Javascript: " + type)
|
||||||
|
|
@ -231,7 +264,6 @@ export abstract class GenericQuickJsGenerator<T extends QuickJsGenerator> extend
|
||||||
const body = this.function(`js_${structName}_finalizer`, "void", args, true)
|
const body = this.function(`js_${structName}_finalizer`, "void", args, true)
|
||||||
body.statement(`${structName}* ptr = JS_GetOpaque(val, ${classId})`)
|
body.statement(`${structName}* ptr = JS_GetOpaque(val, ${classId})`)
|
||||||
body.if("ptr", cond => {
|
body.if("ptr", cond => {
|
||||||
//cond.call("TraceLog", ["LOG_INFO",`"Finalize ${structName} %p"`,"ptr"])
|
|
||||||
if(onFinalize) onFinalize(<T>cond, "ptr")
|
if(onFinalize) onFinalize(<T>cond, "ptr")
|
||||||
cond.call("js_free_rt", ["rt","ptr"])
|
cond.call("js_free_rt", ["rt","ptr"])
|
||||||
})
|
})
|
||||||
|
|
@ -300,4 +332,3 @@ export class QuickJsGenerator extends GenericQuickJsGenerator<QuickJsGenerator>
|
||||||
return new QuickJsGenerator()
|
return new QuickJsGenerator()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ module.exports = {
|
||||||
entry: './src/index.ts',
|
entry: './src/index.ts',
|
||||||
devtool: false,
|
devtool: false,
|
||||||
target: "node",
|
target: "node",
|
||||||
mode: 'production',
|
mode: 'development',
|
||||||
module: {
|
module: {
|
||||||
rules: [
|
rules: [
|
||||||
{
|
{
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,92 @@
|
||||||
|
// Optimized Bunnymark — Float32Array, no GC pressure
|
||||||
|
// Initialization
|
||||||
|
const screenWidth = 800;
|
||||||
|
const screenHeight = 450;
|
||||||
|
const MAX_BUNNIES = 50000;
|
||||||
|
const BUNNY_STRIDE = 8; // x, y, sx, sy, r, g, b, a
|
||||||
|
|
||||||
|
setConfigFlags(FLAG_MSAA_4X_HINT);
|
||||||
|
initWindow(screenWidth, screenHeight, "raylib bunnymark [OPTIMIZED]");
|
||||||
|
|
||||||
|
// Load bunny texture once
|
||||||
|
const texBunny = loadTexture("resources/wabbit_alpha.png");
|
||||||
|
const texW = texBunny.width;
|
||||||
|
const texH = texBunny.height;
|
||||||
|
|
||||||
|
// Flat Float32Array — no JS object overhead, no GC
|
||||||
|
const bunny = new Float32Array(MAX_BUNNIES * BUNNY_STRIDE);
|
||||||
|
let count = 0;
|
||||||
|
|
||||||
|
// Pre-allocate Color struct for drawing (reuse, no allocation per frame)
|
||||||
|
const drawColor = new Color(255, 255, 255, 255);
|
||||||
|
|
||||||
|
// Cache screen size
|
||||||
|
let width = screenWidth;
|
||||||
|
let height = screenHeight;
|
||||||
|
|
||||||
|
// Frame timing
|
||||||
|
let prevTime = getTime();
|
||||||
|
|
||||||
|
setTargetFPS(60);
|
||||||
|
|
||||||
|
// Main loop
|
||||||
|
while (!windowShouldClose()) {
|
||||||
|
// === SPAWN ===
|
||||||
|
if (isMouseButtonDown(MOUSE_BUTTON_LEFT)) {
|
||||||
|
const mx = getMouseX();
|
||||||
|
const my = getMouseY();
|
||||||
|
const limit = Math.min(count + 100, MAX_BUNNIES);
|
||||||
|
for (let i = count; i < limit; i++) {
|
||||||
|
const off = i * BUNNY_STRIDE;
|
||||||
|
bunny[off] = mx; // x
|
||||||
|
bunny[off + 1] = my; // y
|
||||||
|
bunny[off + 2] = getRandomValue(-250, 250) / 60.0; // sx
|
||||||
|
bunny[off + 3] = getRandomValue(-250, 250) / 60.0; // sy
|
||||||
|
bunny[off + 4] = getRandomValue(50, 240); // r
|
||||||
|
bunny[off + 5] = getRandomValue(80, 240); // g
|
||||||
|
bunny[off + 6] = getRandomValue(100, 240); // b
|
||||||
|
bunny[off + 7] = 255; // a
|
||||||
|
}
|
||||||
|
count = limit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// === UPDATE ===
|
||||||
|
const hw = texW / 2;
|
||||||
|
const hh = texH / 2;
|
||||||
|
for (let i = 0; i < count; i++) {
|
||||||
|
const off = i * BUNNY_STRIDE;
|
||||||
|
bunny[off] += bunny[off + 2]; // x += sx
|
||||||
|
bunny[off + 1] += bunny[off + 3]; // y += sy
|
||||||
|
|
||||||
|
if ((bunny[off] + hw) > width || (bunny[off] + hw) < 0)
|
||||||
|
bunny[off + 2] *= -1; // sx
|
||||||
|
if ((bunny[off + 1] + hh) > height || (bunny[off + 1] + hh - 40) < 0)
|
||||||
|
bunny[off + 3] *= -1; // sy
|
||||||
|
}
|
||||||
|
|
||||||
|
// === DRAW ===
|
||||||
|
beginDrawing();
|
||||||
|
clearBackground(RAYWHITE);
|
||||||
|
|
||||||
|
// Batch draw — reuse Color object, only update fields
|
||||||
|
for (let i = 0; i < count; i++) {
|
||||||
|
const off = i * BUNNY_STRIDE;
|
||||||
|
drawColor.r = bunny[off + 4];
|
||||||
|
drawColor.g = bunny[off + 5];
|
||||||
|
drawColor.b = bunny[off + 6];
|
||||||
|
drawColor.a = bunny[off + 7];
|
||||||
|
drawTexture(texBunny, bunny[off], bunny[off + 1], drawColor);
|
||||||
|
}
|
||||||
|
|
||||||
|
// HUD
|
||||||
|
drawRectangle(0, 0, screenWidth, 40, BLACK);
|
||||||
|
const fps = getFPS();
|
||||||
|
drawText(`bunnies: ${count} fps: ${fps}`, 120, 10, 20, GREEN);
|
||||||
|
drawText(`batched draws: ${1 + Math.floor(count / 8192)}`, 320, 10, 20, MAROON);
|
||||||
|
|
||||||
|
endDrawing();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cleanup
|
||||||
|
unloadTexture(texBunny);
|
||||||
|
closeWindow();
|
||||||
1576
generate-bindings.js
1576
generate-bindings.js
File diff suppressed because one or more lines are too long
|
|
@ -37,16 +37,23 @@ JSModuleDef *js_init_module_std(JSContext *ctx, const char *module_name);
|
||||||
JSModuleDef *js_init_module_os(JSContext *ctx, const char *module_name);
|
JSModuleDef *js_init_module_os(JSContext *ctx, const char *module_name);
|
||||||
void js_std_add_helpers(JSContext *ctx, int argc, char **argv);
|
void js_std_add_helpers(JSContext *ctx, int argc, char **argv);
|
||||||
void js_std_loop(JSContext *ctx);
|
void js_std_loop(JSContext *ctx);
|
||||||
|
JSValue js_std_await(JSContext *ctx, JSValue obj);
|
||||||
void js_std_init_handlers(JSRuntime *rt);
|
void js_std_init_handlers(JSRuntime *rt);
|
||||||
void js_std_free_handlers(JSRuntime *rt);
|
void js_std_free_handlers(JSRuntime *rt);
|
||||||
void js_std_dump_error(JSContext *ctx);
|
void js_std_dump_error(JSContext *ctx);
|
||||||
uint8_t *js_load_file(JSContext *ctx, size_t *pbuf_len, const char *filename);
|
uint8_t *js_load_file(JSContext *ctx, size_t *pbuf_len, const char *filename);
|
||||||
int js_module_set_import_meta(JSContext *ctx, JSValueConst func_val,
|
int js_module_set_import_meta(JSContext *ctx, JSValueConst func_val,
|
||||||
JS_BOOL use_realpath, JS_BOOL is_main);
|
JS_BOOL use_realpath, JS_BOOL is_main);
|
||||||
|
int js_module_test_json(JSContext *ctx, JSValueConst attributes);
|
||||||
|
int js_module_check_attributes(JSContext *ctx, void *opaque, JSValueConst attributes);
|
||||||
JSModuleDef *js_module_loader(JSContext *ctx,
|
JSModuleDef *js_module_loader(JSContext *ctx,
|
||||||
const char *module_name, void *opaque);
|
const char *module_name, void *opaque,
|
||||||
|
JSValueConst attributes);
|
||||||
void js_std_eval_binary(JSContext *ctx, const uint8_t *buf, size_t buf_len,
|
void js_std_eval_binary(JSContext *ctx, const uint8_t *buf, size_t buf_len,
|
||||||
int flags);
|
int flags);
|
||||||
|
void js_std_eval_binary_json_module(JSContext *ctx,
|
||||||
|
const uint8_t *buf, size_t buf_len,
|
||||||
|
const char *module_name);
|
||||||
void js_std_promise_rejection_tracker(JSContext *ctx, JSValueConst promise,
|
void js_std_promise_rejection_tracker(JSContext *ctx, JSValueConst promise,
|
||||||
JSValueConst reason,
|
JSValueConst reason,
|
||||||
JS_BOOL is_handled, void *opaque);
|
JS_BOOL is_handled, void *opaque);
|
||||||
|
|
|
||||||
|
|
@ -27,6 +27,7 @@
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
extern "C" {
|
extern "C" {
|
||||||
|
|
@ -48,7 +49,6 @@ extern "C" {
|
||||||
|
|
||||||
typedef struct JSRuntime JSRuntime;
|
typedef struct JSRuntime JSRuntime;
|
||||||
typedef struct JSContext JSContext;
|
typedef struct JSContext JSContext;
|
||||||
typedef struct JSObject JSObject;
|
|
||||||
typedef struct JSClass JSClass;
|
typedef struct JSClass JSClass;
|
||||||
typedef uint32_t JSClassID;
|
typedef uint32_t JSClassID;
|
||||||
typedef uint32_t JSAtom;
|
typedef uint32_t JSAtom;
|
||||||
|
|
@ -64,14 +64,21 @@ typedef uint32_t JSAtom;
|
||||||
#define JS_NAN_BOXING
|
#define JS_NAN_BOXING
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if defined(__SIZEOF_INT128__) && (INTPTR_MAX >= INT64_MAX)
|
||||||
|
#define JS_LIMB_BITS 64
|
||||||
|
#else
|
||||||
|
#define JS_LIMB_BITS 32
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define JS_SHORT_BIG_INT_BITS JS_LIMB_BITS
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
/* all tags with a reference count are negative */
|
/* all tags with a reference count are negative */
|
||||||
JS_TAG_FIRST = -11, /* first negative tag */
|
JS_TAG_FIRST = -9, /* first negative tag */
|
||||||
JS_TAG_BIG_DECIMAL = -11,
|
JS_TAG_BIG_INT = -9,
|
||||||
JS_TAG_BIG_INT = -10,
|
|
||||||
JS_TAG_BIG_FLOAT = -9,
|
|
||||||
JS_TAG_SYMBOL = -8,
|
JS_TAG_SYMBOL = -8,
|
||||||
JS_TAG_STRING = -7,
|
JS_TAG_STRING = -7,
|
||||||
|
JS_TAG_STRING_ROPE = -6,
|
||||||
JS_TAG_MODULE = -3, /* used internally */
|
JS_TAG_MODULE = -3, /* used internally */
|
||||||
JS_TAG_FUNCTION_BYTECODE = -2, /* used internally */
|
JS_TAG_FUNCTION_BYTECODE = -2, /* used internally */
|
||||||
JS_TAG_OBJECT = -1,
|
JS_TAG_OBJECT = -1,
|
||||||
|
|
@ -83,10 +90,12 @@ enum {
|
||||||
JS_TAG_UNINITIALIZED = 4,
|
JS_TAG_UNINITIALIZED = 4,
|
||||||
JS_TAG_CATCH_OFFSET = 5,
|
JS_TAG_CATCH_OFFSET = 5,
|
||||||
JS_TAG_EXCEPTION = 6,
|
JS_TAG_EXCEPTION = 6,
|
||||||
JS_TAG_FLOAT64 = 7,
|
JS_TAG_SHORT_BIG_INT = 7,
|
||||||
|
JS_TAG_FLOAT64 = 8,
|
||||||
/* any larger tag is FLOAT64 if JS_NAN_BOXING */
|
/* any larger tag is FLOAT64 if JS_NAN_BOXING */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* must match the layout of 'JSMallocBlockHeader' */
|
||||||
typedef struct JSRefCountHeader {
|
typedef struct JSRefCountHeader {
|
||||||
int ref_count;
|
int ref_count;
|
||||||
} JSRefCountHeader;
|
} JSRefCountHeader;
|
||||||
|
|
@ -108,6 +117,7 @@ typedef const struct __JSValue *JSValueConst;
|
||||||
#define JS_VALUE_GET_INT(v) (int)((intptr_t)(v) >> 4)
|
#define JS_VALUE_GET_INT(v) (int)((intptr_t)(v) >> 4)
|
||||||
#define JS_VALUE_GET_BOOL(v) JS_VALUE_GET_INT(v)
|
#define JS_VALUE_GET_BOOL(v) JS_VALUE_GET_INT(v)
|
||||||
#define JS_VALUE_GET_FLOAT64(v) (double)JS_VALUE_GET_INT(v)
|
#define JS_VALUE_GET_FLOAT64(v) (double)JS_VALUE_GET_INT(v)
|
||||||
|
#define JS_VALUE_GET_SHORT_BIG_INT(v) JS_VALUE_GET_INT(v)
|
||||||
#define JS_VALUE_GET_PTR(v) (void *)((intptr_t)(v) & ~0xf)
|
#define JS_VALUE_GET_PTR(v) (void *)((intptr_t)(v) & ~0xf)
|
||||||
|
|
||||||
#define JS_MKVAL(tag, val) (JSValue)(intptr_t)(((val) << 4) | (tag))
|
#define JS_MKVAL(tag, val) (JSValue)(intptr_t)(((val) << 4) | (tag))
|
||||||
|
|
@ -127,6 +137,11 @@ static inline JS_BOOL JS_VALUE_IS_NAN(JSValue v)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline JSValue __JS_NewShortBigInt(JSContext *ctx, int32_t d)
|
||||||
|
{
|
||||||
|
return JS_MKVAL(JS_TAG_SHORT_BIG_INT, d);
|
||||||
|
}
|
||||||
|
|
||||||
#elif defined(JS_NAN_BOXING)
|
#elif defined(JS_NAN_BOXING)
|
||||||
|
|
||||||
typedef uint64_t JSValue;
|
typedef uint64_t JSValue;
|
||||||
|
|
@ -136,6 +151,7 @@ typedef uint64_t JSValue;
|
||||||
#define JS_VALUE_GET_TAG(v) (int)((v) >> 32)
|
#define JS_VALUE_GET_TAG(v) (int)((v) >> 32)
|
||||||
#define JS_VALUE_GET_INT(v) (int)(v)
|
#define JS_VALUE_GET_INT(v) (int)(v)
|
||||||
#define JS_VALUE_GET_BOOL(v) (int)(v)
|
#define JS_VALUE_GET_BOOL(v) (int)(v)
|
||||||
|
#define JS_VALUE_GET_SHORT_BIG_INT(v) (int)(v)
|
||||||
#define JS_VALUE_GET_PTR(v) (void *)(intptr_t)(v)
|
#define JS_VALUE_GET_PTR(v) (void *)(intptr_t)(v)
|
||||||
|
|
||||||
#define JS_MKVAL(tag, val) (((uint64_t)(tag) << 32) | (uint32_t)(val))
|
#define JS_MKVAL(tag, val) (((uint64_t)(tag) << 32) | (uint32_t)(val))
|
||||||
|
|
@ -192,12 +208,22 @@ static inline JS_BOOL JS_VALUE_IS_NAN(JSValue v)
|
||||||
return tag == (JS_NAN >> 32);
|
return tag == (JS_NAN >> 32);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline JSValue __JS_NewShortBigInt(JSContext *ctx, int32_t d)
|
||||||
|
{
|
||||||
|
return JS_MKVAL(JS_TAG_SHORT_BIG_INT, d);
|
||||||
|
}
|
||||||
|
|
||||||
#else /* !JS_NAN_BOXING */
|
#else /* !JS_NAN_BOXING */
|
||||||
|
|
||||||
typedef union JSValueUnion {
|
typedef union JSValueUnion {
|
||||||
int32_t int32;
|
uint64_t uint64;
|
||||||
double float64;
|
double float64;
|
||||||
void *ptr;
|
void *ptr;
|
||||||
|
#if JS_SHORT_BIG_INT_BITS == 32
|
||||||
|
int32_t short_big_int;
|
||||||
|
#else
|
||||||
|
int64_t short_big_int;
|
||||||
|
#endif
|
||||||
} JSValueUnion;
|
} JSValueUnion;
|
||||||
|
|
||||||
typedef struct JSValue {
|
typedef struct JSValue {
|
||||||
|
|
@ -210,12 +236,15 @@ typedef struct JSValue {
|
||||||
#define JS_VALUE_GET_TAG(v) ((int32_t)(v).tag)
|
#define JS_VALUE_GET_TAG(v) ((int32_t)(v).tag)
|
||||||
/* same as JS_VALUE_GET_TAG, but return JS_TAG_FLOAT64 with NaN boxing */
|
/* same as JS_VALUE_GET_TAG, but return JS_TAG_FLOAT64 with NaN boxing */
|
||||||
#define JS_VALUE_GET_NORM_TAG(v) JS_VALUE_GET_TAG(v)
|
#define JS_VALUE_GET_NORM_TAG(v) JS_VALUE_GET_TAG(v)
|
||||||
#define JS_VALUE_GET_INT(v) ((v).u.int32)
|
#define JS_VALUE_GET_INT(v) ((int)(v).u.uint64)
|
||||||
#define JS_VALUE_GET_BOOL(v) ((v).u.int32)
|
#define JS_VALUE_GET_BOOL(v) ((int)(v).u.uint64)
|
||||||
#define JS_VALUE_GET_FLOAT64(v) ((v).u.float64)
|
#define JS_VALUE_GET_FLOAT64(v) ((v).u.float64)
|
||||||
|
#define JS_VALUE_GET_SHORT_BIG_INT(v) ((v).u.short_big_int)
|
||||||
#define JS_VALUE_GET_PTR(v) ((v).u.ptr)
|
#define JS_VALUE_GET_PTR(v) ((v).u.ptr)
|
||||||
|
|
||||||
#define JS_MKVAL(tag, val) (JSValue){ (JSValueUnion){ .int32 = val }, tag }
|
/* avoid uninitialized data by using a 64 bit field even if only 32
|
||||||
|
bits are needed because some compilers generate slower code */
|
||||||
|
#define JS_MKVAL(tag, val) (JSValue){ (JSValueUnion){ .uint64 = (uint32_t)(val) }, tag }
|
||||||
#define JS_MKPTR(tag, p) (JSValue){ (JSValueUnion){ .ptr = p }, tag }
|
#define JS_MKPTR(tag, p) (JSValue){ (JSValueUnion){ .ptr = p }, tag }
|
||||||
|
|
||||||
#define JS_TAG_IS_FLOAT64(tag) ((unsigned)(tag) == JS_TAG_FLOAT64)
|
#define JS_TAG_IS_FLOAT64(tag) ((unsigned)(tag) == JS_TAG_FLOAT64)
|
||||||
|
|
@ -242,13 +271,19 @@ static inline JS_BOOL JS_VALUE_IS_NAN(JSValue v)
|
||||||
return (u.u64 & 0x7fffffffffffffff) > 0x7ff0000000000000;
|
return (u.u64 & 0x7fffffffffffffff) > 0x7ff0000000000000;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline JSValue __JS_NewShortBigInt(JSContext *ctx, int64_t d)
|
||||||
|
{
|
||||||
|
JSValue v;
|
||||||
|
v.tag = JS_TAG_SHORT_BIG_INT;
|
||||||
|
v.u.short_big_int = d;
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
#endif /* !JS_NAN_BOXING */
|
#endif /* !JS_NAN_BOXING */
|
||||||
|
|
||||||
#define JS_VALUE_IS_BOTH_INT(v1, v2) ((JS_VALUE_GET_TAG(v1) | JS_VALUE_GET_TAG(v2)) == 0)
|
#define JS_VALUE_IS_BOTH_INT(v1, v2) ((JS_VALUE_GET_TAG(v1) | JS_VALUE_GET_TAG(v2)) == 0)
|
||||||
#define JS_VALUE_IS_BOTH_FLOAT(v1, v2) (JS_TAG_IS_FLOAT64(JS_VALUE_GET_TAG(v1)) && JS_TAG_IS_FLOAT64(JS_VALUE_GET_TAG(v2)))
|
#define JS_VALUE_IS_BOTH_FLOAT(v1, v2) (JS_TAG_IS_FLOAT64(JS_VALUE_GET_TAG(v1)) && JS_TAG_IS_FLOAT64(JS_VALUE_GET_TAG(v2)))
|
||||||
|
|
||||||
#define JS_VALUE_GET_OBJ(v) ((JSObject *)JS_VALUE_GET_PTR(v))
|
|
||||||
#define JS_VALUE_GET_STRING(v) ((JSString *)JS_VALUE_GET_PTR(v))
|
|
||||||
#define JS_VALUE_HAS_REF_COUNT(v) ((unsigned)JS_VALUE_GET_TAG(v) >= (unsigned)JS_TAG_FIRST)
|
#define JS_VALUE_HAS_REF_COUNT(v) ((unsigned)JS_VALUE_GET_TAG(v) >= (unsigned)JS_TAG_FIRST)
|
||||||
|
|
||||||
/* special values */
|
/* special values */
|
||||||
|
|
@ -287,10 +322,11 @@ static inline JS_BOOL JS_VALUE_IS_NAN(JSValue v)
|
||||||
(JS_SetProperty) */
|
(JS_SetProperty) */
|
||||||
#define JS_PROP_THROW_STRICT (1 << 15)
|
#define JS_PROP_THROW_STRICT (1 << 15)
|
||||||
|
|
||||||
#define JS_PROP_NO_ADD (1 << 16) /* internal use */
|
#define JS_PROP_NO_EXOTIC (1 << 16) /* internal use */
|
||||||
#define JS_PROP_NO_EXOTIC (1 << 17) /* internal use */
|
|
||||||
|
|
||||||
#define JS_DEFAULT_STACK_SIZE (256 * 1024)
|
#ifndef JS_DEFAULT_STACK_SIZE
|
||||||
|
#define JS_DEFAULT_STACK_SIZE (1024 * 1024)
|
||||||
|
#endif
|
||||||
|
|
||||||
/* JS_Eval() flags */
|
/* JS_Eval() flags */
|
||||||
#define JS_EVAL_TYPE_GLOBAL (0 << 0) /* global code (default) */
|
#define JS_EVAL_TYPE_GLOBAL (0 << 0) /* global code (default) */
|
||||||
|
|
@ -300,13 +336,15 @@ static inline JS_BOOL JS_VALUE_IS_NAN(JSValue v)
|
||||||
#define JS_EVAL_TYPE_MASK (3 << 0)
|
#define JS_EVAL_TYPE_MASK (3 << 0)
|
||||||
|
|
||||||
#define JS_EVAL_FLAG_STRICT (1 << 3) /* force 'strict' mode */
|
#define JS_EVAL_FLAG_STRICT (1 << 3) /* force 'strict' mode */
|
||||||
#define JS_EVAL_FLAG_STRIP (1 << 4) /* force 'strip' mode */
|
|
||||||
/* compile but do not run. The result is an object with a
|
/* compile but do not run. The result is an object with a
|
||||||
JS_TAG_FUNCTION_BYTECODE or JS_TAG_MODULE tag. It can be executed
|
JS_TAG_FUNCTION_BYTECODE or JS_TAG_MODULE tag. It can be executed
|
||||||
with JS_EvalFunction(). */
|
with JS_EvalFunction(). */
|
||||||
#define JS_EVAL_FLAG_COMPILE_ONLY (1 << 5)
|
#define JS_EVAL_FLAG_COMPILE_ONLY (1 << 5)
|
||||||
/* don't include the stack frames before this eval in the Error() backtraces */
|
/* don't include the stack frames before this eval in the Error() backtraces */
|
||||||
#define JS_EVAL_FLAG_BACKTRACE_BARRIER (1 << 6)
|
#define JS_EVAL_FLAG_BACKTRACE_BARRIER (1 << 6)
|
||||||
|
/* allow top-level await in normal script. JS_Eval() returns a
|
||||||
|
promise. Only allowed with JS_EVAL_TYPE_GLOBAL */
|
||||||
|
#define JS_EVAL_FLAG_ASYNC (1 << 7)
|
||||||
|
|
||||||
typedef JSValue JSCFunction(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv);
|
typedef JSValue JSCFunction(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv);
|
||||||
typedef JSValue JSCFunctionMagic(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv, int magic);
|
typedef JSValue JSCFunctionMagic(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv, int magic);
|
||||||
|
|
@ -359,24 +397,18 @@ JSValue JS_GetClassProto(JSContext *ctx, JSClassID class_id);
|
||||||
/* the following functions are used to select the intrinsic object to
|
/* the following functions are used to select the intrinsic object to
|
||||||
save memory */
|
save memory */
|
||||||
JSContext *JS_NewContextRaw(JSRuntime *rt);
|
JSContext *JS_NewContextRaw(JSRuntime *rt);
|
||||||
void JS_AddIntrinsicBaseObjects(JSContext *ctx);
|
int JS_AddIntrinsicBaseObjects(JSContext *ctx);
|
||||||
void JS_AddIntrinsicDate(JSContext *ctx);
|
int JS_AddIntrinsicDate(JSContext *ctx);
|
||||||
void JS_AddIntrinsicEval(JSContext *ctx);
|
int JS_AddIntrinsicEval(JSContext *ctx);
|
||||||
void JS_AddIntrinsicStringNormalize(JSContext *ctx);
|
int JS_AddIntrinsicStringNormalize(JSContext *ctx);
|
||||||
void JS_AddIntrinsicRegExpCompiler(JSContext *ctx);
|
void JS_AddIntrinsicRegExpCompiler(JSContext *ctx);
|
||||||
void JS_AddIntrinsicRegExp(JSContext *ctx);
|
int JS_AddIntrinsicRegExp(JSContext *ctx);
|
||||||
void JS_AddIntrinsicJSON(JSContext *ctx);
|
int JS_AddIntrinsicJSON(JSContext *ctx);
|
||||||
void JS_AddIntrinsicProxy(JSContext *ctx);
|
int JS_AddIntrinsicProxy(JSContext *ctx);
|
||||||
void JS_AddIntrinsicMapSet(JSContext *ctx);
|
int JS_AddIntrinsicMapSet(JSContext *ctx);
|
||||||
void JS_AddIntrinsicTypedArrays(JSContext *ctx);
|
int JS_AddIntrinsicTypedArrays(JSContext *ctx);
|
||||||
void JS_AddIntrinsicPromise(JSContext *ctx);
|
int JS_AddIntrinsicPromise(JSContext *ctx);
|
||||||
void JS_AddIntrinsicBigInt(JSContext *ctx);
|
int JS_AddIntrinsicWeakRef(JSContext *ctx);
|
||||||
void JS_AddIntrinsicBigFloat(JSContext *ctx);
|
|
||||||
void JS_AddIntrinsicBigDecimal(JSContext *ctx);
|
|
||||||
/* enable operator overloading */
|
|
||||||
void JS_AddIntrinsicOperators(JSContext *ctx);
|
|
||||||
/* enable "use math" */
|
|
||||||
void JS_EnableBignumExt(JSContext *ctx, JS_BOOL enable);
|
|
||||||
|
|
||||||
JSValue js_string_codePointRange(JSContext *ctx, JSValueConst this_val,
|
JSValue js_string_codePointRange(JSContext *ctx, JSValueConst this_val,
|
||||||
int argc, JSValueConst *argv);
|
int argc, JSValueConst *argv);
|
||||||
|
|
@ -426,7 +458,11 @@ void JS_FreeAtom(JSContext *ctx, JSAtom v);
|
||||||
void JS_FreeAtomRT(JSRuntime *rt, JSAtom v);
|
void JS_FreeAtomRT(JSRuntime *rt, JSAtom v);
|
||||||
JSValue JS_AtomToValue(JSContext *ctx, JSAtom atom);
|
JSValue JS_AtomToValue(JSContext *ctx, JSAtom atom);
|
||||||
JSValue JS_AtomToString(JSContext *ctx, JSAtom atom);
|
JSValue JS_AtomToString(JSContext *ctx, JSAtom atom);
|
||||||
const char *JS_AtomToCString(JSContext *ctx, JSAtom atom);
|
const char *JS_AtomToCStringLen(JSContext *ctx, size_t *plen, JSAtom atom);
|
||||||
|
static inline const char *JS_AtomToCString(JSContext *ctx, JSAtom atom)
|
||||||
|
{
|
||||||
|
return JS_AtomToCStringLen(ctx, NULL, atom);
|
||||||
|
}
|
||||||
JSAtom JS_ValueToAtom(JSContext *ctx, JSValueConst val);
|
JSAtom JS_ValueToAtom(JSContext *ctx, JSValueConst val);
|
||||||
|
|
||||||
/* object class support */
|
/* object class support */
|
||||||
|
|
@ -471,6 +507,17 @@ typedef struct JSClassExoticMethods {
|
||||||
/* return < 0 if exception or TRUE/FALSE */
|
/* return < 0 if exception or TRUE/FALSE */
|
||||||
int (*set_property)(JSContext *ctx, JSValueConst obj, JSAtom atom,
|
int (*set_property)(JSContext *ctx, JSValueConst obj, JSAtom atom,
|
||||||
JSValueConst value, JSValueConst receiver, int flags);
|
JSValueConst value, JSValueConst receiver, int flags);
|
||||||
|
|
||||||
|
/* To get a consistent object behavior when get_prototype != NULL,
|
||||||
|
get_property, set_property and set_prototype must be != NULL
|
||||||
|
and the object must be created with a JS_NULL prototype. */
|
||||||
|
JSValue (*get_prototype)(JSContext *ctx, JSValueConst obj);
|
||||||
|
/* return < 0 if exception or TRUE/FALSE */
|
||||||
|
int (*set_prototype)(JSContext *ctx, JSValueConst obj, JSValueConst proto_val);
|
||||||
|
/* return < 0 if exception or TRUE/FALSE */
|
||||||
|
int (*is_extensible)(JSContext *ctx, JSValueConst obj);
|
||||||
|
/* return < 0 if exception or TRUE/FALSE */
|
||||||
|
int (*prevent_extensions)(JSContext *ctx, JSValueConst obj);
|
||||||
} JSClassExoticMethods;
|
} JSClassExoticMethods;
|
||||||
|
|
||||||
typedef void JSClassFinalizer(JSRuntime *rt, JSValue val);
|
typedef void JSClassFinalizer(JSRuntime *rt, JSValue val);
|
||||||
|
|
@ -496,7 +543,10 @@ typedef struct JSClassDef {
|
||||||
JSClassExoticMethods *exotic;
|
JSClassExoticMethods *exotic;
|
||||||
} JSClassDef;
|
} JSClassDef;
|
||||||
|
|
||||||
|
#define JS_INVALID_CLASS_ID 0
|
||||||
JSClassID JS_NewClassID(JSClassID *pclass_id);
|
JSClassID JS_NewClassID(JSClassID *pclass_id);
|
||||||
|
/* Returns the class ID if `v` is an object, otherwise returns JS_INVALID_CLASS_ID. */
|
||||||
|
JSClassID JS_GetClassID(JSValue v);
|
||||||
int JS_NewClass(JSRuntime *rt, JSClassID class_id, const JSClassDef *class_def);
|
int JS_NewClass(JSRuntime *rt, JSClassID class_id, const JSClassDef *class_def);
|
||||||
int JS_IsRegisteredClass(JSRuntime *rt, JSClassID class_id);
|
int JS_IsRegisteredClass(JSRuntime *rt, JSClassID class_id);
|
||||||
|
|
||||||
|
|
@ -544,23 +594,21 @@ JSValue JS_NewBigUint64(JSContext *ctx, uint64_t v);
|
||||||
|
|
||||||
static js_force_inline JSValue JS_NewFloat64(JSContext *ctx, double d)
|
static js_force_inline JSValue JS_NewFloat64(JSContext *ctx, double d)
|
||||||
{
|
{
|
||||||
JSValue v;
|
|
||||||
int32_t val;
|
int32_t val;
|
||||||
union {
|
union {
|
||||||
double d;
|
double d;
|
||||||
uint64_t u;
|
uint64_t u;
|
||||||
} u, t;
|
} u, t;
|
||||||
u.d = d;
|
if (d >= INT32_MIN && d <= INT32_MAX) {
|
||||||
val = (int32_t)d;
|
u.d = d;
|
||||||
t.d = val;
|
val = (int32_t)d;
|
||||||
/* -0 cannot be represented as integer, so we compare the bit
|
t.d = val;
|
||||||
representation */
|
/* -0 cannot be represented as integer, so we compare the bit
|
||||||
if (u.u == t.u) {
|
representation */
|
||||||
v = JS_MKVAL(JS_TAG_INT, val);
|
if (u.u == t.u)
|
||||||
} else {
|
return JS_MKVAL(JS_TAG_INT, val);
|
||||||
v = __JS_NewFloat64(ctx, d);
|
|
||||||
}
|
}
|
||||||
return v;
|
return __JS_NewFloat64(ctx, d);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline JS_BOOL JS_IsNumber(JSValueConst v)
|
static inline JS_BOOL JS_IsNumber(JSValueConst v)
|
||||||
|
|
@ -572,19 +620,7 @@ static inline JS_BOOL JS_IsNumber(JSValueConst v)
|
||||||
static inline JS_BOOL JS_IsBigInt(JSContext *ctx, JSValueConst v)
|
static inline JS_BOOL JS_IsBigInt(JSContext *ctx, JSValueConst v)
|
||||||
{
|
{
|
||||||
int tag = JS_VALUE_GET_TAG(v);
|
int tag = JS_VALUE_GET_TAG(v);
|
||||||
return tag == JS_TAG_BIG_INT;
|
return tag == JS_TAG_BIG_INT || tag == JS_TAG_SHORT_BIG_INT;
|
||||||
}
|
|
||||||
|
|
||||||
static inline JS_BOOL JS_IsBigFloat(JSValueConst v)
|
|
||||||
{
|
|
||||||
int tag = JS_VALUE_GET_TAG(v);
|
|
||||||
return tag == JS_TAG_BIG_FLOAT;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline JS_BOOL JS_IsBigDecimal(JSValueConst v)
|
|
||||||
{
|
|
||||||
int tag = JS_VALUE_GET_TAG(v);
|
|
||||||
return tag == JS_TAG_BIG_DECIMAL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline JS_BOOL JS_IsBool(JSValueConst v)
|
static inline JS_BOOL JS_IsBool(JSValueConst v)
|
||||||
|
|
@ -614,7 +650,8 @@ static inline JS_BOOL JS_IsUninitialized(JSValueConst v)
|
||||||
|
|
||||||
static inline JS_BOOL JS_IsString(JSValueConst v)
|
static inline JS_BOOL JS_IsString(JSValueConst v)
|
||||||
{
|
{
|
||||||
return JS_VALUE_GET_TAG(v) == JS_TAG_STRING;
|
return JS_VALUE_GET_TAG(v) == JS_TAG_STRING ||
|
||||||
|
JS_VALUE_GET_TAG(v) == JS_TAG_STRING_ROPE;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline JS_BOOL JS_IsSymbol(JSValueConst v)
|
static inline JS_BOOL JS_IsSymbol(JSValueConst v)
|
||||||
|
|
@ -628,9 +665,10 @@ static inline JS_BOOL JS_IsObject(JSValueConst v)
|
||||||
}
|
}
|
||||||
|
|
||||||
JSValue JS_Throw(JSContext *ctx, JSValue obj);
|
JSValue JS_Throw(JSContext *ctx, JSValue obj);
|
||||||
|
void JS_SetUncatchableException(JSContext *ctx, JS_BOOL flag);
|
||||||
JSValue JS_GetException(JSContext *ctx);
|
JSValue JS_GetException(JSContext *ctx);
|
||||||
|
JS_BOOL JS_HasException(JSContext *ctx);
|
||||||
JS_BOOL JS_IsError(JSContext *ctx, JSValueConst val);
|
JS_BOOL JS_IsError(JSContext *ctx, JSValueConst val);
|
||||||
void JS_ResetUncatchableError(JSContext *ctx);
|
|
||||||
JSValue JS_NewError(JSContext *ctx);
|
JSValue JS_NewError(JSContext *ctx);
|
||||||
JSValue __js_printf_like(2, 3) JS_ThrowSyntaxError(JSContext *ctx, const char *fmt, ...);
|
JSValue __js_printf_like(2, 3) JS_ThrowSyntaxError(JSContext *ctx, const char *fmt, ...);
|
||||||
JSValue __js_printf_like(2, 3) JS_ThrowTypeError(JSContext *ctx, const char *fmt, ...);
|
JSValue __js_printf_like(2, 3) JS_ThrowTypeError(JSContext *ctx, const char *fmt, ...);
|
||||||
|
|
@ -640,10 +678,16 @@ JSValue __js_printf_like(2, 3) JS_ThrowInternalError(JSContext *ctx, const char
|
||||||
JSValue JS_ThrowOutOfMemory(JSContext *ctx);
|
JSValue JS_ThrowOutOfMemory(JSContext *ctx);
|
||||||
|
|
||||||
void __JS_FreeValue(JSContext *ctx, JSValue v);
|
void __JS_FreeValue(JSContext *ctx, JSValue v);
|
||||||
|
|
||||||
|
static inline JSRefCountHeader *__js_rc(void *ptr)
|
||||||
|
{
|
||||||
|
return (JSRefCountHeader *)((uint32_t *)ptr - 1);
|
||||||
|
}
|
||||||
|
|
||||||
static inline void JS_FreeValue(JSContext *ctx, JSValue v)
|
static inline void JS_FreeValue(JSContext *ctx, JSValue v)
|
||||||
{
|
{
|
||||||
if (JS_VALUE_HAS_REF_COUNT(v)) {
|
if (JS_VALUE_HAS_REF_COUNT(v)) {
|
||||||
JSRefCountHeader *p = (JSRefCountHeader *)JS_VALUE_GET_PTR(v);
|
JSRefCountHeader *p = __js_rc(JS_VALUE_GET_PTR(v));
|
||||||
if (--p->ref_count <= 0) {
|
if (--p->ref_count <= 0) {
|
||||||
__JS_FreeValue(ctx, v);
|
__JS_FreeValue(ctx, v);
|
||||||
}
|
}
|
||||||
|
|
@ -653,7 +697,7 @@ void __JS_FreeValueRT(JSRuntime *rt, JSValue v);
|
||||||
static inline void JS_FreeValueRT(JSRuntime *rt, JSValue v)
|
static inline void JS_FreeValueRT(JSRuntime *rt, JSValue v)
|
||||||
{
|
{
|
||||||
if (JS_VALUE_HAS_REF_COUNT(v)) {
|
if (JS_VALUE_HAS_REF_COUNT(v)) {
|
||||||
JSRefCountHeader *p = (JSRefCountHeader *)JS_VALUE_GET_PTR(v);
|
JSRefCountHeader *p = __js_rc(JS_VALUE_GET_PTR(v));
|
||||||
if (--p->ref_count <= 0) {
|
if (--p->ref_count <= 0) {
|
||||||
__JS_FreeValueRT(rt, v);
|
__JS_FreeValueRT(rt, v);
|
||||||
}
|
}
|
||||||
|
|
@ -663,7 +707,7 @@ static inline void JS_FreeValueRT(JSRuntime *rt, JSValue v)
|
||||||
static inline JSValue JS_DupValue(JSContext *ctx, JSValueConst v)
|
static inline JSValue JS_DupValue(JSContext *ctx, JSValueConst v)
|
||||||
{
|
{
|
||||||
if (JS_VALUE_HAS_REF_COUNT(v)) {
|
if (JS_VALUE_HAS_REF_COUNT(v)) {
|
||||||
JSRefCountHeader *p = (JSRefCountHeader *)JS_VALUE_GET_PTR(v);
|
JSRefCountHeader *p = __js_rc(JS_VALUE_GET_PTR(v));
|
||||||
p->ref_count++;
|
p->ref_count++;
|
||||||
}
|
}
|
||||||
return (JSValue)v;
|
return (JSValue)v;
|
||||||
|
|
@ -672,12 +716,16 @@ static inline JSValue JS_DupValue(JSContext *ctx, JSValueConst v)
|
||||||
static inline JSValue JS_DupValueRT(JSRuntime *rt, JSValueConst v)
|
static inline JSValue JS_DupValueRT(JSRuntime *rt, JSValueConst v)
|
||||||
{
|
{
|
||||||
if (JS_VALUE_HAS_REF_COUNT(v)) {
|
if (JS_VALUE_HAS_REF_COUNT(v)) {
|
||||||
JSRefCountHeader *p = (JSRefCountHeader *)JS_VALUE_GET_PTR(v);
|
JSRefCountHeader *p = __js_rc(JS_VALUE_GET_PTR(v));
|
||||||
p->ref_count++;
|
p->ref_count++;
|
||||||
}
|
}
|
||||||
return (JSValue)v;
|
return (JSValue)v;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
JS_BOOL JS_StrictEq(JSContext *ctx, JSValueConst op1, JSValueConst op2);
|
||||||
|
JS_BOOL JS_SameValue(JSContext *ctx, JSValueConst op1, JSValueConst op2);
|
||||||
|
JS_BOOL JS_SameValueZero(JSContext *ctx, JSValueConst op1, JSValueConst op2);
|
||||||
|
|
||||||
int JS_ToBool(JSContext *ctx, JSValueConst val); /* return -1 for JS_EXCEPTION */
|
int JS_ToBool(JSContext *ctx, JSValueConst val); /* return -1 for JS_EXCEPTION */
|
||||||
int JS_ToInt32(JSContext *ctx, int32_t *pres, JSValueConst val);
|
int JS_ToInt32(JSContext *ctx, int32_t *pres, JSValueConst val);
|
||||||
static inline int JS_ToUint32(JSContext *ctx, uint32_t *pres, JSValueConst val)
|
static inline int JS_ToUint32(JSContext *ctx, uint32_t *pres, JSValueConst val)
|
||||||
|
|
@ -693,7 +741,10 @@ int JS_ToBigInt64(JSContext *ctx, int64_t *pres, JSValueConst val);
|
||||||
int JS_ToInt64Ext(JSContext *ctx, int64_t *pres, JSValueConst val);
|
int JS_ToInt64Ext(JSContext *ctx, int64_t *pres, JSValueConst val);
|
||||||
|
|
||||||
JSValue JS_NewStringLen(JSContext *ctx, const char *str1, size_t len1);
|
JSValue JS_NewStringLen(JSContext *ctx, const char *str1, size_t len1);
|
||||||
JSValue JS_NewString(JSContext *ctx, const char *str);
|
static inline JSValue JS_NewString(JSContext *ctx, const char *str)
|
||||||
|
{
|
||||||
|
return JS_NewStringLen(ctx, str, strlen(str));
|
||||||
|
}
|
||||||
JSValue JS_NewAtomString(JSContext *ctx, const char *str);
|
JSValue JS_NewAtomString(JSContext *ctx, const char *str);
|
||||||
JSValue JS_ToString(JSContext *ctx, JSValueConst val);
|
JSValue JS_ToString(JSContext *ctx, JSValueConst val);
|
||||||
JSValue JS_ToPropertyKey(JSContext *ctx, JSValueConst val);
|
JSValue JS_ToPropertyKey(JSContext *ctx, JSValueConst val);
|
||||||
|
|
@ -720,6 +771,8 @@ JS_BOOL JS_SetConstructorBit(JSContext *ctx, JSValueConst func_obj, JS_BOOL val)
|
||||||
JSValue JS_NewArray(JSContext *ctx);
|
JSValue JS_NewArray(JSContext *ctx);
|
||||||
int JS_IsArray(JSContext *ctx, JSValueConst val);
|
int JS_IsArray(JSContext *ctx, JSValueConst val);
|
||||||
|
|
||||||
|
JSValue JS_NewDate(JSContext *ctx, double epoch_ms);
|
||||||
|
|
||||||
JSValue JS_GetPropertyInternal(JSContext *ctx, JSValueConst obj,
|
JSValue JS_GetPropertyInternal(JSContext *ctx, JSValueConst obj,
|
||||||
JSAtom prop, JSValueConst receiver,
|
JSAtom prop, JSValueConst receiver,
|
||||||
JS_BOOL throw_ref_error);
|
JS_BOOL throw_ref_error);
|
||||||
|
|
@ -733,13 +786,13 @@ JSValue JS_GetPropertyStr(JSContext *ctx, JSValueConst this_obj,
|
||||||
JSValue JS_GetPropertyUint32(JSContext *ctx, JSValueConst this_obj,
|
JSValue JS_GetPropertyUint32(JSContext *ctx, JSValueConst this_obj,
|
||||||
uint32_t idx);
|
uint32_t idx);
|
||||||
|
|
||||||
int JS_SetPropertyInternal(JSContext *ctx, JSValueConst this_obj,
|
int JS_SetPropertyInternal(JSContext *ctx, JSValueConst obj,
|
||||||
JSAtom prop, JSValue val,
|
JSAtom prop, JSValue val, JSValueConst this_obj,
|
||||||
int flags);
|
int flags);
|
||||||
static inline int JS_SetProperty(JSContext *ctx, JSValueConst this_obj,
|
static inline int JS_SetProperty(JSContext *ctx, JSValueConst this_obj,
|
||||||
JSAtom prop, JSValue val)
|
JSAtom prop, JSValue val)
|
||||||
{
|
{
|
||||||
return JS_SetPropertyInternal(ctx, this_obj, prop, val, JS_PROP_THROW);
|
return JS_SetPropertyInternal(ctx, this_obj, prop, val, this_obj, JS_PROP_THROW);
|
||||||
}
|
}
|
||||||
int JS_SetPropertyUint32(JSContext *ctx, JSValueConst this_obj,
|
int JS_SetPropertyUint32(JSContext *ctx, JSValueConst this_obj,
|
||||||
uint32_t idx, JSValue val);
|
uint32_t idx, JSValue val);
|
||||||
|
|
@ -764,6 +817,8 @@ JSValue JS_GetPrototype(JSContext *ctx, JSValueConst val);
|
||||||
|
|
||||||
int JS_GetOwnPropertyNames(JSContext *ctx, JSPropertyEnum **ptab,
|
int JS_GetOwnPropertyNames(JSContext *ctx, JSPropertyEnum **ptab,
|
||||||
uint32_t *plen, JSValueConst obj, int flags);
|
uint32_t *plen, JSValueConst obj, int flags);
|
||||||
|
void JS_FreePropertyEnum(JSContext *ctx, JSPropertyEnum *tab,
|
||||||
|
uint32_t len);
|
||||||
int JS_GetOwnProperty(JSContext *ctx, JSPropertyDescriptor *desc,
|
int JS_GetOwnProperty(JSContext *ctx, JSPropertyDescriptor *desc,
|
||||||
JSValueConst obj, JSAtom prop);
|
JSValueConst obj, JSAtom prop);
|
||||||
|
|
||||||
|
|
@ -801,6 +856,7 @@ int JS_DefinePropertyGetSet(JSContext *ctx, JSValueConst this_obj,
|
||||||
void JS_SetOpaque(JSValue obj, void *opaque);
|
void JS_SetOpaque(JSValue obj, void *opaque);
|
||||||
void *JS_GetOpaque(JSValueConst obj, JSClassID class_id);
|
void *JS_GetOpaque(JSValueConst obj, JSClassID class_id);
|
||||||
void *JS_GetOpaque2(JSContext *ctx, JSValueConst obj, JSClassID class_id);
|
void *JS_GetOpaque2(JSContext *ctx, JSValueConst obj, JSClassID class_id);
|
||||||
|
void *JS_GetAnyOpaque(JSValueConst obj, JSClassID *class_id);
|
||||||
|
|
||||||
/* 'buf' must be zero terminated i.e. buf[buf_len] = '\0'. */
|
/* 'buf' must be zero terminated i.e. buf[buf_len] = '\0'. */
|
||||||
JSValue JS_ParseJSON(JSContext *ctx, const char *buf, size_t buf_len,
|
JSValue JS_ParseJSON(JSContext *ctx, const char *buf, size_t buf_len,
|
||||||
|
|
@ -818,6 +874,24 @@ JSValue JS_NewArrayBuffer(JSContext *ctx, uint8_t *buf, size_t len,
|
||||||
JSValue JS_NewArrayBufferCopy(JSContext *ctx, const uint8_t *buf, size_t len);
|
JSValue JS_NewArrayBufferCopy(JSContext *ctx, const uint8_t *buf, size_t len);
|
||||||
void JS_DetachArrayBuffer(JSContext *ctx, JSValueConst obj);
|
void JS_DetachArrayBuffer(JSContext *ctx, JSValueConst obj);
|
||||||
uint8_t *JS_GetArrayBuffer(JSContext *ctx, size_t *psize, JSValueConst obj);
|
uint8_t *JS_GetArrayBuffer(JSContext *ctx, size_t *psize, JSValueConst obj);
|
||||||
|
|
||||||
|
typedef enum JSTypedArrayEnum {
|
||||||
|
JS_TYPED_ARRAY_UINT8C = 0,
|
||||||
|
JS_TYPED_ARRAY_INT8,
|
||||||
|
JS_TYPED_ARRAY_UINT8,
|
||||||
|
JS_TYPED_ARRAY_INT16,
|
||||||
|
JS_TYPED_ARRAY_UINT16,
|
||||||
|
JS_TYPED_ARRAY_INT32,
|
||||||
|
JS_TYPED_ARRAY_UINT32,
|
||||||
|
JS_TYPED_ARRAY_BIG_INT64,
|
||||||
|
JS_TYPED_ARRAY_BIG_UINT64,
|
||||||
|
JS_TYPED_ARRAY_FLOAT16,
|
||||||
|
JS_TYPED_ARRAY_FLOAT32,
|
||||||
|
JS_TYPED_ARRAY_FLOAT64,
|
||||||
|
} JSTypedArrayEnum;
|
||||||
|
|
||||||
|
JSValue JS_NewTypedArray(JSContext *ctx, int argc, JSValueConst *argv,
|
||||||
|
JSTypedArrayEnum array_type);
|
||||||
JSValue JS_GetTypedArrayBuffer(JSContext *ctx, JSValueConst obj,
|
JSValue JS_GetTypedArrayBuffer(JSContext *ctx, JSValueConst obj,
|
||||||
size_t *pbyte_offset,
|
size_t *pbyte_offset,
|
||||||
size_t *pbyte_length,
|
size_t *pbyte_length,
|
||||||
|
|
@ -831,7 +905,15 @@ typedef struct {
|
||||||
void JS_SetSharedArrayBufferFunctions(JSRuntime *rt,
|
void JS_SetSharedArrayBufferFunctions(JSRuntime *rt,
|
||||||
const JSSharedArrayBufferFunctions *sf);
|
const JSSharedArrayBufferFunctions *sf);
|
||||||
|
|
||||||
|
typedef enum JSPromiseStateEnum {
|
||||||
|
JS_PROMISE_PENDING,
|
||||||
|
JS_PROMISE_FULFILLED,
|
||||||
|
JS_PROMISE_REJECTED,
|
||||||
|
} JSPromiseStateEnum;
|
||||||
|
|
||||||
JSValue JS_NewPromiseCapability(JSContext *ctx, JSValue *resolving_funcs);
|
JSValue JS_NewPromiseCapability(JSContext *ctx, JSValue *resolving_funcs);
|
||||||
|
JSPromiseStateEnum JS_PromiseState(JSContext *ctx, JSValue promise);
|
||||||
|
JSValue JS_PromiseResult(JSContext *ctx, JSValue promise);
|
||||||
|
|
||||||
/* is_handled = TRUE means that the rejection is handled */
|
/* is_handled = TRUE means that the rejection is handled */
|
||||||
typedef void JSHostPromiseRejectionTracker(JSContext *ctx, JSValueConst promise,
|
typedef void JSHostPromiseRejectionTracker(JSContext *ctx, JSValueConst promise,
|
||||||
|
|
@ -844,6 +926,12 @@ typedef int JSInterruptHandler(JSRuntime *rt, void *opaque);
|
||||||
void JS_SetInterruptHandler(JSRuntime *rt, JSInterruptHandler *cb, void *opaque);
|
void JS_SetInterruptHandler(JSRuntime *rt, JSInterruptHandler *cb, void *opaque);
|
||||||
/* if can_block is TRUE, Atomics.wait() can be used */
|
/* if can_block is TRUE, Atomics.wait() can be used */
|
||||||
void JS_SetCanBlock(JSRuntime *rt, JS_BOOL can_block);
|
void JS_SetCanBlock(JSRuntime *rt, JS_BOOL can_block);
|
||||||
|
/* select which debug info is stripped from the compiled code */
|
||||||
|
#define JS_STRIP_SOURCE (1 << 0) /* strip source code */
|
||||||
|
#define JS_STRIP_DEBUG (1 << 1) /* strip all debug info including source code */
|
||||||
|
void JS_SetStripInfo(JSRuntime *rt, int flags);
|
||||||
|
int JS_GetStripInfo(JSRuntime *rt);
|
||||||
|
|
||||||
/* set the [IsHTMLDDA] internal slot */
|
/* set the [IsHTMLDDA] internal slot */
|
||||||
void JS_SetIsHTMLDDA(JSContext *ctx, JSValueConst obj);
|
void JS_SetIsHTMLDDA(JSContext *ctx, JSValueConst obj);
|
||||||
|
|
||||||
|
|
@ -856,15 +944,29 @@ typedef char *JSModuleNormalizeFunc(JSContext *ctx,
|
||||||
const char *module_name, void *opaque);
|
const char *module_name, void *opaque);
|
||||||
typedef JSModuleDef *JSModuleLoaderFunc(JSContext *ctx,
|
typedef JSModuleDef *JSModuleLoaderFunc(JSContext *ctx,
|
||||||
const char *module_name, void *opaque);
|
const char *module_name, void *opaque);
|
||||||
|
typedef JSModuleDef *JSModuleLoaderFunc2(JSContext *ctx,
|
||||||
|
const char *module_name, void *opaque,
|
||||||
|
JSValueConst attributes);
|
||||||
|
/* return -1 if exception, 0 if OK */
|
||||||
|
typedef int JSModuleCheckSupportedImportAttributes(JSContext *ctx, void *opaque,
|
||||||
|
JSValueConst attributes);
|
||||||
|
|
||||||
/* module_normalize = NULL is allowed and invokes the default module
|
/* module_normalize = NULL is allowed and invokes the default module
|
||||||
filename normalizer */
|
filename normalizer */
|
||||||
void JS_SetModuleLoaderFunc(JSRuntime *rt,
|
void JS_SetModuleLoaderFunc(JSRuntime *rt,
|
||||||
JSModuleNormalizeFunc *module_normalize,
|
JSModuleNormalizeFunc *module_normalize,
|
||||||
JSModuleLoaderFunc *module_loader, void *opaque);
|
JSModuleLoaderFunc *module_loader, void *opaque);
|
||||||
|
/* same as JS_SetModuleLoaderFunc but with attributes. if
|
||||||
|
module_check_attrs = NULL, no attribute checking is done. */
|
||||||
|
void JS_SetModuleLoaderFunc2(JSRuntime *rt,
|
||||||
|
JSModuleNormalizeFunc *module_normalize,
|
||||||
|
JSModuleLoaderFunc2 *module_loader,
|
||||||
|
JSModuleCheckSupportedImportAttributes *module_check_attrs,
|
||||||
|
void *opaque);
|
||||||
/* return the import.meta object of a module */
|
/* return the import.meta object of a module */
|
||||||
JSValue JS_GetImportMeta(JSContext *ctx, JSModuleDef *m);
|
JSValue JS_GetImportMeta(JSContext *ctx, JSModuleDef *m);
|
||||||
JSAtom JS_GetModuleName(JSContext *ctx, JSModuleDef *m);
|
JSAtom JS_GetModuleName(JSContext *ctx, JSModuleDef *m);
|
||||||
|
JSValue JS_GetModuleNamespace(JSContext *ctx, JSModuleDef *m);
|
||||||
|
|
||||||
/* JS Job support */
|
/* JS Job support */
|
||||||
|
|
||||||
|
|
@ -902,8 +1004,8 @@ int JS_ResolveModule(JSContext *ctx, JSValueConst obj);
|
||||||
/* only exported for os.Worker() */
|
/* only exported for os.Worker() */
|
||||||
JSAtom JS_GetScriptOrModuleName(JSContext *ctx, int n_stack_levels);
|
JSAtom JS_GetScriptOrModuleName(JSContext *ctx, int n_stack_levels);
|
||||||
/* only exported for os.Worker() */
|
/* only exported for os.Worker() */
|
||||||
JSModuleDef *JS_RunModule(JSContext *ctx, const char *basename,
|
JSValue JS_LoadModule(JSContext *ctx, const char *basename,
|
||||||
const char *filename);
|
const char *filename);
|
||||||
|
|
||||||
/* C function definition */
|
/* C function definition */
|
||||||
typedef enum JSCFunctionEnum { /* XXX: should rename for namespace isolation */
|
typedef enum JSCFunctionEnum { /* XXX: should rename for namespace isolation */
|
||||||
|
|
@ -955,10 +1057,12 @@ static inline JSValue JS_NewCFunctionMagic(JSContext *ctx, JSCFunctionMagic *fun
|
||||||
const char *name,
|
const char *name,
|
||||||
int length, JSCFunctionEnum cproto, int magic)
|
int length, JSCFunctionEnum cproto, int magic)
|
||||||
{
|
{
|
||||||
return JS_NewCFunction2(ctx, (JSCFunction *)func, name, length, cproto, magic);
|
/* Used to squelch a -Wcast-function-type warning. */
|
||||||
|
JSCFunctionType ft = { .generic_magic = func };
|
||||||
|
return JS_NewCFunction2(ctx, ft.generic, name, length, cproto, magic);
|
||||||
}
|
}
|
||||||
void JS_SetConstructor(JSContext *ctx, JSValueConst func_obj,
|
int JS_SetConstructor(JSContext *ctx, JSValueConst func_obj,
|
||||||
JSValueConst proto);
|
JSValueConst proto);
|
||||||
|
|
||||||
/* C property definition */
|
/* C property definition */
|
||||||
|
|
||||||
|
|
@ -1002,6 +1106,8 @@ typedef struct JSCFunctionListEntry {
|
||||||
#define JS_DEF_PROP_UNDEFINED 7
|
#define JS_DEF_PROP_UNDEFINED 7
|
||||||
#define JS_DEF_OBJECT 8
|
#define JS_DEF_OBJECT 8
|
||||||
#define JS_DEF_ALIAS 9
|
#define JS_DEF_ALIAS 9
|
||||||
|
#define JS_DEF_PROP_ATOM 10
|
||||||
|
#define JS_DEF_PROP_BOOL 11
|
||||||
|
|
||||||
/* Note: c++ does not like nested designators */
|
/* Note: c++ does not like nested designators */
|
||||||
#define JS_CFUNC_DEF(name, length, func1) { name, JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE, JS_DEF_CFUNC, 0, .u = { .func = { length, JS_CFUNC_generic, { .generic = func1 } } } }
|
#define JS_CFUNC_DEF(name, length, func1) { name, JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE, JS_DEF_CFUNC, 0, .u = { .func = { length, JS_CFUNC_generic, { .generic = func1 } } } }
|
||||||
|
|
@ -1015,13 +1121,15 @@ typedef struct JSCFunctionListEntry {
|
||||||
#define JS_PROP_INT64_DEF(name, val, prop_flags) { name, prop_flags, JS_DEF_PROP_INT64, 0, .u = { .i64 = val } }
|
#define JS_PROP_INT64_DEF(name, val, prop_flags) { name, prop_flags, JS_DEF_PROP_INT64, 0, .u = { .i64 = val } }
|
||||||
#define JS_PROP_DOUBLE_DEF(name, val, prop_flags) { name, prop_flags, JS_DEF_PROP_DOUBLE, 0, .u = { .f64 = val } }
|
#define JS_PROP_DOUBLE_DEF(name, val, prop_flags) { name, prop_flags, JS_DEF_PROP_DOUBLE, 0, .u = { .f64 = val } }
|
||||||
#define JS_PROP_UNDEFINED_DEF(name, prop_flags) { name, prop_flags, JS_DEF_PROP_UNDEFINED, 0, .u = { .i32 = 0 } }
|
#define JS_PROP_UNDEFINED_DEF(name, prop_flags) { name, prop_flags, JS_DEF_PROP_UNDEFINED, 0, .u = { .i32 = 0 } }
|
||||||
|
#define JS_PROP_ATOM_DEF(name, val, prop_flags) { name, prop_flags, JS_DEF_PROP_ATOM, 0, .u = { .i32 = val } }
|
||||||
|
#define JS_PROP_BOOL_DEF(name, val, prop_flags) { name, prop_flags, JS_DEF_PROP_BOOL, 0, .u = { .i32 = val } }
|
||||||
#define JS_OBJECT_DEF(name, tab, len, prop_flags) { name, prop_flags, JS_DEF_OBJECT, 0, .u = { .prop_list = { tab, len } } }
|
#define JS_OBJECT_DEF(name, tab, len, prop_flags) { name, prop_flags, JS_DEF_OBJECT, 0, .u = { .prop_list = { tab, len } } }
|
||||||
#define JS_ALIAS_DEF(name, from) { name, JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE, JS_DEF_ALIAS, 0, .u = { .alias = { from, -1 } } }
|
#define JS_ALIAS_DEF(name, from) { name, JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE, JS_DEF_ALIAS, 0, .u = { .alias = { from, -1 } } }
|
||||||
#define JS_ALIAS_BASE_DEF(name, from, base) { name, JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE, JS_DEF_ALIAS, 0, .u = { .alias = { from, base } } }
|
#define JS_ALIAS_BASE_DEF(name, from, base) { name, JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE, JS_DEF_ALIAS, 0, .u = { .alias = { from, base } } }
|
||||||
|
|
||||||
void JS_SetPropertyFunctionList(JSContext *ctx, JSValueConst obj,
|
int JS_SetPropertyFunctionList(JSContext *ctx, JSValueConst obj,
|
||||||
const JSCFunctionListEntry *tab,
|
const JSCFunctionListEntry *tab,
|
||||||
int len);
|
int len);
|
||||||
|
|
||||||
/* C module definition */
|
/* C module definition */
|
||||||
|
|
||||||
|
|
@ -1038,6 +1146,29 @@ int JS_SetModuleExport(JSContext *ctx, JSModuleDef *m, const char *export_name,
|
||||||
JSValue val);
|
JSValue val);
|
||||||
int JS_SetModuleExportList(JSContext *ctx, JSModuleDef *m,
|
int JS_SetModuleExportList(JSContext *ctx, JSModuleDef *m,
|
||||||
const JSCFunctionListEntry *tab, int len);
|
const JSCFunctionListEntry *tab, int len);
|
||||||
|
/* associate a JSValue to a C module */
|
||||||
|
int JS_SetModulePrivateValue(JSContext *ctx, JSModuleDef *m, JSValue val);
|
||||||
|
JSValue JS_GetModulePrivateValue(JSContext *ctx, JSModuleDef *m);
|
||||||
|
|
||||||
|
/* debug value output */
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
JS_BOOL show_hidden : 8; /* only show enumerable properties */
|
||||||
|
JS_BOOL raw_dump : 8; /* avoid doing autoinit and avoid any malloc() call (for internal use) */
|
||||||
|
uint32_t max_depth; /* recurse up to this depth, 0 = no limit */
|
||||||
|
uint32_t max_string_length; /* print no more than this length for
|
||||||
|
strings, 0 = no limit */
|
||||||
|
uint32_t max_item_count; /* print no more than this count for
|
||||||
|
arrays or objects, 0 = no limit */
|
||||||
|
} JSPrintValueOptions;
|
||||||
|
|
||||||
|
typedef void JSPrintValueWrite(void *opaque, const char *buf, size_t len);
|
||||||
|
|
||||||
|
void JS_PrintValueSetDefaultOptions(JSPrintValueOptions *options);
|
||||||
|
void JS_PrintValueRT(JSRuntime *rt, JSPrintValueWrite *write_func, void *write_opaque,
|
||||||
|
JSValueConst val, const JSPrintValueOptions *options);
|
||||||
|
void JS_PrintValue(JSContext *ctx, JSPrintValueWrite *write_func, void *write_opaque,
|
||||||
|
JSValueConst val, const JSPrintValueOptions *options);
|
||||||
|
|
||||||
#undef js_unlikely
|
#undef js_unlikely
|
||||||
#undef js_force_inline
|
#undef js_force_inline
|
||||||
|
|
|
||||||
135
readme.md
135
readme.md
|
|
@ -1,22 +1,28 @@
|
||||||

|

|
||||||
# rayjs - Javascript + Raylib
|
# rayjs - JavaScript + Raylib
|
||||||
QuickJS based Javascript bindings for raylib in a single ~3mb executable
|
|
||||||
|
QuickJS based JavaScript bindings for raylib **5.5** in a single ~3MB executable.
|
||||||
|
|
||||||
## What is this?
|
## What is this?
|
||||||
rayjs is small ES2020 compliant Javascript interpreter based on [QuickJS](https://bellard.org/quickjs/) with bindings for [Raylib](https://www.raylib.com/). You can use it to develop desktop games with Javascript.
|
|
||||||
|
rayjs is a small ES2020 compliant JavaScript interpreter based on [QuickJS](https://bellard.org/quickjs/) with bindings for [Raylib](https://www.raylib.com/) (version **5.5**). You can use it to develop desktop games with JavaScript.
|
||||||
|
|
||||||
## What this is not
|
## What this is not
|
||||||
rayjs is not a binding for NodeJS nor is it running in the browser (yet). It's comes with its own Javascript engine (QuickJS) similar to how NodeJS comes with the V8 engine. That makes it much easier to run and distribute rayjs programs as all you need to run a program / game is the small rayjs executable. No installation, no dlls or additional files are needed.
|
|
||||||
|
rayjs is not a binding for NodeJS nor is it running in the browser (yet). It comes with its own JavaScript engine (QuickJS) similar to how NodeJS comes with the V8 engine. That makes it much easier to run and distribute rayjs programs — all you need is the small rayjs executable. No installation, no DLLs or additional files required.
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
|
||||||
* Compiles into a single, small executable without any dependencies for easy distribution
|
* Compiles into a single, small executable without any dependencies for easy distribution
|
||||||
* Use modern Javascript features like classes or async/await
|
* Use modern JavaScript features like classes and async/await
|
||||||
* In-depth auto-complete with definitions for the whole API
|
* Full auto-complete with TypeScript definitions for the entire raylib 5.5 API
|
||||||
|
* Built on raylib 5.5 (stable), raygui 4.0
|
||||||
|
|
||||||
## Getting started
|
## Getting started
|
||||||
|
|
||||||
1. Download the binary for your platform from the [release section](https://github.com/mode777/rayjs/releases).
|
1. Download the binary for your platform from the [release section](https://github.com/mode777/rayjs/releases).
|
||||||
2. Unzip the executable to a folder and create a new text file in the same folder. Rename the file to `main.js`
|
2. Unzip the executable to a folder and create a new text file in the same folder. Rename the file to `main.js`
|
||||||
3. Open the file with a text-editor (e.g. Notepad) and add the following code
|
3. Open the file with a text editor (e.g. Notepad) and add the following code:
|
||||||
```javascript
|
```javascript
|
||||||
const screenWidth = 800;
|
const screenWidth = 800;
|
||||||
const screenHeight = 450;
|
const screenHeight = 450;
|
||||||
|
|
@ -36,25 +42,27 @@ rayjs is not a binding for NodeJS nor is it running in the browser (yet). It's c
|
||||||
closeWindow();
|
closeWindow();
|
||||||
```
|
```
|
||||||
4. Run the `rayjs` executable
|
4. Run the `rayjs` executable
|
||||||
5. Congratulations, you have created your first rayjs app.
|
5. Congratulations, you have created your first rayjs app!
|
||||||
|
|
||||||
## Running code
|
## Running code
|
||||||
rayjs will run code in three different modes
|
|
||||||
1. If no parameter is given it will look for a file called `main.js` in the executable directory
|
|
||||||
2. It will run a given Javascript file given as a command line argument like this `rayjs <filename>`
|
|
||||||
3. It will look for a file called `main.js` in a folder given as a command line argument like this `rayjs <foldername>`
|
|
||||||
|
|
||||||
The directory of the main Javascript module will also be the working directory of the app. Modules and resources will be loaded relative to it.
|
rayjs will run code in three different modes:
|
||||||
|
|
||||||
|
1. If no parameter is given it will look for a file called `main.js` in the executable directory
|
||||||
|
2. It will run a given JavaScript file passed as a command line argument: `rayjs <filename>`
|
||||||
|
3. It will look for a file called `main.js` in a folder given as a command line argument: `rayjs <foldername>`
|
||||||
|
|
||||||
|
The directory of the main JavaScript module will also be the working directory of the app. Modules and resources will be loaded relative to it.
|
||||||
|
|
||||||
## API support
|
## API support
|
||||||
|
|
||||||
The following raylib APIs are supported so far (with a few exceptions):
|
The following raylib 5.5 APIs are supported (with a few exceptions noted in `CHANGELOG_5.5.md`):
|
||||||
|
|
||||||
- core (no VR support yet)
|
- core (no VR support yet)
|
||||||
- shapes
|
- shapes
|
||||||
- textures
|
- textures
|
||||||
- text (no support for GlyphInfo yet)
|
- text (no support for GlyphInfo yet)
|
||||||
- models (no animation support)
|
- models (no animation support yet)
|
||||||
- shaders
|
- shaders
|
||||||
- audio
|
- audio
|
||||||
- raymath
|
- raymath
|
||||||
|
|
@ -63,13 +71,16 @@ The following raylib APIs are supported so far (with a few exceptions):
|
||||||
- raygui
|
- raygui
|
||||||
- reasings
|
- reasings
|
||||||
|
|
||||||
Similar to including a header in C and for your convenience, all types/functions are provided globally. They are additionally available in a module called 'raylib'
|
**718 functions bound** (105 intentionally ignored due to unsupported pointer types).
|
||||||
|
|
||||||
To check which API functions are not available (yet) check `/bindings/src/index.ts` for `ignore` statements.
|
All types and functions are provided globally for convenience. They are additionally available as a module called `'raylib'`.
|
||||||
|
|
||||||
|
To check which API functions are not available, see `MIGRATION_TABLE.md` or the `ignore()` statements in `bindings/src/index.ts`.
|
||||||
|
|
||||||
## Additional APIs
|
## Additional APIs
|
||||||
|
|
||||||
Rayjs comes with some additional functionality on top of raylib to make writing raylib code with Javascript easier
|
rayjs comes with some additional functionality on top of raylib to make writing code with JavaScript easier:
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
/** Replace material in slot materialIndex (Material is NOT unloaded) */
|
/** Replace material in slot materialIndex (Material is NOT unloaded) */
|
||||||
declare function setModelMaterial(model: Model, materialIndex: number, material: Material): void;
|
declare function setModelMaterial(model: Model, materialIndex: number, material: Material): void;
|
||||||
|
|
@ -87,85 +98,91 @@ declare function meshCopy(mesh: Mesh): Mesh;
|
||||||
declare function meshMerge(a: Mesh, b: Mesh): Mesh;
|
declare function meshMerge(a: Mesh, b: Mesh): Mesh;
|
||||||
```
|
```
|
||||||
|
|
||||||
Additionally it also comes with bindings to [lightmapper.h](https://github.com/ands/lightmapper/tree/master). See below for more information.
|
Additionally it comes with bindings to [lightmapper.h](https://github.com/ands/lightmapper/tree/master).
|
||||||
|
|
||||||
## Auto-Complete / Intellisense
|
## Auto-Complete / Intellisense
|
||||||
|
|
||||||
rayjs comes with full auto-complete support in the form of the definitions file `lib.raylib.d.ts`. These will work with Typescript and Javascript. In order to use them with Javascript you should create a Typescript configuration file in the project root (even if you are not using Typescript) called `tsconfig.json` with the following configuration
|
rayjs comes with full auto-complete support via the definitions file `lib.raylib.d.ts`. These work with TypeScript and JavaScript projects. To use with JavaScript, create a `tsconfig.json` in your project root:
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"allowJs": true,
|
"allowJs": true,
|
||||||
"target": "es2020",
|
"target": "es2020",
|
||||||
"lib": [
|
"lib": ["ES2020"]
|
||||||
"ES2020"
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
After that put the `lib.raylib.d.ts` file in the same folder and optionally restart your IDE. Auto-complete should be working:
|
|
||||||
|
Place `lib.raylib.d.ts` in the same folder and restart your IDE. Auto-complete should work:
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
## Examples
|
## Examples
|
||||||
|
|
||||||
Some official raylib examples were already ported to Javascript and can be found in the `examples` folder.
|
Ported raylib examples can be found in the `examples` folder.
|
||||||
|
|
||||||
Additional examples are described here.
|
```bash
|
||||||
|
|
||||||
```
|
|
||||||
./rayjs examples/js_example_project
|
./rayjs examples/js_example_project
|
||||||
```
|
```
|
||||||
Barebones example project on how to structure a project that uses Javascript
|
Barebones example project showing how to structure a JavaScript project.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./rayjs examples/textures/bunnymark.js
|
||||||
```
|
```
|
||||||
./rayjs examples/js_mesh_generation.js
|
Classic bunnymark performance test.
|
||||||
```
|
|
||||||
Shows how to create a mesh from Javascript ArrayBuffers
|
```bash
|
||||||
|
./rayjs examples/textures/bunnymark_opt.js
|
||||||
```
|
```
|
||||||
|
Optimized bunnymark using Float32Array (higher performance, no GC pressure).
|
||||||
|
|
||||||
|
```bash
|
||||||
./rayjs examples/shaders/js_shaders_gradient_lighting.js
|
./rayjs examples/shaders/js_shaders_gradient_lighting.js
|
||||||
```
|
```
|
||||||
Creates a gradient and uses it as lighting for a 3d scene
|
Creates a gradient and uses it as lighting for a 3D scene.
|
||||||
```
|
|
||||||
|
```bash
|
||||||
./rayjs examples/ts_dungeon
|
./rayjs examples/ts_dungeon
|
||||||
```
|
```
|
||||||
Small example game that uses Typescript with Webpack for transpilation and bundling
|
Small example game using TypeScript with Webpack.
|
||||||
```
|
|
||||||
|
```bash
|
||||||
./rayjs examples/ts_game
|
./rayjs examples/ts_game
|
||||||
```
|
```
|
||||||
Example how to integrate existing JS libraries. This example integrates the Inkjs library to compile and play a game written in the Ink interactive fiction language.
|
Example integrating existing JS libraries (Inkjs interactive fiction).
|
||||||
|
|
||||||
|
|
||||||
### Lightmapper usage
|
### Lightmapper usage
|
||||||
Rayjs integrates the [lightmapper.h](https://github.com/ands/lightmapper/tree/master) library to render baked lighting.
|
|
||||||
The example demonstrates it's usage.
|
rayjs integrates the [lightmapper.h](https://github.com/ands/lightmapper/tree/master) library for baked lighting:
|
||||||
```
|
|
||||||
|
```bash
|
||||||
./rayjs examples/js_lightmapper.js
|
./rayjs examples/js_lightmapper.js
|
||||||
```
|
```
|
||||||
|
|
||||||
Meshes must have unwrapped lightmap uvs in the second UV channel.
|
Meshes must have unwrapped lightmap UVs in the second UV channel.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
The example uses an environment that is uniform white which will lead to baking pure ambient occlusion. To bake other light sources, lower the amount of ambient lighting and everything that is rendered with a color other than black will become an emissive lightsource during baking. Rendering will just work as usual and custom shaders are supported. E.g. while the raylib default shader does not support color intensities greater than 1.0, the lightmapper does support them for higher intensity lighting.
|
|
||||||
|
|
||||||
The example will try to bake lighting alongside the render loop which is still buggy and leads to artifacts. Baking before rendering works better.
|
|
||||||
|
|
||||||
## Performance
|
## Performance
|
||||||
QuickJS is one of the [faster JS interpreters](https://bellard.org/quickjs/bench.html). I'm getting about 13000 bunnys in the unmodified bunnmark before dropping any frames on my 2020 Macbook Air M1 which seems pretty good.
|
|
||||||
|
QuickJS is one of the [faster JS interpreters](https://bellard.org/quickjs/bench.html). The optimized bunnymark (`bunnymark_opt.js`) uses Float32Array for buffer-based object storage, significantly reducing GC pressure and improving frame rates.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
## Building
|
## Building from source
|
||||||
Here are some basic steps if you want to compile rayjs yourself.
|
|
||||||
You should use CMake for building. **Please note that QuickJS needs Mingw in order to compile correctly on Windows**
|
### Prerequisites
|
||||||
|
|
||||||
|
- CMake
|
||||||
|
- C compiler (GCC, Clang, MSVC)
|
||||||
|
- Git
|
||||||
|
|
||||||
|
### Build steps
|
||||||
|
|
||||||
### Check out required files
|
|
||||||
```bash
|
```bash
|
||||||
git clone https://github.com/mode777/rayjs.git
|
git clone https://github.com/mode777/rayjs.git
|
||||||
git submodule update --init --recursive
|
git submodule update --init --recursive
|
||||||
```
|
|
||||||
|
|
||||||
### Build with cmake
|
|
||||||
Make sure you have cmake installed and in your path.
|
|
||||||
```bash
|
|
||||||
cd rayjs
|
cd rayjs
|
||||||
mkdir build
|
mkdir build
|
||||||
cd build
|
cd build
|
||||||
|
|
@ -173,4 +190,10 @@ cmake ..
|
||||||
make
|
make
|
||||||
```
|
```
|
||||||
|
|
||||||
|
The executable will be placed in the project root directory.
|
||||||
|
|
||||||
|
## Migration from raylib 4.5
|
||||||
|
|
||||||
|
See `CHANGELOG_5.5.md` for a detailed changelog of all changes made during the migration from raylib 4.5 to 5.5.
|
||||||
|
|
||||||
|
See `MIGRATION_TABLE.md` for a function-by-function comparison.
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -100,8 +100,6 @@ int app_update_quickjs(){
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(IsKeyPressed(KEY_F5)){
|
if(IsKeyPressed(KEY_F5)){
|
||||||
GLFWwindow* window = glfwGetCurrentContext();
|
|
||||||
glfwSetWindowShouldClose(window, GLFW_TRUE);
|
|
||||||
shouldReload = true;
|
shouldReload = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1 +1 @@
|
||||||
Subproject commit 2788d71e823b522b178db3b3660ce93689534e6d
|
Subproject commit 04be246001599f5995fa2f2d8c91a0f198d3f34c
|
||||||
|
|
@ -1 +1 @@
|
||||||
Subproject commit aa81c167f10707ea173ea1190eda18e57d841b8f
|
Subproject commit 25c8c65a6e5f0f4d4b564a0343861898c6f2778b
|
||||||
|
|
@ -1 +1 @@
|
||||||
Subproject commit d3ea64983212f7451a9cfbf644da8a5c43dbc706
|
Subproject commit c1ab645ca298a2801097931d1079b10ff7eb9df8
|
||||||
Loading…
Reference in New Issue