1// Copyright 2011 the V8 project authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5"use strict";
6
7// This file relies on the fact that the following declaration has been made
8// in runtime.js:
9// var $Object = global.Object;
10
11var $Proxy = new $Object();
12
13// -------------------------------------------------------------------
14
15function ProxyCreate(handler, proto) {
16  if (!IS_SPEC_OBJECT(handler))
17    throw MakeTypeError("handler_non_object", ["create"])
18  if (IS_UNDEFINED(proto))
19    proto = null
20  else if (!(IS_SPEC_OBJECT(proto) || IS_NULL(proto)))
21    throw MakeTypeError("proto_non_object", ["create"])
22  return %CreateJSProxy(handler, proto)
23}
24
25function ProxyCreateFunction(handler, callTrap, constructTrap) {
26  if (!IS_SPEC_OBJECT(handler))
27    throw MakeTypeError("handler_non_object", ["create"])
28  if (!IS_SPEC_FUNCTION(callTrap))
29    throw MakeTypeError("trap_function_expected", ["createFunction", "call"])
30  if (IS_UNDEFINED(constructTrap)) {
31    constructTrap = DerivedConstructTrap(callTrap)
32  } else if (IS_SPEC_FUNCTION(constructTrap)) {
33    // Make sure the trap receives 'undefined' as this.
34    var construct = constructTrap
35    constructTrap = function() {
36      return %Apply(construct, UNDEFINED, arguments, 0, %_ArgumentsLength());
37    }
38  } else {
39    throw MakeTypeError("trap_function_expected",
40                        ["createFunction", "construct"])
41  }
42  return %CreateJSFunctionProxy(
43    handler, callTrap, constructTrap, $Function.prototype)
44}
45
46
47// -------------------------------------------------------------------
48
49function SetUpProxy() {
50  %CheckIsBootstrapping()
51
52  var global_proxy = %GlobalProxy(global);
53  global_proxy.Proxy = $Proxy;
54
55  // Set up non-enumerable properties of the Proxy object.
56  InstallFunctions($Proxy, DONT_ENUM, [
57    "create", ProxyCreate,
58    "createFunction", ProxyCreateFunction
59  ])
60}
61
62SetUpProxy();
63
64
65// -------------------------------------------------------------------
66// Proxy Builtins
67
68function DerivedConstructTrap(callTrap) {
69  return function() {
70    var proto = this.prototype
71    if (!IS_SPEC_OBJECT(proto)) proto = $Object.prototype
72    var obj = { __proto__: proto };
73    var result = %Apply(callTrap, obj, arguments, 0, %_ArgumentsLength());
74    return IS_SPEC_OBJECT(result) ? result : obj
75  }
76}
77
78function DelegateCallAndConstruct(callTrap, constructTrap) {
79  return function() {
80    return %Apply(%_IsConstructCall() ? constructTrap : callTrap,
81                  this, arguments, 0, %_ArgumentsLength())
82  }
83}
84
85function DerivedGetTrap(receiver, name) {
86  var desc = this.getPropertyDescriptor(name)
87  if (IS_UNDEFINED(desc)) { return desc }
88  if ('value' in desc) {
89    return desc.value
90  } else {
91    if (IS_UNDEFINED(desc.get)) { return desc.get }
92    // The proposal says: desc.get.call(receiver)
93    return %_CallFunction(receiver, desc.get)
94  }
95}
96
97function DerivedSetTrap(receiver, name, val) {
98  var desc = this.getOwnPropertyDescriptor(name)
99  if (desc) {
100    if ('writable' in desc) {
101      if (desc.writable) {
102        desc.value = val
103        this.defineProperty(name, desc)
104        return true
105      } else {
106        return false
107      }
108    } else { // accessor
109      if (desc.set) {
110        // The proposal says: desc.set.call(receiver, val)
111        %_CallFunction(receiver, val, desc.set)
112        return true
113      } else {
114        return false
115      }
116    }
117  }
118  desc = this.getPropertyDescriptor(name)
119  if (desc) {
120    if ('writable' in desc) {
121      if (desc.writable) {
122        // fall through
123      } else {
124        return false
125      }
126    } else { // accessor
127      if (desc.set) {
128        // The proposal says: desc.set.call(receiver, val)
129        %_CallFunction(receiver, val, desc.set)
130        return true
131      } else {
132        return false
133      }
134    }
135  }
136  this.defineProperty(name, {
137    value: val,
138    writable: true,
139    enumerable: true,
140    configurable: true});
141  return true;
142}
143
144function DerivedHasTrap(name) {
145  return !!this.getPropertyDescriptor(name)
146}
147
148function DerivedHasOwnTrap(name) {
149  return !!this.getOwnPropertyDescriptor(name)
150}
151
152function DerivedKeysTrap() {
153  var names = this.getOwnPropertyNames()
154  var enumerableNames = []
155  for (var i = 0, count = 0; i < names.length; ++i) {
156    var name = names[i]
157    if (IS_SYMBOL(name)) continue
158    var desc = this.getOwnPropertyDescriptor(TO_STRING_INLINE(name))
159    if (!IS_UNDEFINED(desc) && desc.enumerable) {
160      enumerableNames[count++] = names[i]
161    }
162  }
163  return enumerableNames
164}
165
166function DerivedEnumerateTrap() {
167  var names = this.getPropertyNames()
168  var enumerableNames = []
169  for (var i = 0, count = 0; i < names.length; ++i) {
170    var name = names[i]
171    if (IS_SYMBOL(name)) continue
172    var desc = this.getPropertyDescriptor(TO_STRING_INLINE(name))
173    if (!IS_UNDEFINED(desc)) {
174      if (!desc.configurable) {
175        throw MakeTypeError("proxy_prop_not_configurable",
176            [this, "getPropertyDescriptor", name])
177      }
178      if (desc.enumerable) enumerableNames[count++] = names[i]
179    }
180  }
181  return enumerableNames
182}
183
184function ProxyEnumerate(proxy) {
185  var handler = %GetHandler(proxy)
186  if (IS_UNDEFINED(handler.enumerate)) {
187    return %Apply(DerivedEnumerateTrap, handler, [], 0, 0)
188  } else {
189    return ToNameArray(handler.enumerate(), "enumerate", false)
190  }
191}
192