duktape 1.0.2.0 → 1.1.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +17 -0
- data/README.md +50 -19
- data/ext/duktape/duktape.c +18317 -16099
- data/ext/duktape/duktape.h +384 -72
- data/ext/duktape/duktape_ext.c +577 -66
- data/ext/duktape/extconf.rb +1 -0
- data/lib/duktape/version.rb +1 -1
- metadata +2 -2
data/ext/duktape/duktape_ext.c
CHANGED
@@ -1,28 +1,188 @@
|
|
1
1
|
#include "ruby.h"
|
2
|
+
#include "ruby/encoding.h"
|
2
3
|
#include "duktape.h"
|
3
4
|
|
4
5
|
static VALUE mDuktape;
|
5
6
|
static VALUE cContext;
|
6
|
-
static VALUE
|
7
|
+
static VALUE cComplexObject;
|
8
|
+
static VALUE oComplexObject;
|
9
|
+
|
10
|
+
static VALUE eUnimplementedError;
|
11
|
+
static VALUE eUnsupportedError;
|
12
|
+
static VALUE eInternalError;
|
13
|
+
static VALUE eAllocError;
|
14
|
+
static VALUE eAssertionError;
|
15
|
+
static VALUE eAPIError;
|
16
|
+
static VALUE eUncaughtError;
|
17
|
+
|
18
|
+
static VALUE eError;
|
19
|
+
static VALUE eEvalError;
|
20
|
+
static VALUE eRangeError;
|
21
|
+
static VALUE eReferenceError;
|
22
|
+
static VALUE eSyntaxError;
|
23
|
+
static VALUE eTypeError;
|
24
|
+
static VALUE eURIError;
|
25
|
+
static rb_encoding *utf16enc;
|
26
|
+
|
27
|
+
static VALUE sDefaultFilename;
|
28
|
+
static ID id_complex_object;
|
29
|
+
|
7
30
|
static void error_handler(duk_context *, int, const char *);
|
31
|
+
static int ctx_push_hash_element(VALUE key, VALUE val, VALUE extra);
|
32
|
+
|
33
|
+
static unsigned long
|
34
|
+
utf8_to_uv(const char *p, long *lenp);
|
35
|
+
|
36
|
+
#define clean_raise(ctx, ...) (duk_set_top(ctx, 0), rb_raise(__VA_ARGS__))
|
37
|
+
#define clean_raise_exc(ctx, ...) (duk_set_top(ctx, 0), rb_exc_raise(__VA_ARGS__))
|
38
|
+
|
39
|
+
struct state {
|
40
|
+
duk_context *ctx;
|
41
|
+
VALUE complex_object;
|
42
|
+
int was_complex;
|
43
|
+
};
|
8
44
|
|
9
|
-
static void ctx_dealloc(void *
|
45
|
+
static void ctx_dealloc(void *ptr)
|
10
46
|
{
|
11
|
-
|
47
|
+
struct state *state = (struct state *)ptr;
|
48
|
+
duk_destroy_heap(state->ctx);
|
49
|
+
free(state);
|
50
|
+
}
|
51
|
+
|
52
|
+
static void ctx_mark(struct state *state)
|
53
|
+
{
|
54
|
+
rb_gc_mark(state->complex_object);
|
12
55
|
}
|
13
56
|
|
14
57
|
static VALUE ctx_alloc(VALUE klass)
|
15
58
|
{
|
16
59
|
duk_context *ctx = duk_create_heap(NULL, NULL, NULL, NULL, error_handler);
|
17
|
-
|
60
|
+
|
61
|
+
// Undefine require property
|
62
|
+
duk_push_global_object(ctx);
|
63
|
+
duk_push_string(ctx, "require");
|
64
|
+
duk_del_prop(ctx, -2);
|
65
|
+
duk_set_top(ctx, 0);
|
66
|
+
|
67
|
+
struct state *state = malloc(sizeof(struct state));
|
68
|
+
state->ctx = ctx;
|
69
|
+
state->complex_object = oComplexObject;
|
70
|
+
return Data_Wrap_Struct(klass, ctx_mark, ctx_dealloc, state);
|
71
|
+
}
|
72
|
+
|
73
|
+
static VALUE error_code_class(int code) {
|
74
|
+
switch (code) {
|
75
|
+
case DUK_ERR_UNIMPLEMENTED_ERROR:
|
76
|
+
return eUnimplementedError;
|
77
|
+
case DUK_ERR_UNSUPPORTED_ERROR:
|
78
|
+
return eUnsupportedError;
|
79
|
+
case DUK_ERR_INTERNAL_ERROR:
|
80
|
+
return eInternalError;
|
81
|
+
case DUK_ERR_ALLOC_ERROR:
|
82
|
+
return eAllocError;
|
83
|
+
case DUK_ERR_ASSERTION_ERROR:
|
84
|
+
return eAssertionError;
|
85
|
+
case DUK_ERR_API_ERROR:
|
86
|
+
return eAPIError;
|
87
|
+
case DUK_ERR_UNCAUGHT_ERROR:
|
88
|
+
return eUncaughtError;
|
89
|
+
|
90
|
+
case DUK_ERR_ERROR:
|
91
|
+
return eError;
|
92
|
+
case DUK_ERR_EVAL_ERROR:
|
93
|
+
return eEvalError;
|
94
|
+
case DUK_ERR_RANGE_ERROR:
|
95
|
+
return eRangeError;
|
96
|
+
case DUK_ERR_REFERENCE_ERROR:
|
97
|
+
return eReferenceError;
|
98
|
+
case DUK_ERR_SYNTAX_ERROR:
|
99
|
+
return eSyntaxError;
|
100
|
+
case DUK_ERR_TYPE_ERROR:
|
101
|
+
return eTypeError;
|
102
|
+
case DUK_ERR_URI_ERROR:
|
103
|
+
return eURIError;
|
104
|
+
|
105
|
+
default:
|
106
|
+
return eInternalError;
|
107
|
+
}
|
108
|
+
}
|
109
|
+
|
110
|
+
static VALUE error_name_class(const char* name)
|
111
|
+
{
|
112
|
+
if (strcmp(name, "EvalError") == 0) {
|
113
|
+
return eEvalError;
|
114
|
+
} else if (strcmp(name, "RangeError") == 0) {
|
115
|
+
return eRangeError;
|
116
|
+
} else if (strcmp(name, "ReferenceError") == 0) {
|
117
|
+
return eReferenceError;
|
118
|
+
} else if (strcmp(name, "SyntaxError") == 0) {
|
119
|
+
return eSyntaxError;
|
120
|
+
} else if (strcmp(name, "TypeError") == 0) {
|
121
|
+
return eTypeError;
|
122
|
+
} else if (strcmp(name, "URIError") == 0) {
|
123
|
+
return eURIError;
|
124
|
+
} else {
|
125
|
+
return eError;
|
126
|
+
}
|
127
|
+
}
|
128
|
+
|
129
|
+
static VALUE encode_cesu8(struct state *state, VALUE str)
|
130
|
+
{
|
131
|
+
duk_context *ctx = state->ctx;
|
132
|
+
VALUE res = rb_str_new(0, 0);
|
133
|
+
|
134
|
+
VALUE utf16 = rb_str_conv_enc(str, rb_enc_get(str), utf16enc);
|
135
|
+
if (utf16 == str && rb_enc_get(str) != utf16enc) {
|
136
|
+
clean_raise(ctx, rb_eEncodingError, "cannot convert Ruby string to UTF-16");
|
137
|
+
}
|
138
|
+
|
139
|
+
long len = RSTRING_LEN(utf16) / 2;
|
140
|
+
unsigned short *bytes = (unsigned short *)RSTRING_PTR(utf16);
|
141
|
+
|
142
|
+
char buf[8];
|
143
|
+
|
144
|
+
for (int i = 0; i < len; i++) {
|
145
|
+
int length = rb_uv_to_utf8(buf, bytes[i]);
|
146
|
+
rb_str_buf_cat(res, (char*)buf, length);
|
147
|
+
}
|
148
|
+
|
149
|
+
return res;
|
150
|
+
}
|
151
|
+
|
152
|
+
static VALUE decode_cesu8(struct state *state, VALUE str)
|
153
|
+
{
|
154
|
+
duk_context *ctx = state->ctx;
|
155
|
+
VALUE res = rb_str_new(0, 0);
|
156
|
+
|
157
|
+
const char *ptr = RSTRING_PTR(str);
|
158
|
+
const char *end = RSTRING_END(str);
|
159
|
+
long len;
|
160
|
+
|
161
|
+
while (ptr < end) {
|
162
|
+
len = (end - ptr);
|
163
|
+
unsigned short code = utf8_to_uv(ptr, &len);
|
164
|
+
rb_str_buf_cat(res, (char*)&code, 2);
|
165
|
+
ptr += len;
|
166
|
+
}
|
167
|
+
|
168
|
+
rb_enc_associate(res, utf16enc);
|
169
|
+
VALUE utf8res = rb_str_conv_enc(res, utf16enc, rb_utf8_encoding());
|
170
|
+
if (utf8res == res) {
|
171
|
+
clean_raise(ctx, rb_eEncodingError, "cannot convert JavaScript string to UTF-16");
|
172
|
+
}
|
173
|
+
|
174
|
+
return utf8res;
|
18
175
|
}
|
19
176
|
|
20
|
-
static VALUE ctx_stack_to_value(
|
177
|
+
static VALUE ctx_stack_to_value(struct state *state, int index)
|
21
178
|
{
|
179
|
+
duk_context *ctx = state->ctx;
|
22
180
|
size_t len;
|
23
181
|
const char *buf;
|
24
182
|
int type;
|
25
183
|
|
184
|
+
state->was_complex = 0;
|
185
|
+
|
26
186
|
type = duk_get_type(ctx, index);
|
27
187
|
switch (type) {
|
28
188
|
case DUK_TYPE_NULL:
|
@@ -37,147 +197,498 @@ static VALUE ctx_stack_to_value(duk_context *ctx, int index)
|
|
37
197
|
|
38
198
|
case DUK_TYPE_STRING:
|
39
199
|
buf = duk_get_lstring(ctx, index, &len);
|
40
|
-
|
200
|
+
VALUE str = rb_str_new(buf, len);
|
201
|
+
return decode_cesu8(state, str);
|
41
202
|
|
42
203
|
case DUK_TYPE_OBJECT:
|
204
|
+
if (duk_is_function(ctx, index)) {
|
205
|
+
state->was_complex = 1;
|
206
|
+
return state->complex_object;
|
207
|
+
} else if (duk_is_array(ctx, index)) {
|
208
|
+
VALUE ary = rb_ary_new();
|
209
|
+
duk_enum(ctx, index, DUK_ENUM_ARRAY_INDICES_ONLY);
|
210
|
+
while (duk_next(ctx, -1, 1)) {
|
211
|
+
rb_ary_store(ary, duk_to_int(ctx, -2), ctx_stack_to_value(state, -1));
|
212
|
+
duk_pop_2(ctx);
|
213
|
+
}
|
214
|
+
duk_pop(ctx);
|
215
|
+
return ary;
|
216
|
+
} else if (duk_is_object(ctx, index)) {
|
217
|
+
VALUE hash = rb_hash_new();
|
218
|
+
duk_enum(ctx, index, DUK_ENUM_OWN_PROPERTIES_ONLY);
|
219
|
+
while (duk_next(ctx, -1, 1)) {
|
220
|
+
VALUE key = ctx_stack_to_value(state, -2);
|
221
|
+
VALUE val = ctx_stack_to_value(state, -1);
|
222
|
+
duk_pop_2(ctx);
|
223
|
+
if (state->was_complex)
|
224
|
+
continue;
|
225
|
+
rb_hash_aset(hash, key, val);
|
226
|
+
}
|
227
|
+
duk_pop(ctx);
|
228
|
+
return hash;
|
229
|
+
} else {
|
230
|
+
state->was_complex = 1;
|
231
|
+
return state->complex_object;
|
232
|
+
}
|
233
|
+
|
43
234
|
case DUK_TYPE_BUFFER:
|
44
235
|
case DUK_TYPE_POINTER:
|
45
236
|
default:
|
46
|
-
|
237
|
+
return state->complex_object;
|
47
238
|
}
|
48
239
|
|
49
240
|
return Qnil;
|
50
241
|
}
|
51
242
|
|
52
|
-
static
|
243
|
+
static void ctx_push_ruby_object(struct state *state, VALUE obj)
|
53
244
|
{
|
245
|
+
duk_context *ctx = state->ctx;
|
246
|
+
duk_idx_t arr_idx;
|
247
|
+
VALUE str;
|
248
|
+
|
54
249
|
switch (TYPE(obj)) {
|
55
250
|
case T_FIXNUM:
|
56
251
|
duk_push_int(ctx, NUM2INT(obj));
|
57
|
-
|
252
|
+
return;
|
58
253
|
|
59
254
|
case T_FLOAT:
|
60
255
|
duk_push_number(ctx, NUM2DBL(obj));
|
61
|
-
|
256
|
+
return;
|
62
257
|
|
63
258
|
case T_STRING:
|
64
|
-
|
65
|
-
|
259
|
+
str = encode_cesu8(state, obj);
|
260
|
+
duk_push_lstring(ctx, RSTRING_PTR(str), RSTRING_LEN(str));
|
261
|
+
return;
|
66
262
|
|
67
263
|
case T_TRUE:
|
68
264
|
duk_push_true(ctx);
|
69
|
-
|
265
|
+
return;
|
70
266
|
|
71
267
|
case T_FALSE:
|
72
268
|
duk_push_false(ctx);
|
73
|
-
|
269
|
+
return;
|
74
270
|
|
75
271
|
case T_NIL:
|
76
272
|
duk_push_null(ctx);
|
77
|
-
|
273
|
+
return;
|
274
|
+
|
275
|
+
case T_ARRAY:
|
276
|
+
arr_idx = duk_push_array(ctx);
|
277
|
+
for (int idx = 0; idx < RARRAY_LEN(obj); idx++) {
|
278
|
+
ctx_push_ruby_object(state, rb_ary_entry(obj, idx));
|
279
|
+
duk_put_prop_index(ctx, arr_idx, idx);
|
280
|
+
}
|
281
|
+
return;
|
282
|
+
|
283
|
+
case T_HASH:
|
284
|
+
duk_push_object(ctx);
|
285
|
+
rb_hash_foreach(obj, ctx_push_hash_element, (VALUE)state);
|
286
|
+
return;
|
78
287
|
|
79
288
|
default:
|
80
289
|
// Cannot convert
|
81
|
-
|
290
|
+
break;
|
82
291
|
}
|
83
292
|
|
84
|
-
|
85
|
-
return 1;
|
293
|
+
clean_raise(ctx, rb_eTypeError, "cannot convert %s", rb_obj_classname(obj));
|
86
294
|
}
|
87
295
|
|
88
|
-
static
|
296
|
+
static int ctx_push_hash_element(VALUE key, VALUE val, VALUE extra)
|
89
297
|
{
|
90
|
-
|
91
|
-
|
298
|
+
struct state *state = (struct state*) extra;
|
299
|
+
duk_context *ctx = state->ctx;
|
300
|
+
|
301
|
+
Check_Type(key, T_STRING);
|
302
|
+
duk_push_lstring(ctx, RSTRING_PTR(key), RSTRING_LEN(key));
|
303
|
+
ctx_push_ruby_object(state, val);
|
304
|
+
duk_put_prop(ctx, -3);
|
305
|
+
return ST_CONTINUE;
|
306
|
+
}
|
307
|
+
|
308
|
+
static void raise_ctx_error(struct state *state)
|
309
|
+
{
|
310
|
+
duk_context *ctx = state->ctx;
|
311
|
+
duk_get_prop_string(ctx, -1, "name");
|
312
|
+
const char *name = duk_safe_to_string(ctx, -1);
|
92
313
|
|
93
|
-
|
94
|
-
|
95
|
-
duk_compile(ctx, DUK_COMPILE_EVAL);
|
96
|
-
duk_call(ctx, 0);
|
314
|
+
duk_get_prop_string(ctx, -2, "message");
|
315
|
+
const char *message = duk_to_string(ctx, -1);
|
97
316
|
|
98
|
-
VALUE
|
99
|
-
|
317
|
+
VALUE exc_class = error_name_class(name);
|
318
|
+
VALUE exc = rb_exc_new2(exc_class, message);
|
319
|
+
clean_raise_exc(ctx, exc);
|
320
|
+
}
|
321
|
+
|
322
|
+
/*
|
323
|
+
* call-seq:
|
324
|
+
* eval_string(string[, filename]) -> obj
|
325
|
+
*
|
326
|
+
* Evaluate JavaScript expression within context returning the value as a Ruby
|
327
|
+
* object.
|
328
|
+
*
|
329
|
+
* ctx.eval_string("40 + 2") #=> 42
|
330
|
+
*
|
331
|
+
*/
|
332
|
+
static VALUE ctx_eval_string(int argc, VALUE *argv, VALUE self)
|
333
|
+
{
|
334
|
+
struct state *state;
|
335
|
+
Data_Get_Struct(self, struct state, state);
|
336
|
+
|
337
|
+
VALUE source;
|
338
|
+
VALUE filename;
|
339
|
+
|
340
|
+
rb_scan_args(argc, argv, "11", &source, &filename);
|
341
|
+
|
342
|
+
if (NIL_P(filename)) {
|
343
|
+
filename = sDefaultFilename;
|
344
|
+
}
|
345
|
+
|
346
|
+
StringValue(source);
|
347
|
+
StringValue(filename);
|
348
|
+
|
349
|
+
ctx_push_ruby_object(state, source);
|
350
|
+
ctx_push_ruby_object(state, filename);
|
351
|
+
|
352
|
+
if (duk_pcompile(state->ctx, DUK_COMPILE_EVAL) == DUK_EXEC_ERROR) {
|
353
|
+
raise_ctx_error(state);
|
354
|
+
}
|
355
|
+
|
356
|
+
if (duk_pcall(state->ctx, 0) == DUK_EXEC_ERROR) {
|
357
|
+
raise_ctx_error(state);
|
358
|
+
}
|
359
|
+
|
360
|
+
VALUE res = ctx_stack_to_value(state, -1);
|
361
|
+
duk_set_top(state->ctx, 0);
|
100
362
|
return res;
|
101
363
|
}
|
102
364
|
|
103
|
-
|
365
|
+
/*
|
366
|
+
* call-seq:
|
367
|
+
* exec_string(string[, filename]) -> nil
|
368
|
+
*
|
369
|
+
* Evaluate JavaScript expression within context returning the value as a Ruby
|
370
|
+
* object.
|
371
|
+
*
|
372
|
+
* ctx.exec_string("var foo = 42")
|
373
|
+
* ctx.eval_string("foo") #=> 42
|
374
|
+
*
|
375
|
+
*/
|
376
|
+
static VALUE ctx_exec_string(int argc, VALUE *argv, VALUE self)
|
104
377
|
{
|
105
|
-
|
106
|
-
Data_Get_Struct(self,
|
378
|
+
struct state *state;
|
379
|
+
Data_Get_Struct(self, struct state, state);
|
107
380
|
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
381
|
+
VALUE source;
|
382
|
+
VALUE filename;
|
383
|
+
|
384
|
+
rb_scan_args(argc, argv, "11", &source, &filename);
|
385
|
+
|
386
|
+
if (NIL_P(filename)) {
|
387
|
+
filename = sDefaultFilename;
|
388
|
+
}
|
389
|
+
|
390
|
+
StringValue(source);
|
391
|
+
StringValue(filename);
|
392
|
+
|
393
|
+
ctx_push_ruby_object(state, source);
|
394
|
+
ctx_push_ruby_object(state, filename);
|
395
|
+
|
396
|
+
if (duk_pcompile(state->ctx, 0) == DUK_EXEC_ERROR) {
|
397
|
+
raise_ctx_error(state);
|
398
|
+
}
|
399
|
+
|
400
|
+
if (duk_pcall(state->ctx, 0) == DUK_EXEC_ERROR) {
|
401
|
+
raise_ctx_error(state);
|
402
|
+
}
|
403
|
+
|
404
|
+
duk_set_top(state->ctx, 0);
|
113
405
|
return Qnil;
|
114
406
|
}
|
115
407
|
|
116
|
-
static
|
408
|
+
static void ctx_get_one_prop(struct state *state, VALUE name, int strict)
|
117
409
|
{
|
118
|
-
duk_context *ctx;
|
119
|
-
Data_Get_Struct(self, duk_context, ctx);
|
410
|
+
duk_context *ctx = state->ctx;
|
120
411
|
|
121
|
-
|
412
|
+
// Don't allow prop access on undefined/null
|
413
|
+
if (duk_check_type_mask(ctx, -1, DUK_TYPE_MASK_UNDEFINED | DUK_TYPE_MASK_NULL)) {
|
414
|
+
clean_raise(ctx, eTypeError, "invalid base value");
|
415
|
+
}
|
122
416
|
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
const char *str = StringValueCStr(
|
128
|
-
|
417
|
+
duk_push_lstring(ctx, RSTRING_PTR(name), RSTRING_LEN(name));
|
418
|
+
duk_bool_t exists = duk_get_prop(ctx, -2);
|
419
|
+
|
420
|
+
if (!exists && strict) {
|
421
|
+
const char *str = StringValueCStr(name);
|
422
|
+
clean_raise(ctx, eReferenceError, "identifier '%s' undefined", str);
|
129
423
|
}
|
424
|
+
}
|
130
425
|
|
131
|
-
|
132
|
-
|
426
|
+
static void ctx_get_nested_prop(struct state *state, VALUE props)
|
427
|
+
{
|
428
|
+
duk_context *ctx = state->ctx;
|
429
|
+
|
430
|
+
switch (TYPE(props)) {
|
431
|
+
case T_STRING:
|
432
|
+
duk_push_global_object(ctx);
|
433
|
+
ctx_get_one_prop(state, props, 1);
|
434
|
+
return;
|
435
|
+
|
436
|
+
case T_ARRAY:
|
437
|
+
duk_push_global_object(ctx);
|
438
|
+
|
439
|
+
long len = RARRAY_LEN(props);
|
440
|
+
for (int i = 0; i < len; i++) {
|
441
|
+
VALUE item = rb_ary_entry(props, i);
|
442
|
+
Check_Type(item, T_STRING);
|
443
|
+
|
444
|
+
// Only do a strict check on the first item
|
445
|
+
ctx_get_one_prop(state, item, i == 0);
|
446
|
+
}
|
447
|
+
return;
|
448
|
+
|
449
|
+
default:
|
450
|
+
clean_raise(ctx, rb_eTypeError, "wrong argument type %s (expected String or Array)", rb_obj_classname(props));
|
451
|
+
return;
|
452
|
+
}
|
453
|
+
}
|
454
|
+
|
455
|
+
/*
|
456
|
+
* call-seq:
|
457
|
+
* get_prop(name) -> obj
|
458
|
+
* get_prop([names,...]) -> obj
|
459
|
+
*
|
460
|
+
* Access the property of the global object. An Array of names can be given
|
461
|
+
* to access the property on a nested object.
|
462
|
+
*
|
463
|
+
* ctx.exec_string("var n = 42", "foo.js")
|
464
|
+
* ctx.get_prop("n") #=> 42
|
465
|
+
*
|
466
|
+
* ctx.get_prop(["Math", "PI"]) #=> 3.14
|
467
|
+
*
|
468
|
+
*/
|
469
|
+
static VALUE ctx_get_prop(VALUE self, VALUE prop)
|
470
|
+
{
|
471
|
+
struct state *state;
|
472
|
+
Data_Get_Struct(self, struct state, state);
|
473
|
+
|
474
|
+
ctx_get_nested_prop(state, prop);
|
475
|
+
|
476
|
+
VALUE res = ctx_stack_to_value(state, -1);
|
477
|
+
duk_set_top(state->ctx, 0);
|
133
478
|
return res;
|
134
479
|
}
|
135
480
|
|
481
|
+
|
482
|
+
/*
|
483
|
+
* call-seq:
|
484
|
+
* call_prop(name, params,...) -> obj
|
485
|
+
* call_prop([names,...], params,...) -> obj
|
486
|
+
*
|
487
|
+
* Call a function defined in the global scope with the given parameters. An
|
488
|
+
* Array of names can be given to call a function on a nested object.
|
489
|
+
*
|
490
|
+
* ctx.call_prop("parseInt", "42") #=> 42
|
491
|
+
* ctx.call_prop(["Math", "pow"], 2, 10) #=> 1024
|
492
|
+
*
|
493
|
+
*/
|
136
494
|
static VALUE ctx_call_prop(int argc, VALUE* argv, VALUE self)
|
137
495
|
{
|
138
|
-
|
139
|
-
Data_Get_Struct(self,
|
496
|
+
struct state *state;
|
497
|
+
Data_Get_Struct(self, struct state, state);
|
140
498
|
|
141
499
|
VALUE prop;
|
142
500
|
VALUE *prop_args;
|
143
501
|
rb_scan_args(argc, argv, "1*", &prop, &prop_args);
|
144
502
|
|
145
|
-
|
503
|
+
ctx_get_nested_prop(state, prop);
|
146
504
|
|
147
|
-
|
148
|
-
|
505
|
+
// Swap receiver and function
|
506
|
+
duk_swap_top(state->ctx, -2);
|
149
507
|
|
508
|
+
// Push arguments
|
150
509
|
for (int i = 1; i < argc; i++) {
|
151
|
-
|
152
|
-
duk_set_top(ctx, 0);
|
153
|
-
VALUE tmp = rb_inspect(argv[i]);
|
154
|
-
const char *str = StringValueCStr(tmp);
|
155
|
-
rb_raise(eContextError, "unknown object: %s", str);
|
156
|
-
}
|
510
|
+
ctx_push_ruby_object(state, argv[i]);
|
157
511
|
}
|
158
512
|
|
159
|
-
|
160
|
-
|
161
|
-
|
513
|
+
if (duk_pcall_method(state->ctx, (argc - 1)) == DUK_EXEC_ERROR) {
|
514
|
+
raise_ctx_error(state);
|
515
|
+
}
|
516
|
+
|
517
|
+
VALUE res = ctx_stack_to_value(state, -1);
|
518
|
+
duk_set_top(state->ctx, 0);
|
162
519
|
return res;
|
163
520
|
}
|
164
521
|
|
522
|
+
/*
|
523
|
+
* :nodoc:
|
524
|
+
*
|
525
|
+
* Checks that we are in a fine state
|
526
|
+
*/
|
527
|
+
static VALUE ctx_is_valid(VALUE self)
|
528
|
+
{
|
529
|
+
struct state *state;
|
530
|
+
Data_Get_Struct(self, struct state, state);
|
531
|
+
|
532
|
+
if (duk_is_valid_index(state->ctx, -1)) {
|
533
|
+
return Qfalse;
|
534
|
+
} else {
|
535
|
+
return Qtrue;
|
536
|
+
}
|
537
|
+
}
|
538
|
+
|
165
539
|
static void error_handler(duk_context *ctx, int code, const char *msg)
|
166
540
|
{
|
167
|
-
|
168
|
-
|
541
|
+
clean_raise(ctx, error_code_class(code), "%s", msg);
|
542
|
+
}
|
543
|
+
|
544
|
+
VALUE complex_object_instance(VALUE self)
|
545
|
+
{
|
546
|
+
return oComplexObject;
|
547
|
+
}
|
548
|
+
|
549
|
+
/*
|
550
|
+
* call-seq:
|
551
|
+
* Context.new
|
552
|
+
* Context.new(complex_object: obj)
|
553
|
+
*
|
554
|
+
* Returns a new JavaScript evaluation context.
|
555
|
+
*
|
556
|
+
*/
|
557
|
+
static VALUE ctx_initialize(int argc, VALUE *argv, VALUE self)
|
558
|
+
{
|
559
|
+
struct state *state;
|
560
|
+
Data_Get_Struct(self, struct state, state);
|
561
|
+
|
562
|
+
VALUE options;
|
563
|
+
rb_scan_args(argc, argv, ":", &options);
|
564
|
+
if (!NIL_P(options))
|
565
|
+
state->complex_object = rb_hash_lookup2(options, ID2SYM(id_complex_object), state->complex_object);
|
566
|
+
|
567
|
+
return Qnil;
|
568
|
+
}
|
569
|
+
|
570
|
+
/*
|
571
|
+
* call-seq:
|
572
|
+
* complex_object -> obj
|
573
|
+
*
|
574
|
+
* Returns the default complex object, the value that would be returned if a
|
575
|
+
* JavaScript object had no representation in Ruby, such as a JavaScript
|
576
|
+
* Function. See also Context::new.
|
577
|
+
*
|
578
|
+
* ctx.complex_object #=> #<Duktape::ComplexObject>
|
579
|
+
*
|
580
|
+
*/
|
581
|
+
static VALUE ctx_complex_object(VALUE self)
|
582
|
+
{
|
583
|
+
struct state *state;
|
584
|
+
Data_Get_Struct(self, struct state, state);
|
585
|
+
|
586
|
+
return state->complex_object;
|
169
587
|
}
|
170
588
|
|
171
589
|
void Init_duktape_ext()
|
172
590
|
{
|
591
|
+
utf16enc = rb_enc_find("UTF-16LE");
|
592
|
+
id_complex_object = rb_intern("complex_object");
|
593
|
+
|
173
594
|
mDuktape = rb_define_module("Duktape");
|
174
595
|
cContext = rb_define_class_under(mDuktape, "Context", rb_cObject);
|
175
|
-
|
596
|
+
cComplexObject = rb_define_class_under(mDuktape, "ComplexObject", rb_cObject);
|
597
|
+
|
598
|
+
eInternalError = rb_define_class_under(mDuktape, "InternalError", rb_eStandardError);
|
599
|
+
eUnimplementedError = rb_define_class_under(mDuktape, "UnimplementedError", eInternalError);
|
600
|
+
eUnsupportedError = rb_define_class_under(mDuktape, "UnsupportedError", eInternalError);
|
601
|
+
eAllocError = rb_define_class_under(mDuktape, "AllocError", eInternalError);
|
602
|
+
eAssertionError = rb_define_class_under(mDuktape, "AssertionError", eInternalError);
|
603
|
+
eAPIError = rb_define_class_under(mDuktape, "APIError", eInternalError);
|
604
|
+
eUncaughtError = rb_define_class_under(mDuktape, "UncaughtError", eInternalError);
|
605
|
+
|
606
|
+
eError = rb_define_class_under(mDuktape, "Error", rb_eStandardError);
|
607
|
+
eEvalError = rb_define_class_under(mDuktape, "EvalError", eError);
|
608
|
+
eRangeError = rb_define_class_under(mDuktape, "RangeError", eError);
|
609
|
+
eReferenceError = rb_define_class_under(mDuktape, "ReferenceError", eError);
|
610
|
+
eSyntaxError = rb_define_class_under(mDuktape, "SyntaxError", eError);
|
611
|
+
eTypeError = rb_define_class_under(mDuktape, "TypeError", eError);
|
612
|
+
eURIError = rb_define_class_under(mDuktape, "URIError", eError);
|
176
613
|
|
177
614
|
rb_define_alloc_func(cContext, ctx_alloc);
|
178
615
|
|
179
|
-
rb_define_method(cContext, "
|
180
|
-
rb_define_method(cContext, "
|
616
|
+
rb_define_method(cContext, "initialize", ctx_initialize, -1);
|
617
|
+
rb_define_method(cContext, "complex_object", ctx_complex_object, 0);
|
618
|
+
rb_define_method(cContext, "eval_string", ctx_eval_string, -1);
|
619
|
+
rb_define_method(cContext, "exec_string", ctx_exec_string, -1);
|
181
620
|
rb_define_method(cContext, "get_prop", ctx_get_prop, 1);
|
182
621
|
rb_define_method(cContext, "call_prop", ctx_call_prop, -1);
|
622
|
+
rb_define_method(cContext, "_valid?", ctx_is_valid, 0);
|
623
|
+
|
624
|
+
oComplexObject = rb_obj_alloc(cComplexObject);
|
625
|
+
rb_define_singleton_method(cComplexObject, "instance", complex_object_instance, 0);
|
626
|
+
rb_ivar_set(cComplexObject, rb_intern("duktape.instance"), oComplexObject);
|
627
|
+
|
628
|
+
sDefaultFilename = rb_str_new2("(duktape)");
|
629
|
+
OBJ_FREEZE(sDefaultFilename);
|
630
|
+
rb_global_variable(&sDefaultFilename);
|
631
|
+
}
|
632
|
+
|
633
|
+
|
634
|
+
/* UTF8 crap which is not exposed by Ruby */
|
635
|
+
|
636
|
+
static const unsigned long utf8_limits[] = {
|
637
|
+
0x0, /* 1 */
|
638
|
+
0x80, /* 2 */
|
639
|
+
0x800, /* 3 */
|
640
|
+
0x10000, /* 4 */
|
641
|
+
0x200000, /* 5 */
|
642
|
+
0x4000000, /* 6 */
|
643
|
+
0x80000000, /* 7 */
|
644
|
+
};
|
645
|
+
|
646
|
+
static unsigned long
|
647
|
+
utf8_to_uv(const char *p, long *lenp)
|
648
|
+
{
|
649
|
+
int c = *p++ & 0xff;
|
650
|
+
unsigned long uv = c;
|
651
|
+
long n;
|
652
|
+
|
653
|
+
if (!(uv & 0x80)) {
|
654
|
+
*lenp = 1;
|
655
|
+
return uv;
|
656
|
+
}
|
657
|
+
if (!(uv & 0x40)) {
|
658
|
+
*lenp = 1;
|
659
|
+
rb_raise(rb_eArgError, "malformed UTF-8 character");
|
660
|
+
}
|
661
|
+
|
662
|
+
if (!(uv & 0x20)) { n = 2; uv &= 0x1f; }
|
663
|
+
else if (!(uv & 0x10)) { n = 3; uv &= 0x0f; }
|
664
|
+
else if (!(uv & 0x08)) { n = 4; uv &= 0x07; }
|
665
|
+
else if (!(uv & 0x04)) { n = 5; uv &= 0x03; }
|
666
|
+
else if (!(uv & 0x02)) { n = 6; uv &= 0x01; }
|
667
|
+
else {
|
668
|
+
*lenp = 1;
|
669
|
+
rb_raise(rb_eArgError, "malformed UTF-8 character");
|
670
|
+
}
|
671
|
+
if (n > *lenp) {
|
672
|
+
rb_raise(rb_eArgError, "malformed UTF-8 character (expected %ld bytes, given %ld bytes)",
|
673
|
+
n, *lenp);
|
674
|
+
}
|
675
|
+
*lenp = n--;
|
676
|
+
if (n != 0) {
|
677
|
+
while (n--) {
|
678
|
+
c = *p++ & 0xff;
|
679
|
+
if ((c & 0xc0) != 0x80) {
|
680
|
+
*lenp -= n + 1;
|
681
|
+
rb_raise(rb_eArgError, "malformed UTF-8 character");
|
682
|
+
}
|
683
|
+
else {
|
684
|
+
c &= 0x3f;
|
685
|
+
uv = uv << 6 | c;
|
686
|
+
}
|
687
|
+
}
|
688
|
+
}
|
689
|
+
n = *lenp - 1;
|
690
|
+
if (uv < utf8_limits[n]) {
|
691
|
+
rb_raise(rb_eArgError, "redundant UTF-8 sequence");
|
692
|
+
}
|
693
|
+
return uv;
|
183
694
|
}
|