Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
75 changes: 75 additions & 0 deletions integration-tests/tests/new_features.rs
Original file line number Diff line number Diff line change
Expand Up @@ -281,6 +281,81 @@ fn win32_bind_pattern() {
assert_eq!(result, "true", "win32.bind().GetTickCount64() failed");
}

#[test]
fn win32_define_bound_functions() {
let mut rt = Runtime::new(".");
assert_js(
&mut rt,
r#"(function () {
var msvcrt = NSWinRT.win32.define('msvcrt.dll', {
pow: { args: ['f64', 'f64'], ret: 'f64' },
abs: { args: ['i32'], ret: 'i32' },
memchr: { args: ['pointer', 'i32', 'u64'], ret: 'pointer' },
wcslen: { args: ['wstr'], ret: 'u64' },
strlen: { args: ['str'], ret: 'u64' },
});
var kernel32 = NSWinRT.win32.define('kernel32.dll', {
SetLastError: { args: ['u32'], ret: 'void' },
GetLastError: { args: [], ret: 'u32' },
MultiByteToWideChar: { args: ['u32', 'u32', 'pointer', 'i32', 'pointer', 'i32'], ret: 'i32' },
});
var user32 = NSWinRT.win32.define('user32.dll', { IsWindow: { args: ['pointer'], ret: 'bool' } });
var ucrt = NSWinRT.win32.define('ucrtbase.dll', { sqrtf: { args: ['f32'], ret: 'f32' } });
var bytes = new Uint8Array([1, 2, 3, 4]);
var wide = new Uint16Array(8);
var n = kernel32.MultiByteToWideChar(65001, 0, new Uint8Array([104, 105]), 2, wide, 8);
var checks = [
msvcrt.pow(2, 10) === 1024,
ucrt.sqrtf(2.25) === 1.5,
msvcrt.abs(-7) === 7,
msvcrt.abs(true) === 1,
msvcrt.abs('-7') === 7,
msvcrt.memchr(bytes, 3, 4) - msvcrt.memchr(bytes, 1, 4) === 2,
msvcrt.wcslen('nativescript') === 12,
msvcrt.strlen('abc') === 3,
kernel32.SetLastError(1234) === undefined,
kernel32.GetLastError() === 1234,
user32.IsWindow(null) === false,
n === 2 && String.fromCharCode(wide[0], wide[1]) === 'hi',
];
// Call again so TurboFan takes the fast overloads.
for (var i = 0; i < 20000; i++) {
if (msvcrt.abs(-i) !== i || msvcrt.pow(i, 1) !== i || msvcrt.wcslen('ab') !== 2) return 'fast path ' + i;
}
var failed = [];
for (var j = 0; j < checks.length; j++) if (!checks[j]) failed.push(j);
return failed.length === 0 ? true : 'failed checks ' + failed.join(',');
})()"#,
"win32.define bound functions",
);
}

#[test]
fn win32_bind_locks_argument_types() {
let mut rt = Runtime::new(".");
assert_js(
&mut rt,
r#"(function () {
var lstrlenW = NSWinRT.win32.bind('kernel32.dll').lstrlenW;
return lstrlenW('abc') === 3 && lstrlenW(null) === 0 && lstrlenW('hello') === 5;
})()"#,
"win32.bind locks the first call's types in",
);
}

#[test]
fn win32_pointer_argument_rejects_plain_objects() {
let mut rt = Runtime::new(".");
assert_js(
&mut rt,
r#"(function () {
var msvcrt = NSWinRT.win32.define('msvcrt.dll', { memchr: { args: ['pointer', 'i32', 'u64'], ret: 'pointer' } });
try { msvcrt.memchr({}, 1, 4); return 'no throw'; } catch (e) { return e instanceof TypeError && /argument 1/.test(e.message); }
})()"#,
"a plain object is not a pointer",
);
}

#[test]
fn win32_exports_returns_array() {
let mut rt = Runtime::new(".");
Expand Down
93 changes: 93 additions & 0 deletions packages/demo/win32-bench.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
// Win32 call overhead micro benchmarks, for every engine:
// classic: PLAYGROUND_SCRIPT_PATH=packages/demo/win32-bench.js target/release/playground.exe
// napi: nativescript-windows packages/demo/win32-bench.js (standalone engines)
// Node: node packages/demo/win32-bench.js
// An optional filter argument (NSWIN_BENCH_FILTER on the classic engine) selects benchmarks.
'use strict';

if (!globalThis.NSWinRT || !globalThis.NSWinRT.win32) {
if (typeof require === 'function') require('../../windows-napi/nswinrt.js');
}
var win32 = globalThis.NSWinRT.win32;
var filter = (typeof process !== 'undefined' && process.argv && process.argv[2]) ||
(typeof process !== 'undefined' && process.env && process.env.NSWIN_BENCH_FILTER) || '';
var N = 1000000;

function bench(name, fn) {
if (filter && name.indexOf(filter) < 0) return;
for (var i = 0; i < 10000; i++) fn(i); // warm up: the bound function and TurboFan
var t0 = performance.now();
for (var i = 0; i < N; i++) fn(i);
var ms = performance.now() - t0;
var ns = (ms * 1e6 / N).toFixed(1);
while (name.length < 36) name += ' ';
while (ns.length < 7) ns = ' ' + ns;
console.log(name + ns + ' ns/call');
}

var kernel32 = win32.define('kernel32.dll', {
GetCurrentThreadId: { args: [], ret: 'u32' },
GetTickCount64: { args: [], ret: 'u64' },
MulDiv: { args: ['i32', 'i32', 'i32'], ret: 'i32' },
SetLastError: { args: ['u32'], ret: 'void' },
lstrlenW: { args: ['wstr'], ret: 'i32' },
lstrcmpW: { args: ['wstr', 'wstr'], ret: 'i32' },
MultiByteToWideChar: { args: ['u32', 'u32', 'pointer', 'i32', 'pointer', 'i32'], ret: 'i32' },
});
var user32 = win32.define('user32.dll', {
IsWindow: { args: ['pointer'], ret: 'bool' },
GetSystemMetrics: { args: ['i32'], ret: 'i32' },
});
var msvcrt = win32.define('msvcrt.dll', {
abs: { args: ['i32'], ret: 'i32' },
labs: { args: ['i64'], ret: 'i64' },
fabs: { args: ['f64'], ret: 'f64' },
pow: { args: ['f64', 'f64'], ret: 'f64' },
memcmp: { args: ['pointer', 'pointer', 'u64'], ret: 'i32' },
memchr: { args: ['pointer', 'i32', 'u64'], ret: 'pointer' },
wcslen: { args: ['wstr'], ret: 'u64' },
});
var auto = win32.bind('kernel32.dll', 'u32');

var utf8 = new Uint8Array(16);
for (var i = 0; i < 16; i++) utf8[i] = 65 + i;
var wide = new Uint16Array(32);

// integers and void
bench('GetCurrentThreadId()', function () { return kernel32.GetCurrentThreadId(); });
bench('SetLastError(u32) -> void', function (i) { kernel32.SetLastError(i); });
bench('abs(i32)', function (i) { return msvcrt.abs(-i); });
bench('MulDiv(i32, i32, i32)', function (i) { return kernel32.MulDiv(i, 3, 2); });
bench('GetSystemMetrics(i32)', function () { return user32.GetSystemMetrics(0); });
bench('IsWindow(null) -> bool', function () { return user32.IsWindow(null); });

// floats
bench('fabs(f64)', function (i) { return msvcrt.fabs(-i); });
bench('pow(f64, f64)', function (i) { return msvcrt.pow(i, 0.5); });

// 64-bit and pointers
bench('GetTickCount64() -> u64', function () { return kernel32.GetTickCount64(); });
bench('labs(i64)', function (i) { return msvcrt.labs(-i); });
bench('memcmp(buffer, buffer, u64)', function () { return msvcrt.memcmp(utf8, utf8, 16); });
bench('memchr(buffer, i32, u64) -> pointer', function () { return msvcrt.memchr(utf8, 70, 16); });

// strings
bench('lstrlenW(string)', function () { return kernel32.lstrlenW('nativescript'); });
bench('wcslen(string) -> u64', function () { return msvcrt.wcslen('nativescript'); });
bench('lstrcmpW(string, string)', function () {
return kernel32.lstrcmpW('nativescript', 'nativescript');
});

// six arguments: four in registers, two on the stack
bench('MultiByteToWideChar(6 args)', function () {
return kernel32.MultiByteToWideChar(65001, 0, utf8, 16, wide, 32);
});

// the auto-typed and generic paths
bench('bind().GetCurrentThreadId()', function () { return auto.GetCurrentThreadId(); });
bench('call(dll, name, ret)', function () {
return win32.call('kernel32.dll', 'GetCurrentThreadId', 'u32');
});
bench('call(dll, name, ret, {i32})', function (i) {
return win32.call('kernel32.dll', 'SetLastError', 'void', { type: 'u32', value: i });
});
Loading