quickjs 0.1.4 → 0.1.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +65 -15
- data/ext/quickjsrb/extconf.rb +1 -0
- data/ext/quickjsrb/quickjsrb-runtime-state.c +92 -0
- data/ext/quickjsrb/quickjsrb-runtime-state.h +22 -0
- data/ext/quickjsrb/quickjsrb.c +266 -77
- data/lib/quickjs/version.rb +1 -1
- data/lib/quickjs.rb +3 -20
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6fa772eb6185fa2bc744d7bb7b0cdb2024ccaf2d2ce34cb221139aaf2e819371
|
4
|
+
data.tar.gz: de4b5651b61eaaafe975ba56ae24b954c28efc9ea0ba2a6fb34f149b62d4aa58
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1095f8d04c0c90756506b8bd16a519b207b3b2a0eda64abecec8fb541a0a5a5c0e11240abce18b03e4ee4d7b4c3a53138513218f12afa4d7e46462f3a93680ef
|
7
|
+
data.tar.gz: 3237ad5b23217199900665605629c5b69c0333ea5951f3a43cf2e26ac65d6a718ff94f262fdaeb5bf0c3b132e60cb0561d60a121045a26bb201bbfd63e8e3a9c
|
data/README.md
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
A Ruby wrapper for [QuickJS](https://bellard.org/quickjs) to run JavaScript codes via Ruby with a smaller footprint.
|
4
4
|
|
5
|
-
[](https://rubygems.org/gems/quickjs) [](https://github.com/hmsk/quickjs.rb/actions/workflows/main.yml)
|
5
|
+
[](https://rubygems.org/gems/quickjs) [](https://github.com/hmsk/quickjs.rb/actions/workflows/main.yml)
|
6
6
|
|
7
7
|
|
8
8
|
## Installation
|
@@ -17,33 +17,33 @@ gem 'quickjs'
|
|
17
17
|
|
18
18
|
## Usage
|
19
19
|
|
20
|
-
### `Quickjs.
|
20
|
+
### `Quickjs.eval_code`: Evaluate JavaScript code instantly
|
21
21
|
|
22
22
|
```rb
|
23
23
|
require 'quickjs'
|
24
24
|
|
25
|
-
Quickjs.
|
26
|
-
Quickjs.
|
27
|
-
Quickjs.
|
28
|
-
Quickjs.
|
25
|
+
Quickjs.eval_code('const fn = (n, pow) => n ** pow; fn(2,8);') # => 256
|
26
|
+
Quickjs.eval_code('const fn = (name) => `Hi, ${name}!`; fn("Itadori");') # => "Hi, Itadori!
|
27
|
+
Quickjs.eval_code("const isOne = (n) => 1 === n; func(1);") #=> true (TrueClass)
|
28
|
+
Quickjs.eval_code("const isOne = (n) => 1 === n; func(3);") #=> false (FalseClass)
|
29
29
|
|
30
30
|
# When code returns 'object' for `typeof`, the result is converted via JSON.stringify (JS) -> JSON.parse (Ruby)
|
31
|
-
Quickjs.
|
32
|
-
Quickjs.
|
31
|
+
Quickjs.eval_code("[1,2,3]") #=> [1, 2, 3] (Array)
|
32
|
+
Quickjs.eval_code("({ a: '1', b: 1 })") #=> { 'a' => '1', 'b' => 1 } (Hash)
|
33
33
|
|
34
|
-
Quickjs.
|
35
|
-
Quickjs.
|
36
|
-
Quickjs.
|
34
|
+
Quickjs.eval_code("null") #=> nil
|
35
|
+
Quickjs.eval_code('const obj = {}; obj.missingKey;') # => :undefined (Quickjs::Value::Undefined)
|
36
|
+
Quickjs.eval_code("Number('whatever')") #=> :NaN (Quickjs::Value::NAN)
|
37
37
|
```
|
38
38
|
|
39
39
|
#### Limit resources
|
40
40
|
|
41
41
|
```rb
|
42
42
|
# 1GB memory limit
|
43
|
-
Quickjs.
|
43
|
+
Quickjs.eval_code(code, { memory_limit: 1024 ** 3 })
|
44
44
|
|
45
45
|
# 1MB max stack size
|
46
|
-
Quickjs.
|
46
|
+
Quickjs.eval_code(code, { max_stack_size: 1024 ** 2 })
|
47
47
|
```
|
48
48
|
|
49
49
|
#### Enable built-in modules
|
@@ -51,11 +51,61 @@ Quickjs.evalCode(code, { memoryLimit: 1024 ** 2 })
|
|
51
51
|
```rb
|
52
52
|
# enable std module
|
53
53
|
# https://bellard.org/quickjs/quickjs.html#std-module
|
54
|
-
Quickjs.
|
54
|
+
Quickjs.eval_code(code, { features: [Quickjs::MODULE_STD] })
|
55
55
|
|
56
56
|
# enable os module
|
57
57
|
# https://bellard.org/quickjs/quickjs.html#os-module
|
58
|
-
Quickjs.
|
58
|
+
Quickjs.eval_code(code, { features: [Quickjs::MODULE_OS] })
|
59
|
+
```
|
60
|
+
|
61
|
+
### `Quickjs::VM`: Maintain a consistent VM/runtime
|
62
|
+
|
63
|
+
```rb
|
64
|
+
vm = Quickjs::VM.new
|
65
|
+
vm.eval_code('const a = { b: "c" };')
|
66
|
+
vm.eval_code('a.b;') #=> "c"
|
67
|
+
vm.eval_code('a.b = "d";')
|
68
|
+
vm.eval_code('a.b;') #=> "d"
|
69
|
+
```
|
70
|
+
|
71
|
+
#### Config VM
|
72
|
+
|
73
|
+
```rb
|
74
|
+
vm = Quickjs::VM.new(
|
75
|
+
memory_limit: 1024 ** 3,
|
76
|
+
max_stack_size: 1024 ** 2,
|
77
|
+
)
|
78
|
+
```
|
79
|
+
|
80
|
+
```rb
|
81
|
+
# enable std module
|
82
|
+
# https://bellard.org/quickjs/quickjs.html#std-module
|
83
|
+
vm = Quickjs::VM.new(
|
84
|
+
features: [::Quickjs::MODULE_STD],
|
85
|
+
)
|
86
|
+
|
87
|
+
# enable os module
|
88
|
+
# https://bellard.org/quickjs/quickjs.html#os-module
|
89
|
+
vm = Quickjs::VM.new(
|
90
|
+
features: [::Quickjs::MODULE_OS],
|
91
|
+
)
|
92
|
+
```
|
93
|
+
|
94
|
+
#### Dispose VM explicitly
|
95
|
+
|
96
|
+
```rb
|
97
|
+
vm.dispose!
|
98
|
+
```
|
99
|
+
|
100
|
+
#### Define a global function for JS
|
101
|
+
|
102
|
+
```rb
|
103
|
+
vm = Quickjs::VM.new
|
104
|
+
vm.define_function("greetingTo") do |arg1|
|
105
|
+
['Hello!', arg1].join(' ')
|
106
|
+
end
|
107
|
+
|
108
|
+
vm.eval_code("greetingTo('Rick')") #=> 'Hello! Rick'
|
59
109
|
```
|
60
110
|
|
61
111
|
## License
|
data/ext/quickjsrb/extconf.rb
CHANGED
@@ -0,0 +1,92 @@
|
|
1
|
+
#include "quickjsrb-runtime-state.h"
|
2
|
+
|
3
|
+
unsigned int hash(const char *key) {
|
4
|
+
unsigned long int value = 0;
|
5
|
+
unsigned int i = 0;
|
6
|
+
unsigned int key_len = strlen(key);
|
7
|
+
|
8
|
+
for (; i < key_len; ++i) {
|
9
|
+
value = value * 37 + key[i];
|
10
|
+
}
|
11
|
+
return value % MAX_NUM_OF_PROC;
|
12
|
+
}
|
13
|
+
|
14
|
+
ProcEntry *create_proc_entry(const char *key, VALUE proc) {
|
15
|
+
ProcEntry *entry = malloc(sizeof(ProcEntry));
|
16
|
+
entry->key = strdup(key);
|
17
|
+
entry->proc = proc;
|
18
|
+
entry->next = NULL;
|
19
|
+
return entry;
|
20
|
+
}
|
21
|
+
|
22
|
+
ProcEntryMap *create_proc_entries() {
|
23
|
+
ProcEntryMap *entryMap = malloc(sizeof(ProcEntryMap));
|
24
|
+
entryMap->entries = malloc(sizeof(ProcEntry *) * MAX_NUM_OF_PROC);
|
25
|
+
for (int i = 0; i < MAX_NUM_OF_PROC; ++i) {
|
26
|
+
entryMap->entries[i] = NULL;
|
27
|
+
}
|
28
|
+
return entryMap;
|
29
|
+
}
|
30
|
+
|
31
|
+
void set_proc(ProcEntryMap *entryMap, const char *key, VALUE proc) {
|
32
|
+
unsigned int slot = hash(key);
|
33
|
+
ProcEntry *entry = entryMap->entries[slot];
|
34
|
+
|
35
|
+
if (entry == NULL) {
|
36
|
+
entryMap->entries[slot] = create_proc_entry(key, proc);
|
37
|
+
return;
|
38
|
+
}
|
39
|
+
|
40
|
+
ProcEntry *prev;
|
41
|
+
while (entry != NULL) {
|
42
|
+
if (strcmp(entry->key, key) == 0) {
|
43
|
+
entry->proc = proc;
|
44
|
+
return;
|
45
|
+
}
|
46
|
+
prev = entry;
|
47
|
+
entry = prev->next;
|
48
|
+
}
|
49
|
+
|
50
|
+
prev->next = create_proc_entry(key, proc);
|
51
|
+
}
|
52
|
+
|
53
|
+
VALUE get_proc(ProcEntryMap *entryMap, const char *key) {
|
54
|
+
unsigned int slot = hash(key);
|
55
|
+
|
56
|
+
ProcEntry *entry = entryMap->entries[slot];
|
57
|
+
|
58
|
+
while (entry != NULL) {
|
59
|
+
if (strcmp(entry->key, key) == 0) {
|
60
|
+
return entry->proc;
|
61
|
+
}
|
62
|
+
entry = entry->next;
|
63
|
+
}
|
64
|
+
|
65
|
+
return Qnil;
|
66
|
+
}
|
67
|
+
|
68
|
+
void free_proc_entry_map(ProcEntryMap *entryMap) {
|
69
|
+
for (int i = 0; i < MAX_NUM_OF_PROC; ++i) {
|
70
|
+
ProcEntry *entry = entryMap->entries[i];
|
71
|
+
while (entry != NULL) {
|
72
|
+
ProcEntry *temp = entry;
|
73
|
+
entry = entry->next;
|
74
|
+
free(temp->key);
|
75
|
+
free(temp);
|
76
|
+
}
|
77
|
+
}
|
78
|
+
free(entryMap->entries);
|
79
|
+
free(entryMap);
|
80
|
+
}
|
81
|
+
|
82
|
+
QuickjsrbRuntimeState *create_quickjsrb_runtime_state() {
|
83
|
+
QuickjsrbRuntimeState *state = malloc(sizeof(QuickjsrbRuntimeState));
|
84
|
+
state->procs = create_proc_entries();
|
85
|
+
|
86
|
+
return state;
|
87
|
+
}
|
88
|
+
|
89
|
+
void free_quickjsrb_runtime_state(QuickjsrbRuntimeState *state) {
|
90
|
+
free_proc_entry_map(state->procs);
|
91
|
+
free(state);
|
92
|
+
}
|
@@ -0,0 +1,22 @@
|
|
1
|
+
#include "ruby.h"
|
2
|
+
|
3
|
+
#define MAX_NUM_OF_PROC 100
|
4
|
+
|
5
|
+
typedef struct ProcEntry {
|
6
|
+
char *key;
|
7
|
+
VALUE proc;
|
8
|
+
struct ProcEntry *next;
|
9
|
+
} ProcEntry;
|
10
|
+
|
11
|
+
typedef struct ProcEntryMap {
|
12
|
+
ProcEntry **entries;
|
13
|
+
} ProcEntryMap;
|
14
|
+
|
15
|
+
typedef struct QuickjsrbRuntimeState {
|
16
|
+
ProcEntryMap *procs;
|
17
|
+
} QuickjsrbRuntimeState;
|
18
|
+
|
19
|
+
QuickjsrbRuntimeState *create_quickjsrb_runtime_state();
|
20
|
+
VALUE get_proc(ProcEntryMap *entryMap, const char *key);
|
21
|
+
void set_proc(ProcEntryMap *entryMap, const char *key, VALUE proc);
|
22
|
+
void free_quickjsrb_runtime_state(QuickjsrbRuntimeState *state);
|
data/ext/quickjsrb/quickjsrb.c
CHANGED
@@ -1,114 +1,303 @@
|
|
1
1
|
#include "quickjsrb.h"
|
2
|
+
#include "quickjsrb-runtime-state.h"
|
3
|
+
|
4
|
+
struct qvmdata {
|
5
|
+
char alive;
|
6
|
+
struct JSContext *context;
|
7
|
+
struct QuickjsrbRuntimeState *state;
|
8
|
+
};
|
9
|
+
|
10
|
+
void qvm_free(void* data)
|
11
|
+
{
|
12
|
+
free(data);
|
13
|
+
}
|
14
|
+
|
15
|
+
size_t qvm_size(const void* data)
|
16
|
+
{
|
17
|
+
return sizeof(data);
|
18
|
+
}
|
19
|
+
|
20
|
+
static const rb_data_type_t qvm_type = {
|
21
|
+
.wrap_struct_name = "qvm",
|
22
|
+
.function = {
|
23
|
+
.dmark = NULL,
|
24
|
+
.dfree = qvm_free,
|
25
|
+
.dsize = qvm_size,
|
26
|
+
},
|
27
|
+
.data = NULL,
|
28
|
+
.flags = RUBY_TYPED_FREE_IMMEDIATELY,
|
29
|
+
};
|
30
|
+
|
31
|
+
VALUE qvm_alloc(VALUE self)
|
32
|
+
{
|
33
|
+
struct qvmdata *data;
|
34
|
+
|
35
|
+
return TypedData_Make_Struct(self, struct qvmdata, &qvm_type, data);
|
36
|
+
}
|
37
|
+
|
38
|
+
static JSValue js_quickjsrb_call_global (JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) {
|
39
|
+
struct qvmdata *data = JS_GetContextOpaque(ctx);
|
40
|
+
JSValue maybeFuncName = JS_ToString(ctx, argv[0]);
|
41
|
+
const char *funcName = JS_ToCString(ctx, maybeFuncName);
|
42
|
+
|
43
|
+
VALUE proc = get_proc(data->state->procs, funcName);
|
44
|
+
if (proc == Qnil) {
|
45
|
+
return JS_NewString(ctx, "missing");
|
46
|
+
}
|
47
|
+
JS_FreeValue(ctx, maybeFuncName);
|
48
|
+
|
49
|
+
// TODO: support multiple args
|
50
|
+
JSValue maybeString;
|
51
|
+
VALUE r_array = rb_ary_new2(argc);
|
52
|
+
for (int i = 1; i < argc; ++i) {
|
53
|
+
maybeString = JS_ToString(ctx, argv[i]);
|
54
|
+
const char *msg = JS_ToCString(ctx, maybeString);
|
55
|
+
rb_ary_push(r_array, rb_str_new2(msg));
|
56
|
+
}
|
57
|
+
JS_FreeValue(ctx, maybeString);
|
58
|
+
|
59
|
+
VALUE r_result = rb_funcall(proc, rb_intern("call"), argc - 1, r_array);
|
60
|
+
char *result = StringValueCStr(r_result);
|
61
|
+
return JS_NewString(ctx, result);
|
62
|
+
}
|
2
63
|
|
3
64
|
VALUE rb_mQuickjs;
|
4
|
-
VALUE rb_mQuickjsValue;
|
5
65
|
const char *undefinedId = "undefined";
|
6
66
|
const char *nanId = "NaN";
|
7
67
|
|
8
|
-
|
9
|
-
|
10
|
-
VALUE r_code,
|
11
|
-
VALUE r_memoryLimit,
|
12
|
-
VALUE r_maxStackSize,
|
13
|
-
VALUE r_enableStd,
|
14
|
-
VALUE r_enableOs
|
15
|
-
) {
|
16
|
-
JSRuntime *rt = JS_NewRuntime();
|
17
|
-
JSContext *ctx = JS_NewContext(rt);
|
18
|
-
|
19
|
-
JS_SetMemoryLimit(rt, NUM2UINT(r_memoryLimit));
|
20
|
-
JS_SetMaxStackSize(rt, NUM2UINT(r_maxStackSize));
|
21
|
-
|
22
|
-
JS_AddIntrinsicBigFloat(ctx);
|
23
|
-
JS_AddIntrinsicBigDecimal(ctx);
|
24
|
-
JS_AddIntrinsicOperators(ctx);
|
25
|
-
JS_EnableBignumExt(ctx, TRUE);
|
26
|
-
|
27
|
-
JS_SetModuleLoaderFunc(rt, NULL, js_module_loader, NULL);
|
28
|
-
js_std_add_helpers(ctx, 0, NULL);
|
29
|
-
|
30
|
-
js_std_init_handlers(rt);
|
31
|
-
if (r_enableStd == Qtrue) {
|
32
|
-
js_init_module_std(ctx, "std");
|
33
|
-
const char *enableStd = "import * as std from 'std';\n"
|
34
|
-
"globalThis.std = std;\n";
|
35
|
-
JSValue stdEval = JS_Eval(ctx, enableStd, strlen(enableStd), "<code>", JS_EVAL_TYPE_MODULE);
|
36
|
-
JS_FreeValue(ctx, stdEval);
|
37
|
-
}
|
38
|
-
|
39
|
-
if (r_enableOs == Qtrue) {
|
40
|
-
js_init_module_os(ctx, "os");
|
68
|
+
const char *featureStdId = "feature_std";
|
69
|
+
const char *featureOsId = "feature_os";
|
41
70
|
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
71
|
+
VALUE to_rb_value (JSValue jsv, JSContext *ctx) {
|
72
|
+
switch(JS_VALUE_GET_NORM_TAG(jsv)) {
|
73
|
+
case JS_TAG_INT: {
|
74
|
+
int int_res = 0;
|
75
|
+
JS_ToInt32(ctx, &int_res, jsv);
|
76
|
+
return INT2NUM(int_res);
|
46
77
|
}
|
78
|
+
case JS_TAG_FLOAT64: {
|
79
|
+
if (JS_VALUE_IS_NAN(jsv)) {
|
80
|
+
return ID2SYM(rb_intern(nanId));
|
81
|
+
}
|
82
|
+
double double_res;
|
83
|
+
JS_ToFloat64(ctx, &double_res, jsv);
|
84
|
+
return DBL2NUM(double_res);
|
85
|
+
}
|
86
|
+
case JS_TAG_BOOL: {
|
87
|
+
return JS_ToBool(ctx, jsv) > 0 ? Qtrue : Qfalse;
|
88
|
+
}
|
89
|
+
case JS_TAG_STRING: {
|
90
|
+
JSValue maybeString = JS_ToString(ctx, jsv);
|
91
|
+
const char *msg = JS_ToCString(ctx, maybeString);
|
92
|
+
return rb_str_new2(msg);
|
93
|
+
}
|
94
|
+
case JS_TAG_OBJECT: {
|
95
|
+
int promiseState = JS_PromiseState(ctx, jsv);
|
96
|
+
if (promiseState == JS_PROMISE_FULFILLED || promiseState == JS_PROMISE_PENDING) {
|
97
|
+
return to_rb_value(js_std_await(ctx, jsv), ctx);
|
98
|
+
} else if (promiseState == JS_PROMISE_REJECTED) {
|
99
|
+
return to_rb_value(JS_Throw(ctx, JS_PromiseResult(ctx, jsv)), ctx);
|
100
|
+
}
|
47
101
|
|
48
|
-
char *code = StringValueCStr(r_code);
|
49
|
-
JSValue res = JS_Eval(ctx, code, strlen(code), "<code>", JS_EVAL_TYPE_GLOBAL);
|
50
|
-
|
51
|
-
VALUE result;
|
52
|
-
if (JS_IsException(res)) {
|
53
|
-
rb_raise(rb_eRuntimeError, "Something happened by evaluating as JavaScript code");
|
54
|
-
result = Qnil;
|
55
|
-
} else if (JS_IsObject(res)) {
|
56
102
|
JSValue global = JS_GetGlobalObject(ctx);
|
57
103
|
JSValue jsonClass = JS_GetPropertyStr(ctx, global, "JSON");
|
58
104
|
JSValue stringifyFunc = JS_GetPropertyStr(ctx, jsonClass, "stringify");
|
59
|
-
JSValue strigified = JS_Call(ctx, stringifyFunc, jsonClass, 1, &
|
105
|
+
JSValue strigified = JS_Call(ctx, stringifyFunc, jsonClass, 1, &jsv);
|
60
106
|
|
61
107
|
const char *msg = JS_ToCString(ctx, strigified);
|
62
108
|
VALUE rbString = rb_str_new2(msg);
|
63
|
-
VALUE rb_cJson = rb_const_get(rb_cClass, rb_intern("JSON"));
|
64
|
-
result = rb_funcall(rb_cJson, rb_intern("parse"), 1, rbString);
|
65
109
|
|
66
110
|
JS_FreeValue(ctx, global);
|
67
111
|
JS_FreeValue(ctx, strigified);
|
68
112
|
JS_FreeValue(ctx, stringifyFunc);
|
69
113
|
JS_FreeValue(ctx, jsonClass);
|
70
|
-
|
71
|
-
|
72
|
-
}
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
114
|
+
|
115
|
+
return rb_funcall(rb_const_get(rb_cClass, rb_intern("JSON")), rb_intern("parse"), 1, rbString);
|
116
|
+
}
|
117
|
+
case JS_TAG_NULL:
|
118
|
+
return Qnil;
|
119
|
+
case JS_TAG_UNDEFINED:
|
120
|
+
return ID2SYM(rb_intern(undefinedId));
|
121
|
+
case JS_TAG_EXCEPTION: {
|
122
|
+
JSValue exceptionVal = JS_GetException(ctx);
|
123
|
+
if (JS_IsError(ctx, exceptionVal)) {
|
124
|
+
JSValue jsErrorClassName = JS_GetPropertyStr(ctx, exceptionVal, "name");
|
125
|
+
const char *errorClassName = JS_ToCString(ctx, jsErrorClassName);
|
126
|
+
|
127
|
+
JSValue jsErrorClassMessage = JS_GetPropertyStr(ctx, exceptionVal, "message");
|
128
|
+
const char *errorClassMessage = JS_ToCString(ctx, jsErrorClassMessage);
|
129
|
+
|
130
|
+
JS_FreeValue(ctx, jsErrorClassMessage);
|
131
|
+
JS_FreeValue(ctx, jsErrorClassName);
|
132
|
+
|
133
|
+
rb_raise(rb_eRuntimeError, "%s: %s", errorClassName, errorClassMessage);
|
78
134
|
} else {
|
79
|
-
|
80
|
-
|
81
|
-
|
135
|
+
const char *errorMessage = JS_ToCString(ctx, exceptionVal);
|
136
|
+
|
137
|
+
rb_raise(rb_eRuntimeError, "%s", errorMessage);
|
82
138
|
}
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
139
|
+
|
140
|
+
JS_FreeValue(ctx, exceptionVal);
|
141
|
+
return Qnil;
|
142
|
+
}
|
143
|
+
case JS_TAG_BIG_INT: {
|
144
|
+
JSValue toStringFunc = JS_GetPropertyStr(ctx, jsv, "toString");
|
145
|
+
JSValue strigified = JS_Call(ctx, toStringFunc, jsv, 0, NULL);
|
146
|
+
|
147
|
+
const char *msg = JS_ToCString(ctx, strigified);
|
148
|
+
VALUE rbString = rb_str_new2(msg);
|
149
|
+
JS_FreeValue(ctx, strigified);
|
150
|
+
JS_FreeValue(ctx, toStringFunc);
|
151
|
+
|
152
|
+
return rb_funcall(rbString, rb_intern("to_i"), 0, NULL);
|
153
|
+
}
|
154
|
+
case JS_TAG_BIG_FLOAT:
|
155
|
+
case JS_TAG_BIG_DECIMAL:
|
156
|
+
case JS_TAG_SYMBOL:
|
157
|
+
default:
|
158
|
+
return Qnil;
|
95
159
|
}
|
96
|
-
|
160
|
+
}
|
161
|
+
|
162
|
+
VALUE qvm_m_initialize(int argc, VALUE* argv, VALUE self)
|
163
|
+
{
|
164
|
+
VALUE r_opts;
|
165
|
+
rb_scan_args(argc, argv, ":", &r_opts);
|
166
|
+
if (NIL_P(r_opts)) r_opts = rb_hash_new();
|
167
|
+
|
168
|
+
VALUE r_memoryLimit = rb_hash_aref(r_opts, ID2SYM(rb_intern("memory_limit")));
|
169
|
+
if (NIL_P(r_memoryLimit)) r_memoryLimit = UINT2NUM(1024 * 1024 * 128);
|
170
|
+
VALUE r_maxStackSize = rb_hash_aref(r_opts, ID2SYM(rb_intern("max_stack_size")));
|
171
|
+
if (NIL_P(r_maxStackSize)) r_maxStackSize = UINT2NUM(1024 * 1024 * 4);
|
172
|
+
VALUE r_features = rb_hash_aref(r_opts, ID2SYM(rb_intern("features")));
|
173
|
+
if (NIL_P(r_features)) r_features = rb_ary_new();
|
174
|
+
|
175
|
+
struct qvmdata *data;
|
176
|
+
TypedData_Get_Struct(self, struct qvmdata, &qvm_type, data);
|
177
|
+
|
178
|
+
QuickjsrbRuntimeState *state= create_quickjsrb_runtime_state();
|
179
|
+
JSRuntime *runtime = JS_NewRuntime();
|
180
|
+
data->context = JS_NewContext(runtime);
|
181
|
+
data->state = state;
|
182
|
+
data->alive = 1;
|
183
|
+
JS_SetContextOpaque(data->context, data);
|
184
|
+
|
185
|
+
JS_SetMemoryLimit(runtime, NUM2UINT(r_memoryLimit));
|
186
|
+
JS_SetMaxStackSize(runtime, NUM2UINT(r_maxStackSize));
|
187
|
+
|
188
|
+
JS_AddIntrinsicBigFloat(data->context);
|
189
|
+
JS_AddIntrinsicBigDecimal(data->context);
|
190
|
+
JS_AddIntrinsicOperators(data->context);
|
191
|
+
JS_EnableBignumExt(data->context, TRUE);
|
192
|
+
js_std_add_helpers(data->context, 0, NULL);
|
193
|
+
|
194
|
+
JS_SetModuleLoaderFunc(runtime, NULL, js_module_loader, NULL);
|
195
|
+
js_std_init_handlers(runtime);
|
196
|
+
|
197
|
+
if (RTEST(rb_funcall(r_features, rb_intern("include?"), 1, ID2SYM(rb_intern(featureStdId))))) {
|
198
|
+
js_init_module_std(data->context, "std");
|
199
|
+
const char *enableStd = "import * as std from 'std';\n"
|
200
|
+
"globalThis.std = std;\n";
|
201
|
+
JSValue stdEval = JS_Eval(data->context, enableStd, strlen(enableStd), "<vm>", JS_EVAL_TYPE_MODULE);
|
202
|
+
JS_FreeValue(data->context, stdEval);
|
203
|
+
}
|
204
|
+
|
205
|
+
if (RTEST(rb_funcall(r_features, rb_intern("include?"), 1, ID2SYM(rb_intern(featureOsId))))) {
|
206
|
+
js_init_module_os(data->context, "os");
|
207
|
+
const char *enableOs = "import * as os from 'os';\n"
|
208
|
+
"globalThis.os = os;\n";
|
209
|
+
JSValue osEval = JS_Eval(data->context, enableOs, strlen(enableOs), "<vm>", JS_EVAL_TYPE_MODULE);
|
210
|
+
JS_FreeValue(data->context, osEval);
|
211
|
+
}
|
212
|
+
|
213
|
+
const char *setupGlobalRuby = "globalThis.__ruby = {};\n";
|
214
|
+
JSValue rubyEval = JS_Eval(data->context, setupGlobalRuby, strlen(setupGlobalRuby), "<vm>", JS_EVAL_TYPE_MODULE);
|
215
|
+
JS_FreeValue(data->context, rubyEval);
|
216
|
+
|
217
|
+
JSValue global = JS_GetGlobalObject(data->context);
|
218
|
+
JSValue func = JS_NewCFunction(data->context, js_quickjsrb_call_global, "rubyGlobal", 2);
|
219
|
+
JS_SetPropertyStr(data->context, global, "rubyGlobal", func);
|
220
|
+
JS_FreeValue(data->context, global);
|
221
|
+
|
222
|
+
return self;
|
223
|
+
}
|
224
|
+
|
225
|
+
VALUE qvm_m_evalCode(VALUE self, VALUE r_code)
|
226
|
+
{
|
227
|
+
struct qvmdata *data;
|
228
|
+
TypedData_Get_Struct(self, struct qvmdata, &qvm_type, data);
|
97
229
|
|
98
|
-
|
99
|
-
|
100
|
-
|
230
|
+
if (data->alive < 1) {
|
231
|
+
rb_raise(rb_eRuntimeError, "Quickjs::VM was disposed");
|
232
|
+
return Qnil;
|
233
|
+
}
|
234
|
+
char *code = StringValueCStr(r_code);
|
235
|
+
JSValue codeResult = JS_Eval(data->context, code, strlen(code), "<code>", JS_EVAL_TYPE_GLOBAL);
|
236
|
+
VALUE result = to_rb_value(codeResult, data->context);
|
101
237
|
|
238
|
+
JS_FreeValue(data->context, codeResult);
|
102
239
|
return result;
|
103
240
|
}
|
104
241
|
|
242
|
+
VALUE qvm_m_defineGlobalFunction(VALUE self, VALUE r_name)
|
243
|
+
{
|
244
|
+
rb_need_block();
|
245
|
+
|
246
|
+
struct qvmdata *data;
|
247
|
+
TypedData_Get_Struct(self, struct qvmdata, &qvm_type, data);
|
248
|
+
|
249
|
+
if (rb_block_given_p()) {
|
250
|
+
VALUE proc = rb_block_proc();
|
251
|
+
|
252
|
+
char *funcName = StringValueCStr(r_name);
|
253
|
+
|
254
|
+
set_proc(data->state->procs, funcName, proc);
|
255
|
+
|
256
|
+
const char* template = "globalThis.__ruby['%s'] = (...args) => rubyGlobal('%s', args);\nglobalThis['%s'] = globalThis.__ruby['%s'];\n";
|
257
|
+
int length = snprintf(NULL, 0, template, funcName, funcName, funcName, funcName);
|
258
|
+
char* result = (char*)malloc(length + 1);
|
259
|
+
snprintf(result, length + 1, template, funcName, funcName, funcName, funcName);
|
260
|
+
|
261
|
+
JSValue codeResult = JS_Eval(data->context, result, strlen(result), "<vm>", JS_EVAL_TYPE_MODULE);
|
262
|
+
|
263
|
+
JS_FreeValue(data->context, codeResult);
|
264
|
+
free(result);
|
265
|
+
return rb_funcall(r_name, rb_intern("to_sym"), 0, NULL);
|
266
|
+
}
|
267
|
+
|
268
|
+
return Qnil;
|
269
|
+
}
|
270
|
+
|
271
|
+
VALUE qvm_m_dispose(VALUE self)
|
272
|
+
{
|
273
|
+
struct qvmdata *data;
|
274
|
+
TypedData_Get_Struct(self, struct qvmdata, &qvm_type, data);
|
275
|
+
|
276
|
+
JSRuntime *runtime = JS_GetRuntime(data->context);
|
277
|
+
js_std_free_handlers(runtime);
|
278
|
+
free_quickjsrb_runtime_state(data->state);
|
279
|
+
JS_FreeContext(data->context);
|
280
|
+
JS_FreeRuntime(runtime);
|
281
|
+
data->alive = 0;
|
282
|
+
|
283
|
+
return Qnil;
|
284
|
+
}
|
285
|
+
|
105
286
|
RUBY_FUNC_EXPORTED void
|
106
287
|
Init_quickjsrb(void)
|
107
288
|
{
|
108
289
|
rb_mQuickjs = rb_define_module("Quickjs");
|
109
|
-
|
290
|
+
rb_define_const(rb_mQuickjs, "MODULE_STD", ID2SYM(rb_intern(featureStdId)));
|
291
|
+
rb_define_const(rb_mQuickjs, "MODULE_OS", ID2SYM(rb_intern(featureOsId)));
|
110
292
|
|
111
293
|
VALUE valueClass = rb_define_class_under(rb_mQuickjs, "Value", rb_cObject);
|
112
294
|
rb_define_const(valueClass, "UNDEFINED", ID2SYM(rb_intern(undefinedId)));
|
113
295
|
rb_define_const(valueClass, "NAN", ID2SYM(rb_intern(nanId)));
|
296
|
+
|
297
|
+
VALUE vmClass = rb_define_class_under(rb_mQuickjs, "VM", rb_cObject);
|
298
|
+
rb_define_alloc_func(vmClass, qvm_alloc);
|
299
|
+
rb_define_method(vmClass, "initialize", qvm_m_initialize, -1);
|
300
|
+
rb_define_method(vmClass, "eval_code", qvm_m_evalCode, 1);
|
301
|
+
rb_define_method(vmClass, "define_function", qvm_m_defineGlobalFunction, 1);
|
302
|
+
rb_define_method(vmClass, "dispose!", qvm_m_dispose, 0);
|
114
303
|
}
|
data/lib/quickjs/version.rb
CHANGED
data/lib/quickjs.rb
CHANGED
@@ -5,25 +5,8 @@ require_relative "quickjs/version"
|
|
5
5
|
require_relative "quickjs/quickjsrb"
|
6
6
|
|
7
7
|
module Quickjs
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
def evalCode(
|
12
|
-
code,
|
13
|
-
opts = {
|
14
|
-
memoryLimit: nil,
|
15
|
-
maxStackSize: nil,
|
16
|
-
features: []
|
17
|
-
}
|
18
|
-
)
|
19
|
-
|
20
|
-
_evalCode(
|
21
|
-
code,
|
22
|
-
opts[:memoryLimit] || 1024 * 1024 * 128,
|
23
|
-
opts[:maxStackSize] || 1024 * 1024 * 4,
|
24
|
-
opts[:features].include?(Quickjs::MODULE_STD),
|
25
|
-
opts[:features].include?(Quickjs::MODULE_OS),
|
26
|
-
)
|
8
|
+
def eval_code(code, overwrite_opts = {})
|
9
|
+
Quickjs::VM.new(**overwrite_opts).eval_code(code)
|
27
10
|
end
|
28
|
-
module_function :
|
11
|
+
module_function :eval_code
|
29
12
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: quickjs
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.6
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- hmsk
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-
|
11
|
+
date: 2024-07-08 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: json
|
@@ -62,6 +62,8 @@ files:
|
|
62
62
|
- ext/quickjsrb/quickjs/run-test262.c
|
63
63
|
- ext/quickjsrb/quickjs/unicode_gen.c
|
64
64
|
- ext/quickjsrb/quickjs/unicode_gen_def.h
|
65
|
+
- ext/quickjsrb/quickjsrb-runtime-state.c
|
66
|
+
- ext/quickjsrb/quickjsrb-runtime-state.h
|
65
67
|
- ext/quickjsrb/quickjsrb.c
|
66
68
|
- ext/quickjsrb/quickjsrb.h
|
67
69
|
- lib/quickjs.rb
|