OLD | NEW |
1 // Copyright 2012 the V8 project authors. All rights reserved. | 1 // Copyright 2012 the V8 project authors. All rights reserved. |
2 // Redistribution and use in source and binary forms, with or without | 2 // Redistribution and use in source and binary forms, with or without |
3 // modification, are permitted provided that the following conditions are | 3 // modification, are permitted provided that the following conditions are |
4 // met: | 4 // met: |
5 // | 5 // |
6 // * Redistributions of source code must retain the above copyright | 6 // * Redistributions of source code must retain the above copyright |
7 // notice, this list of conditions and the following disclaimer. | 7 // notice, this list of conditions and the following disclaimer. |
8 // * Redistributions in binary form must reproduce the above | 8 // * Redistributions in binary form must reproduce the above |
9 // copyright notice, this list of conditions and the following | 9 // copyright notice, this list of conditions and the following |
10 // disclaimer in the documentation and/or other materials provided | 10 // disclaimer in the documentation and/or other materials provided |
(...skipping 828 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
839 | 839 |
840 // Stack now matches JSFunction abi. | 840 // Stack now matches JSFunction abi. |
841 GenerateFastApiCallBody(masm, | 841 GenerateFastApiCallBody(masm, |
842 optimization, | 842 optimization, |
843 argc, | 843 argc, |
844 receiver, | 844 receiver, |
845 true); | 845 true); |
846 } | 846 } |
847 | 847 |
848 | 848 |
849 class CallInterceptorCompiler BASE_EMBEDDED { | |
850 public: | |
851 CallInterceptorCompiler(CallStubCompiler* stub_compiler, | |
852 Register name) | |
853 : stub_compiler_(stub_compiler), | |
854 name_(name) {} | |
855 | |
856 void Compile(MacroAssembler* masm, | |
857 Handle<JSObject> object, | |
858 Handle<JSObject> holder, | |
859 Handle<Name> name, | |
860 LookupResult* lookup, | |
861 Register receiver, | |
862 Register scratch1, | |
863 Register scratch2, | |
864 Register scratch3, | |
865 Label* miss) { | |
866 ASSERT(holder->HasNamedInterceptor()); | |
867 ASSERT(!holder->GetNamedInterceptor()->getter()->IsUndefined()); | |
868 | |
869 // Check that the receiver isn't a smi. | |
870 __ JumpIfSmi(receiver, miss); | |
871 CallOptimization optimization(lookup); | |
872 if (optimization.is_constant_call()) { | |
873 CompileCacheable(masm, object, receiver, scratch1, scratch2, scratch3, | |
874 holder, lookup, name, optimization, miss); | |
875 } else { | |
876 CompileRegular(masm, object, receiver, scratch1, scratch2, scratch3, | |
877 name, holder, miss); | |
878 } | |
879 } | |
880 | |
881 private: | |
882 void CompileCacheable(MacroAssembler* masm, | |
883 Handle<JSObject> object, | |
884 Register receiver, | |
885 Register scratch1, | |
886 Register scratch2, | |
887 Register scratch3, | |
888 Handle<JSObject> interceptor_holder, | |
889 LookupResult* lookup, | |
890 Handle<Name> name, | |
891 const CallOptimization& optimization, | |
892 Label* miss_label) { | |
893 ASSERT(optimization.is_constant_call()); | |
894 ASSERT(!lookup->holder()->IsGlobalObject()); | |
895 Counters* counters = masm->isolate()->counters(); | |
896 __ IncrementCounter(counters->call_const_interceptor(), 1, | |
897 scratch1, scratch2); | |
898 | |
899 // Check that the maps from receiver to interceptor's holder | |
900 // haven't changed and thus we can invoke interceptor. | |
901 Label miss_cleanup; | |
902 Register holder = | |
903 stub_compiler_->CheckPrototypes( | |
904 IC::CurrentTypeOf(object, masm->isolate()), receiver, | |
905 interceptor_holder, scratch1, scratch2, scratch3, | |
906 name, miss_label); | |
907 | |
908 // Invoke an interceptor and if it provides a value, | |
909 // branch to |regular_invoke|. | |
910 Label regular_invoke; | |
911 LoadWithInterceptor(masm, receiver, holder, interceptor_holder, scratch2, | |
912 ®ular_invoke); | |
913 | |
914 // Interceptor returned nothing for this property. Try to use cached | |
915 // constant function. | |
916 | |
917 // Check that the maps from interceptor's holder to constant function's | |
918 // holder haven't changed and thus we can use cached constant function. | |
919 if (*interceptor_holder != lookup->holder()) { | |
920 stub_compiler_->CheckPrototypes( | |
921 IC::CurrentTypeOf(interceptor_holder, masm->isolate()), holder, | |
922 handle(lookup->holder()), scratch1, scratch2, scratch3, | |
923 name, miss_label); | |
924 } | |
925 | |
926 Handle<JSFunction> function = optimization.constant_function(); | |
927 __ Move(a0, receiver); | |
928 stub_compiler_->GenerateJumpFunction(object, function); | |
929 | |
930 // Invoke a regular function. | |
931 __ bind(®ular_invoke); | |
932 } | |
933 | |
934 void CompileRegular(MacroAssembler* masm, | |
935 Handle<JSObject> object, | |
936 Register receiver, | |
937 Register scratch1, | |
938 Register scratch2, | |
939 Register scratch3, | |
940 Handle<Name> name, | |
941 Handle<JSObject> interceptor_holder, | |
942 Label* miss_label) { | |
943 Register holder = | |
944 stub_compiler_->CheckPrototypes( | |
945 IC::CurrentTypeOf(object, masm->isolate()), receiver, | |
946 interceptor_holder, scratch1, scratch2, scratch3, name, miss_label); | |
947 | |
948 // Call a runtime function to load the interceptor property. | |
949 FrameScope scope(masm, StackFrame::INTERNAL); | |
950 // Save the name_ register across the call. | |
951 __ push(name_); | |
952 | |
953 CompileCallLoadPropertyWithInterceptor( | |
954 masm, receiver, holder, name_, interceptor_holder, | |
955 IC::kLoadPropertyWithInterceptorForCall); | |
956 | |
957 // Restore the name_ register. | |
958 __ pop(name_); | |
959 // Leave the internal frame. | |
960 } | |
961 | |
962 void LoadWithInterceptor(MacroAssembler* masm, | |
963 Register receiver, | |
964 Register holder, | |
965 Handle<JSObject> holder_obj, | |
966 Register scratch, | |
967 Label* interceptor_succeeded) { | |
968 { | |
969 FrameScope scope(masm, StackFrame::INTERNAL); | |
970 | |
971 __ Push(receiver, holder, name_); | |
972 CompileCallLoadPropertyWithInterceptor( | |
973 masm, receiver, holder, name_, holder_obj, | |
974 IC::kLoadPropertyWithInterceptorOnly); | |
975 __ pop(name_); | |
976 __ pop(holder); | |
977 __ pop(receiver); | |
978 } | |
979 // If interceptor returns no-result sentinel, call the constant function. | |
980 __ LoadRoot(scratch, Heap::kNoInterceptorResultSentinelRootIndex); | |
981 __ Branch(interceptor_succeeded, ne, v0, Operand(scratch)); | |
982 } | |
983 | |
984 CallStubCompiler* stub_compiler_; | |
985 Register name_; | |
986 }; | |
987 | |
988 | |
989 void StubCompiler::GenerateTailCall(MacroAssembler* masm, Handle<Code> code) { | 849 void StubCompiler::GenerateTailCall(MacroAssembler* masm, Handle<Code> code) { |
990 __ Jump(code, RelocInfo::CODE_TARGET); | 850 __ Jump(code, RelocInfo::CODE_TARGET); |
991 } | 851 } |
992 | 852 |
993 | 853 |
994 #undef __ | 854 #undef __ |
995 #define __ ACCESS_MASM(masm()) | 855 #define __ ACCESS_MASM(masm()) |
996 | 856 |
997 | 857 |
998 Register StubCompiler::CheckPrototypes(Handle<HeapType> type, | 858 Register StubCompiler::CheckPrototypes(Handle<HeapType> type, |
(...skipping 337 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1336 PushInterceptorArguments(masm(), receiver(), holder_reg, | 1196 PushInterceptorArguments(masm(), receiver(), holder_reg, |
1337 this->name(), interceptor_holder); | 1197 this->name(), interceptor_holder); |
1338 | 1198 |
1339 ExternalReference ref = ExternalReference( | 1199 ExternalReference ref = ExternalReference( |
1340 IC_Utility(IC::kLoadPropertyWithInterceptorForLoad), isolate()); | 1200 IC_Utility(IC::kLoadPropertyWithInterceptorForLoad), isolate()); |
1341 __ TailCallExternalReference(ref, StubCache::kInterceptorArgsLength, 1); | 1201 __ TailCallExternalReference(ref, StubCache::kInterceptorArgsLength, 1); |
1342 } | 1202 } |
1343 } | 1203 } |
1344 | 1204 |
1345 | 1205 |
1346 void CallStubCompiler::GenerateNameCheck(Handle<Name> name, Label* miss) { | |
1347 if (kind_ == Code::KEYED_CALL_IC) { | |
1348 __ Branch(miss, ne, a2, Operand(name)); | |
1349 } | |
1350 } | |
1351 | |
1352 | |
1353 void CallStubCompiler::GenerateFunctionCheck(Register function, | |
1354 Register scratch, | |
1355 Label* miss) { | |
1356 __ JumpIfSmi(function, miss); | |
1357 __ GetObjectType(function, scratch, scratch); | |
1358 __ Branch(miss, ne, scratch, Operand(JS_FUNCTION_TYPE)); | |
1359 } | |
1360 | |
1361 | |
1362 void CallStubCompiler::GenerateLoadFunctionFromCell( | |
1363 Handle<Cell> cell, | |
1364 Handle<JSFunction> function, | |
1365 Label* miss) { | |
1366 // Get the value from the cell. | |
1367 __ li(a3, Operand(cell)); | |
1368 __ lw(a1, FieldMemOperand(a3, Cell::kValueOffset)); | |
1369 | |
1370 // Check that the cell contains the same function. | |
1371 if (heap()->InNewSpace(*function)) { | |
1372 // We can't embed a pointer to a function in new space so we have | |
1373 // to verify that the shared function info is unchanged. This has | |
1374 // the nice side effect that multiple closures based on the same | |
1375 // function can all use this call IC. Before we load through the | |
1376 // function, we have to verify that it still is a function. | |
1377 GenerateFunctionCheck(a1, a3, miss); | |
1378 | |
1379 // Check the shared function info. Make sure it hasn't changed. | |
1380 __ li(a3, Handle<SharedFunctionInfo>(function->shared())); | |
1381 __ lw(t0, FieldMemOperand(a1, JSFunction::kSharedFunctionInfoOffset)); | |
1382 __ Branch(miss, ne, t0, Operand(a3)); | |
1383 } else { | |
1384 __ Branch(miss, ne, a1, Operand(function)); | |
1385 } | |
1386 } | |
1387 | |
1388 | |
1389 void CallStubCompiler::GenerateMissBranch() { | |
1390 Handle<Code> code = | |
1391 isolate()->stub_cache()->ComputeCallMiss(arguments().immediate(), | |
1392 kind_, | |
1393 extra_state()); | |
1394 __ Jump(code, RelocInfo::CODE_TARGET); | |
1395 } | |
1396 | |
1397 | |
1398 Handle<Code> CallStubCompiler::CompileCallField(Handle<JSObject> object, | |
1399 Handle<JSObject> holder, | |
1400 PropertyIndex index, | |
1401 Handle<Name> name) { | |
1402 Label miss; | |
1403 | |
1404 Register reg = HandlerFrontendHeader( | |
1405 object, holder, name, RECEIVER_MAP_CHECK, &miss); | |
1406 GenerateFastPropertyLoad(masm(), a1, reg, index.is_inobject(holder), | |
1407 index.translate(holder), Representation::Tagged()); | |
1408 GenerateJumpFunction(object, a1, &miss); | |
1409 | |
1410 HandlerFrontendFooter(&miss); | |
1411 | |
1412 // Return the generated code. | |
1413 return GetCode(Code::FAST, name); | |
1414 } | |
1415 | |
1416 | |
1417 void StubCompiler::GenerateBooleanCheck(Register object, Label* miss) { | 1206 void StubCompiler::GenerateBooleanCheck(Register object, Label* miss) { |
1418 Label success; | 1207 Label success; |
1419 // Check that the object is a boolean. | 1208 // Check that the object is a boolean. |
1420 __ LoadRoot(at, Heap::kTrueValueRootIndex); | 1209 __ LoadRoot(at, Heap::kTrueValueRootIndex); |
1421 __ Branch(&success, eq, object, Operand(at)); | 1210 __ Branch(&success, eq, object, Operand(at)); |
1422 __ LoadRoot(at, Heap::kFalseValueRootIndex); | 1211 __ LoadRoot(at, Heap::kFalseValueRootIndex); |
1423 __ Branch(miss, ne, object, Operand(at)); | 1212 __ Branch(miss, ne, object, Operand(at)); |
1424 __ bind(&success); | 1213 __ bind(&success); |
1425 } | 1214 } |
1426 | 1215 |
1427 | 1216 |
1428 void CallStubCompiler::PatchImplicitReceiver(Handle<Object> object) { | |
1429 if (object->IsGlobalObject()) { | |
1430 const int argc = arguments().immediate(); | |
1431 const int receiver_offset = argc * kPointerSize; | |
1432 __ LoadRoot(a3, Heap::kUndefinedValueRootIndex); | |
1433 __ sw(a3, MemOperand(sp, receiver_offset)); | |
1434 } | |
1435 } | |
1436 | |
1437 | |
1438 Register CallStubCompiler::HandlerFrontendHeader(Handle<Object> object, | |
1439 Handle<JSObject> holder, | |
1440 Handle<Name> name, | |
1441 CheckType check, | |
1442 Label* miss) { | |
1443 // ----------- S t a t e ------------- | |
1444 // -- a2 : name | |
1445 // -- ra : return address | |
1446 // ----------------------------------- | |
1447 GenerateNameCheck(name, miss); | |
1448 | |
1449 Register reg = a0; | |
1450 | |
1451 // Get the receiver from the stack. | |
1452 const int argc = arguments().immediate(); | |
1453 const int receiver_offset = argc * kPointerSize; | |
1454 __ lw(a0, MemOperand(sp, receiver_offset)); | |
1455 | |
1456 // Check that the receiver isn't a smi. | |
1457 if (check != NUMBER_CHECK) { | |
1458 __ JumpIfSmi(a0, miss); | |
1459 } | |
1460 | |
1461 // Make sure that it's okay not to patch the on stack receiver | |
1462 // unless we're doing a receiver map check. | |
1463 ASSERT(!object->IsGlobalObject() || check == RECEIVER_MAP_CHECK); | |
1464 switch (check) { | |
1465 case RECEIVER_MAP_CHECK: | |
1466 __ IncrementCounter(isolate()->counters()->call_const(), 1, a1, a3); | |
1467 | |
1468 // Check that the maps haven't changed. | |
1469 reg = CheckPrototypes( | |
1470 IC::CurrentTypeOf(object, isolate()), | |
1471 reg, holder, a1, a3, t0, name, miss); | |
1472 break; | |
1473 | |
1474 case STRING_CHECK: { | |
1475 // Check that the object is a string. | |
1476 __ GetObjectType(reg, a3, a3); | |
1477 __ Branch(miss, Ugreater_equal, a3, Operand(FIRST_NONSTRING_TYPE)); | |
1478 // Check that the maps starting from the prototype haven't changed. | |
1479 GenerateDirectLoadGlobalFunctionPrototype( | |
1480 masm(), Context::STRING_FUNCTION_INDEX, a1, miss); | |
1481 break; | |
1482 } | |
1483 case SYMBOL_CHECK: { | |
1484 // Check that the object is a symbol. | |
1485 __ GetObjectType(reg, a1, a3); | |
1486 __ Branch(miss, ne, a3, Operand(SYMBOL_TYPE)); | |
1487 // Check that the maps starting from the prototype haven't changed. | |
1488 GenerateDirectLoadGlobalFunctionPrototype( | |
1489 masm(), Context::SYMBOL_FUNCTION_INDEX, a1, miss); | |
1490 break; | |
1491 } | |
1492 case NUMBER_CHECK: { | |
1493 Label fast; | |
1494 // Check that the object is a smi or a heap number. | |
1495 __ JumpIfSmi(reg, &fast); | |
1496 __ GetObjectType(reg, a3, a3); | |
1497 __ Branch(miss, ne, a3, Operand(HEAP_NUMBER_TYPE)); | |
1498 __ bind(&fast); | |
1499 // Check that the maps starting from the prototype haven't changed. | |
1500 GenerateDirectLoadGlobalFunctionPrototype( | |
1501 masm(), Context::NUMBER_FUNCTION_INDEX, a1, miss); | |
1502 break; | |
1503 } | |
1504 case BOOLEAN_CHECK: { | |
1505 GenerateBooleanCheck(reg, miss); | |
1506 | |
1507 // Check that the maps starting from the prototype haven't changed. | |
1508 GenerateDirectLoadGlobalFunctionPrototype( | |
1509 masm(), Context::BOOLEAN_FUNCTION_INDEX, a1, miss); | |
1510 break; | |
1511 } | |
1512 } | |
1513 | |
1514 if (check != RECEIVER_MAP_CHECK) { | |
1515 Handle<Object> prototype(object->GetPrototype(isolate()), isolate()); | |
1516 reg = CheckPrototypes( | |
1517 IC::CurrentTypeOf(prototype, isolate()), | |
1518 a1, holder, a1, a3, t0, name, miss); | |
1519 } | |
1520 | |
1521 return reg; | |
1522 } | |
1523 | |
1524 | |
1525 void CallStubCompiler::GenerateJumpFunction(Handle<Object> object, | |
1526 Register function, | |
1527 Label* miss) { | |
1528 ASSERT(function.is(a1)); | |
1529 // Check that the function really is a function. | |
1530 GenerateFunctionCheck(function, a3, miss); | |
1531 PatchImplicitReceiver(object); | |
1532 | |
1533 // Invoke the function. | |
1534 __ InvokeFunction(a1, arguments(), JUMP_FUNCTION, NullCallWrapper()); | |
1535 } | |
1536 | |
1537 | |
1538 Handle<Code> CallStubCompiler::CompileCallInterceptor(Handle<JSObject> object, | |
1539 Handle<JSObject> holder, | |
1540 Handle<Name> name) { | |
1541 Label miss; | |
1542 | |
1543 GenerateNameCheck(name, &miss); | |
1544 | |
1545 // Get the number of arguments. | |
1546 const int argc = arguments().immediate(); | |
1547 LookupResult lookup(isolate()); | |
1548 LookupPostInterceptor(holder, name, &lookup); | |
1549 | |
1550 // Get the receiver from the stack. | |
1551 __ lw(a1, MemOperand(sp, argc * kPointerSize)); | |
1552 | |
1553 CallInterceptorCompiler compiler(this, a2); | |
1554 compiler.Compile(masm(), object, holder, name, &lookup, a1, a3, t0, a0, | |
1555 &miss); | |
1556 | |
1557 // Move returned value, the function to call, to a1. | |
1558 __ mov(a1, v0); | |
1559 // Restore receiver. | |
1560 __ lw(a0, MemOperand(sp, argc * kPointerSize)); | |
1561 | |
1562 GenerateJumpFunction(object, a1, &miss); | |
1563 | |
1564 HandlerFrontendFooter(&miss); | |
1565 | |
1566 // Return the generated code. | |
1567 return GetCode(Code::FAST, name); | |
1568 } | |
1569 | |
1570 | |
1571 Handle<Code> CallStubCompiler::CompileCallGlobal( | |
1572 Handle<JSObject> object, | |
1573 Handle<GlobalObject> holder, | |
1574 Handle<PropertyCell> cell, | |
1575 Handle<JSFunction> function, | |
1576 Handle<Name> name) { | |
1577 Label miss; | |
1578 HandlerFrontendHeader(object, holder, name, RECEIVER_MAP_CHECK, &miss); | |
1579 // Potentially loads a closure that matches the shared function info of the | |
1580 // function, rather than function. | |
1581 GenerateLoadFunctionFromCell(cell, function, &miss); | |
1582 Counters* counters = isolate()->counters(); | |
1583 __ IncrementCounter(counters->call_global_inline(), 1, a3, t0); | |
1584 GenerateJumpFunction(object, a1, function); | |
1585 HandlerFrontendFooter(&miss); | |
1586 | |
1587 // Return the generated code. | |
1588 return GetCode(Code::NORMAL, name); | |
1589 } | |
1590 | |
1591 | |
1592 Handle<Code> StoreStubCompiler::CompileStoreCallback( | 1217 Handle<Code> StoreStubCompiler::CompileStoreCallback( |
1593 Handle<JSObject> object, | 1218 Handle<JSObject> object, |
1594 Handle<JSObject> holder, | 1219 Handle<JSObject> holder, |
1595 Handle<Name> name, | 1220 Handle<Name> name, |
1596 Handle<ExecutableAccessorInfo> callback) { | 1221 Handle<ExecutableAccessorInfo> callback) { |
1597 Register holder_reg = HandlerFrontend( | 1222 Register holder_reg = HandlerFrontend( |
1598 IC::CurrentTypeOf(object, isolate()), receiver(), holder, name); | 1223 IC::CurrentTypeOf(object, isolate()), receiver(), holder, name); |
1599 | 1224 |
1600 // Stub never generated for non-global objects that require access | 1225 // Stub never generated for non-global objects that require access |
1601 // checks. | 1226 // checks. |
(...skipping 208 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1810 // Get the value from the cell. | 1435 // Get the value from the cell. |
1811 __ li(a3, Operand(cell)); | 1436 __ li(a3, Operand(cell)); |
1812 __ lw(t0, FieldMemOperand(a3, Cell::kValueOffset)); | 1437 __ lw(t0, FieldMemOperand(a3, Cell::kValueOffset)); |
1813 | 1438 |
1814 // Check for deleted property if property can actually be deleted. | 1439 // Check for deleted property if property can actually be deleted. |
1815 if (!is_dont_delete) { | 1440 if (!is_dont_delete) { |
1816 __ LoadRoot(at, Heap::kTheHoleValueRootIndex); | 1441 __ LoadRoot(at, Heap::kTheHoleValueRootIndex); |
1817 __ Branch(&miss, eq, t0, Operand(at)); | 1442 __ Branch(&miss, eq, t0, Operand(at)); |
1818 } | 1443 } |
1819 | 1444 |
1820 HandlerFrontendFooter(name, &miss); | |
1821 | |
1822 Counters* counters = isolate()->counters(); | 1445 Counters* counters = isolate()->counters(); |
1823 __ IncrementCounter(counters->named_load_global_stub(), 1, a1, a3); | 1446 __ IncrementCounter(counters->named_load_global_stub(), 1, a1, a3); |
1824 __ Ret(USE_DELAY_SLOT); | 1447 __ Ret(USE_DELAY_SLOT); |
1825 __ mov(v0, t0); | 1448 __ mov(v0, t0); |
1826 | 1449 |
| 1450 HandlerFrontendFooter(name, &miss); |
| 1451 |
1827 // Return the generated code. | 1452 // Return the generated code. |
1828 return GetCode(kind(), Code::NORMAL, name); | 1453 return GetCode(kind(), Code::NORMAL, name); |
1829 } | 1454 } |
1830 | 1455 |
1831 | 1456 |
1832 Handle<Code> BaseLoadStoreStubCompiler::CompilePolymorphicIC( | 1457 Handle<Code> BaseLoadStoreStubCompiler::CompilePolymorphicIC( |
1833 TypeHandleList* types, | 1458 TypeHandleList* types, |
1834 CodeHandleList* handlers, | 1459 CodeHandleList* handlers, |
1835 Handle<Name> name, | 1460 Handle<Name> name, |
1836 Code::StubType type, | 1461 Code::StubType type, |
(...skipping 115 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1952 // ----------------------------------- | 1577 // ----------------------------------- |
1953 TailCallBuiltin(masm, Builtins::kKeyedLoadIC_Miss); | 1578 TailCallBuiltin(masm, Builtins::kKeyedLoadIC_Miss); |
1954 } | 1579 } |
1955 | 1580 |
1956 | 1581 |
1957 #undef __ | 1582 #undef __ |
1958 | 1583 |
1959 } } // namespace v8::internal | 1584 } } // namespace v8::internal |
1960 | 1585 |
1961 #endif // V8_TARGET_ARCH_MIPS | 1586 #endif // V8_TARGET_ARCH_MIPS |
OLD | NEW |