Coverage Report

Created: 2025-10-10 06:16

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/moddable/xs/sources/xsGlobal.c
Line
Count
Source
1
/*
2
 * Copyright (c) 2016-2025  Moddable Tech, Inc.
3
 *
4
 *   This file is part of the Moddable SDK Runtime.
5
 * 
6
 *   The Moddable SDK Runtime is free software: you can redistribute it and/or modify
7
 *   it under the terms of the GNU Lesser General Public License as published by
8
 *   the Free Software Foundation, either version 3 of the License, or
9
 *   (at your option) any later version.
10
 * 
11
 *   The Moddable SDK Runtime is distributed in the hope that it will be useful,
12
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
13
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
 *   GNU Lesser General Public License for more details.
15
 * 
16
 *   You should have received a copy of the GNU Lesser General Public License
17
 *   along with the Moddable SDK Runtime.  If not, see <http://www.gnu.org/licenses/>.
18
 *
19
 * This file incorporates work covered by the following copyright and  
20
 * permission notice:  
21
 *
22
 *       Copyright (C) 2010-2016 Marvell International Ltd.
23
 *       Copyright (C) 2002-2010 Kinoma, Inc.
24
 *
25
 *       Licensed under the Apache License, Version 2.0 (the "License");
26
 *       you may not use this file except in compliance with the License.
27
 *       You may obtain a copy of the License at
28
 *
29
 *        http://www.apache.org/licenses/LICENSE-2.0
30
 *
31
 *       Unless required by applicable law or agreed to in writing, software
32
 *       distributed under the License is distributed on an "AS IS" BASIS,
33
 *       WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
34
 *       See the License for the specific language governing permissions and
35
 *       limitations under the License.
36
 */
37
38
#include "xsAll.h"
39
40
static void fx_trace_aux(txMachine* the, txInteger flags);
41
42
static const char ICACHE_RODATA_ATTR gxURIEmptySet[128] = {
43
  /* 0 1 2 3 4 5 6 7 8 9 A B C D E F */
44
   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0x                    */
45
   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 1x                    */
46
   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 2x   !"#$%&'()*+,-./  */
47
   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 3x  0123456789:;<=>?  */
48
   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 4x  @ABCDEFGHIJKLMNO  */
49
   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 5X  PQRSTUVWXYZ[\]^_  */
50
   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 6x  `abcdefghijklmno  */
51
   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0  /* 7X  pqrstuvwxyz{|}~   */
52
};
53
54
static const char ICACHE_RODATA_ATTR gxURIReservedSet[128] = {
55
  /* 0 1 2 3 4 5 6 7 8 9 A B C D E F */
56
   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0x                    */
57
   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 1x                    */
58
   0,0,0,1,1,0,1,0,0,0,0,1,1,0,0,1, /* 2x   !"#$%&'()*+,-./  */
59
   0,0,0,0,0,0,0,0,0,0,1,1,0,1,0,1, /* 3x  0123456789:;<=>?  */
60
   1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 4x  @ABCDEFGHIJKLMNO  */
61
   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 5X  PQRSTUVWXYZ[\]^_  */
62
   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 6x  `abcdefghijklmno  */
63
   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0  /* 7X  pqrstuvwxyz{|}~   */
64
};
65
66
static const char ICACHE_RODATA_ATTR gxURIUnescapedSet[128] = {
67
  /* 0 1 2 3 4 5 6 7 8 9 A B C D E F */
68
   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0x                    */
69
   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 1x                    */
70
   0,1,0,0,0,0,0,1,1,1,1,0,0,1,1,0, /* 2x   !"#$%&'()*+,-./  */
71
   1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0, /* 3x  0123456789:;<=>?  */
72
   0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, /* 4x  @ABCDEFGHIJKLMNO  */
73
   1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,1, /* 5X  PQRSTUVWXYZ[\]^_  */
74
   0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, /* 6x  `abcdefghijklmno  */
75
   1,1,1,1,1,1,1,1,1,1,1,0,0,0,1,0  /* 7X  pqrstuvwxyz{|}~   */
76
};
77
78
static const char ICACHE_RODATA_ATTR gxURIReservedAndUnescapedSet[128] = {
79
  /* 0 1 2 3 4 5 6 7 8 9 A B C D E F */
80
   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0x                    */
81
   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 1x                    */
82
   0,1,0,1,1,0,1,1,1,1,1,1,1,1,1,1, /* 2x   !"#$%&'()*+,-./  */
83
   1,1,1,1,1,1,1,1,1,1,1,1,0,1,0,1, /* 3x  0123456789:;<=>?  */
84
   1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, /* 4x  @ABCDEFGHIJKLMNO  */
85
   1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,1, /* 5X  PQRSTUVWXYZ[\]^_  */
86
   0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, /* 6x  `abcdefghijklmno  */
87
   1,1,1,1,1,1,1,1,1,1,1,0,0,0,1,0  /* 7X  pqrstuvwxyz{|}~   */
88
};
89
90
const txBehavior ICACHE_FLASH_ATTR gxGlobalBehavior = {
91
  fxGlobalGetProperty,
92
  fxGlobalSetProperty,
93
  fxOrdinaryCall,
94
  fxOrdinaryConstruct,
95
  fxOrdinaryDefineOwnProperty,
96
  fxGlobalDeleteProperty,
97
  fxOrdinaryGetOwnProperty,
98
  fxOrdinaryGetPropertyValue,
99
  fxOrdinaryGetPrototype,
100
  fxOrdinaryHasProperty,
101
  fxOrdinaryIsExtensible,
102
  fxOrdinaryOwnKeys,
103
  fxOrdinaryPreventExtensions,
104
  fxOrdinarySetPropertyValue,
105
  fxOrdinarySetPrototype,
106
};
107
      
108
void fxBuildGlobal(txMachine* the)
109
28.2k
{
110
28.2k
  txSlot* slot;
111
112
28.2k
  fxNewInstance(the);
113
28.2k
  mxPull(mxObjectPrototype);
114
  
115
28.2k
  mxPush(mxObjectPrototype);
116
28.2k
  fxNewFunctionInstance(the, XS_NO_ID);
117
28.2k
  mxPull(mxFunctionPrototype);
118
  
119
28.2k
  mxPush(mxObjectPrototype);
120
28.2k
  slot = fxNewObjectInstance(the);
121
28.2k
  mxPull(mxIteratorPrototype);
122
  
123
28.2k
  fxBuildHostFunction(the, mxCallback(fx_isFinite), 1, mxID(_isFinite));
124
28.2k
  mxPull(mxIsFiniteFunction);
125
28.2k
  fxBuildHostFunction(the, mxCallback(fx_isNaN), 1, mxID(_isNaN));
126
28.2k
  mxPull(mxIsNaNFunction);
127
28.2k
  fxBuildHostFunction(the, mxCallback(fx_parseFloat), 1, mxID(_parseFloat));
128
28.2k
  mxPull(mxParseFloatFunction);
129
28.2k
  fxBuildHostFunction(the, mxCallback(fx_parseInt), 2, mxID(_parseInt));
130
28.2k
  mxPull(mxParseIntFunction);
131
28.2k
  fxBuildHostFunction(the, mxCallback(fx_decodeURI), 1, mxID(_decodeURI));
132
28.2k
  mxPull(mxDecodeURIFunction);
133
28.2k
  fxBuildHostFunction(the, mxCallback(fx_decodeURIComponent), 1, mxID(_decodeURIComponent));
134
28.2k
  mxPull(mxDecodeURIComponentFunction);
135
28.2k
  fxBuildHostFunction(the, mxCallback(fx_encodeURI), 1, mxID(_encodeURI));
136
28.2k
  mxPull(mxEncodeURIFunction);
137
28.2k
  fxBuildHostFunction(the, mxCallback(fx_encodeURIComponent), 1, mxID(_encodeURIComponent));
138
28.2k
  mxPull(mxEncodeURIComponentFunction);
139
28.2k
  fxBuildHostFunction(the, mxCallback(fx_escape), 1, mxID(_escape));
140
28.2k
  mxPull(mxEscapeFunction);
141
28.2k
  fxBuildHostFunction(the, mxCallback(fx_eval), 1, mxID(_eval));
142
28.2k
  mxPull(mxEvalFunction);
143
28.2k
  fxBuildHostFunction(the, mxCallback(fx_unescape), 1, mxID(_unescape));
144
28.2k
  mxPull(mxUnescapeFunction);
145
  
146
28.2k
  slot = fxBuildHostFunction(the, mxCallback(fx_trace), 1, mxID(_trace));
147
28.2k
  slot = fxLastProperty(the, slot);
148
28.2k
  slot = fxNextHostFunctionProperty(the, slot, mxCallback(fx_trace_center), 1, mxID(_center), XS_DONT_ENUM_FLAG);
149
28.2k
  slot = fxNextHostFunctionProperty(the, slot, mxCallback(fx_trace_left), 1, mxID(_left), XS_DONT_ENUM_FLAG);
150
28.2k
  slot = fxNextHostFunctionProperty(the, slot, mxCallback(fx_trace_right), 1, mxID(_right), XS_DONT_ENUM_FLAG);
151
28.2k
    mxPull(mxTraceFunction);
152
  
153
28.2k
  fxNewHostFunction(the, mxCallback(fxThrowTypeError), 0, XS_NO_ID, XS_NO_ID);
154
28.2k
  mxThrowTypeErrorFunction = *the->stack;
155
28.2k
  slot = the->stack->value.reference;
156
28.2k
  slot->flag |= XS_DONT_PATCH_FLAG;
157
28.2k
  slot = slot->next;
158
141k
  while (slot) {
159
112k
    slot->flag |= XS_DONT_DELETE_FLAG | XS_DONT_SET_FLAG;
160
112k
    slot = slot->next;
161
112k
  }
162
28.2k
  mxPop();
163
28.2k
}
164
165
txSlot* fxNewGlobalInstance(txMachine* the)
166
28.8k
{
167
28.8k
  txSlot* instance;
168
28.8k
  txSlot* property;
169
28.8k
  txSize size = fxMultiplyChunkSizes(the, XS_INTRINSICS_COUNT, sizeof(txSlot*));
170
28.8k
  instance = fxNewSlot(the);
171
28.8k
  instance->flag = XS_EXOTIC_FLAG;
172
28.8k
  instance->kind = XS_INSTANCE_KIND;
173
28.8k
  instance->value.instance.garbage = C_NULL;
174
28.8k
  instance->value.instance.prototype = the->stack->value.reference;
175
28.8k
  the->stack->value.reference = instance;
176
28.8k
  the->stack->kind = XS_REFERENCE_KIND;
177
28.8k
  property = instance->next = fxNewSlot(the);
178
28.8k
  property->value.table.address = (txSlot**)fxNewChunk(the, size);
179
28.8k
  c_memset(property->value.table.address, 0, size);
180
28.8k
  property->value.table.length = XS_INTRINSICS_COUNT;
181
28.8k
  property->flag = XS_INTERNAL_FLAG;
182
28.8k
  property->ID = XS_GLOBAL_BEHAVIOR;
183
28.8k
  property->kind = XS_GLOBAL_KIND;
184
28.8k
  return instance;
185
28.8k
}
186
187
txBoolean fxGlobalDeleteProperty(txMachine* the, txSlot* instance, txID id, txIndex index) 
188
73.6k
{
189
73.6k
  txBoolean result = fxOrdinaryDeleteProperty(the, instance, id, index);
190
73.6k
  if ((XS_SYMBOL_ID_COUNT <= id) && (id < XS_INTRINSICS_COUNT) && result) {
191
17
    txSlot* globals = instance->next;
192
17
    globals->value.table.address[id] = C_NULL;
193
17
  }
194
73.6k
  return result;
195
73.6k
}
196
197
txSlot* fxGlobalGetProperty(txMachine* the, txSlot* instance, txID id, txIndex index, txFlag flag) 
198
134M
{
199
134M
  txSlot* result = C_NULL;
200
134M
  if ((XS_SYMBOL_ID_COUNT <= id) && (id < XS_INTRINSICS_COUNT)) {
201
21.1M
    txSlot* globals = instance->next;
202
21.1M
    result = globals->value.table.address[id];
203
21.1M
    if (!result) {
204
35.5k
      result = fxOrdinaryGetProperty(the, instance, id, index, flag);
205
35.5k
      globals->value.table.address[id] = result;
206
35.5k
    }
207
21.1M
  }
208
134M
  if (!result)
209
113M
    result = fxOrdinaryGetProperty(the, instance, id, index, flag);
210
134M
  return result;
211
134M
}
212
213
txSlot* fxGlobalSetProperty(txMachine* the, txSlot* instance, txID id, txIndex index, txFlag flag) 
214
35.9M
{
215
35.9M
  txSlot* result = C_NULL;
216
35.9M
  if ((XS_SYMBOL_ID_COUNT <= id) && (id < XS_INTRINSICS_COUNT)) {
217
102k
    txSlot* globals = instance->next;
218
102k
    result = globals->value.table.address[id];
219
102k
    if (!result) {
220
1
      result = fxOrdinarySetProperty(the, instance, id, index, flag);
221
1
      globals->value.table.address[id] = result;
222
1
    }
223
102k
  }
224
35.9M
  if (!result)
225
35.8M
    result = fxOrdinarySetProperty(the, instance, id, index, flag);
226
35.9M
  return result;
227
35.9M
}
228
229
void fx_decodeURI(txMachine* the)
230
1.05M
{
231
1.05M
  if (mxArgc < 1)
232
1
    mxSyntaxError("no URI parameter");
233
1.05M
  fxDecodeURI(the, (txString)gxURIReservedSet);
234
1.05M
}
235
236
void fx_decodeURIComponent(txMachine* the)
237
66.3k
{
238
66.3k
  if (mxArgc < 1)
239
0
    mxSyntaxError("no URI Component parameter");
240
66.3k
  fxDecodeURI(the, (txString)gxURIEmptySet);
241
66.3k
}
242
243
void fx_encodeURI(txMachine* the)
244
10.3k
{
245
10.3k
  if (mxArgc < 1)
246
0
    mxSyntaxError("no URI parameter");
247
10.3k
  fxEncodeURI(the, (txString)gxURIReservedAndUnescapedSet);
248
10.3k
}
249
250
void fx_encodeURIComponent(txMachine* the)
251
83
{
252
83
  if (mxArgc < 1)
253
0
    mxSyntaxError("no URI Component parameter");
254
83
  fxEncodeURI(the, (txString)gxURIUnescapedSet);
255
83
}
256
257
void fx_escape(txMachine* the)
258
104k
{
259
104k
  static const char ICACHE_RODATA_ATTR gxSet[128] = {
260
    /* 0 1 2 3 4 5 6 7 8 9 A B C D E F */
261
104k
     0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0x                    */
262
104k
     0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 1x                    */
263
104k
     0,0,0,0,0,0,0,0,0,0,1,1,0,1,1,1, /* 2x   !"#$%&'()*+,-./  */
264
104k
     1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0, /* 3x  0123456789:;<=>?  */
265
104k
     1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, /* 4x  @ABCDEFGHIJKLMNO  */
266
104k
     1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,1, /* 5X  PQRSTUVWXYZ[\]^_  */
267
104k
     0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, /* 6x  `abcdefghijklmno  */
268
104k
     1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0  /* 7X  pqrstuvwxyz{|}~   */
269
104k
  };
270
104k
  txString src;
271
104k
  txInteger length;
272
104k
  txInteger c;
273
104k
  txString dst;
274
  
275
104k
  if (mxArgc < 1) {
276
356
    mxResult->value.string = mxUndefinedString.value.string;
277
356
    mxResult->kind = mxUndefinedString.kind;
278
356
    return;
279
356
  }
280
104k
  src = fxToString(the, mxArgv(0));
281
104k
  length = 0;
282
1.98M
  while (((src = mxStringByteDecode(src, &c))) && (c != C_EOF)) {
283
1.88M
    if ((c < 128) && c_read8(gxSet + (int)c))
284
1.14M
      length = fxAddChunkSizes(the, length, 1);
285
731k
    else if (c < 256)
286
730k
      length = fxAddChunkSizes(the, length, 3);
287
1.06k
    else if (c < 0x10000)
288
1.04k
      length = fxAddChunkSizes(the, length, 6);
289
20
    else
290
20
      length = fxAddChunkSizes(the, length, 12);
291
1.88M
  }
292
104k
  if (length == (src - mxArgv(0)->value.string)) {
293
122
    mxResult->value.string = mxArgv(0)->value.string;
294
122
    mxResult->kind = mxArgv(0)->kind;
295
122
    return;
296
122
  }
297
104k
  mxResult->value.string = fxNewChunk(the, fxAddChunkSizes(the, length, 1));
298
104k
  mxResult->kind = XS_STRING_KIND;
299
104k
  src = mxArgv(0)->value.string;
300
104k
  dst = mxResult->value.string;
301
1.98M
  while (((src = mxStringByteDecode(src, &c))) && (c != C_EOF)) {
302
1.87M
    if ((c < 128) && c_read8(gxSet + (int)c))
303
1.14M
      *dst++ = (char)c;
304
731k
    else if (c < 256) {
305
730k
      *dst++ = '%';
306
730k
      dst = fxStringifyHexEscape(dst, c);
307
730k
    }
308
1.06k
    else {
309
1.06k
      *dst++ = '%'; 
310
1.06k
      *dst++ = 'u'; 
311
1.06k
      dst = fxStringifyUnicodeEscape(dst, c, '%');
312
1.06k
    }
313
1.87M
  }
314
104k
  *dst = 0;
315
104k
}
316
317
void fx_eval(txMachine* the)
318
3.86k
{
319
3.86k
  txStringStream aStream;
320
3.86k
  txSlot* realm;
321
3.86k
  txSlot* module = mxFunctionInstanceHome(mxFunction->value.reference)->value.home.module;
322
3.86k
  if (!module) module = mxProgram.value.reference;
323
  
324
3.86k
  realm = mxModuleInstanceInternal(module)->value.module.realm;
325
3.86k
  if (mxArgc < 1)
326
3
    return;
327
3.86k
  if (!mxIsStringPrimitive(mxArgv(0))) {
328
3.86k
    *mxResult = *mxArgv(0);
329
3.86k
    return;
330
3.86k
  }
331
1
  aStream.slot = mxArgv(0);
332
1
  aStream.offset = 0;
333
1
  aStream.size = mxStringLength(fxToString(the, mxArgv(0)));
334
1
  fxRunScript(the, fxParseScript(the, &aStream, fxStringGetter, mxProgramFlag | mxEvalFlag), mxRealmGlobal(realm), C_NULL, mxRealmClosures(realm)->value.reference, C_NULL, module);
335
1
  mxPullSlot(mxResult);
336
1
}
337
338
void fx_trace(txMachine* the)
339
11
{
340
//@@#ifdef mxDebug
341
11
  int i;
342
22
  for (i = 0; i < mxArgc; i++) {
343
11
    fxToString(the, mxArgv(i));
344
11
    fxReport(the, "%s", mxArgv(i)->value.string);
345
11
  }
346
//#endif
347
11
}
348
349
void fx_trace_aux(txMachine* the, txInteger flags)
350
0
{
351
0
  txSlot* conversation = C_NULL;
352
0
  void* message = C_NULL;
353
0
  txInteger length = 0;
354
0
  if (mxArgc > 1) {
355
0
    conversation = mxArgv(1);
356
0
    fxToString(the, conversation);
357
0
  }
358
0
  if ((mxArgc > 0) && (mxArgv(0)->kind == XS_REFERENCE_KIND)) {
359
0
    txSlot* slot = mxArgv(0)->value.reference->next;
360
0
    if (slot && (slot->kind == XS_ARRAY_BUFFER_KIND)) {
361
0
      txSlot* bufferInfo = slot->next;
362
0
      length = bufferInfo->value.bufferInfo.length;
363
0
      message = slot->value.arrayBuffer.address;
364
0
      flags |= XS_BUBBLE_BINARY;
365
0
    }
366
0
    else if (slot && (slot->kind == XS_HOST_KIND)) {
367
0
      txSlot* bufferInfo = slot->next;
368
0
      if (bufferInfo && (bufferInfo->kind == XS_BUFFER_INFO_KIND)) {
369
0
        length = bufferInfo->value.bufferInfo.length;
370
0
        message = slot->value.host.data;
371
0
        flags |= XS_BUBBLE_BINARY;
372
0
      }
373
0
    }
374
0
  }
375
0
  if (!message)
376
0
    message = fxToString(the, mxArgv(0));
377
0
  fxBubble(the, flags, message, length, (conversation) ? conversation->value.string : C_NULL);
378
0
}
379
380
void fx_trace_center(txMachine* the)
381
0
{
382
0
  fx_trace_aux(the, XS_NO_FLAG);
383
0
}
384
385
void fx_trace_left(txMachine* the)
386
0
{
387
0
  fx_trace_aux(the, XS_BUBBLE_LEFT);
388
0
}
389
390
void fx_trace_right(txMachine* the)
391
0
{
392
0
  fx_trace_aux(the, XS_BUBBLE_RIGHT);
393
0
}
394
395
void fx_unescape(txMachine* the)
396
0
{
397
0
  txString src;
398
0
  txInteger length;
399
0
  char c;
400
0
  txInteger d;
401
0
  txString dst;
402
403
0
  if (mxArgc < 1) {
404
0
    mxResult->value.string = mxUndefinedString.value.string;
405
0
    mxResult->kind = mxUndefinedString.kind;
406
0
    return;
407
0
  }
408
0
  src = fxToString(the, mxArgv(0));
409
0
  length = 0;
410
0
  while ((c = c_read8(src++))) {
411
0
    if (c == '%') {
412
0
      c = c_read8(src++);
413
0
      if (c == 'u') {
414
0
        if (fxParseUnicodeEscape(&src, &d, 0, '%'))
415
0
          length += mxStringByteLength(d);
416
0
        else
417
0
          length += 2;
418
0
      }
419
0
      else {
420
0
        src--;
421
0
        if (fxParseHexEscape(&src, &d))
422
0
          length += mxStringByteLength(d);
423
0
        else
424
0
          length += 1;
425
0
      }
426
0
    }
427
0
    else
428
0
      length += 1;
429
0
  }   
430
0
  length += 1;
431
0
  if (length == (src - mxArgv(0)->value.string)) {
432
0
    mxResult->value.string = mxArgv(0)->value.string;
433
0
    mxResult->kind = mxArgv(0)->kind;
434
0
    return;
435
0
  }
436
0
  mxResult->value.string = fxNewChunk(the, length);
437
0
  mxResult->kind = XS_STRING_KIND;
438
0
  src = mxArgv(0)->value.string;
439
0
  dst = mxResult->value.string;
440
0
  while ((c = c_read8(src++))) {
441
0
    if (c == '%') {
442
0
      c = c_read8(src++);
443
0
      d = 0;
444
0
      if (c == 'u') {
445
0
        if (fxParseUnicodeEscape(&src, &d, 0, '%'))
446
0
          dst = mxStringByteEncode(dst, d);
447
0
        else {
448
0
          *dst++ = '%';
449
0
          *dst++ = 'u';
450
0
        }
451
0
      }
452
0
      else {
453
0
        src--;
454
0
        if (fxParseHexEscape(&src, &d))
455
0
          dst = mxStringByteEncode(dst, d);
456
0
        else
457
0
          *dst++ = '%';
458
0
      }
459
0
    }
460
0
    else
461
0
      *dst++ = (txU1)c;
462
0
  }
463
0
  *dst = 0;
464
0
}
465
466
void fxDecodeURI(txMachine* the, txString theSet)
467
1.11M
{
468
1.11M
  txString src;
469
1.11M
  txInteger length;
470
1.11M
  txInteger c, d;
471
1.11M
  const txUTF8Sequence *sequence;
472
1.11M
  txInteger size;
473
1.11M
  txString dst;
474
  
475
1.11M
  src = fxToString(the, mxArgv(0));
476
1.11M
  length = 0;
477
52.6M
  while ((c = c_read8(src++))) {
478
51.7M
    if (c == '%') {
479
1.24M
      if (!fxParseHexEscape(&src, &d))
480
127k
        mxURIError("invalid URI");
481
1.11M
      if ((d < 128) && c_read8(theSet + d))
482
530k
        length += 3;
483
588k
      else {
484
815k
        for (sequence = gxUTF8Sequences; sequence->size; sequence++) {
485
815k
          if ((d & sequence->cmask) == sequence->cval)
486
588k
            break;
487
815k
        }
488
588k
        if (!sequence->size)
489
277
          mxURIError("invalid URI");
490
588k
        size = sequence->size - 1;
491
628k
        while (size > 0) {
492
164k
          c = c_read8(src++);
493
164k
          if (c != '%')
494
11
            mxURIError("invalid URI");
495
164k
          if (!fxParseHexEscape(&src, &c))
496
123k
            mxURIError("invalid URI");
497
40.2k
          if ((c & 0xC0) != 0x80)
498
9
            mxURIError("invalid URI");
499
40.2k
          d = (d << 6) | (c & 0x3F);
500
40.2k
          size--;
501
40.2k
        }
502
464k
        d &= sequence->lmask;
503
464k
        if (sequence != gxUTF8Sequences) {
504
5.04k
          if ((sequence[-1].lmask >= (txU4)d) ||   // over-encoding
505
5.04k
            ((d >= 0xD800) & (d <= 0xDFFF)))    // half of surrogate pair
506
7
            mxURIError("invalid URI");
507
5.04k
        }
508
509
464k
        length += mxStringByteLength(d);
510
464k
      }
511
1.11M
    }
512
50.5M
    else
513
50.5M
      length += 1;
514
51.7M
  }    
515
865k
  length += 1;
516
865k
  if (length == (src - mxArgv(0)->value.string)) {
517
407k
    mxResult->value.string = mxArgv(0)->value.string;
518
407k
    mxResult->kind = mxArgv(0)->kind;
519
407k
    return;
520
407k
  }
521
457k
  mxResult->value.string = fxNewChunk(the, length);
522
457k
  mxResult->kind = XS_STRING_KIND;
523
457k
  src = mxArgv(0)->value.string;
524
457k
  dst = mxResult->value.string;
525
37.9M
  while ((c = c_read8(src++))) {
526
37.4M
    if (c == '%') {
527
771k
      fxParseHexEscape(&src, &d);
528
771k
      if ((d < 128) && c_read8(theSet + d)) {
529
306k
        *dst++ = c_read8(src - 3);
530
306k
        *dst++ = c_read8(src - 2);
531
306k
        *dst++ = c_read8(src - 1);
532
306k
      }
533
464k
      else {
534
474k
        for (sequence = gxUTF8Sequences; sequence->size; sequence++) {
535
474k
          if ((d & sequence->cmask) == sequence->cval)
536
464k
            break;
537
474k
        }
538
464k
        size = sequence->size - 1;
539
474k
        while (size > 0) {
540
9.29k
          src++;
541
9.29k
          fxParseHexEscape(&src, &c);
542
9.29k
          d = (d << 6) | (c & 0x3F);
543
9.29k
          size--;
544
9.29k
        }
545
464k
        d &= sequence->lmask;
546
464k
        dst = mxStringByteEncode(dst, d);
547
464k
      }
548
771k
    }
549
36.6M
    else
550
36.6M
      *dst++ = c;
551
37.4M
  }
552
457k
  *dst = 0;
553
457k
}
554
555
void fxEncodeURI(txMachine* the, txString theSet)
556
10.4k
{
557
10.4k
  txString src;
558
10.4k
  txInteger length;
559
10.4k
  txInteger c;
560
10.4k
  txString dst;
561
562
10.4k
  src = fxToString(the, mxArgv(0));
563
10.4k
  length = 0;
564
42.5k
  while (((src = mxStringByteDecode(src, &c))) && (c != C_EOF)) {
565
42.2k
    if (c < 0x80) {
566
18.1k
      if (c_read8(theSet + c))
567
12.8k
        length += 1;
568
5.32k
      else
569
5.32k
        length += 3;
570
18.1k
    }
571
24.0k
    else if (c < 0x800)
572
1.45k
      length += 6;
573
22.6k
    else if ((0xD800 <= c) && (c <= 0xDFFF))
574
10.2k
      mxURIError("invalid string");
575
12.3k
    else if (c < 0x10000)
576
11.7k
      length += 9;
577
612
    else
578
612
      length += 12;
579
42.2k
  }
580
230
  length += 1;
581
230
  if (length == (src - mxArgv(0)->value.string)) {
582
130
    mxResult->value.string = mxArgv(0)->value.string;
583
130
    mxResult->kind = mxArgv(0)->kind;
584
130
    return;
585
130
  }
586
100
  mxResult->value.string = fxNewChunk(the, length);
587
100
  mxResult->kind = XS_STRING_KIND;
588
100
  src = mxArgv(0)->value.string;
589
100
  dst = mxResult->value.string;
590
31.1k
  while (((src = mxStringByteDecode(src, &c))) && (c != C_EOF)) {
591
31.0k
    if (c < 0x80) {
592
17.4k
      if (c_read8(theSet + c))
593
12.2k
        *dst++ = (char)c;
594
5.22k
      else {
595
5.22k
        *dst++ = '%';
596
5.22k
        dst = fxStringifyHexEscape(dst, c);
597
5.22k
      }
598
17.4k
    }
599
13.6k
    else if (c < 0x800) {
600
1.44k
      *dst++ = '%';
601
1.44k
      dst = fxStringifyHexEscape(dst, 0xC0 | (c >> 6));
602
1.44k
      *dst++ = '%';
603
1.44k
      dst = fxStringifyHexEscape(dst, 0x80 | (c & 0x3F));
604
1.44k
    }
605
12.1k
    else if (c < 0x10000) {
606
11.5k
      *dst++ = '%';
607
11.5k
      dst = fxStringifyHexEscape(dst, 0xE0 | (c >> 12));
608
11.5k
      *dst++ = '%';
609
11.5k
      dst = fxStringifyHexEscape(dst, 0x80 | ((c >> 6) & 0x3F));
610
11.5k
      *dst++ = '%';
611
11.5k
      dst = fxStringifyHexEscape(dst, 0x80 | (c & 0x3F));
612
11.5k
    }
613
599
    else {
614
599
      *dst++ = '%';
615
599
      dst = fxStringifyHexEscape(dst, 0xF0 | (c >> 18));
616
599
      *dst++ = '%';
617
599
      dst = fxStringifyHexEscape(dst, 0x80 | ((c >> 12) & 0x3F));
618
599
      *dst++ = '%';
619
599
      dst = fxStringifyHexEscape(dst, 0x80 | ((c >> 6) & 0x3F));
620
599
      *dst++ = '%';
621
599
      dst = fxStringifyHexEscape(dst, 0x80 | (c & 0x3F));
622
599
    }
623
31.0k
  }
624
100
  *dst = 0;
625
100
}