OLD | NEW |
1 // Copyright 2011 the V8 project authors. All rights reserved. | 1 // Copyright 2011 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 355 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
366 | 366 |
367 // ES5 8.10.2. | 367 // ES5 8.10.2. |
368 function IsDataDescriptor(desc) { | 368 function IsDataDescriptor(desc) { |
369 if (IS_UNDEFINED(desc)) return false; | 369 if (IS_UNDEFINED(desc)) return false; |
370 return desc.hasValue() || desc.hasWritable(); | 370 return desc.hasValue() || desc.hasWritable(); |
371 } | 371 } |
372 | 372 |
373 | 373 |
374 // ES5 8.10.3. | 374 // ES5 8.10.3. |
375 function IsGenericDescriptor(desc) { | 375 function IsGenericDescriptor(desc) { |
| 376 if (IS_UNDEFINED(desc)) return false; |
376 return !(IsAccessorDescriptor(desc) || IsDataDescriptor(desc)); | 377 return !(IsAccessorDescriptor(desc) || IsDataDescriptor(desc)); |
377 } | 378 } |
378 | 379 |
379 | 380 |
380 function IsInconsistentDescriptor(desc) { | 381 function IsInconsistentDescriptor(desc) { |
381 return IsAccessorDescriptor(desc) && IsDataDescriptor(desc); | 382 return IsAccessorDescriptor(desc) && IsDataDescriptor(desc); |
382 } | 383 } |
383 | 384 |
384 | 385 |
385 // ES5 8.10.4 | 386 // ES5 8.10.4 |
(...skipping 311 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
697 | 698 |
698 var current = ConvertDescriptorArrayToDescriptor(current_or_access); | 699 var current = ConvertDescriptorArrayToDescriptor(current_or_access); |
699 var extensible = %IsExtensible(ToObject(obj)); | 700 var extensible = %IsExtensible(ToObject(obj)); |
700 | 701 |
701 // Error handling according to spec. | 702 // Error handling according to spec. |
702 // Step 3 | 703 // Step 3 |
703 if (IS_UNDEFINED(current) && !extensible) { | 704 if (IS_UNDEFINED(current) && !extensible) { |
704 if (should_throw) { | 705 if (should_throw) { |
705 throw MakeTypeError("define_disallowed", [p]); | 706 throw MakeTypeError("define_disallowed", [p]); |
706 } else { | 707 } else { |
707 return; | 708 return false; |
708 } | 709 } |
709 } | 710 } |
710 | 711 |
711 if (!IS_UNDEFINED(current)) { | 712 if (!IS_UNDEFINED(current)) { |
712 // Step 5 and 6 | 713 // Step 5 and 6 |
713 if ((IsGenericDescriptor(desc) || | 714 if ((IsGenericDescriptor(desc) || |
714 IsDataDescriptor(desc) == IsDataDescriptor(current)) && | 715 IsDataDescriptor(desc) == IsDataDescriptor(current)) && |
715 (!desc.hasEnumerable() || | 716 (!desc.hasEnumerable() || |
716 SameValue(desc.isEnumerable(), current.isEnumerable())) && | 717 SameValue(desc.isEnumerable(), current.isEnumerable())) && |
717 (!desc.hasConfigurable() || | 718 (!desc.hasConfigurable() || |
718 SameValue(desc.isConfigurable(), current.isConfigurable())) && | 719 SameValue(desc.isConfigurable(), current.isConfigurable())) && |
719 (!desc.hasWritable() || | 720 (!desc.hasWritable() || |
720 SameValue(desc.isWritable(), current.isWritable())) && | 721 SameValue(desc.isWritable(), current.isWritable())) && |
721 (!desc.hasValue() || | 722 (!desc.hasValue() || |
722 SameValue(desc.getValue(), current.getValue())) && | 723 SameValue(desc.getValue(), current.getValue())) && |
723 (!desc.hasGetter() || | 724 (!desc.hasGetter() || |
724 SameValue(desc.getGet(), current.getGet())) && | 725 SameValue(desc.getGet(), current.getGet())) && |
725 (!desc.hasSetter() || | 726 (!desc.hasSetter() || |
726 SameValue(desc.getSet(), current.getSet()))) { | 727 SameValue(desc.getSet(), current.getSet()))) { |
727 return true; | 728 return true; |
728 } | 729 } |
729 if (!current.isConfigurable()) { | 730 if (!current.isConfigurable()) { |
730 // Step 7 | 731 // Step 7 |
731 if (desc.isConfigurable() || | 732 if (desc.isConfigurable() || |
732 (desc.hasEnumerable() && | 733 (desc.hasEnumerable() && |
733 desc.isEnumerable() != current.isEnumerable())) { | 734 desc.isEnumerable() != current.isEnumerable())) { |
734 if (should_throw) { | 735 if (should_throw) { |
735 throw MakeTypeError("redefine_disallowed", [p]); | 736 throw MakeTypeError("redefine_disallowed", [p]); |
736 } else { | 737 } else { |
737 return; | 738 return false; |
738 } | 739 } |
739 } | 740 } |
740 // Step 8 | 741 // Step 8 |
741 if (!IsGenericDescriptor(desc)) { | 742 if (!IsGenericDescriptor(desc)) { |
742 // Step 9a | 743 // Step 9a |
743 if (IsDataDescriptor(current) != IsDataDescriptor(desc)) { | 744 if (IsDataDescriptor(current) != IsDataDescriptor(desc)) { |
744 if (should_throw) { | 745 if (should_throw) { |
745 throw MakeTypeError("redefine_disallowed", [p]); | 746 throw MakeTypeError("redefine_disallowed", [p]); |
746 } else { | 747 } else { |
747 return; | 748 return false; |
748 } | 749 } |
749 } | 750 } |
750 // Step 10a | 751 // Step 10a |
751 if (IsDataDescriptor(current) && IsDataDescriptor(desc)) { | 752 if (IsDataDescriptor(current) && IsDataDescriptor(desc)) { |
752 if (!current.isWritable() && desc.isWritable()) { | 753 if (!current.isWritable() && desc.isWritable()) { |
753 if (should_throw) { | 754 if (should_throw) { |
754 throw MakeTypeError("redefine_disallowed", [p]); | 755 throw MakeTypeError("redefine_disallowed", [p]); |
755 } else { | 756 } else { |
756 return; | 757 return false; |
757 } | 758 } |
758 } | 759 } |
759 if (!current.isWritable() && desc.hasValue() && | 760 if (!current.isWritable() && desc.hasValue() && |
760 !SameValue(desc.getValue(), current.getValue())) { | 761 !SameValue(desc.getValue(), current.getValue())) { |
761 if (should_throw) { | 762 if (should_throw) { |
762 throw MakeTypeError("redefine_disallowed", [p]); | 763 throw MakeTypeError("redefine_disallowed", [p]); |
763 } else { | 764 } else { |
764 return; | 765 return false; |
765 } | 766 } |
766 } | 767 } |
767 } | 768 } |
768 // Step 11 | 769 // Step 11 |
769 if (IsAccessorDescriptor(desc) && IsAccessorDescriptor(current)) { | 770 if (IsAccessorDescriptor(desc) && IsAccessorDescriptor(current)) { |
770 if (desc.hasSetter() && !SameValue(desc.getSet(), current.getSet())) { | 771 if (desc.hasSetter() && !SameValue(desc.getSet(), current.getSet())) { |
771 if (should_throw) { | 772 if (should_throw) { |
772 throw MakeTypeError("redefine_disallowed", [p]); | 773 throw MakeTypeError("redefine_disallowed", [p]); |
773 } else { | 774 } else { |
774 return; | 775 return false; |
775 } | 776 } |
776 } | 777 } |
777 if (desc.hasGetter() && !SameValue(desc.getGet(),current.getGet())) { | 778 if (desc.hasGetter() && !SameValue(desc.getGet(),current.getGet())) { |
778 if (should_throw) { | 779 if (should_throw) { |
779 throw MakeTypeError("redefine_disallowed", [p]); | 780 throw MakeTypeError("redefine_disallowed", [p]); |
780 } else { | 781 } else { |
781 return; | 782 return false; |
782 } | 783 } |
783 } | 784 } |
784 } | 785 } |
785 } | 786 } |
786 } | 787 } |
787 } | 788 } |
788 | 789 |
789 // Send flags - enumerable and configurable are common - writable is | 790 // Send flags - enumerable and configurable are common - writable is |
790 // only send to the data descriptor. | 791 // only send to the data descriptor. |
791 // Take special care if enumerable and configurable is not defined on | 792 // Take special care if enumerable and configurable is not defined on |
(...skipping 82 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
874 } | 875 } |
875 | 876 |
876 // Step 4 - Special handling for array index. | 877 // Step 4 - Special handling for array index. |
877 var index = ToUint32(p); | 878 var index = ToUint32(p); |
878 if (index == ToNumber(p) && index != 4294967295) { | 879 if (index == ToNumber(p) && index != 4294967295) { |
879 if ((index >= length && !length_desc.isWritable()) || | 880 if ((index >= length && !length_desc.isWritable()) || |
880 !DefineObjectProperty(obj, p, desc, true)) { | 881 !DefineObjectProperty(obj, p, desc, true)) { |
881 if (should_throw) { | 882 if (should_throw) { |
882 throw MakeTypeError("define_disallowed", [p]); | 883 throw MakeTypeError("define_disallowed", [p]); |
883 } else { | 884 } else { |
884 return; | 885 return false; |
885 } | 886 } |
886 } | 887 } |
887 if (index >= length) { | 888 if (index >= length) { |
888 // TODO(mstarzinger): We should actually set the value of the property | 889 // TODO(mstarzinger): We should actually set the value of the property |
889 // descriptor here and pass it to DefineObjectProperty(). Take a look at | 890 // descriptor here and pass it to DefineObjectProperty(). Take a look at |
890 // ES5 section 15.4.5.1, step 4.e.i and 4.e.ii for details. | 891 // ES5 section 15.4.5.1, step 4.e.i and 4.e.ii for details. |
891 obj.length = index + 1; | 892 obj.length = index + 1; |
892 } | 893 } |
893 return true; | 894 return true; |
894 } | 895 } |
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
929 } | 930 } |
930 | 931 |
931 | 932 |
932 // For Harmony proxies | 933 // For Harmony proxies |
933 function ToStringArray(obj, trap) { | 934 function ToStringArray(obj, trap) { |
934 if (!IS_SPEC_OBJECT(obj)) { | 935 if (!IS_SPEC_OBJECT(obj)) { |
935 throw MakeTypeError("proxy_non_object_prop_names", [obj, trap]); | 936 throw MakeTypeError("proxy_non_object_prop_names", [obj, trap]); |
936 } | 937 } |
937 var n = ToUint32(obj.length); | 938 var n = ToUint32(obj.length); |
938 var array = new $Array(n); | 939 var array = new $Array(n); |
939 var names = {} | 940 var names = {} // TODO(rossberg): use sets once they are ready. |
940 for (var index = 0; index < n; index++) { | 941 for (var index = 0; index < n; index++) { |
941 var s = ToString(obj[index]); | 942 var s = ToString(obj[index]); |
942 if (s in names) { | 943 if (s in names) { |
943 throw MakeTypeError("proxy_repeated_prop_name", [obj, trap, s]) | 944 throw MakeTypeError("proxy_repeated_prop_name", [obj, trap, s]) |
944 } | 945 } |
945 array[index] = s; | 946 array[index] = s; |
946 names.s = 0; | 947 names[s] = 0; |
947 } | 948 } |
948 return array; | 949 return array; |
949 } | 950 } |
950 | 951 |
951 | 952 |
952 // ES5 section 15.2.3.4. | 953 // ES5 section 15.2.3.4. |
953 function ObjectGetOwnPropertyNames(obj) { | 954 function ObjectGetOwnPropertyNames(obj) { |
954 if (!IS_SPEC_OBJECT(obj)) | 955 if (!IS_SPEC_OBJECT(obj)) |
955 throw MakeTypeError("obj_ctor_property_non_object", ["getOwnPropertyNames"])
; | 956 throw MakeTypeError("obj_ctor_property_non_object", ["getOwnPropertyNames"])
; |
956 | 957 |
(...skipping 114 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1071 return names; | 1072 return names; |
1072 } | 1073 } |
1073 | 1074 |
1074 | 1075 |
1075 // ES5 section 15.2.3.7. | 1076 // ES5 section 15.2.3.7. |
1076 function ObjectDefineProperties(obj, properties) { | 1077 function ObjectDefineProperties(obj, properties) { |
1077 if (!IS_SPEC_OBJECT(obj)) | 1078 if (!IS_SPEC_OBJECT(obj)) |
1078 throw MakeTypeError("obj_ctor_property_non_object", ["defineProperties"]); | 1079 throw MakeTypeError("obj_ctor_property_non_object", ["defineProperties"]); |
1079 var props = ToObject(properties); | 1080 var props = ToObject(properties); |
1080 var names = GetOwnEnumerablePropertyNames(props); | 1081 var names = GetOwnEnumerablePropertyNames(props); |
| 1082 var descriptors = new InternalArray(); |
1081 for (var i = 0; i < names.length; i++) { | 1083 for (var i = 0; i < names.length; i++) { |
1082 var name = names[i]; | 1084 descriptors.push(ToPropertyDescriptor(props[names[i]])); |
1083 var desc = ToPropertyDescriptor(props[name]); | 1085 } |
1084 DefineOwnProperty(obj, name, desc, true); | 1086 for (var i = 0; i < names.length; i++) { |
| 1087 DefineOwnProperty(obj, names[i], descriptors[i], true); |
1085 } | 1088 } |
1086 return obj; | 1089 return obj; |
1087 } | 1090 } |
1088 | 1091 |
1089 | 1092 |
1090 // Harmony proxies. | 1093 // Harmony proxies. |
1091 function ProxyFix(obj) { | 1094 function ProxyFix(obj) { |
1092 var handler = %GetHandler(obj); | 1095 var handler = %GetHandler(obj); |
1093 var props = CallTrap0(handler, "fix", void 0); | 1096 var props = CallTrap0(handler, "fix", void 0); |
1094 if (IS_UNDEFINED(props)) { | 1097 if (IS_UNDEFINED(props)) { |
(...skipping 415 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1510 | 1513 |
1511 | 1514 |
1512 function FunctionToString() { | 1515 function FunctionToString() { |
1513 return FunctionSourceString(this); | 1516 return FunctionSourceString(this); |
1514 } | 1517 } |
1515 | 1518 |
1516 | 1519 |
1517 // ES5 15.3.4.5 | 1520 // ES5 15.3.4.5 |
1518 function FunctionBind(this_arg) { // Length is 1. | 1521 function FunctionBind(this_arg) { // Length is 1. |
1519 if (!IS_SPEC_FUNCTION(this)) { | 1522 if (!IS_SPEC_FUNCTION(this)) { |
1520 throw new $TypeError('Bind must be called on a function'); | 1523 throw new $TypeError('Bind must be called on a function'); |
1521 } | 1524 } |
1522 // this_arg is not an argument that should be bound. | 1525 var boundFunction = function () { |
1523 var argc_bound = (%_ArgumentsLength() || 1) - 1; | 1526 // Poison .arguments and .caller, but is otherwise not detectable. |
1524 var fn = this; | 1527 "use strict"; |
| 1528 // This function must not use any object literals (Object, Array, RegExp), |
| 1529 // since the literals-array is being used to store the bound data. |
| 1530 if (%_IsConstructCall()) { |
| 1531 return %NewObjectFromBound(boundFunction); |
| 1532 } |
| 1533 var bindings = %BoundFunctionGetBindings(boundFunction); |
1525 | 1534 |
1526 if (argc_bound == 0) { | 1535 var argc = %_ArgumentsLength(); |
1527 var result = function() { | 1536 if (argc == 0) { |
1528 if (%_IsConstructCall()) { | 1537 return %Apply(bindings[0], bindings[1], bindings, 2, bindings.length - 2); |
1529 // %NewObjectFromBound implicitly uses arguments passed to this | |
1530 // function. We do not pass the arguments object explicitly to avoid | |
1531 // materializing it and guarantee that this function will be optimized. | |
1532 return %NewObjectFromBound(fn, null); | |
1533 } | |
1534 return %Apply(fn, this_arg, arguments, 0, %_ArgumentsLength()); | |
1535 }; | |
1536 } else { | |
1537 var bound_args = new InternalArray(argc_bound); | |
1538 for(var i = 0; i < argc_bound; i++) { | |
1539 bound_args[i] = %_Arguments(i+1); | |
1540 } | 1538 } |
| 1539 if (bindings.length === 2) { |
| 1540 return %Apply(bindings[0], bindings[1], arguments, 0, argc); |
| 1541 } |
| 1542 var bound_argc = bindings.length - 2; |
| 1543 var argv = new InternalArray(bound_argc + argc); |
| 1544 for (var i = 0; i < bound_argc; i++) { |
| 1545 argv[i] = bindings[i + 2]; |
| 1546 } |
| 1547 for (var j = 0; j < argc; j++) { |
| 1548 argv[i++] = %_Arguments(j); |
| 1549 } |
| 1550 return %Apply(bindings[0], bindings[1], argv, 0, bound_argc + argc); |
| 1551 }; |
1541 | 1552 |
1542 var result = function() { | 1553 %FunctionRemovePrototype(boundFunction); |
1543 // If this is a construct call we use a special runtime method | 1554 var new_length = 0; |
1544 // to generate the actual object using the bound function. | 1555 if (%_ClassOf(this) == "Function") { |
1545 if (%_IsConstructCall()) { | 1556 // Function or FunctionProxy. |
1546 // %NewObjectFromBound implicitly uses arguments passed to this | 1557 var old_length = this.length; |
1547 // function. We do not pass the arguments object explicitly to avoid | 1558 // FunctionProxies might provide a non-UInt32 value. If so, ignore it. |
1548 // materializing it and guarantee that this function will be optimized. | 1559 if ((typeof old_length === "number") && |
1549 return %NewObjectFromBound(fn, bound_args); | 1560 ((old_length >>> 0) === old_length)) { |
1550 } | |
1551 | |
1552 // Combine the args we got from the bind call with the args | |
1553 // given as argument to the invocation. | |
1554 var argc = %_ArgumentsLength(); | 1561 var argc = %_ArgumentsLength(); |
1555 var args = new InternalArray(argc + argc_bound); | 1562 if (argc > 0) argc--; // Don't count the thisArg as parameter. |
1556 // Add bound arguments. | 1563 new_length = old_length - argc; |
1557 for (var i = 0; i < argc_bound; i++) { | 1564 if (new_length < 0) new_length = 0; |
1558 args[i] = bound_args[i]; | 1565 } |
1559 } | |
1560 // Add arguments from call. | |
1561 for (var i = 0; i < argc; i++) { | |
1562 args[argc_bound + i] = %_Arguments(i); | |
1563 } | |
1564 return %Apply(fn, this_arg, args, 0, argc + argc_bound); | |
1565 }; | |
1566 } | 1566 } |
| 1567 // This runtime function finds any remaining arguments on the stack, |
| 1568 // so we don't pass the arguments object. |
| 1569 var result = %FunctionBindArguments(boundFunction, this, this_arg, new_length)
; |
1567 | 1570 |
1568 // We already have caller and arguments properties on functions, | 1571 // We already have caller and arguments properties on functions, |
1569 // which are non-configurable. It therefore makes no sence to | 1572 // which are non-configurable. It therefore makes no sence to |
1570 // try to redefine these as defined by the spec. The spec says | 1573 // try to redefine these as defined by the spec. The spec says |
1571 // that bind should make these throw a TypeError if get or set | 1574 // that bind should make these throw a TypeError if get or set |
1572 // is called and make them non-enumerable and non-configurable. | 1575 // is called and make them non-enumerable and non-configurable. |
1573 // To be consistent with our normal functions we leave this as it is. | 1576 // To be consistent with our normal functions we leave this as it is. |
1574 | 1577 // TODO(lrn): Do set these to be thrower. |
1575 %FunctionRemovePrototype(result); | |
1576 %FunctionSetBound(result); | |
1577 // Set the correct length. If this is a function proxy, this.length might | |
1578 // throw, or return a bogus result. Leave length alone in that case. | |
1579 // TODO(rossberg): This is underspecified in the current proxy proposal. | |
1580 try { | |
1581 var old_length = ToInteger(this.length); | |
1582 var length = (old_length - argc_bound) > 0 ? old_length - argc_bound : 0; | |
1583 %BoundFunctionSetLength(result, length); | |
1584 } catch(x) {} | |
1585 return result; | 1578 return result; |
1586 } | 1579 } |
1587 | 1580 |
1588 | 1581 |
1589 function NewFunction(arg1) { // length == 1 | 1582 function NewFunction(arg1) { // length == 1 |
1590 var n = %_ArgumentsLength(); | 1583 var n = %_ArgumentsLength(); |
1591 var p = ''; | 1584 var p = ''; |
1592 if (n > 1) { | 1585 if (n > 1) { |
1593 p = new InternalArray(n - 1); | 1586 p = new InternalArray(n - 1); |
1594 for (var i = 0; i < n - 1; i++) p[i] = %_Arguments(i); | 1587 for (var i = 0; i < n - 1; i++) p[i] = %_Arguments(i); |
(...skipping 19 matching lines...) Expand all Loading... |
1614 | 1607 |
1615 function SetUpFunction() { | 1608 function SetUpFunction() { |
1616 %CheckIsBootstrapping(); | 1609 %CheckIsBootstrapping(); |
1617 InstallFunctions($Function.prototype, DONT_ENUM, $Array( | 1610 InstallFunctions($Function.prototype, DONT_ENUM, $Array( |
1618 "bind", FunctionBind, | 1611 "bind", FunctionBind, |
1619 "toString", FunctionToString | 1612 "toString", FunctionToString |
1620 )); | 1613 )); |
1621 } | 1614 } |
1622 | 1615 |
1623 SetUpFunction(); | 1616 SetUpFunction(); |
OLD | NEW |