github.com/goplusjs/gopherjs@v1.2.6-0.20211206034512-f187917453b8/compiler/natives/src/syscall/js/js.go (about) 1 // +build js 2 3 package js 4 5 import ( 6 "reflect" 7 "unsafe" 8 9 "github.com/gopherjs/gopherjs/js" 10 ) 11 12 type Type int 13 14 const ( 15 TypeUndefined Type = iota 16 TypeNull 17 TypeBoolean 18 TypeNumber 19 TypeString 20 TypeSymbol 21 TypeObject 22 TypeFunction 23 ) 24 25 func (t Type) String() string { 26 switch t { 27 case TypeUndefined: 28 return "undefined" 29 case TypeNull: 30 return "null" 31 case TypeBoolean: 32 return "boolean" 33 case TypeNumber: 34 return "number" 35 case TypeString: 36 return "string" 37 case TypeSymbol: 38 return "symbol" 39 case TypeObject: 40 return "object" 41 case TypeFunction: 42 return "function" 43 default: 44 panic("bad type") 45 } 46 } 47 48 func Global() Value { 49 return objectToValue(js.Global) 50 } 51 52 func Null() Value { 53 return objectToValue(nil) 54 } 55 56 func Undefined() Value { 57 return objectToValue(js.Undefined) 58 } 59 60 type Func struct { 61 Value 62 } 63 64 func (f Func) Release() { 65 f.Value = Null() 66 } 67 68 func FuncOf(fn func(this Value, args []Value) interface{}) Func { 69 return Func{ 70 Value: objectToValue(js.MakeFunc(func(this *js.Object, args []*js.Object) interface{} { 71 vargs := make([]Value, len(args)) 72 for i, a := range args { 73 vargs[i] = objectToValue(a) 74 } 75 return fn(objectToValue(this), vargs) 76 })), 77 } 78 } 79 80 type Error struct { 81 Value 82 } 83 84 func (e Error) Error() string { 85 return "JavaScript error: " + e.Get("message").String() 86 } 87 88 type Value struct { 89 v *js.Object 90 91 // inited represents whether Value is non-zero value. true represents the value is not 'undefined'. 92 inited bool 93 } 94 95 func objectToValue(obj *js.Object) Value { 96 if obj == js.Undefined { 97 return Value{} 98 } 99 return Value{obj, true} 100 } 101 102 var ( 103 id *js.Object 104 instanceOf *js.Object 105 getValueType *js.Object 106 ) 107 108 func init() { 109 if js.Global != nil { 110 id = js.Global.Call("eval", "(function(x) { return x; })") 111 instanceOf = js.Global.Call("eval", "(function(x, y) { return x instanceof y; })") 112 getValueType = js.Global.Call("eval", `(function(x) { 113 if (typeof(x) === "undefined") { 114 return 0; // TypeUndefined 115 } 116 if (x === null) { 117 return 1; // TypeNull 118 } 119 if (typeof(x) === "boolean") { 120 return 2; // TypeBoolean 121 } 122 if (typeof(x) === "number") { 123 return 3; // TypeNumber 124 } 125 if (typeof(x) === "string") { 126 return 4; // TypeString 127 } 128 if (typeof(x) === "symbol") { 129 return 5; // TypeSymbol 130 } 131 if (typeof(x) === "function") { 132 return 7; // TypeFunction 133 } 134 return 6; // TypeObject 135 })`) 136 } 137 } 138 139 func ValueOf(x interface{}) Value { 140 switch x := x.(type) { 141 case Value: 142 return x 143 case Func: 144 return x.Value 145 case TypedArray: 146 return x.Value 147 case nil: 148 return Null() 149 case bool, int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64, float32, float64, unsafe.Pointer, string, map[string]interface{}, []interface{}: 150 return objectToValue(id.Invoke(x)) 151 default: 152 panic(`invalid arg: ` + reflect.TypeOf(x).String()) 153 } 154 } 155 156 func (v Value) internal() *js.Object { 157 if !v.inited { 158 return js.Undefined 159 } 160 return v.v 161 } 162 163 func (v Value) Bool() bool { 164 if vType := v.Type(); vType != TypeBoolean { 165 panic(&ValueError{"Value.Bool", vType}) 166 } 167 return v.internal().Bool() 168 } 169 170 // convertArgs converts arguments into values for GopherJS arguments. 171 func convertArgs(args ...interface{}) []interface{} { 172 newArgs := []interface{}{} 173 for _, arg := range args { 174 v := ValueOf(arg) 175 newArgs = append(newArgs, v.internal()) 176 } 177 return newArgs 178 } 179 180 func (v Value) Call(m string, args ...interface{}) Value { 181 if vType := v.Type(); vType != TypeObject && vType != TypeFunction { 182 panic(&ValueError{"Value.Call", vType}) 183 } 184 if propType := v.Get(m).Type(); propType != TypeFunction { 185 panic("js: Value.Call: property " + m + " is not a function, got " + propType.String()) 186 } 187 return objectToValue(v.internal().Call(m, convertArgs(args...)...)) 188 } 189 190 func (v Value) Float() float64 { 191 if vType := v.Type(); vType != TypeNumber { 192 panic(&ValueError{"Value.Float", vType}) 193 } 194 return v.internal().Float() 195 } 196 197 func (t Type) isObject() bool { 198 return t == TypeObject || t == TypeFunction 199 } 200 201 func (v Value) Get(p string) Value { 202 if vType := v.Type(); !vType.isObject() { 203 panic(&ValueError{"Value.Get", vType}) 204 } 205 return objectToValue(v.internal().Get(p)) 206 } 207 208 func (v Value) Index(i int) Value { 209 if vType := v.Type(); !vType.isObject() { 210 panic(&ValueError{"Value.Index", vType}) 211 } 212 return objectToValue(v.internal().Index(i)) 213 } 214 215 func (v Value) Int() int { 216 if vType := v.Type(); vType != TypeNumber { 217 panic(&ValueError{"Value.Int", vType}) 218 } 219 return v.internal().Int() 220 } 221 222 func (v Value) InstanceOf(t Value) bool { 223 return instanceOf.Invoke(v.internal(), t.internal()).Bool() 224 } 225 226 func (v Value) Invoke(args ...interface{}) Value { 227 if vType := v.Type(); vType != TypeFunction { 228 panic(&ValueError{"Value.Invoke", vType}) 229 } 230 return objectToValue(v.internal().Invoke(convertArgs(args...)...)) 231 } 232 233 func (v Value) JSValue() Value { 234 return v 235 } 236 237 func (v Value) Length() int { 238 return v.internal().Length() 239 } 240 241 func (v Value) newWrap(args ...interface{}) (obj *js.Object, err error) { 242 defer func() { 243 ret := recover() 244 if r, ok := ret.(*js.Error); ok { 245 err = r 246 } 247 }() 248 obj = v.internal().New(convertArgs(args...)...) 249 return 250 } 251 252 func (v Value) New(args ...interface{}) Value { 253 obj, err := v.newWrap(args...) 254 if err != nil { 255 if vType := v.Type(); vType != TypeFunction { // check here to avoid overhead in success case 256 panic(&ValueError{"Value.Invoke", vType}) 257 } 258 panic(Error{objectToValue(obj)}) 259 } 260 return objectToValue(obj) 261 } 262 263 func (v Value) Set(p string, x interface{}) { 264 if vType := v.Type(); !vType.isObject() { 265 panic(&ValueError{"Value.Set", vType}) 266 } 267 v.internal().Set(p, convertArgs(x)[0]) 268 } 269 270 func (v Value) SetIndex(i int, x interface{}) { 271 if vType := v.Type(); !vType.isObject() { 272 panic(&ValueError{"Value.SetIndex", vType}) 273 } 274 v.internal().SetIndex(i, convertArgs(x)[0]) 275 } 276 277 // func (v Value) String() string { 278 // return v.internal().String() 279 // } 280 281 func (v Value) Truthy() bool { 282 return v.internal().Bool() 283 } 284 285 func (v Value) Type() Type { 286 return Type(getValueType.Invoke(v.internal()).Int()) 287 } 288 289 type TypedArray struct { 290 Value 291 } 292 293 func TypedArrayOf(slice interface{}) TypedArray { 294 switch slice := slice.(type) { 295 case []int8, []int16, []int32, []uint8, []uint16, []uint32, []float32, []float64: 296 return TypedArray{objectToValue(id.Invoke(slice))} 297 default: 298 panic("TypedArrayOf: not a supported slice") 299 } 300 } 301 302 func (t *TypedArray) Release() { 303 t.Value = Value{} 304 } 305 306 type ValueError struct { 307 Method string 308 Type Type 309 } 310 311 func (e *ValueError) Error() string { 312 return "syscall/js: call of " + e.Method + " on " + e.Type.String() 313 } 314 315 type Wrapper interface { 316 JSValue() Value 317 }