jsonnet 0.1.1 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,206 @@
1
+ #include <libjsonnet.h>
2
+ #include <ruby/ruby.h>
3
+ #include <ruby/intern.h>
4
+
5
+ #include "ruby_jsonnet.h"
6
+
7
+ static struct JsonnetJsonValue *protect_obj_to_json(struct JsonnetVm *vm, VALUE obj,
8
+ struct JsonnetJsonValue *parent);
9
+
10
+ /**
11
+ * Converts a Jsonnet JSON value into a Ruby object.
12
+ *
13
+ * Arrays and objects in JSON are not supported due to the limitation of
14
+ * libjsonnet API.
15
+ */
16
+ VALUE
17
+ rubyjsonnet_json_to_obj(struct JsonnetVm *vm, const struct JsonnetJsonValue *value)
18
+ {
19
+ union {
20
+ const char *str;
21
+ double num;
22
+ } typed_value;
23
+
24
+ if ((typed_value.str = jsonnet_json_extract_string(vm, value))) {
25
+ return rb_enc_str_new_cstr(typed_value.str, rb_utf8_encoding());
26
+ }
27
+ if (jsonnet_json_extract_number(vm, value, &typed_value.num)) {
28
+ return DBL2NUM(typed_value.num);
29
+ }
30
+
31
+ switch (jsonnet_json_extract_bool(vm, value)) {
32
+ case 0:
33
+ return Qfalse;
34
+ case 1:
35
+ return Qtrue;
36
+ case 2:
37
+ break;
38
+ default:
39
+ /* never happens */
40
+ rb_raise(rb_eRuntimeError, "unrecognized json bool value");
41
+ }
42
+ if (jsonnet_json_extract_null(vm, value)) {
43
+ return Qnil;
44
+ }
45
+
46
+ /* TODO: support arrays and objects when they get accessible */
47
+ rb_raise(rb_eArgError, "unsupported type of JSON value");
48
+ }
49
+
50
+ static struct JsonnetJsonValue *
51
+ string_to_json(struct JsonnetVm *vm, VALUE str)
52
+ {
53
+ rubyjsonnet_assert_asciicompat(str);
54
+ return jsonnet_json_make_string(vm, RSTRING_PTR(str));
55
+ }
56
+
57
+ static struct JsonnetJsonValue *
58
+ num_to_json(struct JsonnetVm *vm, VALUE n)
59
+ {
60
+ return jsonnet_json_make_number(vm, NUM2DBL(n));
61
+ }
62
+
63
+ static struct JsonnetJsonValue *
64
+ ary_to_json(struct JsonnetVm *vm, VALUE ary)
65
+ {
66
+ struct JsonnetJsonValue *const json_array = jsonnet_json_make_array(vm);
67
+
68
+ int i;
69
+ for (i = 0; i < RARRAY_LEN(ary); ++i) {
70
+ struct JsonnetJsonValue *const v = protect_obj_to_json(vm, RARRAY_AREF(ary, i), json_array);
71
+ jsonnet_json_array_append(vm, json_array, v);
72
+ }
73
+ return json_array;
74
+ }
75
+
76
+ struct hash_to_json_params {
77
+ struct JsonnetVm *vm;
78
+ struct JsonnetJsonValue *obj;
79
+ };
80
+
81
+ static int
82
+ hash_item_to_json(VALUE key, VALUE value, VALUE paramsval)
83
+ {
84
+ const struct hash_to_json_params *const params = (const struct hash_to_json_params *)paramsval;
85
+
86
+ StringValue(key);
87
+ rubyjsonnet_assert_asciicompat(key);
88
+
89
+ jsonnet_json_object_append(params->vm, params->obj, StringValueCStr(key),
90
+ protect_obj_to_json(params->vm, value, params->obj));
91
+
92
+ return ST_CONTINUE;
93
+ }
94
+
95
+ static struct JsonnetJsonValue *
96
+ hash_to_json(struct JsonnetVm *vm, VALUE hash)
97
+ {
98
+ struct JsonnetJsonValue *const json_obj = jsonnet_json_make_object(vm);
99
+ const struct hash_to_json_params args = {vm, json_obj};
100
+ rb_hash_foreach(hash, hash_item_to_json, (VALUE)&args);
101
+ return json_obj;
102
+ }
103
+
104
+ /**
105
+ * Converts a Ruby object into a Jsonnet JSON value
106
+ *
107
+ * TODO(yugui): Safely destorys an intermediate object on exception.
108
+ */
109
+ static struct JsonnetJsonValue *
110
+ obj_to_json(struct JsonnetVm *vm, VALUE obj)
111
+ {
112
+ VALUE converted;
113
+
114
+ switch (obj) {
115
+ case Qnil:
116
+ return jsonnet_json_make_null(vm);
117
+ case Qtrue:
118
+ return jsonnet_json_make_bool(vm, 1);
119
+ case Qfalse:
120
+ return jsonnet_json_make_bool(vm, 0);
121
+ }
122
+
123
+ converted = rb_check_string_type(obj);
124
+ if (converted != Qnil) {
125
+ return string_to_json(vm, converted);
126
+ }
127
+
128
+ converted = rb_check_to_float(obj);
129
+ if (converted != Qnil) {
130
+ return num_to_json(vm, converted);
131
+ }
132
+
133
+ converted = rb_check_array_type(obj);
134
+ if (converted != Qnil) {
135
+ return ary_to_json(vm, converted);
136
+ }
137
+
138
+ converted = rb_check_hash_type(obj);
139
+ if (converted != Qnil) {
140
+ return hash_to_json(vm, converted);
141
+ }
142
+
143
+ converted = rb_any_to_s(obj);
144
+ return string_to_json(vm, converted);
145
+ }
146
+
147
+ struct protect_args {
148
+ struct JsonnetVm *vm;
149
+ VALUE obj;
150
+ };
151
+
152
+ static VALUE
153
+ protect_obj_to_json_block(VALUE paramsval)
154
+ {
155
+ const struct protect_args *const params = (const struct protect_args *)paramsval;
156
+ return (VALUE)obj_to_json(params->vm, params->obj);
157
+ }
158
+
159
+ /**
160
+ * Safely converts a Ruby object into a JSON value.
161
+ *
162
+ * It automatically destroys \a parent on exception.
163
+ * @param[in] vm a Jsonnet VM
164
+ * @param[in] obj a Ruby object to be converted
165
+ * @param[in] parent destroys this value on failure
166
+ * @throws can throw \c TypeError or other exceptions
167
+ */
168
+ static struct JsonnetJsonValue *
169
+ protect_obj_to_json(struct JsonnetVm *vm, VALUE obj, struct JsonnetJsonValue *parent)
170
+ {
171
+ const struct protect_args args = {vm, obj};
172
+ int state = 0;
173
+
174
+ VALUE result = rb_protect(protect_obj_to_json_block, (VALUE)&args, &state);
175
+ if (!state) {
176
+ return (struct JsonnetJsonValue *)result;
177
+ }
178
+
179
+ jsonnet_json_destroy(vm, parent);
180
+ rb_jump_tag(state);
181
+ }
182
+
183
+ /**
184
+ * Converts a Ruby object into a JSON value.
185
+ * Returns an error message on failure.
186
+ *
187
+ * @param[in] vm a Jsonnet VM
188
+ * @param[in] obj a Ruby object to be converted
189
+ * @param[out] success set to 1 on success, set to 0 on failure.
190
+ * @returns the converted value on success, an error message on failure.
191
+ */
192
+ struct JsonnetJsonValue *
193
+ rubyjsonnet_obj_to_json(struct JsonnetVm *vm, VALUE obj, int *success)
194
+ {
195
+ int state = 0;
196
+ const struct protect_args args = {vm, obj};
197
+ VALUE result = rb_protect(protect_obj_to_json_block, (VALUE)&args, &state);
198
+ if (state) {
199
+ const VALUE msg = rubyjsonnet_format_exception(rb_errinfo());
200
+ rb_set_errinfo(Qnil);
201
+ *success = 0;
202
+ return string_to_json(vm, msg);
203
+ }
204
+ *success = 1;
205
+ return (struct JsonnetJsonValue *)result;
206
+ }
@@ -0,0 +1,39 @@
1
+ #ifndef RUBY_JSONNET_RUBY_JSONNET_H_
2
+ #define RUBY_JSONNET_RUBY_JSONNET_H_
3
+
4
+ #include <ruby/ruby.h>
5
+ #include <ruby/encoding.h>
6
+
7
+ extern const rb_data_type_t jsonnet_vm_type;
8
+
9
+ struct native_callback_ctx {
10
+ VALUE callback;
11
+ long arity;
12
+ VALUE vm;
13
+ };
14
+
15
+ struct jsonnet_vm_wrap {
16
+ struct JsonnetVm *vm;
17
+
18
+ VALUE import_callback;
19
+ struct {
20
+ long len;
21
+ struct native_callback_ctx **contexts;
22
+ } native_callbacks;
23
+ };
24
+
25
+ void rubyjsonnet_init_vm(VALUE mod);
26
+ void rubyjsonnet_init_callbacks(VALUE cVM);
27
+ void rubyjsonnet_init_helpers(VALUE mod);
28
+
29
+ struct jsonnet_vm_wrap *rubyjsonnet_obj_to_vm(VALUE vm);
30
+
31
+ VALUE rubyjsonnet_json_to_obj(struct JsonnetVm *vm, const struct JsonnetJsonValue *value);
32
+ struct JsonnetJsonValue *rubyjsonnet_obj_to_json(struct JsonnetVm *vm, VALUE obj, int *success);
33
+
34
+ rb_encoding *rubyjsonnet_assert_asciicompat(VALUE str);
35
+ char *rubyjsonnet_str_to_cstr(struct JsonnetVm *vm, VALUE str);
36
+ VALUE rubyjsonnet_format_exception(VALUE exc);
37
+ int rubyjsonnet_jump_tag(const char *exc_mesg);
38
+
39
+ #endif /* RUBY_JSONNET_RUBY_JSONNET_H_ */
@@ -0,0 +1,351 @@
1
+ #include <libjsonnet.h>
2
+ #include <ruby/ruby.h>
3
+ #include <ruby/intern.h>
4
+
5
+ #include "ruby_jsonnet.h"
6
+
7
+ /*
8
+ * defines the core part of Jsonnet::VM
9
+ */
10
+
11
+ /*
12
+ * Jsonnet evaluator
13
+ *
14
+ * call-seq:
15
+ * Jsonnet::VM
16
+ */
17
+ static VALUE cVM;
18
+
19
+ /*
20
+ * Raised on evaluation errors in a Jsonnet VM.
21
+ */
22
+ static VALUE eEvaluationError;
23
+
24
+ static void raise_eval_error(struct JsonnetVm *vm, char *msg, rb_encoding *enc);
25
+ static VALUE str_new_json(struct JsonnetVm *vm, char *json, rb_encoding *enc);
26
+ static VALUE fileset_new(struct JsonnetVm *vm, char *buf, rb_encoding *enc);
27
+
28
+ static void vm_free(void *ptr);
29
+ static void vm_mark(void *ptr);
30
+
31
+ const rb_data_type_t jsonnet_vm_type = {
32
+ "JsonnetVm",
33
+ {
34
+ /* dmark = */ vm_mark,
35
+ /* dfree = */ vm_free,
36
+ /* dsize = */ 0,
37
+ },
38
+ /* parent = */ 0,
39
+ /* data = */ 0,
40
+ /* flags = */ RUBY_TYPED_FREE_IMMEDIATELY,
41
+ };
42
+
43
+ struct jsonnet_vm_wrap *
44
+ rubyjsonnet_obj_to_vm(VALUE wrap)
45
+ {
46
+ struct jsonnet_vm_wrap *vm;
47
+ TypedData_Get_Struct(wrap, struct jsonnet_vm_wrap, &jsonnet_vm_type, vm);
48
+
49
+ return vm;
50
+ }
51
+
52
+ static VALUE
53
+ vm_s_new(int argc, const VALUE *argv, VALUE klass)
54
+ {
55
+ struct jsonnet_vm_wrap *vm;
56
+ VALUE self = TypedData_Make_Struct(cVM, struct jsonnet_vm_wrap, &jsonnet_vm_type, vm);
57
+ vm->vm = jsonnet_make();
58
+ vm->import_callback = Qnil;
59
+ vm->native_callbacks.len = 0;
60
+ vm->native_callbacks.contexts = NULL;
61
+
62
+ rb_obj_call_init(self, argc, argv);
63
+ return self;
64
+ }
65
+
66
+ static void
67
+ vm_free(void *ptr)
68
+ {
69
+ int i;
70
+ struct jsonnet_vm_wrap *vm = (struct jsonnet_vm_wrap *)ptr;
71
+ jsonnet_destroy(vm->vm);
72
+
73
+ for (i = 0; i < vm->native_callbacks.len; ++i) {
74
+ struct native_callback_ctx *ctx = vm->native_callbacks.contexts[i];
75
+ RB_REALLOC_N(ctx, struct native_callback_ctx, 0);
76
+ }
77
+ RB_REALLOC_N(vm->native_callbacks.contexts, struct native_callback_ctx *, 0);
78
+
79
+ RB_REALLOC_N(vm, struct jsonnet_vm_wrap, 0);
80
+ }
81
+
82
+ static void
83
+ vm_mark(void *ptr)
84
+ {
85
+ int i;
86
+ struct jsonnet_vm_wrap *vm = (struct jsonnet_vm_wrap *)ptr;
87
+
88
+ rb_gc_mark(vm->import_callback);
89
+ for (i = 0; i < vm->native_callbacks.len; ++i) {
90
+ rb_gc_mark(vm->native_callbacks.contexts[i]->callback);
91
+ }
92
+ }
93
+
94
+ static VALUE
95
+ vm_evaluate_file(VALUE self, VALUE fname, VALUE encoding, VALUE multi_p)
96
+ {
97
+ int error;
98
+ char *result;
99
+ rb_encoding *const enc = rb_to_encoding(encoding);
100
+ struct jsonnet_vm_wrap *vm = rubyjsonnet_obj_to_vm(self);
101
+
102
+ FilePathValue(fname);
103
+ if (RTEST(multi_p)) {
104
+ result = jsonnet_evaluate_file_multi(vm->vm, StringValueCStr(fname), &error);
105
+ } else {
106
+ result = jsonnet_evaluate_file(vm->vm, StringValueCStr(fname), &error);
107
+ }
108
+
109
+ if (error) {
110
+ raise_eval_error(vm->vm, result, rb_enc_get(fname));
111
+ }
112
+ return RTEST(multi_p) ? fileset_new(vm->vm, result, enc) : str_new_json(vm->vm, result, enc);
113
+ }
114
+
115
+ static VALUE
116
+ vm_evaluate(VALUE self, VALUE snippet, VALUE fname, VALUE multi_p)
117
+ {
118
+ int error;
119
+ char *result;
120
+ struct jsonnet_vm_wrap *vm = rubyjsonnet_obj_to_vm(self);
121
+
122
+ rb_encoding *enc = rubyjsonnet_assert_asciicompat(StringValue(snippet));
123
+ FilePathValue(fname);
124
+ if (RTEST(multi_p)) {
125
+ result = jsonnet_evaluate_snippet_multi(vm->vm, StringValueCStr(fname),
126
+ StringValueCStr(snippet), &error);
127
+ } else {
128
+ result = jsonnet_evaluate_snippet(vm->vm, StringValueCStr(fname), StringValueCStr(snippet),
129
+ &error);
130
+ }
131
+
132
+ if (error) {
133
+ raise_eval_error(vm->vm, result, rb_enc_get(fname));
134
+ }
135
+ return RTEST(multi_p) ? fileset_new(vm->vm, result, enc) : str_new_json(vm->vm, result, enc);
136
+ }
137
+
138
+ #define vm_bind_variable(type, self, key, val) \
139
+ do { \
140
+ struct jsonnet_vm_wrap *vm; \
141
+ \
142
+ rubyjsonnet_assert_asciicompat(StringValue(key)); \
143
+ rubyjsonnet_assert_asciicompat(StringValue(val)); \
144
+ TypedData_Get_Struct(self, struct jsonnet_vm_wrap, &jsonnet_vm_type, vm); \
145
+ jsonnet_##type(vm->vm, StringValueCStr(key), StringValueCStr(val)); \
146
+ } while (0)
147
+
148
+ /*
149
+ * Binds an external variable to a value.
150
+ * @param [String] key name of the variable
151
+ * @param [String] val the value
152
+ */
153
+ static VALUE
154
+ vm_ext_var(VALUE self, VALUE key, VALUE val)
155
+ {
156
+ vm_bind_variable(ext_var, self, key, val);
157
+ return Qnil;
158
+ }
159
+
160
+ /*
161
+ * Binds an external variable to a code fragment.
162
+ * @param [String] key name of the variable
163
+ * @param [String] code Jsonnet expression
164
+ */
165
+ static VALUE
166
+ vm_ext_code(VALUE self, VALUE key, VALUE code)
167
+ {
168
+ vm_bind_variable(ext_code, self, key, code);
169
+ return Qnil;
170
+ }
171
+
172
+ /*
173
+ * Binds a top-level argument to a value.
174
+ * @param [String] key name of the variable
175
+ * @param [String] val the value
176
+ */
177
+ static VALUE
178
+ vm_tla_var(VALUE self, VALUE key, VALUE val)
179
+ {
180
+ vm_bind_variable(tla_var, self, key, val);
181
+ return Qnil;
182
+ }
183
+
184
+ /*
185
+ * Binds a top-level argument to a code fragment.
186
+ * @param [String] key name of the variable
187
+ * @param [String] code Jsonnet expression
188
+ */
189
+ static VALUE
190
+ vm_tla_code(VALUE self, VALUE key, VALUE code)
191
+ {
192
+ vm_bind_variable(tla_code, self, key, code);
193
+ return Qnil;
194
+ }
195
+
196
+ /*
197
+ * Adds library search paths
198
+ */
199
+ static VALUE
200
+ vm_jpath_add_m(int argc, const VALUE *argv, VALUE self)
201
+ {
202
+ int i;
203
+ struct jsonnet_vm_wrap *vm = rubyjsonnet_obj_to_vm(self);
204
+
205
+ for (i = 0; i < argc; ++i) {
206
+ VALUE jpath = argv[i];
207
+ FilePathValue(jpath);
208
+ jsonnet_jpath_add(vm->vm, StringValueCStr(jpath));
209
+ }
210
+ return Qnil;
211
+ }
212
+
213
+ static VALUE
214
+ vm_set_max_stack(VALUE self, VALUE val)
215
+ {
216
+ struct jsonnet_vm_wrap *vm = rubyjsonnet_obj_to_vm(self);
217
+ jsonnet_max_stack(vm->vm, NUM2UINT(val));
218
+ return Qnil;
219
+ }
220
+
221
+ static VALUE
222
+ vm_set_gc_min_objects(VALUE self, VALUE val)
223
+ {
224
+ struct jsonnet_vm_wrap *vm = rubyjsonnet_obj_to_vm(self);
225
+ jsonnet_gc_min_objects(vm->vm, NUM2UINT(val));
226
+ return Qnil;
227
+ }
228
+
229
+ static VALUE
230
+ vm_set_gc_growth_trigger(VALUE self, VALUE val)
231
+ {
232
+ struct jsonnet_vm_wrap *vm = rubyjsonnet_obj_to_vm(self);
233
+ jsonnet_gc_growth_trigger(vm->vm, NUM2DBL(val));
234
+ return Qnil;
235
+ }
236
+
237
+ /*
238
+ * Let #evaluate and #evaluate_file return a raw String instead of JSON-encoded string if val is
239
+ * true
240
+ * @param [Boolean] val
241
+ */
242
+ static VALUE
243
+ vm_set_string_output(VALUE self, VALUE val)
244
+ {
245
+ struct jsonnet_vm_wrap *vm = rubyjsonnet_obj_to_vm(self);
246
+ jsonnet_string_output(vm->vm, RTEST(val));
247
+ return Qnil;
248
+ }
249
+
250
+ static VALUE
251
+ vm_set_max_trace(VALUE self, VALUE val)
252
+ {
253
+ struct jsonnet_vm_wrap *vm = rubyjsonnet_obj_to_vm(self);
254
+ jsonnet_max_trace(vm->vm, NUM2UINT(val));
255
+ return Qnil;
256
+ }
257
+
258
+ void
259
+ rubyjsonnet_init_vm(VALUE mJsonnet)
260
+ {
261
+ cVM = rb_define_class_under(mJsonnet, "VM", rb_cData);
262
+ rb_define_singleton_method(cVM, "new", vm_s_new, -1);
263
+ rb_define_private_method(cVM, "eval_file", vm_evaluate_file, 3);
264
+ rb_define_private_method(cVM, "eval_snippet", vm_evaluate, 3);
265
+ rb_define_method(cVM, "ext_var", vm_ext_var, 2);
266
+ rb_define_method(cVM, "ext_code", vm_ext_code, 2);
267
+ rb_define_method(cVM, "tla_var", vm_tla_var, 2);
268
+ rb_define_method(cVM, "tla_code", vm_tla_code, 2);
269
+ rb_define_method(cVM, "jpath_add", vm_jpath_add_m, -1);
270
+ rb_define_method(cVM, "max_stack=", vm_set_max_stack, 1);
271
+ rb_define_method(cVM, "gc_min_objects=", vm_set_gc_min_objects, 1);
272
+ rb_define_method(cVM, "gc_growth_trigger=", vm_set_gc_growth_trigger, 1);
273
+ rb_define_method(cVM, "string_output=", vm_set_string_output, 1);
274
+ rb_define_method(cVM, "max_trace=", vm_set_max_trace, 1);
275
+
276
+ rubyjsonnet_init_callbacks(cVM);
277
+
278
+ eEvaluationError = rb_define_class_under(mJsonnet, "EvaluationError", rb_eRuntimeError);
279
+ }
280
+
281
+ /**
282
+ * raises an EvaluationError whose message is \c msg.
283
+ * @param[in] vm a JsonnetVM
284
+ * @param[in] msg must be a NUL-terminated string returned by \c vm.
285
+ * @return never returns
286
+ * @throw EvaluationError
287
+ * @sa rescue_callback
288
+ */
289
+ static void
290
+ raise_eval_error(struct JsonnetVm *vm, char *msg, rb_encoding *enc)
291
+ {
292
+ VALUE ex;
293
+ const int state = rubyjsonnet_jump_tag(msg);
294
+ if (state) {
295
+ /*
296
+ * This is not actually an exception but another type of long jump
297
+ * with the state, temporarily caught by rescue_callback().
298
+ */
299
+ jsonnet_realloc(vm, msg, 0);
300
+ rb_jump_tag(state);
301
+ }
302
+
303
+ ex = rb_exc_new3(eEvaluationError, rb_enc_str_new_cstr(msg, enc));
304
+ jsonnet_realloc(vm, msg, 0);
305
+ rb_exc_raise(ex);
306
+ }
307
+
308
+ /**
309
+ * Returns a String whose contents is equal to \c json.
310
+ * It automatically frees \c json just after constructing the return value.
311
+ *
312
+ * @param[in] vm a JsonnetVM
313
+ * @param[in] json must be a NUL-terminated string returned by \c vm.
314
+ * @return Ruby string equal to \c json.
315
+ */
316
+ static VALUE
317
+ str_new_json(struct JsonnetVm *vm, char *json, rb_encoding *enc)
318
+ {
319
+ VALUE str = rb_enc_str_new_cstr(json, enc);
320
+ jsonnet_realloc(vm, json, 0);
321
+ return str;
322
+ }
323
+
324
+ /**
325
+ * Returns a Hash, whose keys are file names in the multi-mode of Jsonnet,
326
+ * and whose values are corresponding JSON values.
327
+ * It automatically frees \c json just after constructing the return value.
328
+ *
329
+ * @param[in] vm a JsonnetVM
330
+ * @param[in] buf NUL-separated and double-NUL-terminated sequence of strings returned by \c vm.
331
+ * @return Hash
332
+ */
333
+ static VALUE
334
+ fileset_new(struct JsonnetVm *vm, char *buf, rb_encoding *enc)
335
+ {
336
+ VALUE fileset = rb_hash_new();
337
+ char *ptr, *json;
338
+ for (ptr = buf; *ptr; ptr = json + strlen(json) + 1) {
339
+ json = ptr + strlen(ptr) + 1;
340
+ if (!*json) {
341
+ VALUE ex = rb_exc_new3(eEvaluationError,
342
+ rb_enc_sprintf(enc, "output file %s without body", ptr));
343
+ jsonnet_realloc(vm, buf, 0);
344
+ rb_exc_raise(ex);
345
+ }
346
+
347
+ rb_hash_aset(fileset, rb_enc_str_new_cstr(ptr, enc), rb_enc_str_new_cstr(json, enc));
348
+ }
349
+ jsonnet_realloc(vm, buf, 0);
350
+ return fileset;
351
+ }