1// Copyright 2011 the V8 project authors. All rights reserved.
2// Redistribution and use in source and binary forms, with or without
3// modification, are permitted provided that the following conditions are
4// met:
5//
6//     * Redistributions of source code must retain the above copyright
7//       notice, this list of conditions and the following disclaimer.
8//     * Redistributions in binary form must reproduce the above
9//       copyright notice, this list of conditions and the following
10//       disclaimer in the documentation and/or other materials provided
11//       with the distribution.
12//     * Neither the name of Google Inc. nor the names of its
13//       contributors may be used to endorse or promote products derived
14//       from this software without specific prior written permission.
15//
16// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27
28"use strict";
29
30// This file relies on the fact that the following declaration has been made
31// in runtime.js:
32// var $Object = global.Object;
33
34var $Proxy = new $Object();
35
36// -------------------------------------------------------------------
37
38function ProxyCreate(handler, proto) {
39  if (!IS_SPEC_OBJECT(handler))
40    throw MakeTypeError("handler_non_object", ["create"])
41  if (IS_UNDEFINED(proto))
42    proto = null
43  else if (!(IS_SPEC_OBJECT(proto) || IS_NULL(proto)))
44    throw MakeTypeError("proto_non_object", ["create"])
45  return %CreateJSProxy(handler, proto)
46}
47
48function ProxyCreateFunction(handler, callTrap, constructTrap) {
49  if (!IS_SPEC_OBJECT(handler))
50    throw MakeTypeError("handler_non_object", ["create"])
51  if (!IS_SPEC_FUNCTION(callTrap))
52    throw MakeTypeError("trap_function_expected", ["createFunction", "call"])
53  if (IS_UNDEFINED(constructTrap)) {
54    constructTrap = DerivedConstructTrap(callTrap)
55  } else if (IS_SPEC_FUNCTION(constructTrap)) {
56    // Make sure the trap receives 'undefined' as this.
57    var construct = constructTrap
58    constructTrap = function() {
59      return %Apply(construct, UNDEFINED, arguments, 0, %_ArgumentsLength());
60    }
61  } else {
62    throw MakeTypeError("trap_function_expected",
63                        ["createFunction", "construct"])
64  }
65  return %CreateJSFunctionProxy(
66    handler, callTrap, constructTrap, $Function.prototype)
67}
68
69
70// -------------------------------------------------------------------
71
72function SetUpProxy() {
73  %CheckIsBootstrapping()
74
75  global.Proxy = $Proxy;
76
77  // Set up non-enumerable properties of the Proxy object.
78  InstallFunctions($Proxy, DONT_ENUM, [
79    "create", ProxyCreate,
80    "createFunction", ProxyCreateFunction
81  ])
82}
83
84SetUpProxy();
85
86
87// -------------------------------------------------------------------
88// Proxy Builtins
89
90function DerivedConstructTrap(callTrap) {
91  return function() {
92    var proto = this.prototype
93    if (!IS_SPEC_OBJECT(proto)) proto = $Object.prototype
94    var obj = { __proto__: proto };
95    var result = %Apply(callTrap, obj, arguments, 0, %_ArgumentsLength());
96    return IS_SPEC_OBJECT(result) ? result : obj
97  }
98}
99
100function DelegateCallAndConstruct(callTrap, constructTrap) {
101  return function() {
102    return %Apply(%_IsConstructCall() ? constructTrap : callTrap,
103                  this, arguments, 0, %_ArgumentsLength())
104  }
105}
106
107function DerivedGetTrap(receiver, name) {
108  var desc = this.getPropertyDescriptor(name)
109  if (IS_UNDEFINED(desc)) { return desc }
110  if ('value' in desc) {
111    return desc.value
112  } else {
113    if (IS_UNDEFINED(desc.get)) { return desc.get }
114    // The proposal says: desc.get.call(receiver)
115    return %_CallFunction(receiver, desc.get)
116  }
117}
118
119function DerivedSetTrap(receiver, name, val) {
120  var desc = this.getOwnPropertyDescriptor(name)
121  if (desc) {
122    if ('writable' in desc) {
123      if (desc.writable) {
124        desc.value = val
125        this.defineProperty(name, desc)
126        return true
127      } else {
128        return false
129      }
130    } else { // accessor
131      if (desc.set) {
132        // The proposal says: desc.set.call(receiver, val)
133        %_CallFunction(receiver, val, desc.set)
134        return true
135      } else {
136        return false
137      }
138    }
139  }
140  desc = this.getPropertyDescriptor(name)
141  if (desc) {
142    if ('writable' in desc) {
143      if (desc.writable) {
144        // fall through
145      } else {
146        return false
147      }
148    } else { // accessor
149      if (desc.set) {
150        // The proposal says: desc.set.call(receiver, val)
151        %_CallFunction(receiver, val, desc.set)
152        return true
153      } else {
154        return false
155      }
156    }
157  }
158  this.defineProperty(name, {
159    value: val,
160    writable: true,
161    enumerable: true,
162    configurable: true});
163  return true;
164}
165
166function DerivedHasTrap(name) {
167  return !!this.getPropertyDescriptor(name)
168}
169
170function DerivedHasOwnTrap(name) {
171  return !!this.getOwnPropertyDescriptor(name)
172}
173
174function DerivedKeysTrap() {
175  var names = this.getOwnPropertyNames()
176  var enumerableNames = []
177  for (var i = 0, count = 0; i < names.length; ++i) {
178    var name = names[i]
179    if (IS_SYMBOL(name)) continue
180    var desc = this.getOwnPropertyDescriptor(TO_STRING_INLINE(name))
181    if (!IS_UNDEFINED(desc) && desc.enumerable) {
182      enumerableNames[count++] = names[i]
183    }
184  }
185  return enumerableNames
186}
187
188function DerivedEnumerateTrap() {
189  var names = this.getPropertyNames()
190  var enumerableNames = []
191  for (var i = 0, count = 0; i < names.length; ++i) {
192    var name = names[i]
193    if (IS_SYMBOL(name)) continue
194    var desc = this.getPropertyDescriptor(TO_STRING_INLINE(name))
195    if (!IS_UNDEFINED(desc)) {
196      if (!desc.configurable) {
197        throw MakeTypeError("proxy_prop_not_configurable",
198            [this, "getPropertyDescriptor", name])
199      }
200      if (desc.enumerable) enumerableNames[count++] = names[i]
201    }
202  }
203  return enumerableNames
204}
205
206function ProxyEnumerate(proxy) {
207  var handler = %GetHandler(proxy)
208  if (IS_UNDEFINED(handler.enumerate)) {
209    return %Apply(DerivedEnumerateTrap, handler, [], 0, 0)
210  } else {
211    return ToNameArray(handler.enumerate(), "enumerate", false)
212  }
213}
214