proxy.js revision 3ef787dbeca8a5fb1086949cda830dccee07bfbd
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
30global.Proxy = new $Object();
31
32var $Proxy = global.Proxy
33
34$Proxy.create = function(handler, proto) {
35  if (!IS_SPEC_OBJECT(handler))
36    throw MakeTypeError("handler_non_object", ["create"])
37  if (IS_UNDEFINED(proto))
38    proto = null
39  else if (!(IS_SPEC_OBJECT(proto) || proto === null))
40    throw MakeTypeError("proto_non_object", ["create"])
41  return %CreateJSProxy(handler, proto)
42}
43
44$Proxy.createFunction = function(handler, callTrap, constructTrap) {
45  if (!IS_SPEC_OBJECT(handler))
46    throw MakeTypeError("handler_non_object", ["create"])
47  if (!IS_SPEC_FUNCTION(callTrap))
48    throw MakeTypeError("trap_function_expected", ["createFunction", "call"])
49  if (IS_UNDEFINED(constructTrap)) {
50    constructTrap = DerivedConstructTrap(callTrap)
51  } else if (IS_SPEC_FUNCTION(constructTrap)) {
52    // Make sure the trap receives 'undefined' as this.
53    var construct = constructTrap
54    constructTrap = function() {
55      return %Apply(construct, void 0, arguments, 0, %_ArgumentsLength());
56    }
57  } else {
58    throw MakeTypeError("trap_function_expected",
59                        ["createFunction", "construct"])
60  }
61  return %CreateJSFunctionProxy(
62    handler, callTrap, constructTrap, $Function.prototype)
63}
64
65
66
67////////////////////////////////////////////////////////////////////////////////
68// Builtins
69////////////////////////////////////////////////////////////////////////////////
70
71function DerivedConstructTrap(callTrap) {
72  return function() {
73    var proto = this.prototype
74    if (!IS_SPEC_OBJECT(proto)) proto = $Object.prototype
75    var obj = new $Object()
76    obj.__proto__ = proto
77    var result = %Apply(callTrap, obj, arguments, 0, %_ArgumentsLength());
78    return IS_SPEC_OBJECT(result) ? result : obj
79  }
80}
81
82function DelegateCallAndConstruct(callTrap, constructTrap) {
83  return function() {
84    return %Apply(%_IsConstructCall() ? constructTrap : callTrap,
85                  this, arguments, 0, %_ArgumentsLength())
86  }
87}
88
89function DerivedGetTrap(receiver, name) {
90  var desc = this.getPropertyDescriptor(name)
91  if (IS_UNDEFINED(desc)) { return desc }
92  if ('value' in desc) {
93    return desc.value
94  } else {
95    if (IS_UNDEFINED(desc.get)) { return desc.get }
96    // The proposal says: desc.get.call(receiver)
97    return %_CallFunction(receiver, desc.get)
98  }
99}
100
101function DerivedSetTrap(receiver, name, val) {
102  var desc = this.getOwnPropertyDescriptor(name)
103  if (desc) {
104    if ('writable' in desc) {
105      if (desc.writable) {
106        desc.value = val
107        this.defineProperty(name, desc)
108        return true
109      } else {
110        return false
111      }
112    } else { // accessor
113      if (desc.set) {
114        // The proposal says: desc.set.call(receiver, val)
115        %_CallFunction(receiver, val, desc.set)
116        return true
117      } else {
118        return false
119      }
120    }
121  }
122  desc = this.getPropertyDescriptor(name)
123  if (desc) {
124    if ('writable' in desc) {
125      if (desc.writable) {
126        // fall through
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  this.defineProperty(name, {
141    value: val,
142    writable: true,
143    enumerable: true,
144    configurable: true});
145  return true;
146}
147
148function DerivedHasTrap(name) {
149  return !!this.getPropertyDescriptor(name)
150}
151
152function DerivedHasOwnTrap(name) {
153  return !!this.getOwnPropertyDescriptor(name)
154}
155
156function DerivedKeysTrap() {
157  var names = this.getOwnPropertyNames()
158  var enumerableNames = []
159  for (var i = 0, count = 0; i < names.length; ++i) {
160    var name = names[i]
161    var desc = this.getOwnPropertyDescriptor(TO_STRING_INLINE(name))
162    if (!IS_UNDEFINED(desc) && desc.enumerable) {
163      enumerableNames[count++] = names[i]
164    }
165  }
166  return enumerableNames
167}
168
169function DerivedEnumerateTrap() {
170  var names = this.getPropertyNames()
171  var enumerableNames = []
172  for (var i = 0, count = 0; i < names.length; ++i) {
173    var name = names[i]
174    var desc = this.getPropertyDescriptor(TO_STRING_INLINE(name))
175    if (!IS_UNDEFINED(desc) && desc.enumerable) {
176      enumerableNames[count++] = names[i]
177    }
178  }
179  return enumerableNames
180}
181
182function ProxyEnumerate(proxy) {
183  var handler = %GetHandler(proxy)
184  if (IS_UNDEFINED(handler.enumerate)) {
185    return %Apply(DerivedEnumerateTrap, handler, [], 0, 0)
186  } else {
187    return ToStringArray(handler.enumerate(), "enumerate")
188  }
189}
190