skia-canvas
Version:
A multi-threaded, GPU-accelerated, Canvas API for Node
102 lines (85 loc) • 2.96 kB
JavaScript
//
// Neon <-> Node interface
//
const {inspect} = require('util')
// if defined, throw TypeErrors for canvas API calls with invalid arguments
const STRICT = !["0", "false", "off"].includes((process.env.SKIA_CANVAS_STRICT || "0").trim().toLowerCase())
const ø = Symbol.for('📦'), // the attr containing the boxed struct
core = (obj) => (obj||{})[ø], // dereference the boxed struct
wrap = (type, struct) => { // create new instance for struct
let obj = internal(Object.create(type.prototype), ø, struct)
return struct && internal(obj, 'native', neon[type.name])
},
neon = Object.entries(require('../skia.node')).reduce( (api, [name, fn]) => {
let [_, struct, getset, attr] = name.match(/(.*?)_(?:([sg]et)_)?(.*)/),
cls = api[struct] || (api[struct] = {}),
slot = getset ? (cls[attr] || (cls[attr] = {})) : cls
slot[getset || attr] = fn
return api
}, {})
class RustClass{
constructor(type){
internal(this, 'native', neon[type.name])
}
alloc(...args){
try{
return this.init('new', ...args)
}catch(error){
rustError(error, this.alloc)
}
}
init(fn, ...args){
try{
return internal(this, ø, this.native[fn](null, ...args))
}catch(error){
rustError(error, this.init)
}
}
ref(key, val){
return arguments.length > 1 ? this[Symbol.for(key)] = val : this[Symbol.for(key)]
}
prop(attr, ...vals){
try{
let getset = arguments.length > 1 ? 'set' : 'get'
return this.native[attr][getset](this[ø], ...vals)
}catch(error){
rustError(error, this.prop)
}
}
ƒ(fn, ...args){
try{
return this.native[fn](this[ø], ...args)
}catch(error){
rustError(error, this.ƒ)
}
}
}
// shorthands for attaching read-only attributes
const readOnly = (obj, attr, value) => (
Object.defineProperty(obj, attr, {value, writable:false, enumerable:true})
)
const internal = (obj, attr, value) => (
Object.defineProperty(obj, attr, {value, writable:false, enumerable:false})
)
// convert arguments list to a string of type abbreviations
function signature(args){
return args.map(v => (Array.isArray(v) ? 'a' : {string:'s', number:'n', object:'o'}[typeof v] || 'x')).join('')
}
// validate number of args in invocation
const argc = (args, ...expected) => {
if (expected.includes(args.length) || args.length > Math.max(...expected)) return
let error = new TypeError("not enough arguments")
Error.captureStackTrace(error, argc)
throw error
}
// remove internals from stack trace and filter non-strict errors
const rustError = (error, stack) => {
if (error.message.startsWith("⚠️")){
if (STRICT) error.message = error.message.substr(1)
else return
}
Error.captureStackTrace(error, stack)
throw error
}
module.exports = {neon, core, wrap, signature, argc, readOnly, RustClass, inspect, REPR:inspect.custom}