1
1
2
+ import { Constants } from "./constants" ;
3
+ import { isArray , isString } from "./global/type" ;
2
4
import { AcornNode } from "./typings" ;
3
- import { getPropInPrototypeChain } from "./utils" ;
5
+ import { legalArrayIndex , legalArrayLength } from "./utils/array" ;
6
+ import { throwException } from "./utils/error" ;
7
+ import {
8
+ bindClassPrototype ,
9
+ getPropInPrototypeChain ,
10
+ placeholderGet_ ,
11
+ placeholderSet_
12
+ } from "./utils/object" ;
4
13
5
14
// 对象构造函数
6
15
class ObjectConstructor {
@@ -10,18 +19,19 @@ class ObjectConstructor {
10
19
getter : { [ key : string ] : ( ) => any } ;
11
20
setter : { [ key : string ] : ( value : any ) => void } ;
12
21
properties : { length : number ; name ?: string ; message ?: string } ;
13
- proto : object | null ;
22
+ proto : ObjectConstructor | null ;
14
23
class : string = 'Object' ;
15
24
data : Date | RegExp | boolean | number | string | null = null ; // 值
16
25
preventExtensions ?: boolean ;
26
+ illegalConstructor ?: boolean ;
17
27
18
28
constructor ( proto ) {
19
29
this . getter = Object . create ( null ) ;
20
30
this . setter = Object . create ( null ) ;
21
31
this . properties = Object . create ( null ) ;
22
32
this . proto = proto ;
33
+ bindClassPrototype ( ObjectConstructor , this ) ;
23
34
}
24
-
25
35
toString ( ) {
26
36
if ( ! ObjectConstructor . currentInterpreter_ ) {
27
37
// Called from outside an interpreter.
@@ -93,6 +103,184 @@ class ObjectConstructor {
93
103
}
94
104
return /** @type {(boolean|number|string) } */ ( this . data ) ; // 基础类型
95
105
}
106
+ getProperty ( name : string , script ) {
107
+ if ( script . getterStep_ ) {
108
+ throw Error ( 'Getter not supported in that context' ) ;
109
+ }
110
+ name = String ( name ) ;
111
+ let obj : ObjectConstructor | null = this ;
112
+ if ( obj === undefined || obj === null ) {
113
+ throwException ( Constants . ERROR_KEYS . TypeError ,
114
+ "Cannot read property '" + name + "' of " + obj , script ) ;
115
+ }
116
+ if ( typeof obj === 'object' && ! ( obj instanceof ObjectConstructor ) ) {
117
+ throw TypeError ( 'Expecting native value or pseudo object' ) ;
118
+ }
119
+ if ( name === 'length' ) {
120
+ // Special cases for magic length property.
121
+ if ( isString ( obj ) ) {
122
+ return String ( obj ) . length ;
123
+ }
124
+ } else if ( name . charCodeAt ( 0 ) < 0x40 ) {
125
+ // Might have numbers in there?
126
+ // Special cases for string array indexing
127
+ if ( isString ( obj ) ) {
128
+ const n = legalArrayIndex ( name ) ;
129
+ if ( ! isNaN ( n ) && n < String ( obj ) . length ) {
130
+ return String ( obj ) [ n ] ;
131
+ }
132
+ }
133
+ }
134
+ while ( obj !== null ) {
135
+ if ( obj . properties && name in obj . properties ) {
136
+ const getter = obj . getter [ name ] ;
137
+ if ( getter ) {
138
+ script . getterStep_ = true ;
139
+ return getter ;
140
+ }
141
+ return obj . properties [ name ] ;
142
+ }
143
+ obj = obj . proto ;
144
+ }
145
+ return undefined ;
146
+ }
147
+ setProperty ( name , value , opt_descriptor , script ) {
148
+ const obj = this ;
149
+ if ( script ?. setterStep_ ) {
150
+ // Getter from previous call to setProperty was not handled.
151
+ throw Error ( 'Setter not supported in that context' ) ;
152
+ }
153
+ const strict = ! script ?. stateStack || script ?. getScope ( ) ?. strict ;
154
+ if ( isString ( obj ) ) {
155
+ const n = legalArrayIndex ( name ) ;
156
+ if ( name === 'length' || ( ! isNaN ( n ) && n < String ( obj ) . length ) ) {
157
+ // Can't set length or letters on String objects.
158
+ if ( strict ) {
159
+ throwException ( Constants . ERROR_KEYS . TypeError , "Cannot assign to read only " +
160
+ "property '" + name + "' of String '" + obj . data + "'" , script ) ;
161
+ }
162
+ return ;
163
+ }
164
+ }
165
+ if ( isArray ( obj ) ) {
166
+ // Arrays have a magic length variable that is bound to the elements.
167
+ const len = obj . properties . length ;
168
+ if ( name === 'length' ) {
169
+ if ( opt_descriptor && ! ( 'value' in opt_descriptor ) ) {
170
+ return ;
171
+ }
172
+ const maxLen = legalArrayLength ( opt_descriptor && 'value' in opt_descriptor ? opt_descriptor [ 'value' ] : value ) ;
173
+ if ( isNaN ( maxLen ) ) {
174
+ throwException ( Constants . ERROR_KEYS . RangeError , 'Invalid array length' , script ) ;
175
+ }
176
+ if ( maxLen < len ) {
177
+ for ( let key in obj . properties ) {
178
+ const index = legalArrayIndex ( key ) ;
179
+ if ( ! isNaN ( index ) && index >= maxLen ) {
180
+ delete obj . properties [ index ] ;
181
+ }
182
+ }
183
+ }
184
+ } else {
185
+ // Increase length if this index is larger.
186
+ const index = legalArrayIndex ( name ) ;
187
+ if ( ! isNaN ( index ) ) {
188
+ obj . properties . length = Math . max ( len , index + 1 ) ;
189
+ }
190
+ }
191
+ }
192
+ if ( obj . preventExtensions && ! ( name in obj . properties ) ) {
193
+ if ( strict ) {
194
+ throwException ( Constants . ERROR_KEYS . TypeError , "Can't add property '" + name +
195
+ "', object is not extensible" , script ) ;
196
+ }
197
+ return ;
198
+ }
199
+ if ( opt_descriptor ) {
200
+ if ( opt_descriptor && ( 'get' in opt_descriptor || 'set' in opt_descriptor ) &&
201
+ ( 'value' in opt_descriptor || 'writable' in opt_descriptor ) ) {
202
+ throwException ( Constants . ERROR_KEYS . TypeError , 'Invalid property descriptor. ' +
203
+ 'Cannot both specify accessors and a value or writable attribute' , script ) ;
204
+ }
205
+ // Define the property.
206
+ const descriptor = { } ;
207
+ if ( 'get' in opt_descriptor && opt_descriptor [ 'get' ] ) {
208
+ obj . getter [ name ] = opt_descriptor [ 'get' ] ;
209
+ descriptor [ 'get' ] = placeholderGet_ ;
210
+ }
211
+ if ( 'set' in opt_descriptor && opt_descriptor [ 'set' ] ) {
212
+ obj . setter [ name ] = opt_descriptor [ 'set' ] ;
213
+ descriptor [ 'set' ] = placeholderSet_ ;
214
+ }
215
+ if ( 'configurable' in opt_descriptor ) {
216
+ descriptor [ 'configurable' ] = opt_descriptor [ 'configurable' ] ;
217
+ }
218
+ if ( 'enumerable' in opt_descriptor ) {
219
+ descriptor [ 'enumerable' ] = opt_descriptor [ 'enumerable' ] ;
220
+ }
221
+ if ( 'writable' in opt_descriptor ) {
222
+ descriptor [ 'writable' ] = opt_descriptor [ 'writable' ] ;
223
+ delete obj . getter [ name ] ;
224
+ delete obj . setter [ name ] ;
225
+ }
226
+ if ( 'value' in opt_descriptor ) {
227
+ descriptor [ 'value' ] = opt_descriptor [ 'value' ] ;
228
+ delete obj . getter [ name ] ;
229
+ delete obj . setter [ name ] ;
230
+ } else if ( value !== Constants . VALUE_IN_DESCRIPTOR ) {
231
+ descriptor [ 'value' ] = value ;
232
+ delete obj . getter [ name ] ;
233
+ delete obj . setter [ name ] ;
234
+ }
235
+ try {
236
+ Object . defineProperty ( obj . properties , name , descriptor ) ;
237
+ } catch ( e ) {
238
+ throwException ( Constants . ERROR_KEYS . TypeError , 'Cannot redefine property: ' + name , script ) ;
239
+ }
240
+ // Now that the definition has suceeded, clean up any obsolete get/set funcs.
241
+ if ( 'get' in opt_descriptor && ! opt_descriptor [ 'get' ] ) {
242
+ delete obj . getter [ name ] ;
243
+ }
244
+ if ( 'set' in opt_descriptor && ! opt_descriptor [ 'set' ] ) {
245
+ delete obj . setter [ name ] ;
246
+ }
247
+ } else {
248
+ // Set the property.
249
+ if ( value === Constants . VALUE_IN_DESCRIPTOR ) {
250
+ throw ReferenceError ( 'Value not specified' ) ;
251
+ }
252
+ // Determine the parent (possibly self) where the property is defined.
253
+ let defObj : ObjectConstructor | null = obj ;
254
+ while ( ! ( name in defObj . properties ) ) {
255
+ defObj = defObj . proto ;
256
+ if ( ! defObj ) {
257
+ // This is a new property.
258
+ defObj = obj ;
259
+ break ;
260
+ }
261
+ }
262
+ if ( defObj . setter && defObj . setter [ name ] ) {
263
+ script . setterStep_ = true ;
264
+ return defObj . setter [ name ] ;
265
+ }
266
+ if ( defObj . getter && defObj . getter [ name ] ) {
267
+ if ( strict ) {
268
+ throwException ( Constants . ERROR_KEYS . TypeError , "Cannot set property '" + name +
269
+ "' of object '" + obj + "' which only has a getter" , script ) ;
270
+ }
271
+ } else {
272
+ // No setter, simple assignment.
273
+ try {
274
+ obj . properties [ name ] = value ;
275
+ } catch ( _e ) {
276
+ if ( strict ) {
277
+ throwException ( Constants . ERROR_KEYS . TypeError , "Cannot assign to read only " +
278
+ "property '" + name + "' of object '" + obj + "'" , script ) ;
279
+ }
280
+ }
281
+ }
282
+ }
283
+ }
96
284
} ;
97
285
98
286
// 作用域构造函数
0 commit comments