Guest User

Untitled

a guest
Sep 13th, 2018
102
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 64.98 KB | None | 0 0
  1. # HG changeset patch
  2. # User Dmitry Volyntsev <xeioex@nginx.com>
  3. # Date 1536859188 -10800
  4. # Thu Sep 13 20:19:48 2018 +0300
  5. # Node ID ebc9a6f65579c68976a54f88bdeff1248e1d0be8
  6. # Parent c4f9a49486979285b6b508109d4988b4bc30d35d
  7. Fixed Array.prototype.length setter.
  8.  
  9. diff --git a/njs/njs_array.c b/njs/njs_array.c
  10. --- a/njs/njs_array.c
  11. +++ b/njs/njs_array.c
  12. @@ -377,7 +377,7 @@ njs_array_prototype_length(njs_vm_t *vm,
  13. njs_value_t *setval, njs_value_t *retval)
  14. {
  15. double num;
  16. - int32_t size;
  17. + int64_t size;
  18. uint32_t length;
  19. njs_ret_t ret;
  20. njs_value_t *val;
  21. @@ -399,7 +399,7 @@ njs_array_prototype_length(njs_vm_t *vm,
  22. return NJS_ERROR;
  23. }
  24.  
  25. - size = (int32_t) (length - array->length);
  26. + size = (int64_t) length - array->length;
  27.  
  28. if (size > 0) {
  29. ret = njs_array_expand(vm, array, 0, size);
  30. diff --git a/njs/test/njs_unit_test.c b/njs/test/njs_unit_test.c
  31. --- a/njs/test/njs_unit_test.c
  32. +++ b/njs/test/njs_unit_test.c
  33. @@ -2790,9 +2790,15 @@ static njs_unit_test_t njs_test[] =
  34. { nxt_string("[].length = {}"),
  35. nxt_string("RangeError: Invalid array length") },
  36.  
  37. + { nxt_string("[].length = 2**32 - 1"),
  38. + nxt_string("MemoryError") },
  39. +
  40. { nxt_string("[].length = 2**32"),
  41. nxt_string("RangeError: Invalid array length") },
  42.  
  43. + { nxt_string("[].length = 2**32 + 1"),
  44. + nxt_string("RangeError: Invalid array length") },
  45. +
  46. { nxt_string("[].length = -1"),
  47. nxt_string("RangeError: Invalid array length") },
  48.  
  49. # HG changeset patch
  50. # User Dmitry Volyntsev <xeioex@nginx.com>
  51. # Date 1536859188 -10800
  52. # Thu Sep 13 20:19:48 2018 +0300
  53. # Node ID 84d4d83acd928ca222143dbba3ad48c985027262
  54. # Parent ebc9a6f65579c68976a54f88bdeff1248e1d0be8
  55. Fixed njs_array_alloc() for length > 2**31.
  56.  
  57. diff --git a/njs/njs_array.c b/njs/njs_array.c
  58. --- a/njs/njs_array.c
  59. +++ b/njs/njs_array.c
  60. @@ -117,7 +117,7 @@ njs_array_alloc(njs_vm_t *vm, uint32_t l
  61. goto memory_error;
  62. }
  63.  
  64. - size = length + spare;
  65. + size = (size_t) length + spare;
  66.  
  67. if (nxt_slow_path(size * sizeof(njs_value_t) < size)) {
  68. goto memory_error;
  69. # HG changeset patch
  70. # User Dmitry Volyntsev <xeioex@nginx.com>
  71. # Date 1536859189 -10800
  72. # Thu Sep 13 20:19:49 2018 +0300
  73. # Node ID 7baa55f6e48df106e5226bea86949a64804b93d0
  74. # Parent 84d4d83acd928ca222143dbba3ad48c985027262
  75. Fixed njs_string_slice().
  76.  
  77. dst retval argument was ignored.
  78.  
  79. diff --git a/njs/njs_string.c b/njs/njs_string.c
  80. --- a/njs/njs_string.c
  81. +++ b/njs/njs_string.c
  82. @@ -1288,7 +1288,7 @@ njs_string_slice_args(njs_slice_prop_t *
  83.  
  84. nxt_noinline njs_ret_t
  85. njs_string_slice(njs_vm_t *vm, njs_value_t *dst,
  86. - const njs_string_prop_t *string, njs_slice_prop_t *slice)
  87. + const njs_string_prop_t *string, const njs_slice_prop_t *slice)
  88. {
  89. size_t size, n, length;
  90. const u_char *p, *start, *end;
  91. @@ -1325,10 +1325,10 @@ njs_string_slice(njs_vm_t *vm, njs_value
  92. }
  93.  
  94. if (nxt_fast_path(size != 0)) {
  95. - return njs_string_new(vm, &vm->retval, start, size, length);
  96. + return njs_string_new(vm, dst, start, size, length);
  97. }
  98.  
  99. - vm->retval = njs_string_empty;
  100. + *dst = njs_string_empty;
  101.  
  102. return NXT_OK;
  103. }
  104. diff --git a/njs/njs_string.h b/njs/njs_string.h
  105. --- a/njs/njs_string.h
  106. +++ b/njs/njs_string.h
  107. @@ -146,7 +146,7 @@ njs_ret_t njs_string_constructor(njs_vm_
  108. nxt_bool_t njs_string_eq(const njs_value_t *val1, const njs_value_t *val2);
  109. nxt_int_t njs_string_cmp(const njs_value_t *val1, const njs_value_t *val2);
  110. njs_ret_t njs_string_slice(njs_vm_t *vm, njs_value_t *dst,
  111. - const njs_string_prop_t *string, njs_slice_prop_t *slice);
  112. + const njs_string_prop_t *string, const njs_slice_prop_t *slice);
  113. const u_char *njs_string_offset(const u_char *start, const u_char *end,
  114. size_t index);
  115. nxt_noinline uint32_t njs_string_index(njs_string_prop_t *string,
  116. # HG changeset patch
  117. # User Dmitry Volyntsev <xeioex@nginx.com>
  118. # Date 1536860013 -10800
  119. # Thu Sep 13 20:33:33 2018 +0300
  120. # Node ID ffe3b161805980547f16f3ff1142fc55f3f628ca
  121. # Parent 7baa55f6e48df106e5226bea86949a64804b93d0
  122. Object property quering is refactored.
  123.  
  124. njs_property_query() only returns property descriptors and
  125. does not try to invoke PROPERTY_HANDLER for
  126. AccessorDescriptors.
  127.  
  128. njs_property_query_t.own can be used to query for an object's
  129. OwnProperty.
  130.  
  131. njs_value_property() is introduced which corresponds to [[Get]]
  132. method from specification.
  133.  
  134. This fixes #32 issue on Github.
  135.  
  136. diff --git a/njs/njs_object.c b/njs/njs_object.c
  137. --- a/njs/njs_object.c
  138. +++ b/njs/njs_object.c
  139. @@ -246,13 +246,15 @@ njs_object_property(njs_vm_t *vm, const
  140.  
  141. njs_ret_t
  142. njs_property_query(njs_vm_t *vm, njs_property_query_t *pq, njs_value_t *object,
  143. - njs_value_t *property)
  144. + const njs_value_t *property)
  145. {
  146. uint32_t index;
  147. uint32_t (*hash)(const void *, size_t);
  148. njs_ret_t ret;
  149. + njs_array_t *array;
  150. njs_object_t *obj;
  151. njs_function_t *function;
  152. + njs_object_prop_t *prop;
  153. const njs_extern_t *ext_proto;
  154.  
  155. hash = nxt_djb_hash;
  156. @@ -285,6 +287,34 @@ njs_property_query(njs_vm_t *vm, njs_pro
  157.  
  158. if (nxt_fast_path(index < NJS_ARRAY_MAX_LENGTH)) {
  159. return njs_array_property_query(vm, pq, object, index);
  160. +
  161. + } else if (pq->query == NJS_PROPERTY_QUERY_GET) {
  162. +
  163. + /* FIXME: length should be a DataProperty */
  164. +
  165. + njs_primitive_value_to_string(vm, &pq->value, property);
  166. + njs_string_get(&pq->value, &pq->lhq.key);
  167. +
  168. + if (nxt_slow_path(pq->lhq.key.length == 6
  169. + && memcmp(pq->lhq.key.start, "length", 6)
  170. + == 0))
  171. + {
  172. + prop = &pq->scratch;
  173. +
  174. + prop->name = (njs_value_t) njs_string("length");
  175. +
  176. + array = object->data.u.array;
  177. + njs_value_number_set(&prop->value, array->length);
  178. +
  179. + prop->type = NJS_PROPERTY;
  180. + prop->configurable = 0;
  181. + prop->enumerable = 0;
  182. + prop->writable = 1;
  183. +
  184. + pq->lhq.value = prop;
  185. +
  186. + return NXT_OK;
  187. + }
  188. }
  189.  
  190. } else {
  191. @@ -381,6 +411,7 @@ njs_object_property_query(njs_vm_t *vm,
  192. njs_value_t *value, njs_object_t *object)
  193. {
  194. njs_ret_t ret;
  195. + njs_object_t *proto;
  196. njs_object_prop_t *prop;
  197.  
  198. pq->lhq.proto = &njs_object_hash_proto;
  199. @@ -392,70 +423,49 @@ njs_object_property_query(njs_vm_t *vm,
  200. }
  201. }
  202.  
  203. + proto = object;
  204. +
  205. do {
  206. - pq->prototype = object;
  207. + pq->prototype = proto;
  208.  
  209. - ret = nxt_lvlhsh_find(&object->hash, &pq->lhq);
  210. + /* length and other shared properties should be Own property */
  211.  
  212. - if (ret == NXT_OK) {
  213. - prop = pq->lhq.value;
  214. + if (nxt_fast_path(!pq->own || proto == object)) {
  215. + ret = nxt_lvlhsh_find(&proto->hash, &pq->lhq);
  216.  
  217. - if (prop->type != NJS_WHITEOUT) {
  218. + if (ret == NXT_OK) {
  219. + prop = pq->lhq.value;
  220. pq->shared = 0;
  221.  
  222. - return ret;
  223. + return (prop->type != NJS_WHITEOUT) ? ret : NXT_DECLINED;
  224. }
  225. -
  226. - goto next;
  227. }
  228.  
  229. if (pq->query > NJS_PROPERTY_QUERY_IN) {
  230. - /* NXT_DECLINED */
  231. - return ret;
  232. + return NXT_DECLINED;
  233. }
  234.  
  235. - ret = nxt_lvlhsh_find(&object->shared_hash, &pq->lhq);
  236. + ret = nxt_lvlhsh_find(&proto->shared_hash, &pq->lhq);
  237.  
  238. if (ret == NXT_OK) {
  239. pq->shared = 1;
  240.  
  241. - if (pq->query == NJS_PROPERTY_QUERY_GET) {
  242. - prop = pq->lhq.value;
  243. -
  244. - if (prop->type == NJS_PROPERTY_HANDLER) {
  245. - pq->scratch = *prop;
  246. - prop = &pq->scratch;
  247. - ret = prop->value.data.u.prop_handler(vm, value, NULL,
  248. - &prop->value);
  249. -
  250. - if (nxt_fast_path(ret == NXT_OK)) {
  251. - prop->type = NJS_PROPERTY;
  252. - pq->lhq.value = prop;
  253. - }
  254. - }
  255. - }
  256. -
  257. return ret;
  258. }
  259.  
  260. if (pq->query > NJS_PROPERTY_QUERY_IN) {
  261. - /* NXT_DECLINED */
  262. - return ret;
  263. + return NXT_DECLINED;
  264. }
  265.  
  266. - next:
  267. + proto = proto->__proto__;
  268.  
  269. - object = object->__proto__;
  270. -
  271. - } while (object != NULL);
  272. + } while (proto != NULL);
  273.  
  274. if (njs_is_string(value)) {
  275. return NJS_STRING_VALUE;
  276. }
  277.  
  278. - /* NXT_DECLINED */
  279. -
  280. - return ret;
  281. + return NXT_DECLINED;
  282. }
  283.  
  284.  
  285. @@ -1037,12 +1047,9 @@ njs_object_get_own_property_descriptor(n
  286. lhq.proto = &njs_object_hash_proto;
  287.  
  288. if (prop == NULL) {
  289. - pq.query = NJS_PROPERTY_QUERY_GET;
  290. - pq.lhq.key.length = 0;
  291. - pq.lhq.key.start = NULL;
  292. + njs_property_query_init(&pq, NJS_PROPERTY_QUERY_GET, 1);
  293.  
  294. - ret = njs_property_query(vm, &pq, (njs_value_t *) value,
  295. - (njs_value_t *) property);
  296. + ret = njs_property_query(vm, &pq, (njs_value_t *) value, property);
  297.  
  298. if (ret != NXT_OK) {
  299. vm->retval = njs_value_void;
  300. @@ -1050,6 +1057,16 @@ njs_object_get_own_property_descriptor(n
  301. }
  302.  
  303. prop = pq.lhq.value;
  304. +
  305. + if (prop->type == NJS_PROPERTY_HANDLER) {
  306. + pq.scratch = *prop;
  307. + prop = &pq.scratch;
  308. + ret = prop->value.data.u.prop_handler(vm, (njs_value_t *) value,
  309. + NULL, &prop->value);
  310. + if (nxt_slow_path(ret != NXT_OK)) {
  311. + return ret;
  312. + }
  313. + }
  314. }
  315.  
  316. descriptor = njs_object_alloc(vm);
  317. diff --git a/njs/njs_object.h b/njs/njs_object.h
  318. --- a/njs/njs_object.h
  319. +++ b/njs/njs_object.h
  320. @@ -51,9 +51,17 @@ typedef struct {
  321. njs_object_t *prototype;
  322. uint8_t query;
  323. uint8_t shared;
  324. + uint8_t own;
  325. } njs_property_query_t;
  326.  
  327.  
  328. +#define njs_property_query_init(pq, _query, _own) \
  329. + do { \
  330. + (pq)->lhq.key.length = 0; \
  331. + (pq)->query = _query; \
  332. + (pq)->own = _own; \
  333. + } while (0)
  334. +
  335.  
  336. struct njs_object_init_s {
  337. nxt_str_t name;
  338. @@ -67,10 +75,12 @@ njs_object_t *njs_object_value_copy(njs_
  339. njs_object_t *njs_object_value_alloc(njs_vm_t *vm, const njs_value_t *value,
  340. nxt_uint_t type);
  341. njs_array_t *njs_object_keys_array(njs_vm_t *vm, const njs_value_t *object);
  342. +njs_ret_t njs_value_property(njs_vm_t *vm, njs_value_t *value,
  343. + const njs_value_t *property, njs_value_t *retval);
  344. njs_object_prop_t *njs_object_property(njs_vm_t *vm, const njs_object_t *obj,
  345. nxt_lvlhsh_query_t *lhq);
  346. njs_ret_t njs_property_query(njs_vm_t *vm, njs_property_query_t *pq,
  347. - njs_value_t *object, njs_value_t *property);
  348. + njs_value_t *object, const njs_value_t *property);
  349. nxt_int_t njs_object_hash_create(njs_vm_t *vm, nxt_lvlhsh_t *hash,
  350. const njs_object_prop_t *prop, nxt_uint_t n);
  351. njs_ret_t njs_object_constructor(njs_vm_t *vm, njs_value_t *args,
  352. diff --git a/njs/njs_vm.c b/njs/njs_vm.c
  353. --- a/njs/njs_vm.c
  354. +++ b/njs/njs_vm.c
  355. @@ -476,147 +476,13 @@ njs_ret_t
  356. njs_vmcode_property_get(njs_vm_t *vm, njs_value_t *object,
  357. njs_value_t *property)
  358. {
  359. - void *obj;
  360. - int32_t index;
  361. - uintptr_t data;
  362. - njs_ret_t ret;
  363. - njs_value_t *val, ext_val;
  364. - njs_slice_prop_t slice;
  365. - njs_string_prop_t string;
  366. - njs_object_prop_t *prop;
  367. - const njs_value_t *retval;
  368. - const njs_extern_t *ext_proto;
  369. - njs_property_query_t pq;
  370. -
  371. - pq.query = NJS_PROPERTY_QUERY_GET;
  372. -
  373. - ret = njs_property_query(vm, &pq, object, property);
  374. -
  375. - retval = &njs_value_void;
  376. -
  377. - switch (ret) {
  378. -
  379. - case NXT_OK:
  380. - prop = pq.lhq.value;
  381. -
  382. - switch (prop->type) {
  383. -
  384. - case NJS_METHOD:
  385. - if (pq.shared) {
  386. - ret = njs_method_private_copy(vm, &pq);
  387. -
  388. - if (nxt_slow_path(ret != NXT_OK)) {
  389. - return ret;
  390. - }
  391. -
  392. - prop = pq.lhq.value;
  393. - }
  394. -
  395. - /* Fall through. */
  396. -
  397. - case NJS_PROPERTY:
  398. - retval = &prop->value;
  399. - break;
  400. -
  401. - default:
  402. - nxt_thread_log_alert("invalid property get type:%d", prop->type);
  403. -
  404. - return NXT_ERROR;
  405. - }
  406. -
  407. - break;
  408. -
  409. - case NXT_DECLINED:
  410. - case NJS_PRIMITIVE_VALUE:
  411. - break;
  412. -
  413. - case NJS_STRING_VALUE:
  414. -
  415. - /* string[n]. */
  416. -
  417. - index = (int32_t) njs_value_to_index(property);
  418. -
  419. - if (nxt_fast_path(index >= 0)) {
  420. - slice.start = index;
  421. - slice.length = 1;
  422. - slice.string_length = njs_string_prop(&string, object);
  423. -
  424. - if (slice.start < slice.string_length) {
  425. - /*
  426. - * A single codepoint string fits in vm->retval
  427. - * so the function cannot fail.
  428. - */
  429. - (void) njs_string_slice(vm, &vm->retval, &string, &slice);
  430. -
  431. - return sizeof(njs_vmcode_prop_get_t);
  432. - }
  433. - }
  434. -
  435. - break;
  436. -
  437. - case NJS_ARRAY_VALUE:
  438. - val = pq.lhq.value;
  439. -
  440. - if (njs_is_valid(val)) {
  441. - retval = val;
  442. - }
  443. -
  444. - break;
  445. -
  446. - case NJS_EXTERNAL_VALUE:
  447. - ext_proto = object->external.proto;
  448. -
  449. - ret = nxt_lvlhsh_find(&ext_proto->hash, &pq.lhq);
  450. -
  451. - if (ret == NXT_OK) {
  452. - ext_proto = pq.lhq.value;
  453. -
  454. - ext_val.type = NJS_EXTERNAL;
  455. - ext_val.data.truth = 1;
  456. - ext_val.external.proto = ext_proto;
  457. - ext_val.external.index = object->external.index;
  458. -
  459. - if ((ext_proto->type & NJS_EXTERN_OBJECT) != 0) {
  460. - retval = &ext_val;
  461. - break;
  462. - }
  463. -
  464. - data = ext_proto->data;
  465. -
  466. - } else {
  467. - data = (uintptr_t) &pq.lhq.key;
  468. - }
  469. -
  470. - vm->retval = njs_value_void;
  471. -
  472. - if (ext_proto->get != NULL) {
  473. - obj = njs_extern_object(vm, object);
  474. -
  475. - ret = ext_proto->get(vm, &vm->retval, obj, data);
  476. - if (nxt_slow_path(ret != NXT_OK)) {
  477. - return ret;
  478. - }
  479. -
  480. - /* The vm->retval is already retained by ext_proto->get(). */
  481. - }
  482. -
  483. - if (ext_proto->type == NJS_EXTERN_METHOD) {
  484. - vm->retval.data.u.function = ext_proto->function;
  485. - vm->retval.type = NJS_FUNCTION;
  486. - vm->retval.data.truth = 1;
  487. - }
  488. -
  489. - return sizeof(njs_vmcode_prop_get_t);
  490. -
  491. - case NJS_TRAP:
  492. - case NXT_ERROR:
  493. - default:
  494. -
  495. + njs_ret_t ret;
  496. +
  497. + ret = njs_value_property(vm, object, property, &vm->retval);
  498. + if (ret == NXT_ERROR || ret == NJS_TRAP) {
  499. return ret;
  500. }
  501.  
  502. - vm->retval = *retval;
  503. -
  504. /* GC: njs_retain(retval) */
  505.  
  506. return sizeof(njs_vmcode_prop_get_t);
  507. @@ -646,8 +512,7 @@ njs_vmcode_property_set(njs_vm_t *vm, nj
  508. code = (njs_vmcode_prop_set_t *) vm->current;
  509. value = njs_vmcode_operand(vm, code->value);
  510.  
  511. - pq.lhq.key.length = 0;
  512. - pq.query = NJS_PROPERTY_QUERY_SET;
  513. + njs_property_query_init(&pq, NJS_PROPERTY_QUERY_SET, 0);
  514.  
  515. ret = njs_property_query(vm, &pq, object, property);
  516.  
  517. @@ -767,7 +632,7 @@ njs_vmcode_property_in(njs_vm_t *vm, njs
  518.  
  519. retval = &njs_value_false;
  520.  
  521. - pq.query = NJS_PROPERTY_QUERY_IN;
  522. + njs_property_query_init(&pq, NJS_PROPERTY_QUERY_IN, 0);
  523.  
  524. ret = njs_property_query(vm, &pq, object, property);
  525.  
  526. @@ -851,8 +716,7 @@ njs_vmcode_property_delete(njs_vm_t *vm,
  527.  
  528. retval = &njs_value_false;
  529.  
  530. - pq.lhq.key.length = 0;
  531. - pq.query = NJS_PROPERTY_QUERY_DELETE;
  532. + njs_property_query_init(&pq, NJS_PROPERTY_QUERY_DELETE, 0);
  533.  
  534. ret = njs_property_query(vm, &pq, object, property);
  535.  
  536. @@ -1097,12 +961,10 @@ njs_ret_t
  537. njs_vmcode_instance_of(njs_vm_t *vm, njs_value_t *object,
  538. njs_value_t *constructor)
  539. {
  540. - nxt_int_t ret;
  541. - njs_value_t *value;
  542. - njs_object_t *prototype, *proto;
  543. - njs_object_prop_t *prop;
  544. - const njs_value_t *retval;
  545. - njs_property_query_t pq;
  546. + nxt_int_t ret;
  547. + njs_value_t *value, val;
  548. + njs_object_t *prototype, *proto;
  549. + const njs_value_t *retval;
  550.  
  551. static njs_value_t prototype_string = njs_string("prototype");
  552.  
  553. @@ -1114,13 +976,10 @@ njs_vmcode_instance_of(njs_vm_t *vm, njs
  554. retval = &njs_value_false;
  555.  
  556. if (njs_is_object(object)) {
  557. - pq.query = NJS_PROPERTY_QUERY_GET;
  558. -
  559. - ret = njs_property_query(vm, &pq, constructor, &prototype_string);
  560. + ret = njs_value_property(vm, constructor, &prototype_string, &val);
  561.  
  562. if (nxt_fast_path(ret == NXT_OK)) {
  563. - prop = pq.lhq.value;
  564. - value = &prop->value;
  565. + value = &val;
  566.  
  567. /* TODO: test prop->value is object. */
  568.  
  569. @@ -2123,9 +1982,7 @@ njs_vmcode_method_frame(njs_vm_t *vm, nj
  570.  
  571. method = (njs_vmcode_method_frame_t *) vm->current;
  572.  
  573. - pq.lhq.key.length = 0;
  574. - pq.lhq.key.start = NULL;
  575. - pq.query = NJS_PROPERTY_QUERY_GET;
  576. + njs_property_query_init(&pq, NJS_PROPERTY_QUERY_GET, 0);
  577.  
  578. ret = njs_property_query(vm, &pq, object, name);
  579.  
  580. @@ -2134,6 +1991,16 @@ njs_vmcode_method_frame(njs_vm_t *vm, nj
  581. case NXT_OK:
  582. prop = pq.lhq.value;
  583.  
  584. + if (prop->type == NJS_PROPERTY_HANDLER) {
  585. + pq.scratch = *prop;
  586. + prop = &pq.scratch;
  587. + ret = prop->value.data.u.prop_handler(vm, object, NULL,
  588. + &prop->value);
  589. + if (nxt_slow_path(ret != NXT_OK)) {
  590. + return ret;
  591. + }
  592. + }
  593. +
  594. ret = njs_function_frame_create(vm, &prop->value, object, method->nargs,
  595. method->code.ctor);
  596. break;
  597. @@ -3159,6 +3026,45 @@ njs_vmcode_string_argument(njs_vm_t *vm,
  598. }
  599.  
  600.  
  601. +static njs_ret_t
  602. +njs_vmcode_restart(njs_vm_t *vm, njs_value_t *invld1, njs_value_t *invld2)
  603. +{
  604. + u_char *restart;
  605. + njs_ret_t ret;
  606. + njs_value_t *retval, *value1;
  607. + njs_native_frame_t *frame;
  608. + njs_vmcode_generic_t *vmcode;
  609. +
  610. + frame = vm->top_frame;
  611. + restart = frame->trap_restart;
  612. + frame->trap_restart = NULL;
  613. + vm->current = restart;
  614. + vmcode = (njs_vmcode_generic_t *) restart;
  615. +
  616. + value1 = &frame->trap_values[0];
  617. +
  618. + if (frame->trap_reference) {
  619. + value1 = value1->data.u.value;
  620. + }
  621. +
  622. + ret = vmcode->code.operation(vm, value1, &frame->trap_values[1]);
  623. +
  624. + if (nxt_slow_path(ret == NJS_TRAP)) {
  625. + /* Trap handlers are not reentrant. */
  626. + njs_internal_error(vm, "trap inside restart instruction");
  627. + return NXT_ERROR;
  628. + }
  629. +
  630. + retval = njs_vmcode_operand(vm, vmcode->operand1);
  631. +
  632. + //njs_release(vm, retval);
  633. +
  634. + *retval = vm->retval;
  635. +
  636. + return ret;
  637. +}
  638. +
  639. +
  640. /*
  641. * A hint value is 0 for numbers and 1 for strings. The value chooses
  642. * method calls order specified by ECMAScript 5.1: "valueOf", "toString"
  643. @@ -3257,42 +3163,165 @@ njs_primitive_value(njs_vm_t *vm, njs_va
  644. }
  645.  
  646.  
  647. -static njs_ret_t
  648. -njs_vmcode_restart(njs_vm_t *vm, njs_value_t *invld1, njs_value_t *invld2)
  649. +/*
  650. + * ES5.1, 8.12.3: [[Get]].
  651. + * NXT_OK property has been found in object,
  652. + * retval will contain the property's value
  653. + *
  654. + * NXT_DECLINED property was not found in object,
  655. + * NJS_TRAP the property trap must be called,
  656. + * NXT_ERROR exception has been thrown.
  657. + * retval will contain undefined
  658. + */
  659. +njs_ret_t
  660. +njs_value_property(njs_vm_t *vm, njs_value_t *value,
  661. + const njs_value_t *property, njs_value_t *retval)
  662. {
  663. - u_char *restart;
  664. + void *obj;
  665. + int32_t index;
  666. + uintptr_t data;
  667. njs_ret_t ret;
  668. - njs_value_t *retval, *value1;
  669. - njs_native_frame_t *frame;
  670. - njs_vmcode_generic_t *vmcode;
  671. -
  672. - frame = vm->top_frame;
  673. - restart = frame->trap_restart;
  674. - frame->trap_restart = NULL;
  675. - vm->current = restart;
  676. - vmcode = (njs_vmcode_generic_t *) restart;
  677. -
  678. - value1 = &frame->trap_values[0];
  679. -
  680. - if (frame->trap_reference) {
  681. - value1 = value1->data.u.value;
  682. + njs_value_t *val, ext_val;
  683. + njs_slice_prop_t slice;
  684. + njs_string_prop_t string;
  685. + njs_object_prop_t *prop;
  686. + const njs_extern_t *ext_proto;
  687. + njs_property_query_t pq;
  688. +
  689. + *retval = njs_value_void;
  690. +
  691. + njs_property_query_init(&pq, NJS_PROPERTY_QUERY_GET, 0);
  692. +
  693. + ret = njs_property_query(vm, &pq, value, property);
  694. +
  695. + switch (ret) {
  696. +
  697. + case NXT_OK:
  698. + prop = pq.lhq.value;
  699. +
  700. + switch (prop->type) {
  701. +
  702. + case NJS_METHOD:
  703. + if (pq.shared) {
  704. + ret = njs_method_private_copy(vm, &pq);
  705. +
  706. + if (nxt_slow_path(ret != NXT_OK)) {
  707. + return ret;
  708. + }
  709. +
  710. + prop = pq.lhq.value;
  711. + }
  712. +
  713. + /* Fall through. */
  714. +
  715. + case NJS_PROPERTY:
  716. + *retval = prop->value;
  717. + break;
  718. +
  719. + case NJS_PROPERTY_HANDLER:
  720. + pq.scratch = *prop;
  721. + prop = &pq.scratch;
  722. + ret = prop->value.data.u.prop_handler(vm, value, NULL,
  723. + &prop->value);
  724. +
  725. + if (nxt_fast_path(ret == NXT_OK)) {
  726. + *retval = prop->value;
  727. + }
  728. +
  729. + break;
  730. +
  731. + default:
  732. + njs_internal_error(vm, "invalid property get type:%d", prop->type);
  733. +
  734. + return NXT_ERROR;
  735. + }
  736. +
  737. + break;
  738. +
  739. + case NXT_DECLINED:
  740. + case NJS_PRIMITIVE_VALUE:
  741. + break;
  742. +
  743. + case NJS_STRING_VALUE:
  744. +
  745. + /* string[n]. */
  746. +
  747. + index = (int32_t) njs_value_to_index(property);
  748. +
  749. + if (nxt_fast_path(index >= 0)) {
  750. + slice.start = index;
  751. + slice.length = 1;
  752. + slice.string_length = njs_string_prop(&string, value);
  753. +
  754. + if (slice.start < slice.string_length) {
  755. + /*
  756. + * A single codepoint string fits in retval
  757. + * so the function cannot fail.
  758. + */
  759. + (void) njs_string_slice(vm, retval, &string, &slice);
  760. + }
  761. + }
  762. +
  763. + break;
  764. +
  765. + case NJS_ARRAY_VALUE:
  766. + val = pq.lhq.value;
  767. +
  768. + if (njs_is_valid(val)) {
  769. + *retval = *val;
  770. + }
  771. +
  772. + break;
  773. +
  774. + case NJS_EXTERNAL_VALUE:
  775. + ext_proto = value->external.proto;
  776. +
  777. + ret = nxt_lvlhsh_find(&ext_proto->hash, &pq.lhq);
  778. +
  779. + if (ret == NXT_OK) {
  780. + ext_proto = pq.lhq.value;
  781. +
  782. + ext_val.type = NJS_EXTERNAL;
  783. + ext_val.data.truth = 1;
  784. + ext_val.external.proto = ext_proto;
  785. + ext_val.external.index = value->external.index;
  786. +
  787. + if ((ext_proto->type & NJS_EXTERN_OBJECT) != 0) {
  788. + *retval = ext_val;
  789. + break;
  790. + }
  791. +
  792. + data = ext_proto->data;
  793. +
  794. + } else {
  795. + data = (uintptr_t) &pq.lhq.key;
  796. + }
  797. +
  798. + if (ext_proto->get != NULL) {
  799. + obj = njs_extern_object(vm, value);
  800. +
  801. + ret = ext_proto->get(vm, retval, obj, data);
  802. + if (nxt_slow_path(ret != NXT_OK)) {
  803. + return ret;
  804. + }
  805. + }
  806. +
  807. + if (ext_proto->type == NJS_EXTERN_METHOD) {
  808. + retval->data.u.function = ext_proto->function;
  809. + retval->type = NJS_FUNCTION;
  810. + retval->data.truth = 1;
  811. + }
  812. +
  813. + break;
  814. +
  815. + case NJS_TRAP:
  816. + case NXT_ERROR:
  817. + default:
  818. +
  819. + return ret;
  820. }
  821.  
  822. - ret = vmcode->code.operation(vm, value1, &frame->trap_values[1]);
  823. -
  824. - if (nxt_slow_path(ret == NJS_TRAP)) {
  825. - /* Trap handlers are not reentrant. */
  826. - njs_internal_error(vm, "trap inside restart instruction");
  827. - return NXT_ERROR;
  828. - }
  829. -
  830. - retval = njs_vmcode_operand(vm, vmcode->operand1);
  831. -
  832. - //njs_release(vm, retval);
  833. -
  834. - *retval = vm->retval;
  835. -
  836. - return ret;
  837. + return (ret == NXT_OK) ? NXT_OK : NXT_DECLINED;
  838. }
  839.  
  840.  
  841. diff --git a/njs/test/njs_unit_test.c b/njs/test/njs_unit_test.c
  842. --- a/njs/test/njs_unit_test.c
  843. +++ b/njs/test/njs_unit_test.c
  844. @@ -7001,9 +7001,18 @@ static njs_unit_test_t njs_test[] =
  845. { nxt_string("Object.getOwnPropertyDescriptor([3,4], 1).value"),
  846. nxt_string("4") },
  847.  
  848. + { nxt_string("Object.getOwnPropertyDescriptor(Object.create({a:1}), 'a')"),
  849. + nxt_string("undefined") },
  850. +
  851. { nxt_string("Object.getOwnPropertyDescriptor([], 'length').value"),
  852. nxt_string("0") },
  853.  
  854. + { nxt_string("Object.getOwnPropertyDescriptor(Array.of, 'length').value"),
  855. + nxt_string("0") },
  856. +
  857. + { nxt_string("JSON.stringify(Object.getOwnPropertyDescriptor(new String('abc'), 'length'))"),
  858. + nxt_string("{\"value\":3,\"configurable\":false,\"enumerable\":false,\"writable\":false}") },
  859. +
  860. { nxt_string("JSON.stringify(Object.getOwnPropertyDescriptor([3,4], 'length'))"),
  861. nxt_string("{\"value\":2,\"configurable\":false,\"enumerable\":false,\"writable\":true}") },
  862.  
  863. # HG changeset patch
  864. # User Dmitry Volyntsev <xeioex@nginx.com>
  865. # Date 1536860015 -10800
  866. # Thu Sep 13 20:33:35 2018 +0300
  867. # Node ID b9a1326c940d11f303c3d6328dcd1557d20d507f
  868. # Parent ffe3b161805980547f16f3ff1142fc55f3f628ca
  869. Fixed delete operator.
  870.  
  871. # HG changeset patch
  872. # User Dmitry Volyntsev <xeioex@nginx.com>
  873. # Date 1536860015 -10800
  874. # Thu Sep 13 20:33:35 2018 +0300
  875. # Node ID b85d3a6cf1eafd0f6d8b43d61f8e6421282a55ef
  876. # Parent b9a1326c940d11f303c3d6328dcd1557d20d507f
  877. Added njs_primitive_value_to_number().
  878.  
  879. diff --git a/njs/njs_number.c b/njs/njs_number.c
  880. --- a/njs/njs_number.c
  881. +++ b/njs/njs_number.c
  882. @@ -64,6 +64,24 @@ njs_value_to_index(const njs_value_t *va
  883.  
  884.  
  885. double
  886. +njs_primitive_value_to_number(const njs_value_t *value)
  887. +{
  888. + if (nxt_fast_path(njs_is_numeric(value))) {
  889. + return value->data.u.number;
  890. + }
  891. +
  892. + return njs_string_to_number(value, 1);
  893. +}
  894. +
  895. +
  896. +uint32_t
  897. +njs_primitive_value_to_uint32(const njs_value_t *value)
  898. +{
  899. + return njs_number_to_integer(njs_primitive_value_to_number(value));
  900. +}
  901. +
  902. +
  903. +double
  904. njs_number_dec_parse(const u_char **start, const u_char *end)
  905. {
  906. return nxt_strtod(start, end);
  907. diff --git a/njs/njs_number.h b/njs/njs_number.h
  908. --- a/njs/njs_number.h
  909. +++ b/njs/njs_number.h
  910. @@ -13,6 +13,8 @@
  911.  
  912.  
  913. uint32_t njs_value_to_index(const njs_value_t *value);
  914. +double njs_primitive_value_to_number(const njs_value_t *value);
  915. +uint32_t njs_primitive_value_to_uint32(const njs_value_t *value);
  916. double njs_number_dec_parse(const u_char **start, const u_char *end);
  917. uint64_t njs_number_oct_parse(const u_char **start, const u_char *end);
  918. uint64_t njs_number_bin_parse(const u_char **start, const u_char *end);
  919. # HG changeset patch
  920. # User Dmitry Volyntsev <xeioex@nginx.com>
  921. # Date 1536860016 -10800
  922. # Thu Sep 13 20:33:36 2018 +0300
  923. # Node ID b87565896b6efc8ac59da011665ed1acbc23a1e6
  924. # Parent b85d3a6cf1eafd0f6d8b43d61f8e6421282a55ef
  925. Fixed String.slice() for undefined arguments.
  926.  
  927. diff --git a/njs/njs_string.c b/njs/njs_string.c
  928. --- a/njs/njs_string.c
  929. +++ b/njs/njs_string.c
  930. @@ -1239,45 +1239,43 @@ njs_string_slice_args(njs_slice_prop_t *
  931. ssize_t start, end, length;
  932.  
  933. length = slice->string_length;
  934. - start = 0;
  935. -
  936. - if (nargs > 1) {
  937. - start = args[1].data.u.number;
  938. +
  939. + start = njs_arg(args, nargs, 1)->data.u.number;
  940. +
  941. + if (start < 0) {
  942. + start += length;
  943.  
  944. if (start < 0) {
  945. - start += length;
  946. -
  947. - if (start < 0) {
  948. - start = 0;
  949. - }
  950. + start = 0;
  951. }
  952. -
  953. - if (start >= length) {
  954. - start = 0;
  955. - length = 0;
  956. + }
  957. +
  958. + if (start >= length) {
  959. + start = 0;
  960. + length = 0;
  961. +
  962. + } else {
  963. + if (!njs_is_void(njs_arg(args, nargs, 2))) {
  964. + end = njs_arg(args, nargs, 2)->data.u.number;
  965.  
  966. } else {
  967. end = length;
  968. -
  969. - if (nargs > 2) {
  970. - end = args[2].data.u.number;
  971. -
  972. - if (end < 0) {
  973. - end += length;
  974. - }
  975. + }
  976. +
  977. + if (end < 0) {
  978. + end += length;
  979. + }
  980. +
  981. + if (length >= end) {
  982. + length = end - start;
  983. +
  984. + if (length < 0) {
  985. + start = 0;
  986. + length = 0;
  987. }
  988.  
  989. - if (length >= end) {
  990. - length = end - start;
  991. -
  992. - if (length < 0) {
  993. - start = 0;
  994. - length = 0;
  995. - }
  996. -
  997. - } else {
  998. - length -= start;
  999. - }
  1000. + } else {
  1001. + length -= start;
  1002. }
  1003. }
  1004.  
  1005. diff --git a/njs/test/njs_unit_test.c b/njs/test/njs_unit_test.c
  1006. --- a/njs/test/njs_unit_test.c
  1007. +++ b/njs/test/njs_unit_test.c
  1008. @@ -3800,6 +3800,18 @@ static njs_unit_test_t njs_test[] =
  1009. { nxt_string("'abcdefgh'.slice(3)"),
  1010. nxt_string("defgh") },
  1011.  
  1012. + { nxt_string("'abcdefgh'.slice(undefined, undefined)"),
  1013. + nxt_string("abcdefgh") },
  1014. +
  1015. + { nxt_string("'abcdefgh'.slice(undefined)"),
  1016. + nxt_string("abcdefgh") },
  1017. +
  1018. + { nxt_string("'abcdefgh'.slice(undefined, 1)"),
  1019. + nxt_string("a") },
  1020. +
  1021. + { nxt_string("'abcdefgh'.slice(3, undefined)"),
  1022. + nxt_string("defgh") },
  1023. +
  1024. { nxt_string("'abcde'.slice(50)"),
  1025. nxt_string("") },
  1026.  
  1027. # HG changeset patch
  1028. # User Dmitry Volyntsev <xeioex@nginx.com>
  1029. # Date 1536860016 -10800
  1030. # Thu Sep 13 20:33:36 2018 +0300
  1031. # Node ID 96e1d261f0b2c36a797c139b4fb689fe535d0ea8
  1032. # Parent b87565896b6efc8ac59da011665ed1acbc23a1e6
  1033. Added njs_string_slice_string_prop().
  1034.  
  1035. diff --git a/njs/njs_string.c b/njs/njs_string.c
  1036. --- a/njs/njs_string.c
  1037. +++ b/njs/njs_string.c
  1038. @@ -1284,46 +1284,60 @@ njs_string_slice_args(njs_slice_prop_t *
  1039. }
  1040.  
  1041.  
  1042. +nxt_noinline void
  1043. +njs_string_slice_string_prop(njs_string_prop_t *dst,
  1044. + const njs_string_prop_t *string, const njs_slice_prop_t *slice)
  1045. +{
  1046. + size_t size, n, length;
  1047. + const u_char *p, *start, *end;
  1048. +
  1049. + length = slice->length;
  1050. + start = string->start;
  1051. +
  1052. + if (string->size == slice->string_length) {
  1053. + /* Byte or ASCII string. */
  1054. + start += slice->start;
  1055. + size = slice->length;
  1056. +
  1057. + if (string->length == 0) {
  1058. + /* Byte string. */
  1059. + length = 0;
  1060. + }
  1061. +
  1062. + } else {
  1063. + /* UTF-8 string. */
  1064. + end = start + string->size;
  1065. + start = njs_string_offset(start, end, slice->start);
  1066. +
  1067. + /* Evaluate size of the slice in bytes and ajdust length. */
  1068. + p = start;
  1069. + n = length;
  1070. +
  1071. + do {
  1072. + p = nxt_utf8_next(p, end);
  1073. + n--;
  1074. + } while (n != 0 && p < end);
  1075. +
  1076. + size = p - start;
  1077. + length -= n;
  1078. + }
  1079. +
  1080. + dst->start = (u_char *) start;
  1081. + dst->length = length;
  1082. + dst->size = size;
  1083. +}
  1084. +
  1085. +
  1086. nxt_noinline njs_ret_t
  1087. njs_string_slice(njs_vm_t *vm, njs_value_t *dst,
  1088. const njs_string_prop_t *string, const njs_slice_prop_t *slice)
  1089. {
  1090. - size_t size, n, length;
  1091. - const u_char *p, *start, *end;
  1092. -
  1093. - length = slice->length;
  1094. - start = string->start;
  1095. -
  1096. - if (string->size == slice->string_length) {
  1097. - /* Byte or ASCII string. */
  1098. - start += slice->start;
  1099. - size = slice->length;
  1100. -
  1101. - if (string->length == 0) {
  1102. - /* Byte string. */
  1103. - length = 0;
  1104. - }
  1105. -
  1106. - } else {
  1107. - /* UTF-8 string. */
  1108. - end = start + string->size;
  1109. - start = njs_string_offset(start, end, slice->start);
  1110. -
  1111. - /* Evaluate size of the slice in bytes and ajdust length. */
  1112. - p = start;
  1113. - n = length;
  1114. -
  1115. - do {
  1116. - p = nxt_utf8_next(p, end);
  1117. - n--;
  1118. - } while (n != 0 && p < end);
  1119. -
  1120. - size = p - start;
  1121. - length -= n;
  1122. - }
  1123. -
  1124. - if (nxt_fast_path(size != 0)) {
  1125. - return njs_string_new(vm, dst, start, size, length);
  1126. + njs_string_prop_t prop;
  1127. +
  1128. + njs_string_slice_string_prop(&prop, string, slice);
  1129. +
  1130. + if (nxt_fast_path(prop.size != 0)) {
  1131. + return njs_string_new(vm, dst, prop.start, prop.size, prop.length);
  1132. }
  1133.  
  1134. *dst = njs_string_empty;
  1135. diff --git a/njs/njs_string.h b/njs/njs_string.h
  1136. --- a/njs/njs_string.h
  1137. +++ b/njs/njs_string.h
  1138. @@ -145,6 +145,8 @@ njs_ret_t njs_string_constructor(njs_vm_
  1139. nxt_uint_t nargs, njs_index_t unused);
  1140. nxt_bool_t njs_string_eq(const njs_value_t *val1, const njs_value_t *val2);
  1141. nxt_int_t njs_string_cmp(const njs_value_t *val1, const njs_value_t *val2);
  1142. +nxt_noinline void njs_string_slice_string_prop(njs_string_prop_t *dst,
  1143. + const njs_string_prop_t *string, const njs_slice_prop_t *slice);
  1144. njs_ret_t njs_string_slice(njs_vm_t *vm, njs_value_t *dst,
  1145. const njs_string_prop_t *string, const njs_slice_prop_t *slice);
  1146. const u_char *njs_string_offset(const u_char *start, const u_char *end,
  1147. # HG changeset patch
  1148. # User Dmitry Volyntsev <xeioex@nginx.com>
  1149. # Date 1536860017 -10800
  1150. # Thu Sep 13 20:33:37 2018 +0300
  1151. # Node ID 0b601d903078774410f9c44ee3554c7e9b7db6ee
  1152. # Parent 96e1d261f0b2c36a797c139b4fb689fe535d0ea8
  1153. Hanling non-array values properly in Array.prototype.slice.
  1154.  
  1155. diff --git a/njs/njs_array.c b/njs/njs_array.c
  1156. --- a/njs/njs_array.c
  1157. +++ b/njs/njs_array.c
  1158. @@ -9,6 +9,19 @@
  1159.  
  1160.  
  1161. typedef struct {
  1162. + union {
  1163. + njs_continuation_t cont;
  1164. + u_char padding[NJS_CONTINUATION_SIZE];
  1165. + } u;
  1166. + /*
  1167. + * This retval value must be aligned so the continuation is padded
  1168. + * to aligned size.
  1169. + */
  1170. + njs_value_t length;
  1171. +} njs_array_slice_t;
  1172. +
  1173. +
  1174. +typedef struct {
  1175. njs_continuation_t cont;
  1176. njs_value_t *values;
  1177. uint32_t max;
  1178. @@ -67,6 +80,8 @@ typedef struct {
  1179. } njs_array_sort_t;
  1180.  
  1181.  
  1182. +static njs_ret_t njs_array_prototype_slice_continuation(njs_vm_t *vm,
  1183. + njs_value_t *args, nxt_uint_t nargs, njs_index_t unused);
  1184. static njs_ret_t njs_array_prototype_to_string_continuation(njs_vm_t *vm,
  1185. njs_value_t *args, nxt_uint_t nargs, njs_index_t retval);
  1186. static njs_ret_t njs_array_prototype_join_continuation(njs_vm_t *vm,
  1187. @@ -434,55 +449,87 @@ static njs_ret_t
  1188. njs_array_prototype_slice(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs,
  1189. njs_index_t unused)
  1190. {
  1191. - int32_t start, end, length;
  1192. - uint32_t n;
  1193. - njs_array_t *array;
  1194. - njs_value_t *value;
  1195. + njs_ret_t ret;
  1196. + njs_array_slice_t *slice;
  1197. +
  1198. + static const njs_value_t njs_string_length = njs_string("length");
  1199. +
  1200. + slice = njs_vm_continuation(vm);
  1201. + slice->u.cont.function = njs_array_prototype_slice_continuation;
  1202. +
  1203. + ret = njs_value_property(vm, &args[0], &njs_string_length, &slice->length);
  1204. + if (nxt_slow_path(ret == NXT_ERROR || ret == NJS_TRAP)) {
  1205. + return ret;
  1206. + }
  1207. +
  1208. + return njs_array_prototype_slice_continuation(vm, args, nargs, unused);
  1209. +}
  1210. +
  1211. +
  1212. +static njs_ret_t
  1213. +njs_array_prototype_slice_continuation(njs_vm_t *vm, njs_value_t *args,
  1214. + nxt_uint_t nargs, njs_index_t unused)
  1215. +{
  1216. + size_t size;
  1217. + u_char *dst;
  1218. + int32_t start, end;
  1219. + int64_t length;
  1220. + uint32_t n;
  1221. + njs_ret_t ret;
  1222. + njs_array_t *array;
  1223. + njs_value_t *value, name, retval, *this;
  1224. + const u_char *src, *last;
  1225. + njs_slice_prop_t string_slice;
  1226. + njs_array_slice_t *slice;
  1227. + njs_string_prop_t string;
  1228.  
  1229. start = 0;
  1230. - length = 0;
  1231. -
  1232. - if (njs_is_array(&args[0])) {
  1233. - length = args[0].data.u.array->length;
  1234. -
  1235. - if (nargs > 1) {
  1236. - start = args[1].data.u.number;
  1237. -
  1238. - if (start < 0) {
  1239. - start += length;
  1240. -
  1241. - if (start < 0) {
  1242. - start = 0;
  1243. - }
  1244. - }
  1245. -
  1246. - if (start >= length) {
  1247. +
  1248. + slice = njs_vm_continuation(vm);
  1249. +
  1250. + if (nxt_slow_path(!njs_is_primitive(&slice->length))) {
  1251. + njs_vm_trap_value(vm, &slice->length);
  1252. + return njs_trap(vm, NJS_TRAP_NUMBER_ARG);
  1253. + }
  1254. +
  1255. + length = njs_primitive_value_to_uint32(&slice->length);
  1256. +
  1257. + start = njs_arg(args, nargs, 1)->data.u.number;
  1258. +
  1259. + if (start < 0) {
  1260. + start += length;
  1261. +
  1262. + if (start < 0) {
  1263. + start = 0;
  1264. + }
  1265. + }
  1266. +
  1267. + if (start >= length) {
  1268. + start = 0;
  1269. + length = 0;
  1270. +
  1271. + } else {
  1272. + if (!njs_is_void(njs_arg(args, nargs, 2))) {
  1273. + end = njs_arg(args, nargs, 2)->data.u.number;
  1274. +
  1275. + } else {
  1276. + end = length;
  1277. + }
  1278. +
  1279. + if (end < 0) {
  1280. + end += length;
  1281. + }
  1282. +
  1283. + if (length >= end) {
  1284. + length = end - start;
  1285. +
  1286. + if (length < 0) {
  1287. start = 0;
  1288. length = 0;
  1289. -
  1290. - } else {
  1291. - end = length;
  1292. -
  1293. - if (nargs > 2) {
  1294. - end = args[2].data.u.number;
  1295. -
  1296. - if (end < 0) {
  1297. - end += length;
  1298. - }
  1299. - }
  1300. -
  1301. - if (length >= end) {
  1302. - length = end - start;
  1303. -
  1304. - if (length < 0) {
  1305. - start = 0;
  1306. - length = 0;
  1307. - }
  1308. -
  1309. - } else {
  1310. - length -= start;
  1311. - }
  1312. }
  1313. +
  1314. + } else {
  1315. + length -= start;
  1316. }
  1317. }
  1318.  
  1319. @@ -496,14 +543,56 @@ njs_array_prototype_slice(njs_vm_t *vm,
  1320. vm->retval.data.truth = 1;
  1321.  
  1322. if (length != 0) {
  1323. - value = args[0].data.u.array->start;
  1324. n = 0;
  1325. -
  1326. - do {
  1327. - /* GC: retain long string and object in values[start]. */
  1328. - array->start[n++] = value[start++];
  1329. - length--;
  1330. - } while (length != 0);
  1331. + this = &args[0];
  1332. +
  1333. + if (nxt_fast_path(njs_is_array(this))) {
  1334. + value = this->data.u.array->start;
  1335. +
  1336. + do {
  1337. + /* GC: retain long string and object in values[start]. */
  1338. + array->start[n++] = value[start++];
  1339. + length--;
  1340. + } while (length != 0);
  1341. +
  1342. + } else if (njs_is_string(this) || this->type == NJS_OBJECT_STRING) {
  1343. +
  1344. + if (this->type == NJS_OBJECT_STRING) {
  1345. + this = &this->data.u.object_value->value;
  1346. + }
  1347. +
  1348. + string_slice.start = start;
  1349. + string_slice.length = length;
  1350. + string_slice.string_length = njs_string_prop(&string, this);
  1351. +
  1352. + njs_string_slice_string_prop(&string, &string, &string_slice);
  1353. +
  1354. + src = string.start;
  1355. + last = src + string.size;
  1356. +
  1357. + do {
  1358. + value = &array->start[n++];
  1359. + dst = njs_string_short_start(value);
  1360. + dst = nxt_utf8_copy(dst, &src, last);
  1361. + size = dst - njs_string_short_start(value);
  1362. + njs_string_short_set(value, size, (size == 1) ? 0 : 1);
  1363. +
  1364. + length--;
  1365. + } while (length != 0);
  1366. +
  1367. +
  1368. + } else if (njs_is_object(this)) {
  1369. +
  1370. + do {
  1371. + njs_uint32_to_string(&name, start++);
  1372. +
  1373. + ret = njs_value_property(vm, this, &name, &retval);
  1374. +
  1375. + array->start[n++] = (ret == NXT_OK) ? retval
  1376. + : njs_value_invalid;
  1377. + length--;
  1378. + } while (length != 0);
  1379. + }
  1380. }
  1381.  
  1382. return NXT_OK;
  1383. @@ -2101,7 +2190,8 @@ static const njs_object_prop_t njs_arra
  1384. {
  1385. .type = NJS_METHOD,
  1386. .name = njs_string("slice"),
  1387. - .value = njs_native_function(njs_array_prototype_slice, 0,
  1388. + .value = njs_native_function(njs_array_prototype_slice,
  1389. + njs_continuation_size(njs_array_slice_t),
  1390. NJS_OBJECT_ARG, NJS_INTEGER_ARG, NJS_INTEGER_ARG),
  1391. },
  1392.  
  1393. diff --git a/njs/njs_object_hash.h b/njs/njs_object_hash.h
  1394. --- a/njs/njs_object_hash.h
  1395. +++ b/njs/njs_object_hash.h
  1396. @@ -108,6 +108,16 @@
  1397. 'j'), 'o'), 'i'), 'n')
  1398.  
  1399.  
  1400. +#define NJS_LENGTH_HASH \
  1401. + nxt_djb_hash_add( \
  1402. + nxt_djb_hash_add( \
  1403. + nxt_djb_hash_add( \
  1404. + nxt_djb_hash_add( \
  1405. + nxt_djb_hash_add( \
  1406. + nxt_djb_hash_add(NXT_DJB_HASH_INIT, \
  1407. + 'l'), 'e'), 'n'), 'g'), 't'), 'h')
  1408. +
  1409. +
  1410. #define NJS_NAME_HASH \
  1411. nxt_djb_hash_add( \
  1412. nxt_djb_hash_add( \
  1413. diff --git a/njs/test/njs_unit_test.c b/njs/test/njs_unit_test.c
  1414. --- a/njs/test/njs_unit_test.c
  1415. +++ b/njs/test/njs_unit_test.c
  1416. @@ -2981,12 +2981,93 @@ static njs_unit_test_t njs_test[] =
  1417. { nxt_string("Array.prototype.slice(1,2)"),
  1418. nxt_string("") },
  1419.  
  1420. + { nxt_string("Array.prototype.slice.call(undefined)"),
  1421. + nxt_string("TypeError: cannot convert void to object") },
  1422. +
  1423. + { nxt_string("Array.prototype.slice.call(1)"),
  1424. + nxt_string("") },
  1425. +
  1426. + { nxt_string("Array.prototype.slice.call(false)"),
  1427. + nxt_string("") },
  1428. +
  1429. + { nxt_string("Array.prototype.slice.call({'0':'a', '1':'b', length:1})"),
  1430. + nxt_string("a") },
  1431. +
  1432. + { nxt_string("Array.prototype.slice.call({'0':'a', '1':'b', length:2})"),
  1433. + nxt_string("a,b") },
  1434. +
  1435. + { nxt_string("Array.prototype.slice.call({'0':'a', '1':'b', length:4})"),
  1436. + nxt_string("a,b,,") },
  1437. +
  1438. + { nxt_string("Array.prototype.slice.call({'0':'a', '1':'b', length:2}, 1)"),
  1439. + nxt_string("b") },
  1440. +
  1441. + { nxt_string("Array.prototype.slice.call({'0':'a', '1':'b', length:2}, 1, 2)"),
  1442. + nxt_string("b") },
  1443. +
  1444. + { nxt_string("Array.prototype.slice.call({length:'2'})"),
  1445. + nxt_string(",") },
  1446. +
  1447. + { nxt_string("njs.dump(Array.prototype.slice.call({length: 3, 1: undefined }))"),
  1448. + nxt_string("[<empty>,undefined,<empty>]") },
  1449. +
  1450. + { nxt_string("Array.prototype.slice.call({length:new Number(3)})"),
  1451. + nxt_string(",,") },
  1452. +
  1453. + { nxt_string("Array.prototype.slice.call({length: { valueOf: function() { return 2; } }})"),
  1454. + nxt_string(",") },
  1455. +
  1456. + { nxt_string("Array.prototype.slice.call({ length: Object.create(null) })"),
  1457. + nxt_string("TypeError: Cannot convert object to primitive value") },
  1458. +
  1459. + { nxt_string("Array.prototype.slice.call({length:-1})"),
  1460. + nxt_string("MemoryError") },
  1461. +
  1462. + { nxt_string("Array.prototype.slice.call('αβZγ')"),
  1463. + nxt_string("α,β,Z,γ") },
  1464. +
  1465. + { nxt_string("Array.prototype.slice.call('αβZγ', 1)"),
  1466. + nxt_string("β,Z,γ") },
  1467. +
  1468. + { nxt_string("Array.prototype.slice.call('αβZγ', 2)"),
  1469. + nxt_string("Z,γ") },
  1470. +
  1471. + { nxt_string("Array.prototype.slice.call('αβZγ', 3)"),
  1472. + nxt_string("γ") },
  1473. +
  1474. + { nxt_string("Array.prototype.slice.call('αβZγ', 4)"),
  1475. + nxt_string("") },
  1476. +
  1477. + { nxt_string("Array.prototype.slice.call('αβZγ', 0, 1)"),
  1478. + nxt_string("α") },
  1479. +
  1480. + { nxt_string("Array.prototype.slice.call('αβZγ', 1, 2)"),
  1481. + nxt_string("β") },
  1482. +
  1483. + { nxt_string("Array.prototype.slice.call('αβZγ').length"),
  1484. + nxt_string("4") },
  1485. +
  1486. + { nxt_string("Array.prototype.slice.call('αβZγ')[1].length"),
  1487. + nxt_string("1") },
  1488. +
  1489. + { nxt_string("Array.prototype.slice.call(new String('αβZγ'))"),
  1490. + nxt_string("α,β,Z,γ") },
  1491. +
  1492. { nxt_string("Array.prototype.pop()"),
  1493. nxt_string("undefined") },
  1494.  
  1495. { nxt_string("Array.prototype.shift()"),
  1496. nxt_string("undefined") },
  1497.  
  1498. + { nxt_string("[0,1].slice()"),
  1499. + nxt_string("0,1") },
  1500. +
  1501. + { nxt_string("[0,1].slice(undefined)"),
  1502. + nxt_string("0,1") },
  1503. +
  1504. + { nxt_string("[0,1].slice(undefined, undefined)"),
  1505. + nxt_string("0,1") },
  1506. +
  1507. { nxt_string("[0,1,2,3,4].slice(1,4)"),
  1508. nxt_string("1,2,3") },
  1509.  
  1510. @@ -9442,6 +9523,9 @@ static njs_unit_test_t njs_test[] =
  1511. { nxt_string("njs.dump({a:1, b:[1,,2,{c:new Boolean(1)}]})"),
  1512. nxt_string("{a:1,b:[1,<empty>,2,{c:[Boolean: true]}]}") },
  1513.  
  1514. + { nxt_string("njs.dump(Array.prototype.slice.call({'1':'b', length:2}))"),
  1515. + nxt_string("[<empty>,'b']") },
  1516. +
  1517. { nxt_string("njs.dump($r.props)"),
  1518. nxt_string("{a:{type:\"property\",props:[\"getter\"]},b:{type:\"property\",props:[\"getter\"]}}") },
  1519.  
  1520. # HG changeset patch
  1521. # User Dmitry Volyntsev <xeioex@nginx.com>
  1522. # Date 1536860117 -10800
  1523. # Thu Sep 13 20:35:17 2018 +0300
  1524. # Node ID fb2b4c6ac8b022a4530428ad744e9297a342906a
  1525. # Parent 0b601d903078774410f9c44ee3554c7e9b7db6ee
  1526. Added arguments object.
  1527.  
  1528. This closes #19 issue on Github.
  1529.  
  1530. diff --git a/njs/njs_disassembler.c b/njs/njs_disassembler.c
  1531. --- a/njs/njs_disassembler.c
  1532. +++ b/njs/njs_disassembler.c
  1533. @@ -24,6 +24,8 @@ static njs_code_name_t code_names[] = {
  1534. nxt_string("OBJECT ") },
  1535. { njs_vmcode_function, sizeof(njs_vmcode_function_t),
  1536. nxt_string("FUNCTION ") },
  1537. + { njs_vmcode_arguments, sizeof(njs_vmcode_arguments_t),
  1538. + nxt_string("ARGUMENTS ") },
  1539. { njs_vmcode_regexp, sizeof(njs_vmcode_regexp_t),
  1540. nxt_string("REGEXP ") },
  1541. { njs_vmcode_object_copy, sizeof(njs_vmcode_object_copy_t),
  1542. diff --git a/njs/njs_function.c b/njs/njs_function.c
  1543. --- a/njs/njs_function.c
  1544. +++ b/njs/njs_function.c
  1545. @@ -8,6 +8,8 @@
  1546. #include <string.h>
  1547.  
  1548.  
  1549. +static njs_ret_t njs_function_arguments_thrower(njs_vm_t *vm,
  1550. + njs_value_t *value, njs_value_t *setval, njs_value_t *retval);
  1551. static njs_ret_t njs_function_activate(njs_vm_t *vm, njs_function_t *function,
  1552. njs_value_t *this, njs_value_t *args, nxt_uint_t nargs, njs_index_t retval);
  1553.  
  1554. @@ -95,6 +97,137 @@ njs_function_value_copy(njs_vm_t *vm, nj
  1555. }
  1556.  
  1557.  
  1558. +/*
  1559. + * ES5.1, 10.6: CreateArgumentsObject.
  1560. + */
  1561. +njs_ret_t
  1562. +njs_function_arguments_object_init(njs_vm_t *vm, njs_native_frame_t *frame)
  1563. +{
  1564. + nxt_int_t ret;
  1565. + nxt_uint_t nargs, n;
  1566. + njs_value_t val, *value;
  1567. + njs_object_t *obj;
  1568. + njs_object_prop_t *prop;
  1569. + nxt_lvlhsh_query_t lhq;
  1570. +
  1571. + static const njs_value_t njs_string_length = njs_string("length");
  1572. + static const njs_value_t njs_string_callee = njs_string("callee");
  1573. + static const njs_value_t njs_string_caller = njs_string("caller");
  1574. +
  1575. + obj = njs_object_alloc(vm);
  1576. + if (nxt_slow_path(obj == NULL)) {
  1577. + return NXT_ERROR;
  1578. + }
  1579. +
  1580. + nargs = frame->nargs;
  1581. +
  1582. + value = &val;
  1583. + njs_value_number_set(value, nargs);
  1584. +
  1585. + prop = njs_object_prop_alloc(vm, &njs_string_length, value, 1);
  1586. + if (nxt_slow_path(prop == NULL)) {
  1587. + return NXT_ERROR;
  1588. + }
  1589. +
  1590. + prop->enumerable = 0;
  1591. +
  1592. + lhq.value = prop;
  1593. + lhq.key_hash = NJS_LENGTH_HASH;
  1594. + njs_string_get(&prop->name, &lhq.key);
  1595. +
  1596. + lhq.replace = 0;
  1597. + lhq.pool = vm->mem_cache_pool;
  1598. + lhq.proto = &njs_object_hash_proto;
  1599. +
  1600. + ret = nxt_lvlhsh_insert(&obj->hash, &lhq);
  1601. +
  1602. + if (nxt_slow_path(ret != NXT_OK)) {
  1603. + njs_internal_error(vm, "lvlhsh insert failed");
  1604. + return NXT_ERROR;
  1605. + }
  1606. +
  1607. + prop = njs_object_prop_handler_alloc(vm, &njs_string_callee,
  1608. + njs_function_arguments_thrower, 0);
  1609. + if (nxt_slow_path(prop == NULL)) {
  1610. + return NXT_ERROR;
  1611. + }
  1612. +
  1613. + lhq.value = prop;
  1614. + njs_string_get(&prop->name, &lhq.key);
  1615. + lhq.key_hash = nxt_djb_hash(lhq.key.start, lhq.key.length);
  1616. +
  1617. + lhq.replace = 0;
  1618. + lhq.pool = vm->mem_cache_pool;
  1619. + lhq.proto = &njs_object_hash_proto;
  1620. +
  1621. + ret = nxt_lvlhsh_insert(&obj->hash, &lhq);
  1622. +
  1623. + if (nxt_slow_path(ret != NXT_OK)) {
  1624. + njs_internal_error(vm, "lvlhsh insert failed");
  1625. + return NXT_ERROR;
  1626. + }
  1627. +
  1628. + prop = njs_object_prop_handler_alloc(vm, &njs_string_caller,
  1629. + njs_function_arguments_thrower, 0);
  1630. + if (nxt_slow_path(prop == NULL)) {
  1631. + return NXT_ERROR;
  1632. + }
  1633. +
  1634. + lhq.value = prop;
  1635. + njs_string_get(&prop->name, &lhq.key);
  1636. + lhq.key_hash = nxt_djb_hash(lhq.key.start, lhq.key.length);
  1637. +
  1638. + lhq.replace = 0;
  1639. + lhq.pool = vm->mem_cache_pool;
  1640. + lhq.proto = &njs_object_hash_proto;
  1641. +
  1642. + ret = nxt_lvlhsh_insert(&obj->hash, &lhq);
  1643. +
  1644. + if (nxt_slow_path(ret != NXT_OK)) {
  1645. + njs_internal_error(vm, "lvlhsh insert failed");
  1646. + return NXT_ERROR;
  1647. + }
  1648. +
  1649. + for (n = 0; n < nargs; n++) {
  1650. + njs_uint32_to_string(&val, n);
  1651. +
  1652. + prop = njs_object_prop_alloc(vm, &val, &frame->arguments[n + 1], 1);
  1653. + if (nxt_slow_path(prop == NULL)) {
  1654. + return NXT_ERROR;
  1655. + }
  1656. +
  1657. + lhq.value = prop;
  1658. + njs_string_get(&prop->name, &lhq.key);
  1659. + lhq.key_hash = nxt_djb_hash(lhq.key.start, lhq.key.length);
  1660. +
  1661. + lhq.replace = 0;
  1662. + lhq.pool = vm->mem_cache_pool;
  1663. + lhq.proto = &njs_object_hash_proto;
  1664. +
  1665. + ret = nxt_lvlhsh_insert(&obj->hash, &lhq);
  1666. +
  1667. + if (nxt_slow_path(ret != NXT_OK)) {
  1668. + njs_internal_error(vm, "lvlhsh insert failed");
  1669. + return NXT_ERROR;
  1670. + }
  1671. +
  1672. + }
  1673. +
  1674. + frame->arguments_object = obj;
  1675. +
  1676. + return NXT_OK;
  1677. +}
  1678. +
  1679. +
  1680. +static njs_ret_t
  1681. +njs_function_arguments_thrower(njs_vm_t *vm, njs_value_t *value,
  1682. + njs_value_t *setval, njs_value_t *retval)
  1683. +{
  1684. + njs_type_error(vm, "'caller', 'callee' properties may not be accessed");
  1685. + return NXT_ERROR;
  1686. +}
  1687. +
  1688. +
  1689. njs_ret_t
  1690. njs_function_native_frame(njs_vm_t *vm, njs_function_t *function,
  1691. const njs_value_t *this, njs_value_t *args, nxt_uint_t nargs,
  1692. @@ -315,6 +448,7 @@ nxt_noinline njs_ret_t
  1693. njs_function_call(njs_vm_t *vm, njs_index_t retval, size_t advance)
  1694. {
  1695. size_t size;
  1696. + njs_ret_t ret;
  1697. nxt_uint_t n, nesting;
  1698. njs_frame_t *frame;
  1699. njs_value_t *value;
  1700. @@ -393,6 +527,13 @@ njs_function_call(njs_vm_t *vm, njs_inde
  1701. vm->scopes[NJS_SCOPE_CLOSURE + n] = &closure->u.values;
  1702. }
  1703.  
  1704. + if (lambda->arguments_object) {
  1705. + ret = njs_function_arguments_object_init(vm, &frame->native);
  1706. + if (nxt_slow_path(ret != NXT_OK)) {
  1707. + return NXT_ERROR;
  1708. + }
  1709. + }
  1710. +
  1711. vm->active_frame = frame;
  1712.  
  1713. return NJS_APPLIED;
  1714. diff --git a/njs/njs_function.h b/njs/njs_function.h
  1715. --- a/njs/njs_function.h
  1716. +++ b/njs/njs_function.h
  1717. @@ -30,6 +30,8 @@ struct njs_function_lambda_s {
  1718. /* Function internal block closures levels. */
  1719. uint8_t block_closures; /* 4 bits */
  1720.  
  1721. + uint8_t arguments_object;/* 1 bit */
  1722. +
  1723. /* Initial values of local scope. */
  1724. njs_value_t *local_scope;
  1725.  
  1726. @@ -102,7 +104,9 @@ struct njs_native_frame_s {
  1727.  
  1728. njs_function_t *function;
  1729. njs_native_frame_t *previous;
  1730. +
  1731. njs_value_t *arguments;
  1732. + njs_object_t *arguments_object;
  1733.  
  1734. njs_exception_t exception;
  1735.  
  1736. @@ -147,6 +151,8 @@ struct njs_frame_s {
  1737. njs_function_t *njs_function_alloc(njs_vm_t *vm);
  1738. njs_function_t *njs_function_value_copy(njs_vm_t *vm, njs_value_t *value);
  1739. njs_native_frame_t *njs_function_frame_alloc(njs_vm_t *vm, size_t size);
  1740. +njs_ret_t njs_function_arguments_object_init(njs_vm_t *vm,
  1741. + njs_native_frame_t *frame);
  1742. njs_ret_t njs_function_prototype_create(njs_vm_t *vm, njs_value_t *value,
  1743. njs_value_t *setval, njs_value_t *retval);
  1744. njs_value_t *njs_function_property_prototype_create(njs_vm_t *vm,
  1745. diff --git a/njs/njs_generator.c b/njs/njs_generator.c
  1746. --- a/njs/njs_generator.c
  1747. +++ b/njs/njs_generator.c
  1748. @@ -14,6 +14,8 @@ static nxt_int_t njs_generate_name(njs_v
  1749. njs_parser_node_t *node);
  1750. static nxt_int_t njs_generate_builtin_object(njs_vm_t *vm, njs_parser_t *parser,
  1751. njs_parser_node_t *node);
  1752. +static nxt_int_t njs_generate_arguments_object(njs_vm_t *vm,
  1753. + njs_parser_t *parser, njs_parser_node_t *node);
  1754. static nxt_int_t njs_generate_variable(njs_vm_t *vm, njs_parser_t *parser,
  1755. njs_parser_node_t *node);
  1756. static nxt_int_t njs_generate_var_statement(njs_vm_t *vm, njs_parser_t *parser,
  1757. @@ -308,6 +310,9 @@ njs_generator(njs_vm_t *vm, njs_parser_t
  1758. case NJS_TOKEN_CLEAR_TIMEOUT:
  1759. return njs_generate_builtin_object(vm, parser, node);
  1760.  
  1761. + case NJS_TOKEN_ARGUMENTS:
  1762. + return njs_generate_arguments_object(vm, parser, node);
  1763. +
  1764. case NJS_TOKEN_FUNCTION:
  1765. return njs_generate_function_declaration(vm, parser, node);
  1766.  
  1767. @@ -396,6 +401,29 @@ njs_generate_builtin_object(njs_vm_t *vm
  1768.  
  1769.  
  1770. static nxt_int_t
  1771. +njs_generate_arguments_object(njs_vm_t *vm, njs_parser_t *parser,
  1772. + njs_parser_node_t *node)
  1773. +{
  1774. + njs_vmcode_arguments_t *gen;
  1775. +
  1776. + parser->arguments_object = 1;
  1777. +
  1778. + node->index = njs_generator_object_dest_index(vm, parser, node);
  1779. + if (nxt_slow_path(node->index == NJS_INDEX_ERROR)) {
  1780. + return NXT_ERROR;
  1781. + }
  1782. +
  1783. + njs_generate_code(parser, njs_vmcode_arguments_t, gen);
  1784. + gen->code.operation = njs_vmcode_arguments;
  1785. + gen->code.operands = NJS_VMCODE_1OPERAND;
  1786. + gen->code.retval = NJS_VMCODE_RETVAL;
  1787. + gen->retval = node->index;
  1788. +
  1789. + return NXT_OK;
  1790. +}
  1791. +
  1792. +
  1793. +static nxt_int_t
  1794. njs_generate_variable(njs_vm_t *vm, njs_parser_t *parser,
  1795. njs_parser_node_t *node)
  1796. {
  1797. @@ -2010,6 +2038,7 @@ njs_generate_function_scope(njs_vm_t *vm
  1798. parser->code_size += node->scope->argument_closures
  1799. * sizeof(njs_vmcode_move_t);
  1800.  
  1801. + parser->arguments_object = 0;
  1802. ret = njs_generate_scope(vm, parser, node);
  1803.  
  1804. if (nxt_fast_path(ret == NXT_OK)) {
  1805. @@ -2023,6 +2052,7 @@ njs_generate_function_scope(njs_vm_t *vm
  1806.  
  1807. lambda->nesting = node->scope->nesting;
  1808. lambda->closure_size = size;
  1809. + lambda->arguments_object = parser->arguments_object;
  1810.  
  1811. lambda->local_size = parser->scope_size;
  1812. lambda->local_scope = parser->local_scope;
  1813. diff --git a/njs/njs_lexer_keyword.c b/njs/njs_lexer_keyword.c
  1814. --- a/njs/njs_lexer_keyword.c
  1815. +++ b/njs/njs_lexer_keyword.c
  1816. @@ -53,6 +53,7 @@ static const njs_keyword_t njs_keywords
  1817. /* Builtin objects. */
  1818.  
  1819. { nxt_string("this"), NJS_TOKEN_THIS, 0 },
  1820. + { nxt_string("arguments"), NJS_TOKEN_ARGUMENTS, 0 },
  1821. { nxt_string("njs"), NJS_TOKEN_NJS, 0 },
  1822. { nxt_string("Math"), NJS_TOKEN_MATH, 0 },
  1823. { nxt_string("JSON"), NJS_TOKEN_JSON, 0 },
  1824. diff --git a/njs/njs_object.c b/njs/njs_object.c
  1825. --- a/njs/njs_object.c
  1826. +++ b/njs/njs_object.c
  1827. @@ -203,6 +203,36 @@ njs_object_prop_alloc(njs_vm_t *vm, cons
  1828.  
  1829.  
  1830. nxt_noinline njs_object_prop_t *
  1831. +njs_object_prop_handler_alloc(njs_vm_t *vm, const njs_value_t *name,
  1832. + njs_prop_handler_t handler, uint8_t attributes)
  1833. +{
  1834. + njs_object_prop_t *prop;
  1835. +
  1836. + prop = nxt_mem_cache_align(vm->mem_cache_pool, sizeof(njs_value_t),
  1837. + sizeof(njs_object_prop_t));
  1838. +
  1839. + if (nxt_fast_path(prop != NULL)) {
  1840. + /* GC: retain. */
  1841. + njs_set_invalid(&prop->value);
  1842. + prop->value.data.u.prop_handler = handler;
  1843. +
  1844. + /* GC: retain. */
  1845. + prop->name = *name;
  1846. +
  1847. + prop->type = NJS_PROPERTY_HANDLER;
  1848. + prop->enumerable = attributes;
  1849. + prop->writable = attributes;
  1850. + prop->configurable = attributes;
  1851. + return prop;
  1852. + }
  1853. +
  1854. + njs_memory_error(vm);
  1855. +
  1856. + return NULL;
  1857. +}
  1858. +
  1859. +
  1860. +nxt_noinline njs_object_prop_t *
  1861. njs_object_property(njs_vm_t *vm, const njs_object_t *object,
  1862. nxt_lvlhsh_query_t *lhq)
  1863. {
  1864. diff --git a/njs/njs_object.h b/njs/njs_object.h
  1865. --- a/njs/njs_object.h
  1866. +++ b/njs/njs_object.h
  1867. @@ -87,6 +87,8 @@ njs_ret_t njs_object_constructor(njs_vm_
  1868. nxt_uint_t nargs, njs_index_t unused);
  1869. njs_object_prop_t *njs_object_prop_alloc(njs_vm_t *vm, const njs_value_t *name,
  1870. const njs_value_t *value, uint8_t attributes);
  1871. +nxt_noinline njs_object_prop_t *njs_object_prop_handler_alloc(njs_vm_t *vm,
  1872. + const njs_value_t *name, njs_prop_handler_t handler, uint8_t attributes);
  1873. njs_ret_t njs_primitive_prototype_get_proto(njs_vm_t *vm, njs_value_t *value,
  1874. njs_value_t *setval, njs_value_t *retval);
  1875. njs_ret_t njs_object_prototype_create(njs_vm_t *vm, njs_value_t *value,
  1876. diff --git a/njs/njs_parser.c b/njs/njs_parser.c
  1877. --- a/njs/njs_parser.c
  1878. +++ b/njs/njs_parser.c
  1879. @@ -455,6 +455,13 @@ njs_parser_function_declaration(njs_vm_t
  1880. }
  1881.  
  1882. if (token != NJS_TOKEN_NAME) {
  1883. + if (token == NJS_TOKEN_ARGUMENTS || token == NJS_TOKEN_EVAL) {
  1884. + njs_parser_syntax_error(vm, parser, "Identifier \"%.*s\" "
  1885. + "is forbidden in function declaration",
  1886. + (int) parser->lexer->text.length,
  1887. + parser->lexer->text.start);
  1888. + }
  1889. +
  1890. return NJS_TOKEN_ILLEGAL;
  1891. }
  1892.  
  1893. @@ -821,6 +828,13 @@ njs_parser_var_statement(njs_vm_t *vm, n
  1894. }
  1895.  
  1896. if (token != NJS_TOKEN_NAME) {
  1897. + if (token == NJS_TOKEN_ARGUMENTS || token == NJS_TOKEN_EVAL) {
  1898. + njs_parser_syntax_error(vm, parser, "Identifier \"%.*s\" "
  1899. + "is forbidden in var declaration",
  1900. + (int) parser->lexer->text.length,
  1901. + parser->lexer->text.start);
  1902. + }
  1903. +
  1904. return NJS_TOKEN_ILLEGAL;
  1905. }
  1906.  
  1907. @@ -1306,6 +1320,13 @@ njs_parser_for_var_statement(njs_vm_t *v
  1908. }
  1909.  
  1910. if (token != NJS_TOKEN_NAME) {
  1911. + if (token == NJS_TOKEN_ARGUMENTS || token == NJS_TOKEN_EVAL) {
  1912. + njs_parser_syntax_error(vm, parser, "Identifier \"%.*s\" "
  1913. + "is forbidden in for-in var declaration",
  1914. + (int) parser->lexer->text.length,
  1915. + parser->lexer->text.start);
  1916. + }
  1917. +
  1918. return NJS_TOKEN_ILLEGAL;
  1919. }
  1920.  
  1921. @@ -1973,6 +1994,24 @@ njs_parser_terminal(njs_vm_t *vm, njs_pa
  1922. case NJS_TOKEN_JSON:
  1923. return njs_parser_builtin_object(vm, parser, node);
  1924.  
  1925. + case NJS_TOKEN_ARGUMENTS:
  1926. + nxt_thread_log_debug("JS: arguments");
  1927. +
  1928. + if (parser->scope->type <= NJS_SCOPE_GLOBAL) {
  1929. + njs_parser_syntax_error(vm, parser, "\"%.*s\" object "
  1930. + "in global scope",
  1931. + (int) parser->lexer->text.length,
  1932. + parser->lexer->text.start);
  1933. +
  1934. + return NJS_TOKEN_ILLEGAL;
  1935. + }
  1936. +
  1937. + node->token = NJS_TOKEN_ARGUMENTS;
  1938. +
  1939. + parser->code_size += sizeof(njs_vmcode_arguments_t);
  1940. +
  1941. + break;
  1942. +
  1943. case NJS_TOKEN_OBJECT_CONSTRUCTOR:
  1944. node->index = NJS_INDEX_OBJECT;
  1945. break;
  1946. diff --git a/njs/njs_parser.h b/njs/njs_parser.h
  1947. --- a/njs/njs_parser.h
  1948. +++ b/njs/njs_parser.h
  1949. @@ -161,6 +161,7 @@ typedef enum {
  1950. NJS_TOKEN_THROW,
  1951.  
  1952. NJS_TOKEN_THIS,
  1953. + NJS_TOKEN_ARGUMENTS,
  1954.  
  1955. #define NJS_TOKEN_FIRST_OBJECT NJS_TOKEN_GLOBAL_THIS
  1956.  
  1957. @@ -346,6 +347,8 @@ struct njs_parser_s {
  1958. u_char *code_end;
  1959.  
  1960. njs_parser_t *parent;
  1961. +
  1962. + nxt_uint_t arguments_object;
  1963. };
  1964.  
  1965.  
  1966. diff --git a/njs/njs_parser_expression.c b/njs/njs_parser_expression.c
  1967. --- a/njs/njs_parser_expression.c
  1968. +++ b/njs/njs_parser_expression.c
  1969. @@ -414,8 +414,19 @@ njs_parser_assignment_expression(njs_vm_
  1970. }
  1971.  
  1972. if (!njs_parser_is_lvalue(parser->node)) {
  1973. - njs_parser_ref_error(vm, parser,
  1974. - "Invalid left-hand side in assignment");
  1975. + token = parser->node->token;
  1976. +
  1977. + if (token == NJS_TOKEN_ARGUMENTS || token == NJS_TOKEN_EVAL) {
  1978. + njs_parser_syntax_error(vm, parser, "Identifier \"%s\" "
  1979. + "is forbidden as left-hand in assignment",
  1980. + (token == NJS_TOKEN_EVAL) ? "eval"
  1981. + : "arguments");
  1982. +
  1983. + } else {
  1984. + njs_parser_ref_error(vm, parser,
  1985. + "Invalid left-hand side in assignment");
  1986. + }
  1987. +
  1988. return NJS_TOKEN_ILLEGAL;
  1989. }
  1990.  
  1991. diff --git a/njs/njs_vm.c b/njs/njs_vm.c
  1992. --- a/njs/njs_vm.c
  1993. +++ b/njs/njs_vm.c
  1994. @@ -415,6 +415,29 @@ njs_vmcode_function(njs_vm_t *vm, njs_va
  1995.  
  1996.  
  1997. njs_ret_t
  1998. +njs_vmcode_arguments(njs_vm_t *vm, njs_value_t *invld1, njs_value_t *invld2)
  1999. +{
  2000. + njs_ret_t ret;
  2001. + njs_frame_t *frame;
  2002. +
  2003. + frame = (njs_frame_t *) vm->active_frame;
  2004. +
  2005. + if (frame->native.arguments_object == NULL) {
  2006. + ret = njs_function_arguments_object_init(vm, &frame->native);
  2007. + if (nxt_slow_path(ret != NXT_OK)) {
  2008. + return NXT_ERROR;
  2009. + }
  2010. + }
  2011. +
  2012. + vm->retval.data.u.object = frame->native.arguments_object;
  2013. + vm->retval.type = NJS_OBJECT;
  2014. + vm->retval.data.truth = 1;
  2015. +
  2016. + return sizeof(njs_vmcode_arguments_t);
  2017. +}
  2018. +
  2019. +
  2020. +njs_ret_t
  2021. njs_vmcode_regexp(njs_vm_t *vm, njs_value_t *invld1, njs_value_t *invld2)
  2022. {
  2023. njs_regexp_t *regexp;
  2024. diff --git a/njs/njs_vm.h b/njs/njs_vm.h
  2025. --- a/njs/njs_vm.h
  2026. +++ b/njs/njs_vm.h
  2027. @@ -644,6 +644,12 @@ typedef struct {
  2028. typedef struct {
  2029. njs_vmcode_t code;
  2030. njs_index_t retval;
  2031. +} njs_vmcode_arguments_t;
  2032. +
  2033. +
  2034. +typedef struct {
  2035. + njs_vmcode_t code;
  2036. + njs_index_t retval;
  2037. uintptr_t length;
  2038. } njs_vmcode_array_t;
  2039.  
  2040. @@ -1112,6 +1118,8 @@ njs_ret_t njs_vmcode_array(njs_vm_t *vm,
  2041. njs_value_t *inlvd2);
  2042. njs_ret_t njs_vmcode_function(njs_vm_t *vm, njs_value_t *inlvd1,
  2043. njs_value_t *invld2);
  2044. +njs_ret_t njs_vmcode_arguments(njs_vm_t *vm, njs_value_t *inlvd1,
  2045. + njs_value_t *invld2);
  2046. njs_ret_t njs_vmcode_regexp(njs_vm_t *vm, njs_value_t *inlvd1,
  2047. njs_value_t *invld2);
  2048. njs_ret_t njs_vmcode_object_copy(njs_vm_t *vm, njs_value_t *value,
  2049. diff --git a/njs/test/njs_unit_test.c b/njs/test/njs_unit_test.c
  2050. --- a/njs/test/njs_unit_test.c
  2051. +++ b/njs/test/njs_unit_test.c
  2052. @@ -5574,6 +5574,77 @@ static njs_unit_test_t njs_test[] =
  2053. "var b = a(); b(2)"),
  2054. nxt_string("3") },
  2055.  
  2056. + /* arguments object. */
  2057. +
  2058. + { nxt_string("var arguments"),
  2059. + nxt_string("SyntaxError: Identifier \"arguments\" is forbidden in var declaration in 1") },
  2060. +
  2061. + { nxt_string("for (var arguments in []) {}"),
  2062. + nxt_string("SyntaxError: Identifier \"arguments\" is forbidden in for-in var declaration in 1") },
  2063. +
  2064. + { nxt_string("function arguments(){}"),
  2065. + nxt_string("SyntaxError: Identifier \"arguments\" is forbidden in function declaration in 1") },
  2066. +
  2067. + { nxt_string("(function () {arguments = [];})"),
  2068. + nxt_string("SyntaxError: Identifier \"arguments\" is forbidden as left-hand in assignment in 1") },
  2069. +
  2070. + { nxt_string("(function(){return arguments[0];})(1,2,3)"),
  2071. + nxt_string("1") },
  2072. +
  2073. + { nxt_string("(function(){return arguments[2];})(1,2,3)"),
  2074. + nxt_string("3") },
  2075. +
  2076. + { nxt_string("(function(){return arguments[3];})(1,2,3)"),
  2077. + nxt_string("undefined") },
  2078. +
  2079. + { nxt_string("(function(a,b,c){return a;})(1,2,3)"),
  2080. + nxt_string("1") },
  2081. +
  2082. + { nxt_string("(function(a,b,c){arguments[0] = 4; return a;})(1,2,3)"),
  2083. + nxt_string("1") },
  2084. +
  2085. + { nxt_string("(function(a,b,c){a = 4; return arguments[0];})(1,2,3)"),
  2086. + nxt_string("1") },
  2087. +
  2088. + { nxt_string("function check(v) {if (v == false) {throw TypeError('Too few arguments')}}; "
  2089. + "function f() {check(arguments.length > 1); return 1}; f()"),
  2090. + nxt_string("TypeError: Too few arguments") },
  2091. +
  2092. + { nxt_string("function check(v) {if (v == false) {throw TypeError('Too few arguments')}}; "
  2093. + "function f() {check(arguments.length > 1); return 1}; f(1,2)"),
  2094. + nxt_string("1") },
  2095. +
  2096. + { nxt_string("(function(a,b){delete arguments[0]; return arguments[0]})(1,1)"),
  2097. + nxt_string("undefined") },
  2098. +
  2099. + { nxt_string("(function(){return arguments.length;})()"),
  2100. + nxt_string("0") },
  2101. +
  2102. + { nxt_string("(function(){return arguments.length;})(1,2,3)"),
  2103. + nxt_string("3") },
  2104. +
  2105. + { nxt_string("(function(){arguments.length = 1; return arguments.length;})(1,2,3)"),
  2106. + nxt_string("1") },
  2107. +
  2108. + { nxt_string("(function(){return arguments.callee;})()"),
  2109. + nxt_string("TypeError: 'caller', 'callee' properties may not be accessed") },
  2110. +
  2111. + { nxt_string("(function(){return arguments.caller;})()"),
  2112. + nxt_string("TypeError: 'caller', 'callee' properties may not be accessed") },
  2113. +
  2114. + { nxt_string("(function(){return arguments.callee;})()"),
  2115. + nxt_string("TypeError: 'caller', 'callee' properties may not be accessed") },
  2116. +
  2117. + { nxt_string("function sum() { var args = Array.prototype.slice.call(arguments); "
  2118. + "return args.reduce(function(prev, curr) {return prev + curr})};"
  2119. + "[sum(1), sum(1,2), sum(1,2,3), sum(1,2,3,4)]"),
  2120. + nxt_string("1,3,6,10") },
  2121. +
  2122. + { nxt_string("function concat(sep) { var args = Array.prototype.slice.call(arguments, 1); "
  2123. + "return args.join(sep)};"
  2124. + "[concat('.',1,2,3), concat('+',1,2,3,4)]"),
  2125. + nxt_string("1.2.3,1+2+3+4") },
  2126. +
  2127. /* Scopes. */
  2128.  
  2129. { nxt_string("function f(x) { a = x } var a; f(5); a"),
Add Comment
Please, Sign In to add comment