1from __future__ import unicode_literals
2from ..conversions import *
3from ..func_utils import *
4from ..base import is_data_descriptor
5import six
6
7
8def Object(this, args):
9    val = get_arg(args, 0)
10    if is_null(val) or is_undefined(val):
11        return args.space.NewObject()
12    return to_object(val, args.space)
13
14
15def ObjectCreate(args, space):
16    if len(args):
17        val = get_arg(args, 0)
18        if is_object(val):
19            #  Implementation dependent, but my will simply return :)
20            return val
21        elif type(val) in (NUMBER_TYPE, STRING_TYPE, BOOLEAN_TYPE):
22            return to_object(val, space)
23    return space.NewObject()
24
25
26class ObjectMethods:
27    def getPrototypeOf(this, args):
28        obj = get_arg(args, 0)
29        if not is_object(obj):
30            raise MakeError('TypeError',
31                            'Object.getPrototypeOf called on non-object')
32        return null if obj.prototype is None else obj.prototype
33
34    def getOwnPropertyDescriptor(this, args):
35        obj = get_arg(args, 0)
36        prop = get_arg(args, 1)
37        if not is_object(obj):
38            raise MakeError(
39                'TypeError',
40                'Object.getOwnPropertyDescriptor called on non-object')
41        desc = obj.own.get(to_string(prop))
42        return convert_to_js_type(desc, args.space)
43
44    def getOwnPropertyNames(this, args):
45        obj = get_arg(args, 0)
46        if not is_object(obj):
47            raise MakeError(
48                'TypeError',
49                'Object.getOwnPropertyDescriptor called on non-object')
50        return args.space.ConstructArray(obj.own.keys())
51
52    def create(this, args):
53        obj = get_arg(args, 0)
54        if not (is_object(obj) or is_null(obj)):
55            raise MakeError('TypeError',
56                            'Object prototype may only be an Object or null')
57        temp = args.space.NewObject()
58        temp.prototype = None if is_null(obj) else obj
59        if len(args) > 1 and not is_undefined(args[1]):
60            if six.PY2:
61                args.tup = (args[1], )
62                ObjectMethods.defineProperties.__func__(temp, args)
63            else:
64                args.tup = (args[1], )
65                ObjectMethods.defineProperties(temp, args)
66        return temp
67
68    def defineProperty(this, args):
69        obj = get_arg(args, 0)
70        prop = get_arg(args, 1)
71        attrs = get_arg(args, 2)
72        if not is_object(obj):
73            raise MakeError('TypeError',
74                            'Object.defineProperty called on non-object')
75        name = to_string(prop)
76        if not obj.define_own_property(name, ToPropertyDescriptor(attrs),
77                                       False):
78            raise MakeError('TypeError', 'Cannot redefine property: %s' % name)
79        return obj
80
81    def defineProperties(this, args):
82        obj = get_arg(args, 0)
83        properties = get_arg(args, 1)
84        if not is_object(obj):
85            raise MakeError('TypeError',
86                            'Object.defineProperties called on non-object')
87        props = to_object(properties, args.space)
88        for k, v in props.own.items():
89            if not v.get('enumerable'):
90                continue
91            desc = ToPropertyDescriptor(props.get(unicode(k)))
92            if not obj.define_own_property(unicode(k), desc, False):
93                raise MakeError('TypeError',
94                                'Failed to define own property: %s' % k)
95        return obj
96
97    def seal(this, args):
98        obj = get_arg(args, 0)
99        if not is_object(obj):
100            raise MakeError('TypeError', 'Object.seal called on non-object')
101        for desc in obj.own.values():
102            desc['configurable'] = False
103        obj.extensible = False
104        return obj
105
106    def freeze(this, args):
107        obj = get_arg(args, 0)
108        if not is_object(obj):
109            raise MakeError('TypeError', 'Object.freeze called on non-object')
110        for desc in obj.own.values():
111            desc['configurable'] = False
112            if is_data_descriptor(desc):
113                desc['writable'] = False
114        obj.extensible = False
115        return obj
116
117    def preventExtensions(this, args):
118        obj = get_arg(args, 0)
119        if not is_object(obj):
120            raise MakeError('TypeError',
121                            'Object.preventExtensions on non-object')
122        obj.extensible = False
123        return obj
124
125    def isSealed(this, args):
126        obj = get_arg(args, 0)
127        if not is_object(obj):
128            raise MakeError('TypeError',
129                            'Object.isSealed called on non-object')
130        if obj.extensible:
131            return False
132        for desc in obj.own.values():
133            if desc.get('configurable'):
134                return False
135        return True
136
137    def isFrozen(this, args):
138        obj = get_arg(args, 0)
139        if not is_object(obj):
140            raise MakeError('TypeError',
141                            'Object.isFrozen called on non-object')
142        if obj.extensible:
143            return False
144        for desc in obj.own.values():
145            if desc.get('configurable'):
146                return False
147            if is_data_descriptor(desc) and desc.get('writable'):
148                return False
149        return True
150
151    def isExtensible(this, args):
152        obj = get_arg(args, 0)
153        if not is_object(obj):
154            raise MakeError('TypeError',
155                            'Object.isExtensible called on non-object')
156        return obj.extensible
157
158    def keys(this, args):
159        obj = get_arg(args, 0)
160        if not is_object(obj):
161            raise MakeError('TypeError', 'Object.keys called on non-object')
162        return args.space.ConstructArray([
163            unicode(e) for e, d in six.iteritems(obj.own)
164            if d.get('enumerable')
165        ])
166
167
168# some utility functions:
169
170
171def ToPropertyDescriptor(obj):  # page 38 (50 absolute)
172    if not is_object(obj):
173        raise MakeError('TypeError',
174                        'Can\'t convert non-object to property descriptor')
175    desc = {}
176    if obj.has_property('enumerable'):
177        desc['enumerable'] = to_boolean(obj.get('enumerable'))
178    if obj.has_property('configurable'):
179        desc['configurable'] = to_boolean(obj.get('configurable'))
180    if obj.has_property('value'):
181        desc['value'] = obj.get('value')
182    if obj.has_property('writable'):
183        desc['writable'] = to_boolean(obj.get('writable'))
184    if obj.has_property('get'):
185        cand = obj.get('get')
186        if not (is_undefined(cand) or is_callable(cand)):
187            raise MakeError(
188                'TypeError',
189                'Invalid getter (it has to be a function or undefined)')
190        desc['get'] = cand
191    if obj.has_property('set'):
192        cand = obj.get('set')
193        if not (is_undefined(cand) or is_callable(cand)):
194            raise MakeError(
195                'TypeError',
196                'Invalid setter (it has to be a function or undefined)')
197        desc['set'] = cand
198    if ('get' in desc or 'set' in desc) and ('value' in desc
199                                             or 'writable' in desc):
200        raise MakeError(
201            'TypeError',
202            'Invalid property.  A property cannot both have accessors and be writable or have a value.'
203        )
204    return desc
205