mirror of
git://jb55.com/damus
synced 2024-09-29 16:30:44 +00:00
nscript: load script view
This allows you to open and run scripts for testing purposes, but only from external links such as nostr:nscript...
This commit is contained in:
parent
640fbf23ea
commit
a04a401292
@ -753,15 +753,6 @@ static char *instr_name(enum instr_tag tag)
|
||||
return unk;
|
||||
}
|
||||
|
||||
static INLINE int was_section_parsed(struct module *module,
|
||||
enum section_tag section)
|
||||
{
|
||||
if (section == section_custom)
|
||||
return module->custom_sections > 0;
|
||||
|
||||
return module->parsed & (1 << section);
|
||||
}
|
||||
|
||||
static INLINE int was_name_section_parsed(struct module *module,
|
||||
enum name_subsection_tag subsection)
|
||||
{
|
||||
@ -1322,7 +1313,7 @@ static int parse_valtype(struct wasm_parser *p, enum valtype *valtype)
|
||||
}
|
||||
|
||||
if (unlikely(!is_valtype((unsigned char)*valtype))) {
|
||||
cursor_print_around(&p->cur, 10);
|
||||
//cursor_print_around(&p->cur, 10);
|
||||
p->cur.p = start;
|
||||
return parse_err(p, "0x%02x is not a valid valtype tag", *valtype);
|
||||
}
|
||||
@ -1684,7 +1675,7 @@ static int parse_reftype(struct wasm_parser *p, enum reftype *reftype)
|
||||
}
|
||||
|
||||
if (!is_valid_reftype(tag)) {
|
||||
cursor_print_around(&p->cur, 10);
|
||||
//cursor_print_around(&p->cur, 10);
|
||||
parse_err(p, "invalid reftype: 0x%02x", tag);
|
||||
return 0;
|
||||
}
|
||||
@ -2176,6 +2167,7 @@ static int parse_const_expr(struct expr_parser *p, struct expr *expr)
|
||||
}
|
||||
|
||||
if (unlikely(!is_const_instr(tag))) {
|
||||
//cursor_print_around(p->code, 20);
|
||||
return note_error(p->errs, p->code,
|
||||
"invalid const expr instruction: '%s'",
|
||||
instr_name(tag));
|
||||
@ -2551,7 +2543,7 @@ static int parse_wdata(struct wasm_parser *p, struct wdata *data)
|
||||
}
|
||||
|
||||
if (tag > 2) {
|
||||
cursor_print_around(&p->cur, 10);
|
||||
//cursor_print_around(&p->cur, 10);
|
||||
return parse_err(p, "invalid datasegment tag: 0x%x", tag);
|
||||
}
|
||||
|
||||
|
@ -837,4 +837,14 @@ static INLINE struct callframe *top_callframes(struct cursor *cur, int top)
|
||||
return (struct callframe*)cursor_topn(cur, sizeof(struct callframe), top);
|
||||
}
|
||||
|
||||
static INLINE int was_section_parsed(struct module *module,
|
||||
enum section_tag section)
|
||||
{
|
||||
if (section == section_custom)
|
||||
return module->custom_sections > 0;
|
||||
|
||||
return module->parsed & (1 << section);
|
||||
}
|
||||
|
||||
|
||||
#endif /* PROTOVERSE_WASM_H */
|
||||
|
@ -46,6 +46,7 @@
|
||||
4C0A3F93280F66F5000448DE /* ReplyMap.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C0A3F92280F66F5000448DE /* ReplyMap.swift */; };
|
||||
4C190F202A535FC200027FD5 /* CustomizeZapModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C190F1F2A535FC200027FD5 /* CustomizeZapModel.swift */; };
|
||||
4C190F222A53950D00027FD5 /* bool_setting.wasm in Resources */ = {isa = PBXBuildFile; fileRef = 4C190F212A53950D00027FD5 /* bool_setting.wasm */; };
|
||||
4C190F252A547D2000027FD5 /* LoadScript.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C190F242A547D2000027FD5 /* LoadScript.swift */; };
|
||||
4C198DEF29F88C6B004C165C /* BlurHashEncode.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C198DEB29F88C6B004C165C /* BlurHashEncode.swift */; };
|
||||
4C198DF029F88C6B004C165C /* Readme.md in Resources */ = {isa = PBXBuildFile; fileRef = 4C198DEC29F88C6B004C165C /* Readme.md */; };
|
||||
4C198DF129F88C6B004C165C /* License.txt in Resources */ = {isa = PBXBuildFile; fileRef = 4C198DED29F88C6B004C165C /* License.txt */; };
|
||||
@ -470,6 +471,7 @@
|
||||
4C0A3F92280F66F5000448DE /* ReplyMap.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReplyMap.swift; sourceTree = "<group>"; };
|
||||
4C190F1F2A535FC200027FD5 /* CustomizeZapModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CustomizeZapModel.swift; sourceTree = "<group>"; };
|
||||
4C190F212A53950D00027FD5 /* bool_setting.wasm */ = {isa = PBXFileReference; lastKnownFileType = file; name = bool_setting.wasm; path = nostrscript/bool_setting.wasm; sourceTree = SOURCE_ROOT; };
|
||||
4C190F242A547D2000027FD5 /* LoadScript.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoadScript.swift; sourceTree = "<group>"; };
|
||||
4C198DEB29F88C6B004C165C /* BlurHashEncode.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BlurHashEncode.swift; sourceTree = "<group>"; };
|
||||
4C198DEC29F88C6B004C165C /* Readme.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = Readme.md; sourceTree = "<group>"; };
|
||||
4C198DED29F88C6B004C165C /* License.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = License.txt; sourceTree = "<group>"; };
|
||||
@ -974,6 +976,14 @@
|
||||
path = Zaps;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
4C190F232A547D1700027FD5 /* NostrScript */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
4C190F242A547D2000027FD5 /* LoadScript.swift */,
|
||||
);
|
||||
path = NostrScript;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
4C198DEA29F88C6B004C165C /* BlurHash */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
@ -1048,6 +1058,7 @@
|
||||
4C75EFA227FA576C0006080F /* Views */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
4C190F232A547D1700027FD5 /* NostrScript */,
|
||||
4C7D09692A0AEA0400943473 /* CodeScanner */,
|
||||
4C7D095A2A098C5C00943473 /* Wallet */,
|
||||
4C8D1A6D29F31E4100ACDF75 /* Buttons */,
|
||||
@ -1753,6 +1764,7 @@
|
||||
4C285C86283892E7008A31F1 /* CreateAccountModel.swift in Sources */,
|
||||
4C64987C286D03E000EAE2B3 /* DirectMessagesView.swift in Sources */,
|
||||
7C902AE32981D55B002AB16E /* ZoomableScrollView.swift in Sources */,
|
||||
4C190F252A547D2000027FD5 /* LoadScript.swift in Sources */,
|
||||
4CE8794C2995B59E00F758CC /* RelayMetadatas.swift in Sources */,
|
||||
4C363A8C28236B92006E126D /* PubkeyView.swift in Sources */,
|
||||
4CDA128A29E9D10C0006FA5A /* SignalView.swift in Sources */,
|
||||
|
@ -224,6 +224,12 @@ struct ContentView: View {
|
||||
navigationCoordinator.push(route: Route.Wallet(wallet: damus_state!.wallet))
|
||||
}
|
||||
|
||||
func open_script(_ script: [UInt8]) {
|
||||
print("pushing script nav")
|
||||
let model = ScriptModel(data: script, state: .not_loaded)
|
||||
navigationCoordinator.push(route: Route.Script(script: model))
|
||||
}
|
||||
|
||||
func open_profile(id: String) {
|
||||
let profile_model = ProfileModel(pubkey: id, damus: damus_state!)
|
||||
let followers = FollowersModel(damus_state: damus_state!, target: id)
|
||||
@ -331,7 +337,9 @@ struct ContentView: View {
|
||||
case .filter(let filt): self.open_search(filt: filt)
|
||||
case .profile(let id): self.open_profile(id: id)
|
||||
case .event(let ev): self.open_event(ev: ev)
|
||||
case .wallet_connect(let nwc): self.open_wallet(nwc: nwc)}
|
||||
case .wallet_connect(let nwc): self.open_wallet(nwc: nwc)
|
||||
case .script(let data): self.open_script(data)
|
||||
}
|
||||
}
|
||||
}
|
||||
.onReceive(handle_notify(.compose)) { notif in
|
||||
@ -946,6 +954,7 @@ enum OpenResult {
|
||||
case filter(NostrFilter)
|
||||
case event(NostrEvent)
|
||||
case wallet_connect(WalletConnectURL)
|
||||
case script([UInt8])
|
||||
}
|
||||
|
||||
func on_open_url(state: DamusState, url: URL, result: @escaping (OpenResult?) -> Void) {
|
||||
@ -973,5 +982,9 @@ func on_open_url(state: DamusState, url: URL, result: @escaping (OpenResult?) ->
|
||||
result(.filter(filt))
|
||||
break
|
||||
// TODO: handle filter searches?
|
||||
case .script(let script):
|
||||
result(.script(script))
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -11,6 +11,7 @@ import Foundation
|
||||
enum NostrLink: Equatable {
|
||||
case ref(ReferencedId)
|
||||
case filter(NostrFilter)
|
||||
case script([UInt8])
|
||||
}
|
||||
|
||||
func encode_pubkey_uri(_ ref: ReferencedId) -> String {
|
||||
@ -105,6 +106,8 @@ func decode_nostr_bech32_uri(_ s: String) -> NostrLink? {
|
||||
return .ref(ReferencedId(ref_id: pubkey, relay_id: nil, key: "p"))
|
||||
case .note(let id):
|
||||
return .ref(ReferencedId(ref_id: id, relay_id: nil, key: "e"))
|
||||
case .nscript(let data):
|
||||
return .script(data)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -12,6 +12,7 @@ enum Bech32Object {
|
||||
case nsec(String)
|
||||
case npub(String)
|
||||
case note(String)
|
||||
case nscript([UInt8])
|
||||
|
||||
static func parse(_ str: String) -> Bech32Object? {
|
||||
guard let decoded = try? bech32_decode(str) else {
|
||||
@ -24,6 +25,8 @@ enum Bech32Object {
|
||||
return .nsec(hex_encode(decoded.data))
|
||||
} else if decoded.hrp == "note" {
|
||||
return .note(hex_encode(decoded.data))
|
||||
} else if decoded.hrp == "nscript" {
|
||||
return .nscript(decoded.data.bytes)
|
||||
}
|
||||
|
||||
return nil
|
||||
|
@ -16,6 +16,7 @@ enum Route: Hashable {
|
||||
case Following(following: FollowingModel)
|
||||
case MuteList(users: [String])
|
||||
case RelayConfig
|
||||
case Script(script: ScriptModel)
|
||||
case Bookmarks
|
||||
case Config
|
||||
case EditMetadata
|
||||
@ -105,6 +106,8 @@ enum Route: Hashable {
|
||||
WalletScannerView(result: walletScanResult)
|
||||
case .FollowersYouKnow(let friendedFollowers, let followers):
|
||||
FollowersYouKnowView(damus_state: damusState, friended_followers: friendedFollowers, followers: followers)
|
||||
case .Script(let load_model):
|
||||
LoadScript(pool: damusState.pool, model: load_model)
|
||||
}
|
||||
}
|
||||
|
||||
@ -172,8 +175,10 @@ enum Route: Hashable {
|
||||
return true
|
||||
case (.FollowersYouKnow(_, _), .FollowersYouKnow(_, _)):
|
||||
return true
|
||||
case (.Script(_), .Script(_)):
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
@ -259,6 +264,9 @@ enum Route: Hashable {
|
||||
hasher.combine("followersYouKnow")
|
||||
hasher.combine(friendedFollowers)
|
||||
hasher.combine(followers.sub_id)
|
||||
case .Script(let model):
|
||||
hasher.combine("script")
|
||||
hasher.combine(model.data.count)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
163
damus/Views/NostrScript/LoadScript.swift
Normal file
163
damus/Views/NostrScript/LoadScript.swift
Normal file
@ -0,0 +1,163 @@
|
||||
//
|
||||
// LoadScript.swift
|
||||
// damus
|
||||
//
|
||||
// Created by William Casarin on 2023-07-04.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
struct ScriptLoaded {
|
||||
let script: NostrScript
|
||||
let state: LoadedState
|
||||
}
|
||||
|
||||
enum LoadedState {
|
||||
case loaded
|
||||
case running
|
||||
case ran(NostrScriptRunResult)
|
||||
}
|
||||
|
||||
enum LoadScriptState {
|
||||
case not_loaded
|
||||
case loading
|
||||
case loaded(ScriptLoaded)
|
||||
case failed(NostrScriptLoadErr)
|
||||
|
||||
static func loaded(script: NostrScript) -> LoadScriptState {
|
||||
return .loaded(ScriptLoaded(script: script, state: .loaded))
|
||||
}
|
||||
}
|
||||
|
||||
class ScriptModel: ObservableObject {
|
||||
var data: [UInt8]
|
||||
@Published var state: LoadScriptState
|
||||
|
||||
init(data: [UInt8], state: LoadScriptState) {
|
||||
self.data = data
|
||||
self.state = state
|
||||
}
|
||||
|
||||
func run() async {
|
||||
guard case .loaded(let script) = state else {
|
||||
return
|
||||
}
|
||||
self.state = .loaded(.init(script: script.script, state: .running))
|
||||
|
||||
let t = Task.detached {
|
||||
return script.script.run()
|
||||
}
|
||||
|
||||
let res = await t.value
|
||||
self.state = .loaded(.init(script: script.script, state: .ran(res)))
|
||||
}
|
||||
|
||||
@MainActor
|
||||
func load(pool: RelayPool) async {
|
||||
guard case .not_loaded = state else {
|
||||
return
|
||||
}
|
||||
self.state = .loading
|
||||
let script = NostrScript(pool: pool, data: self.data)
|
||||
let t = Task.detached {
|
||||
print("loading script")
|
||||
return script.load()
|
||||
}
|
||||
|
||||
let load_err = await t.value
|
||||
|
||||
let t2 = Task { @MainActor in
|
||||
if let load_err {
|
||||
self.state = .failed(load_err)
|
||||
return
|
||||
}
|
||||
|
||||
self.state = .loaded(script: script)
|
||||
}
|
||||
|
||||
await t2.value
|
||||
}
|
||||
}
|
||||
|
||||
struct LoadScript: View {
|
||||
let pool: RelayPool
|
||||
|
||||
@ObservedObject var model: ScriptModel
|
||||
|
||||
func ScriptView(_ script: ScriptLoaded) -> some View {
|
||||
ScrollView {
|
||||
VStack {
|
||||
let imports = script.script.imports()
|
||||
|
||||
(Text(verbatim: "\(imports.count)") +
|
||||
Text(" Imports"))
|
||||
.font(.title)
|
||||
|
||||
ForEach(imports.indices, id: \.self) { ind in
|
||||
Text(imports[ind])
|
||||
}
|
||||
|
||||
switch script.state {
|
||||
case .loaded:
|
||||
BigButton("Run") {
|
||||
Task {
|
||||
await model.run()
|
||||
}
|
||||
}
|
||||
case .running:
|
||||
Text("Running...")
|
||||
case .ran(let result):
|
||||
switch result {
|
||||
case .runtime_err(let errs):
|
||||
Text("Runtime error")
|
||||
.font(.title2)
|
||||
ForEach(errs.indices, id: \.self) { ind in
|
||||
Text(verbatim: errs[ind])
|
||||
}
|
||||
case .suspend:
|
||||
Text("Ran to suspension.")
|
||||
case .finished(let code):
|
||||
Text("Executed successfuly, returned with code \(code)")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
Group {
|
||||
switch self.model.state {
|
||||
case .not_loaded:
|
||||
ProgressView()
|
||||
.progressViewStyle(.circular)
|
||||
case .loading:
|
||||
ProgressView()
|
||||
.progressViewStyle(.circular)
|
||||
case .loaded(let loaded):
|
||||
ScriptView(loaded)
|
||||
case .failed(let load_err):
|
||||
VStack(spacing: 20) {
|
||||
Text("NostrScript Error")
|
||||
.font(.title)
|
||||
switch load_err {
|
||||
case .parse:
|
||||
Text("Failed to parse")
|
||||
case .module_init:
|
||||
Text("Failed to initialize")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.task {
|
||||
await model.load(pool: self.pool)
|
||||
}
|
||||
.navigationTitle("NostrScript")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
#Preview {
|
||||
LoadScript()
|
||||
}
|
||||
*/
|
@ -38,13 +38,13 @@ final class NostrScriptTests: XCTestCase {
|
||||
func test_bool_set() throws {
|
||||
var data = try load_bool_set_test_wasm().bytes
|
||||
let pool = RelayPool()
|
||||
let script = NostrScript(pool: pool)
|
||||
let script = NostrScript(pool: pool, data: data)
|
||||
let pk = "32e1827635450ebb3c5a7d12c1f8e7b2b514439ac10a67eef3d9fd9c5c68e245"
|
||||
UserSettingsStore.pubkey = pk
|
||||
let key = pk_setting_key(pk, key: "nozaps")
|
||||
UserDefaults.standard.set(true, forKey: key)
|
||||
|
||||
let load_err = script.load(wasm: &data)
|
||||
let load_err = script.load()
|
||||
XCTAssertNil(load_err)
|
||||
|
||||
let res = script.run()
|
||||
@ -62,9 +62,9 @@ final class NostrScriptTests: XCTestCase {
|
||||
func test_nostrscript() throws {
|
||||
var data = try loadTestWasm().bytes
|
||||
let pool = RelayPool()
|
||||
let script = NostrScript(pool: pool)
|
||||
let script = NostrScript(pool: pool, data: data)
|
||||
|
||||
let load_err = script.load(wasm: &data)
|
||||
let load_err = script.load()
|
||||
XCTAssertNil(load_err)
|
||||
|
||||
let res = script.run()
|
||||
|
@ -41,21 +41,29 @@ enum NostrScriptLoadResult {
|
||||
case loaded(wasm_interp)
|
||||
}
|
||||
|
||||
enum NostrScriptError: Error {
|
||||
case not_loaded
|
||||
}
|
||||
|
||||
class NostrScript {
|
||||
private var interp: wasm_interp
|
||||
private var parser: wasm_parser
|
||||
var waiting_on: NScriptWaiting?
|
||||
var loaded: Bool
|
||||
var data: [UInt8]
|
||||
|
||||
private(set) var runstate: NostrScriptRunResult?
|
||||
private(set) var pool: RelayPool
|
||||
private(set) var event: NostrResponse?
|
||||
|
||||
init(pool: RelayPool) {
|
||||
init(pool: RelayPool, data: [UInt8]) {
|
||||
self.interp = wasm_interp()
|
||||
self.parser = wasm_parser()
|
||||
self.pool = pool
|
||||
self.event = nil
|
||||
self.runstate = nil
|
||||
self.loaded = false
|
||||
self.data = data
|
||||
}
|
||||
|
||||
deinit {
|
||||
@ -80,15 +88,37 @@ class NostrScript {
|
||||
}
|
||||
}
|
||||
|
||||
func test(_ str: String) {
|
||||
print("hello from \(str)")
|
||||
func imports() -> [String] {
|
||||
guard self.loaded,
|
||||
was_section_parsed(interp.module, section_import) > 0,
|
||||
let module = maybe_pointee(interp.module)
|
||||
else {
|
||||
return []
|
||||
}
|
||||
|
||||
var imports = [String]()
|
||||
|
||||
var i = 0
|
||||
while i < module.import_section.num_imports {
|
||||
let imp = module.import_section.imports[i]
|
||||
|
||||
imports.append(String(cString: imp.name))
|
||||
|
||||
i += 1
|
||||
}
|
||||
|
||||
return imports
|
||||
}
|
||||
|
||||
func load(wasm: inout [UInt8]) -> NostrScriptLoadErr? {
|
||||
switch nscript_load(&parser, &interp, &wasm, UInt(wasm.count)) {
|
||||
func load() -> NostrScriptLoadErr? {
|
||||
guard !loaded else {
|
||||
return nil
|
||||
}
|
||||
switch nscript_load(&parser, &interp, &self.data, UInt(data.count)) {
|
||||
case NSCRIPT_LOADED:
|
||||
print("load num_exports \(interp.module.pointee.export_section.num_exports)")
|
||||
interp.context = Unmanaged.passUnretained(self).toOpaque()
|
||||
self.loaded = true
|
||||
return nil
|
||||
case NSCRIPT_INIT_ERR:
|
||||
return .module_init
|
||||
@ -292,7 +322,9 @@ public func nscript_set_bool(interp: UnsafeMutablePointer<wasm_interp>?, setting
|
||||
}
|
||||
|
||||
let key = pk_setting_key(UserSettingsStore.pubkey ?? "", key: setting)
|
||||
UserDefaults.standard.set(val > 0 ? true : false, forKey: key)
|
||||
let b = val > 0 ? true : false
|
||||
print("nscript setting bool setting \(setting) to \(b)")
|
||||
UserDefaults.standard.set(b, forKey: key)
|
||||
|
||||
stack_push_i32(interp, 1);
|
||||
return 1;
|
||||
@ -316,7 +348,7 @@ public func nscript_pool_send_to(interp: UnsafeMutablePointer<wasm_interp>?, pre
|
||||
}
|
||||
|
||||
func nscript_pool_send(script: NostrScript, req req_str: String) -> Int32 {
|
||||
script.test("pool_send: '\(req_str)'")
|
||||
//script.test("pool_send: '\(req_str)'")
|
||||
|
||||
DispatchQueue.main.sync {
|
||||
script.pool.send_raw(.custom(req_str), skip_ephemeral: false)
|
||||
|
Loading…
Reference in New Issue
Block a user